Dynamically switch ItemProcessor based on JobParameters - java

Following is the code sample of a reader bean
<bean id="reader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql"
value="#{#sqlStatements[jobParameters['key']]}" />
<property name="rowMapper" ref="#{jobParameters['key'] + 'Mapper'}" />
</bean>
There is <util:map id="sqlStatements"/>. I have a similar bean for writer. I want to have a dynamic switcher for processor. The interface ItemProcessor<I,O> needs I & O to be mentioned, either I must switch it in some way or create a custom ItemProcessor.
I tried the below code but it didn't work.
<batch:job id="springBatch">
<batch:step id="step1">
<batch:tasklet>
<batch:chunk reader="reader" processor="#{jobParameters['key'] + 'Processor'}"
writer="writer" commit-interval="1"></batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
It gave a org.springframework.expression.spel.SpelEvaluationException for jobParameters, probably because scope=step was not defined for it.
Could someone please provide an alternative for this ?

Using a Classifier can simplify a lot resolution of this problem: create a custom ItemProcessor<Object, Object> and inject a Classifier<Class, ItemProcessor> (a PatternMatchingClassifier can fits) and in ItemProcessor.process() detect right processor using classifier.
Also look at ClassifierCompositeItemWriter.

Not sure if I follow your question completely but one simple solution can be creating a custom processor as delegator. This delegator will delegate processor code to relevant Item Processor class based on job parameters key or whatever logic you want.

Related

How do you know the actually value for a property set by a map of beans on java?

Ive found this configuration on a applicationContext xml and I still dont understand it (even if I found info about it, is not clear) could someone give me an idea of how java chooses the right bean for its property? I did not find this bean in other file of the project context, seems like is "magic"
<bean id="mailCreator" class="com.project.ConfigurableXferEmailCreator">
<property name="mailCreatorMap">
<util:map id="mailCreatorMap" key-type="java.lang.String"
value-type="com.project.BaseMailCreator">
<entry>
<key>
<util:constant static-field="com.project.creatorType.TYPE1"/>
</key>
<bean class="com.project.creator1" parent="baseCreator">
<property name="service" ref="someService1" />
</bean>
</entry>
<entry>
<key>
<util:constant static-field="com.project.creatorType.TYPE2"/>
</key>
<bean class="com.project.creator1" parent="baseCreator">
<property name="service" ref="someService2" />
</bean>
</entry>
.... and so on
I really have no idea how java recognizes which one will use, I just know it uses the right service but I dont see where is being specifically set, could someone give me a hand?
I checked couple of sites like this but still no idea , does it call all services??
http://www.java2s.com/Tutorials/Java/Spring/0140__Spring_Map_Properties.htm
Your question:
"how java chooses the right bean for its property"
I assume you mean for example this line:
<property name="service" ref="someService1" />
if you do not see another XML bean element that defines "someService1"
fr example:
<bean id="someService1" class="com.project.SomeService1Impl">
Then it exists in the Java Code
try to find an annotation like below
#Service("someService1")
public class SomeService1Impl {
...
How?:
Spring loads "someService1" in the Spring Context on initialization , so the XML can reference it.

Spring batch Unique name for mutiple file writer

I am using spring batch in my application. I am using multithreading using spring batch at step level .
Below is my configuration:
<step id="testStep">
<tasklet
task-executor="taskExecutor" throttle-limit="5">
<chunk reader="reader" writer="writer"
commit-interval="10">
</chunk>
</tasklet>
</step>
<bean id="writer" class="org.springframework.batch.item.file.FlatFileItemWriter"
scope="step">
<property name="resource" value="file:${output.file.location.directory}/<<<NEED TO PASS CURRENT EXECUTING THREAD NAME>>>" />
<property name="appendAllowed" value="true"></property>
<property name="lineAggregator" ref="aggregator" />
</bean>
So in resource property of flatfileitemWriter , i need to pass current executing thread name, so that each thread wil write in a separate unique file.
I can able to get the current executing thread name in writer class but dont know how to get the same in the spring confirguration file .
Any idea to do so ?
If you want you can use spEL; with this scripting language you can use standard java method and to retrive current thread name the syntax is #{T(java.lang.Thread).getCurrentThread().getName()}.
In a more clear way use a StepExecutionListener.beforeStep() injecting the current thread name into step execution context (eg. currentThreadName key) and retrieve from step xml config in the standard way
<property name="resource" value="file:${output.file.location.directory}/#{stepExecutionContext['currentThreadName']}" />

Conditional initialization of classes in spring

I have a service which refers to a single source.
<bean id="XYZService" class="com.services.impl.DataService1">
<constructor-arg ref="DataSource1" />
</bean>
<bean id="DataSource1" class="com.source.impl.DataSource1">
<constructor-arg ref="DBDataSource"/>
<constructor-arg value="xyz"/>
</bean>
<bean id="DataSource2" class="com.source.impl.DataSource2">
<constructor-arg ref="MsgDataSource"/>
<constructor-arg value="xyz"/>
</bean>
Now if i want to perform a conditional check and my service should be able listen to particular source based on a input variable something like below.
<bean id="XYZService" class="com.services.impl.DataService1">
<constructor-arg ref=" $VARIABLE == true ? DataSource1 : DataSource2" />
</bean>
I did tried SPEL however no luck. I am beginner in spring. Any help will be appreciated.
Thanks.
There are many solutions. Here are two: You can use profiles for this. Define two profiles, define the DataSource beans with the same name but different profiles. (docs)
Alternatively, you can use a single bean and a static factory method (docs).
<bean id="DataSource" class="com.source.impl.DataSourceFactory"
factory-method="createInstance"/>
Inside of DataSourceFactory.createInstance(), you can check the flag and then create the correct data source in plain Java.
The latter is a bit easier to understand, IMO. Using profiles allows you to keep everything in XML (but you should really consider switching to the Java Configuration). The drawback with profiles is that you must not forget to activate at least one of the bean won't be defined.
A third option is to use three XML files and then modify the list of XML files that should be parsed when you pass it to the ApplicationContext. But that only works if you have control over this part of the code.
Assuming you are using Spring 3.1 or later, Spring Profiles may be the best solution.
Using the example of Production and Dev/QA environments, common bean declarations go in a shared file
<beans>
<bean id="XYZService" class="com.services.impl.DataService1">
<constructor-arg ref="DataSource" />
</bean>
</beans>
A separate configuration contains production references
<beans profiles="prod">
<bean id="DataSource" class="com.source.impl.DataSource1">
<constructor-arg ref="DBDataSource"/>
<constructor-arg value="xyz"/>
</bean>
</beans>
Another contains dev references
<beans profile="dev">
<bean id="DataSource" class="com.source.impl.DataSource2">
<constructor-arg ref="MsgDataSource"/>
<constructor-arg value="xyz"/>
</bean>
</beans>
To activate the given profile add -Dspring.profiles.active=prod to your JVM arguments
You can find more info here
Another approach uses factory methods.
<bean id="DataSource" class="com.source.impl.DataSourceFactory" factory-method="getInstance">
<constructor-arg value="#{VARIABLE}" />
</bean>
The above fragment assumes that you want your factory method to explcitly invoke the constructor of each of your services. If you dead set on using Spring to create the instances you can pass each datasource implementation as constructor arguments and use the constructor method as a simple dispatcher.
You need something like this:
<constructor-arg
ref="#{systemProperties.variable == 'true' ? 'DataSource1' : 'DataSource2'}" />
where "variable" is set like -Dvariable=true.

Setting a properties file in Spring Batch via a JobParameter

I have three different .properties files in a Spring Batch project, and I'm trying to set which .properties file should be used as a JobParameter. I'd like to be able to run the job like so:
java CommandLineJobRunner context.xml jobName region=us
The region should specify which .properties file should be used. The problem is getting the context to recognize the JobParameter. I've tried the following to no avail:
<context:property-placeholder location="classpath:batch.#{jobParameters['region']}.properties"/>
And also:
<util:properties id="batchProperties" location="classpath:batch.#{jobParameters['region']}.properties"></util:properties>
I had heard that adding scope="step" could fix similar issues, but I tried adding that to both of the above solutions and still had exceptions.
I think I'm missing a fundamental idea of why I can't get this working, but I'm unable to figure out what that idea is.
If anyone has any suggestions on getting this working and/or explaining why my previous approaches failed, I'd appreciate it.
This is not the right way to proceed (it is impossible do what you are trying to do).
You have to think that jobParameters is available only when a job is running and only for its composing steps marked with scope="step" (and not <context:property-placeholder> nor <util:properties> has a step attribute).
A way for solving the problem is to load properties file in job's execution context before first step is running with a listener:
public class PropertyLoaderJobExecutionListener extends StepExecutionListenerSupport {
Properties countryProperties;
public void setCountryProperties(Properties pfb) {
this.countryProperties = pfb;
}
#Override
public void beforeStep(StepExecution stepExecution) {
super.beforeStep(stepExecution);
// Store property file content in jobExecutionContext with name "batchProperties"
stepExecution.getJobExecution().getExecutionContext().put("batchProperties", countryProperties);
}
}
in your job.xml
<bean id="loadPropertiesListener" class="PropertyLoaderJobExecutionListener" scope="step">
<property name="pfb">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:batch.#{jobParameters['region']}.properties" />
</bean>
</property>
</bean>
and register this listener in your first step (you can't do that in your JobExectionListsner.beforeJob() because there isn't a scope="job" for now and late-binding of #{jobParameters['region']} value is not available).
To access your data with spEL use this syntax:
#{jobExecutionContext.get('batchProperties').getProperty('language')}
or a better syntax to access properties (IDK spEL so good, sorry).
Hope to be clear and can help to solve your problem.
EDIT (full code of my working job.xml):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-util-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<job id="sourceJob" xmlns="http://www.springframework.org/schema/batch">
<step id="step1">
<tasklet ref="getRemoteFileTasklet" />
<listeners>
<listener ref="loadPropertiesListener" />
</listeners>
</step>
</job>
<bean id="loadPropertiesListener" class="PropertyLoaderJobExecutionListener" scope="step">
<property name="pfb">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:batch.#{jobParameters['region']}.properties" />
</bean>
</property>
</bean>
<bean id="getRemoteFileTasklet" class="GetRemoteFileTasklet" />

Building custom Spring config tags for a framework

I have a framework which currently requires pretty verbose setup in Spring:
<bean id="dpHibernateRemotingAdapter"
class="org.springframework.flex.core.ManageableComponentFactoryBean">
<constructor-arg value="org.dphibernate.adapters.RemotingAdapter" />
<property name="properties">
<value>
{"dpHibernate" :
{
"serializerFactory" : "org.dphibernate.serialization.SpringContextSerializerFactory"
}
}
</value>
</property>
</bean>
<bean id="dataAccessService" class="org.dphibernate.services.SpringLazyLoadService"
autowire="constructor">
<flex:remoting-destination />
</bean>
<bean id="dpHibernateSerializer" class="org.dphibernate.serialization.HibernateSerializer"
scope="prototype">
<property name="pageSize" value="10" />
</bean>
<bean id="dpHibernateDeserializer" class="org.dphibernate.serialization.HibernateDeserializer"
scope="prototype" />
I'd like to look at providing a more elegant configuration tag, similar to user-friendly tags used elsewhere in spring:
<context:annotation-config />
<mvc:annotation-driven />
<tx:annotation-driven />
<flex:message-broker/>
etc.,
However, I don't really know where to start.
How does this approach work? What are these tags called? What's their base class?
If someone could point me to the class names in the source (ideally, the <flex:message-broker />, as that's the closest problem set to my project), then I can go from there. I just don't really know where to start!
Custom XML namespaces are certainly possible (see Appendix D), but in my experience a royal pain to get working properly.
I strongly recommend that instead you use #Bean-style configuration. This lets you use Java to compose your bean graphs, instead of XML. Not only can it be much more concise in certain situations, it's properly type-safe, and more easily re-used.
Either way, you'll end up writing some Java that wires objects together. It's a question of how you want to expose that.
See Appendix D. Extensible XML authoring.

Categories

Resources