Spring Batch - Bean Inheritance with JdbcCursorItemReader - java

I'm facing a problem with Spring Batch, which is normally pretty simple.
I have multiple steps, chunk processing oriented. For every steps, I want to use the same ItemReader.
<bean id="myItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql"
value="select * from ...."/>
<property name="rowMapper">
<bean class="MyRowMapper" />
</property>
</bean>
And here is my step declaration :
<step id="mySpecificStep">
<tasklet>
<chunk reader="mySpecificItemReader"
writer="myItemWriter"
commit-interval="${commit-interval}"
skip-policy="skipPolicy">
</chunk>
</tasklet>
</step>
My request have some parameters, which I want to replace with a PreparedStatementSetter.
So my intention was to create multiples beans (for every steps), in order to call a specific PreparedStatement, for each bean.
<bean id="mySpecificItemReader" parent="myItemReader" >
<property name="preparedStatementSetter" ref="mySpecificStatement"/>
</bean>
<bean id="mySpecificStatement" class="PreparedStatementSpecificProduct">
<property name="product" value="XXX" />
</bean>
I have the following error :
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'preparedStatementSetter' of bean class [org.springframework.aop.scope.ScopedProxyFactoryBean]: Bean property 'preparedStatementSetter' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
What am I doing wrong ? Let me know if you need more details.
Thank you for your help.

Ok, I finally made it work ! Thanks to #M. Deinum, I used abstract on my declaration. Here is how I did it :
<bean id="myItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step" abstact="true">
<bean id="mySpecificItemReader" parent="myItemReader" scope="step">
<property name="preparedStatementSetter">
<bean id="mySpecificStatement" class="PreparedStatementSpecificProduct">
<property name="product" value="XXX" />
</bean>
</property>
</bean>
Thank you everyone for your help !

Related

org.springframework.beans.NotWritablePropertyException. Invalid property <blah> is not writable or has an invalid setter method

I have the requirement to remove all passwords and encryption keys from the source code of my project. I'm struggling to get this to work in my spring-servlet.xml file.
This worked before I tried making the change:
the database username, encrypted password, URL and driver were defined in a file jdbc_server.properties as a classpath resource.
the encryption/decryption key was passed on start-up as -DENCRYPTION_PASSWORD=
I want to move the jdbc_server_properties file to the filesystem and include the key in the file.
This is my last (of at least 25) attempt at getting this to work.
<bean id="propertyConfigurer" class="org.jasypt.spring4.properties.EncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="standardEncryptor" />
<property name="jdbcUsername" value="JDBC_USERNAME" />
<property name="jdbcPassword" value="JDBC_PASSWORD" />
<property name="jdbcUrl" value="JDBC_URL" />
<property name="jdbcDriver" value="JDBC_DRIVER" />
<property name="locations">
<list>
<value>file:///${USERPROFILE}/credentials/jdbc_server.properties</value>
</list>
</property>
</bean>
<bean id="standardEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config" ref="environmentVariablesConfiguration" />
</bean>
<bean id="environmentVariablesConfiguration" class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndDES" />
<property name="passwordSysPropertyName" value="ENCRYPTION_PASSWORD" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="${jdbcDriver}" p:url="${jdbcUrl}"
p:username="${jdbcUsername}" p:password="${jdbcPassword}">
</bean>
Using the above configuration, I get a NotWritablePropertyException exception. I've seen tons of posts on this issue but not where both the properties and encryption key are in a file on the filesystem.
When this was working and the properties were read from a file in the classpath, there were no getters/setters for jdbcUsername (or the other properties) so I don't know why it's failing in this way now.
I tried adding the getters and setters (as String) to my BaseDaoImpl class but I still get the same error so if I'm supposed to add them, I'm not sure where they go.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'propertyConfigurer' defined in ServletContext resource [/WEB-INF/spring-servlet.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'jdbcUsername' of bean class [org.jasypt.spring4.properties.EncryptablePropertyPlaceholderConfigurer]: Bean property 'jdbcUsername' 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.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1568)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1276)
at ...
That is probably because jdbcUsername is not a property of EncryptablePropertyPlaceholderConfigurer.
Please, try something like this (just remove the jdbc* properties from the EncryptablePropertyPlaceholderConfigurer configuration, and use it directly in your dataSource bean):
<bean id="propertyConfigurer" class="org.jasypt.spring4.properties.EncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="standardEncryptor" />
<property name="locations">
<list>
<value>file:///${USERPROFILE}/credentials/jdbc_server.properties</value>
</list>
</property>
</bean>
<bean id="standardEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config" ref="environmentVariablesConfiguration" />
</bean>
<bean id="environmentVariablesConfiguration" class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndDES" />
<property name="passwordSysPropertyName" value="ENCRYPTION_PASSWORD" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="${JDBC_DRIVER}" p:url="${JDBC_URL}"
p:username="${JDBC_USERNAME}" p:password="${JDBC_PASSWORD}">
</bean>
Assuming your jdbc_server.properties file contains the required information:
JDBC_USERNAME=ENC(...)
JDBC_PASSWORD=ENC(...)
JDBC_URL=ENC(...)
JDBC_DRIVER=your.jdbc.driver

Spring config properties from database and properties

I asked a similar question, but based on the responses, I did a bad job describing what I am after. I have a spring 4 webapp that loads properties from a properties file. We consume those properties both via the "${proper.name"} expressions in spring, as well as by injecting a properties object into some of our classes.
We want to move most of the properties to a database table and make them reloadable. However, a few need to stay in local properties, potentially overriding the database setting. These should also be loaded dynamically after the app is running.
I know that once a particular bean is injected, it won't get reloaded, that doesn't concern me, it's up to that module to handle that. But I am having trouble getting the behavior I want. In particular, I have implemented an AbstractConfiguration from apache commons configuration to get the dual source and overriding I am after. But while it works for injecting the properties object, expressions loaded with "${prop.name}" don't work at all.
How can I get them to work? Did I override the wrong thing? Is it just some config detail?
<bean id="sysProperties" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="databaseConfigurator" />
<property name="targetMethod" value="getProperties"/>
</bean>
<bean id="databaseConfigurator" class="my.util.config.MyDatabaseConfigurator">
<property name="datasource" ref="dataSource" />
<property name="propertyFile" value="/WEB-INF/my.properties" />
<property name="applicationName" value="ThisApp" />
</bean>
<bean id="dbConfigFactory" class="org.apache.commons.configuration.ConfigurationConverter" factory-method="getProperties">
<constructor-arg ref="databaseConfigurator" />
</bean>
I haven't tested this, but I think it might work.
<bean id="sysProperties" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="databaseConfigurator" />
<property name="targetMethod" value="getProperties"/>
</bean>
<bean id="databaseConfigurator" class="my.util.config.MyDatabaseConfigurator">
<property name="datasource" ref="dataSource" />
<property name="propertyFile" value="/WEB-INF/my.properties" />
<property name="applicationName" value="ThisApp" />
</bean>
<bean name="PropertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="CommonsConfigurationFactoryBean"/>
</bean>
<bean name="CommonsConfigurationFactoryBean" class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
<constructor-arg ref="databaseConfigurator"/>
</bean>

How to reference bean property values?

Here's what I am trying to do. I have a spring batch job which triggers a bean with multiple properties. I want these properties to be divided into separate beans for organizational purposes.
So I currently have this:
<bean id="runSQL" class="Tasklet"
scope="step">
<property name="sourceSQL"
value="SQL STATEMENT HERE" />
<property name="targetSQL"
value="SQL STATEMENT HERE"></property>
<property name="filePath" value="#{jobParameters['OUTPUT.FILE.PATH']}"> </property>
</bean>
but I basically want this (not working due to lack of class definition and I don't know if #{souce.sourceSQL} is a valid way of grabbing bean properties):
<bean id="runSQL" class="Tasklet"
scope="step">
<property name="sourceSQL"
value="#{source.sourceSQL}" />
<property name="targetSQL"
value="#{target.targetSQL}"></property>
<property name="filePath" value="#{jobParameters['OUTPUT.FILE.PATH']}"> </property>
</bean>
<bean id="sourceSQL" class="Class not needed"
scope="step">
<property name="sourceSQL"
value="SQL STATEMENT HERE" />
</property>
</bean>
<bean id="targetSQL" class="Class not needed"
scope="step">
<property name="sourceSQL"
value="SQL STATEMENT HERE" />
</property>
</bean>
I have tried to reference the beans traditionally with
<ref bean="someBean"/>
but my Tasklet isn't designed to receive a bean, just the property values and I would prefer to leave the Tasklet as is. How do I get around this or alternative ways of storing this data for the beans?
You're on the right track with #{...}. If you want to reference a bean, stick a # in front of the Spring bean ID, for example #{#sourceSQL.sourceSQL} and #{#targetSQL.sourceSQL}.
See the documentation for the Spring Expression language.

Is there a shorthand for creating a String constant in Spring context XML file?

I need to define a string value in Spring context XML file that is shared by multiple beans.
This is how I do it:
<bean id="aSharedProperty" class="java.lang.String">
<constructor-arg type="java.lang.String" value="All beans need me :)"/>
</bean>
Creating a java.lang.String bean by passing a constructor argument of java.lang.String seems kludgy.
Is there a shortcut?
I know this property can be passed using PropertyOverrideConfigurer, but I want to keep this property within the XML file.
You can use PropertyPlaceholderConfigurer and keep values in xml:
<context:property-placeholder properties-ref="myProperties"/>
<bean id="myProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="aSharedProperty">All beans need me :)</prop>
</props>
</property>
</bean>
Then you reference it with:
<bean id="myBean" class="my.package.MyClass">
<property name="someField" value="${aSharedProperty}"/>
</bean>
A shorthand to the solution proposed by mrembisz goes like this:
<context:property-placeholder properties-ref="myProperties"/>
<util:properties id="myProperties">
<prop key="aSharedProperty">All beans need me :)</prop>
</util:properties>
You may be able to use the following:
<bean id="abstractParent" abstract="true">
<property name="sharedProperty" value="All child beans need me" />
</bean>
<bean id="bean1" class="MyClass1" parent="abstractParent">
...non-shared properties...
</bean>
<bean id="bean2" class="MyClass2" parent="abstractParent">
...non-shared properties...
</bean>
However, that relies on the property having the same name, so may not be applicable for you.
Something I've used in the past is SpEL to make sure that a bean has the same value as another:
<bean id="myBean" class="xxx.yyy.Foo" >
<property name="myProperty" value="1729" />
</bean>
<bean id="copyCat" class="xxx.yyy.Bar" >
<property name="anotherProperty" value="#{myBean.myProperty}" />
</bean>
I have found this to be particularly useful when setting the value did something other than a simple assignment.

Upgrading to springframework.scheduling.concurrent?

As of Spring 3.0 the ScheduledTimerTask is deprecated and I can't understand how to upgrade to org.springframework.scheduling.concurrent.
<bean id="timerFactoryBean" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref bean="onlineTimeSchedule" />
</list>
</property>
</bean>
<bean id="onlineTimeSchedule" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="timerTask" class="com.example.OnlineTimerTask" />
</property>
<property name="period" value="60000" />
<property name="delay" value="1000" />
</bean>
Where the OnlineTimerTask extends java.util.TimerTask. It's simple task which publishes a message to publisher every minute. I checked the documentation, but nothing.. I can't understand which way to use from the concurrent package and which suits the best.
Also I want to turn this xml into #Bean in Java.
EDIT: So I tried to implement the xml with #Bean and #Configuration instead and here is what I got.
#Configuration
public class ContextConfiguration {
#Bean
public ScheduledExecutorFactoryBean scheduledExecutorFactoryBean() {
ScheduledExecutorFactoryBean scheduledFactoryBean = new ScheduledExecutorFactoryBean();
scheduledFactoryBean.setScheduledExecutorTasks(new ScheduledExecutorTask[] {onlineTimeSchedule()});
return scheduledFactoryBean;
}
#Bean
public ScheduledExecutorTask onlineTimeSchedule() {
ScheduledExecutorTask scheduledTask = new ScheduledExecutorTask();
scheduledTask.setDelay(1000);
scheduledTask.setPeriod(60000);
scheduledTask.setRunnable(new OnlineTimerTask());
return scheduledTask;
}
}
Will the code above be correct replacement for xml? Will in my case the setScheduledExecutorTasks work properly? I mean will the referencing to the same bean instance, if onlineTimeSchedule() is called more than once, will work here?
scheduledFactoryBean.setScheduledExecutorTasks(new ScheduledExecutorTask[] {onlineTimeSchedule()});
Use org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean in place of org.springframework.scheduling.timer.TimerFactoryBean and use org.springframework.scheduling.concurrent.ScheduledExecutorTask in place of org.springframework.scheduling.timer.ScheduledTimerTask. You will need to adjust the property names and values as needed but, that should be pretty self evident.
Optionally, you could refactor your com.example.OnlineTimerTask to not extend java.util.TimeTask as the ScheduledTimerTask only requires a runnable.
Spring 4 configuration - Below configuration working after spring migration from 3.2.x to 4.6.x
<bean id="schedulerTask"
class="org.springframework.scheduling.support.MethodInvokingRunnable">
<property name="targetObject" ref="springJmsListnerContainer" />
<property name="targetMethod" value="execute" />
</bean>
<bean id="timerTask" class="org.springframework.scheduling.concurrent.ScheduledExecutorTask">
<property name="runnable" ref="schedulerTask" />
<property name="delay" value="100" />
<property name="period" value="60000" />
</bean>
<bean class="org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean">
<property name="scheduledExecutorTasks">
<list>
<ref bean="timerTask" />
</list>
</property>
</bean>
The answer is - add one "runnable" field
<bean id="scheduledExecutorTask"
class="org.springframework.scheduling.concurrent.ScheduledExecutorTask">
<!-- wait 10 milli seconds before starting repeated execution -->
<property name="delay">
<value>10</value>
</property>
<!-- run every 1 second -->
<property name="period">
<value>1000</value>
</property>
<property name="runnable">
<ref bean="checkInvokingTask"/>
</property>
</bean>

Categories

Resources