How to I pass hardcoded SQL queries into a JdbcBatchItemWriter? - java

Right now I have this:
<bean id="hardcodedQuery"
class="org.springframework.batch.item.database.JdbcBatchItemWriter">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
DELETE from GENERIC_TABLE
WHERE id = 999
]]>
</value>
</property>
<property name="itemSqlParameterSourceProvider">
<bean
class="org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider" />
</property>
</bean>
But I keep getting the java.lang.IllegalArgumentException, saying that my SQL query is paramaterized. I know they want me to put some reference to the reader and have :randomVariable in the command, but I adamantly oppose this apparent requirement. Am I mistaken and is there a workaround? Or do I have to use a completely different class for my bean?
This is the error message:
Caused by: java.lang.IllegalArgumentException: Using SQL statement with '?' placeholders requires an ItemPreparedStatementSetter
Thanks!

The short answer is that this class isn't meant for what you're trying to do. The ItemWriter contract assumes item based processing which would mean that you need to parameterize the SQL for each item. If you want to run just a delete statement, use a Tasklet implementation that uses the JdbcTemplate.

Related

Spring-security creates malformed ldap-query

I am trying to get a Single-signon solution working, which has previously worked with Novell eDirectory as an LDAP-provider. Now, I am trying to get it to work with OpenLDAP. In the signon-process, one of the steps is retrieving the actual user from the LDAP. This is done by Spring-security, but it seems to have a bug.
For getting the user, LdapUserDetailsService#loadUserByUsername is called. This uses a predefined LdapUserSearch to find a user with the given username. In my case, the configuration looks something like this:
base:ou=something,ou=somethingElse,dc=oh,dc=my,dc=god
filter:cn={0}
where {0} is replaced with the actual username. So far this works, and the user is retrieved, in the form of userdata returned by FilterBasedLdapUserSearch#searchForUser. But, the userdata does not separate between dn and base, so it has:
base: (empty)
dn:cn=someUsername,ou=something,ou=somethingElse,dc=oh,dc=my,dc=god
This userdata-object is in turn used to retrieve the authorities for the user, so it the tries to do another search with the base above, and the dn as filter. But, this query fails, since OpenLDAP does not allow any queries with an empty string as base.
I have tried with 3.1.3.RELEASE, 3.1.7.RELEASE and 3.2.7.RELEASE, all have the same issue.
Is this a bug in spring-security? Or am I doing something wrong here?
Managed to fix it, by moving away from the "template" way of defining the ldap user service:
Before:
<sec:ldap-user-service id="ldapUserDetailsService"
server-ref="contextSource"
user-search-filter="${my.ldap.filter}"
user-search-base="${my.ldap.base}"
user-context-mapper-ref="myUserDetailsContextMapper"/>
After:
<bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value="${my.ldap.base}"/>
<constructor-arg index="1" value="${my.ldap.filter}"/>
<constructor-arg index="2" ref="contextSource"/>
</bean>
<bean id="ldapAuthoritiesPopulator" class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource"/>
<constructor-arg value="${my.ldap.usersearch.base}"/>
<property name="groupSearchFilter" value="${my.group.filter}" />
<property name="groupRoleAttribute" value="${my.group.attribute}" />
<property name="searchSubtree" value="true" />
</bean>
<bean id="ldapUserDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
<constructor-arg ref="userSearch"/>
<constructor-arg ref="ldapAuthoritiesPopulator"/>
</bean>
Don't ask me how or why this works..

Spring Batch - more than one writer based on field value

I am working on spring batch, for writer currently using FlatFileItemWriter.
I would like to write my input file content to more than one flat file based on some field value. Is Spring batch support any kind of functionality by default.[something similar to CompositeItemWriter]
For example, my input file content is something like this.
john,35,retail,10000
joe,34,homeloan,20000
Amy,23,retail,2000
Now i would like to write two different files based on third column, it means row 1 and row 3 should go to file1 and row 2 should go to file2.
My writer configuration is:
<bean id="fileWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:C:/output.dat"/>
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="|" />
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="field1,field2...." />
</bean>
</property>
</bean>
</property>
</bean>
Take a look at the ClassifierCompositeItemWriter. This ItemWriter implementation allows you to define a Classifier that chooses which of the defined delegate ItemWriter instances to delegate to. In your case, you'd create a Classifier that decided based on field4 and delegate the writing to the appropriate instance of the FlatFileItemWriter.
You can read more about the ClassifierCompositeItemWriter in the documentation here: http://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/item/support/ClassifierCompositeItemWriter.html
Use a ClassifierCompositeItemWriter
Calls one of a collection of ItemWriters for each item, based on a
router pattern implemented through the provided Classifier.
Router pattern is based on bean content

What is the most Spring-like way to wire a String property to another bean's method?

I've got two beans, and I'd like to wire the String property of one bean to the String value of the property of another (which is computed from the environment of the program). What is the best way to wire these together?
The best solution I can think of is:
<bean id="thisBean">
<property name="foo">
<bean factory-bean="otherBean" factory-method="getStringForThisBean" />
</property>
</bean>
There are other ways to do it, like using SpringEL or working inside Spring's property system, but this is the easiest. It just doesn't feel right creating a heavy-weight bean to store a String value.
Is there a better way?
I feel Spring-EL would be a much better fit, it would be this way with Spring-EL:
<bean id="thisBean">
<property name="foo" value="#{otherbean.property}"/>
</bean>

Concatenate strings within a Spring XML configuration file?

I have a String value in a Spring configuration file that comes to be as the result of a JNDI lookup -- it happens to be a path name:
<jee:jndi-lookup id="myAppHomeDir" jndi-name="myAppHomeDir" />
Now I need to concatenate on to the end of this path another string and hand it off to another Spring bean as follows (which of course doesn't work):
<bean id="LogPath" class="org.mystuff.initBean">
<property name="logDirectory">
<jee:jndi-lookup id="myAppHomeDir"
jndi-name="myAppHomeDir" /> + "/logs"
</property>
</bean>
Is there a simple way to do this without me having to write a utility class in Java?
Try using Spring EL (expression language). I would try the following (not tested):
<jee:jndi-lookup id="myAppHomeDir" jndi-name="myAppHomeDir" />
<bean id="LogPath" class="org.mystuff.initBean">
<property name="logDirectory" value="#{myAppHomeDir+'/logs'}"/>
</bean>
Not quite sure if it would work. The thing that troubles me is the cast from File (I guess) to String when concatenating. So if the previous one didn't work, I would try:
#{myAppHomeDir.canonicalPath+'/logs'}
Let us know if it works.

conditional beans using spring

I am trying to write a ValidatorFactory which will give me a validator based on its type
public Validator getNewValidator(ValidatorType type){
switch:
case a : new Validator1();
break;
case b : new Validator2();
break;
}
I want to write using spring xml beans definition
I can use method injection but it will let me create only one object and the method does
not take any arguments.
I don't want to use FactoryBean.. I am just looking whether we can do this using spring xml
bean definition.
you can do conditional bean injection with plain xml. The "ref" attribute can be triggered by property values from a property file and thus create conditional beans depending on property values. This feature is not documented but it works perfect.
<bean id="validatorFactory" class="ValidatorFactory">
<property name="validator" ref="${validatorType}" />
</bean>
<bean id="validatorTypeOne" class="Validator1" lazy-init="true" />
<bean id="validatorTypeTwo" class="Validator2" lazy-init="true" />
And the content of the property file would be:
validatorType=validatorTypeOne
To use the property file in your xml just add this context to the top of your spring config
<context:property-placeholder location="classpath:app.properties" />
For complex cases (more complex than the one exposed), Spring JavaConfig could be your friend.
If you are using annotation (#Autowired, #Qualifier etc) instead of xml, you are not able to make conditional beans work (at least at current version 3). This is due to #Qualifier does not support expression
#Qualifier(value="${validatorType}")
More information is at https://stackoverflow.com/a/7813228/418439
I had an slightly different requirements. In my case I wanted to have encoded password in production but plain text in development. Also, I didn't have access to parent bean parentEncoder. This is how I managed to achieve that:
<bean id="plainTextPassword" class="org.springframework.security.authentication.encoding.PlaintextPasswordEncoder"/>
<bean id="shaPassword" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
<constructor-arg type="int" value="256"/>
</bean>
<bean id="parentEncoder" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource">
<bean class="org.springframework.aop.target.HotSwappableTargetSource">
<constructor-arg ref="${password.encoding}Password"/>
</bean>
</property>
</bean>
Of course, I defined password.encoding in a property file with possible values as sha or plainText.
You should be able to do this:
<bean id="myValidator" factory-bean="validatorFactory" factory-method="getNewValidator" scope="prototype">
<constructor-arg><ref bean="validatorType"/></constructor-arg>
</bean>
<bean id="validatorType" ... />
Of course, it uses an automatically configured FactoryBean underneath but you avoid any Spring dependency in your code.

Categories

Resources