JobParameters from Spring Batch - java

I am trying to inject job parameters into a custom ItemReader. I have reviewed all of the StackOverflow notes on the subject (example: How to get access to job parameters from ItemReader, in Spring Batch?), and I see this is a common pain point that is mostly unresolved. I am hoping that a spring guru (#Michael Minella anyone) will see this and have some insight.
I have got as far as determining that the jobparameters are available about one out of 10 runs, even with no code or configuration changes. This is a case of a random success rather than a random failure, so it's proving hard to track down.
I dug into the spring code with the debugger, and determined that when this fails, no bean of the name jobParameters is registered in Spring at the time that the injection is taking place.
I am using Spring 4.1.4 with spring-batch 3.0.2 and spring-data-jpa 1.7.1 and spring-data-commons 1.9.1, running in java 8.
Java class
#Component("sourceSelectionReader")
#Scope("step")
public class SourceSelectionReaderImpl
implements ItemReader<MyThing> {
private Map<String,Object> jobParameters;
// ... snip ...
#Autowired
#Lazy
#Qualifier(value="#{jobParameters}")
public void setJobParameters(Map<String, Object> jobParameters) {
this.jobParameters = jobParameters;
}
}
Job launch parameters:
launch-context.xml job1 jobid(long)=1
launch-context.xml (minus the fluff):
<context:property-placeholder location="classpath:batch.properties" />
<context:component-scan base-package="com.maxis.maximo.ilm" />
<jdbc:initialize-database data-source="myDataSource" enabled="false">
<jdbc:script location="${batch.schema.script}" />
</jdbc:initialize-database>
<batch:job-repository id="jobRepository"
data-source="myDataSource"
transaction-manager="transactionManager"
isolation-level-for-create="DEFAULT"
max-varchar-length="1000"/>
<import resource="classpath:/META-INF/spring/module-context.xml" />
Module-context.xml (minus the fluff):
<description>Example job to get you started. It provides a skeleton for a typical batch application.</description>
<import resource="classpath:/META-INF/spring/hibernate-context.xml"/>
<import resource="classpath:/META-INF/spring/myapp-context.xml"/>
<context:component-scan base-package="com.me" />
<bean class="org.springframework.batch.core.scope.StepScope" />
<batch:job id="job1">
<batch:step id="step0002" >
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="sourceSelectionReader" writer="selectedDataWriter" commit-interval="1" />
</batch:tasklet>
</batch:step>
</batch:job>

The important steps to get Job Parameters to work is to define the StepScope bean and to make sure that your reader is a #StepScope component.
I would try the following:
First make sure that there is a step-bean defined. This is nice to setup using Java Configuration:
#Configuration
public class JobFrameworkConfig {
#Bean
public static StepScope scope() {
return new StepScope();
}
// jobRegistry, transactionManager etc...
}
Then, make sure that your bean is step-scoped by the use of the #StepScope-annotation (almost as in your example). Inject a #Value that is not #Lazy.
#Component("sourceSelectionReader")
#StepScope // required, also works with #Scope("step")
public class SourceSelectionReaderImpl implements ItemReader<MyThing> {
private final long myParam;
// Not lazy, specified param name for the jobParameters
#Autowired
public SourceSelectionReaderImpl(#Value("#{jobParameters['myParam']}") final long myParam) {
this.myParam = myParam;
}
// the rest of the reader...
}

Try add #DependsOn("jobParameters") after #Component("sourceSelectionReader")

Related

spring injection without constructor and setter

I have the question. If my class has dependency like:
public class Test {
public Depend depend;
//Here methods
}
And it does not have setter for Depend property or constructor with Depend as argument, and it has no annotation for Spring but has xml config like:
<bean id="depend" class="xxx.Depend"></bean>
<bean id="test" class="xxx.Test">
<property name="depend" ref="depend" />
</bean>
Is it possible to inject Depend into Test using such config (actually his config does not work. I just wonder - can I change smth to make it work not using annotations or setter/constructor)?
It is not possible without using annotations.
Your current configuration needs some simple changes to make this work. Annotate the depend field with #Autowired and enable component scanning.
Here's a detailed explanation: http://www.mkyong.com/spring/spring-auto-scanning-components/
Yes it is possible without annotations, but you would need to create a TestBeanFactory and then create an object of Test and set Depend yourself before returning it.
<bean id="depend" class="xxx.Depend"></bean>
<bean id="testFactory" class="xxx.TestFactory">
<property name="depend" ref="depend" />
</bean>
<bean id="test" factory-bean="testFactory" factory-method="createTest">
</bean>
Then your test factory would look something like this.
public class TestFactory {
private Depend depend;
public setDepend(Depend depend) {
this.depend = depend
}
public Test createTest() {
Test test = new Test();
test.depend = this.depend;
return test;
}
}

Spring XML equivalent of #EnableAsync

Is there a way to turn on Spring's Async configuration from XML? All the examples I saw are using programmatic context declaration and use #EnableAsync
Is there an XML equivalent for this. In some places I saw <context:annotation-config /> being used, but this doesn't mention anything about async .
I am using Spring 4.
Did you try using this
<task:annotation-driven />
Yes, you can use something like this
<beans>
<task:annotation-driven executor="myExecutor" exception-handler="exceptionHandler"/>
<task:executor id="myExecutor" pool-size="7-42" queue-capacity="11"/>
<bean id="asyncBean" class="com.foo.MyAsyncBean"/>
<bean id="exceptionHandler" class="com.foo.MyAsyncUncaughtExceptionHandler"/>
</beans>
According to the Spring documentation, this is equivalent to using #EnableAsync
In the annotation based approach you have to have #EnableAsync on the Configuration class. Something like as shown below:
#Configuration
#EnableAsync
#ComponentScan(basePackages ="com.spring.sample.demoAsync")
public class SpringAsyncConfig {
}
Then you create a component class to have a function that is called Asynchronously. Something like as shown below:
#Component
public class AsyncClass {
#Async
public Future<String> asyncMethod() {
System.out.println("Executing Thread Async:" +Thread.currentThread().getName());
return new AsyncResult<String>(Thread.currentThread().getName());
}
}
To have the xml equivalent of this approach, you can create a bean in the applicationContext.xml file as shown below:
<bean id="AsyncClass" class="com.spring.sample.demoAsync.AsyncClass"/>
To call the function asyncMethod() in your flow, you can refer AsyncClass bean from any other bean or service. Below is something that I tried to stitch the flow:
<bean id="callingBean" class="comspring.sample.demoAsync.CallingBeanClass">
<property name="AsyncClassBean" ref="AsyncClass"/>
</bean>
It's not necessary to follow this step but is an alternative approach.
In my applicationContext.xml file, I also imported the task schema by using:
xmlns:task="http://www.springframework.org/schema/task
xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"
and then mentioning the executor as a task in the same file:
<task:executor id="myexecutor" pool-size="5" />
Now my AsyncClass looks like this without #component annotation.
public class AsyncClass {
#Async("myexecutor")
public Future<String> asyncMethod() {
System.out.println("Executing Thread Async:" +Thread.currentThread().getName());
return new AsyncResult<String>(Thread.currentThread().getName());
}
}
and then finally invoking the asyncMethod() asynchronously from the CallingBeanClass.

Spring MVC : Asynchronous giving beancurrently in creation exception

I am working on a Spring-MVC application in which I want to use #Async at-least for the methods which are fire-and-forget. When I try to use #Async and I have used #EnableAsync annotation too for class, the actions inside the method are not performed. When I add task executor in servlet-context.xml, then I get an error bean is getting currently created. I am new to Async, can anyone tell me how I can use it.
I am not using Eager loading btw.
Error log :
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'groupNotesService': Bean with name 'groupNotesService' has been injected into other beans [mattachService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
Code :
GroupNotesServiceImpl :
#Service
#Transactional
#EnableAsync
public class GroupNotesServiceImpl implements GroupNotesService {
#Override
#Async
public void editGroupNote(GroupNotes mnotes, int msectionId) {
//Code to be executed, which is not getting executed
}
}
Servlet-context.xml :
<task:annotation-driven executor="executor" />
<task:executor id="executor" pool-size="20"/>
<mvc:annotation-driven />
<mvc:default-servlet-handler />
<resources mapping="/resources/" location="/resources/" />
If I remove any of the mvc lines above, I get a servlet.init() threw load exception error.
Also, Is it possible to use Async where I am returning int? I checked out the Future tag, but I don't know what modifications are required.
Here is the method that returns int.
#Override
public int saveGroupNoteAndReturnId(GroupNotes mnotes, int msectionid) {
// saves note and returns its id.
}
MattachService bean :
<beans:bean id="mattachDAO"
class="com.journaldev.spring.dao.GroupAttachmentsDAOImpl">
<beans:property name="sessionFactory"
ref="hibernate4AnnotatedSessionFactory" />
</beans:bean>
<beans:bean id="mattachService"
class="com.journaldev.spring.service.GroupAttachmentsServiceImpl">
<beans:property name="groupAttachmentsDAO" ref="mattachDAO" />
</beans:bean>
Edit
I checked out that there is a problem to run #Transactional and #Async both in one class. Jira SPR-7147. The workaround suggested there was to introduce a normal facade, and I really don't know what that means.
#EnableAsync should be in configuration, not the service itself. Since you seem to use xml configuration, check this https://stackoverflow.com/a/20127051/562721. It recommends to declare org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor to take care of #Async annotations

Spring Beans: Scan for Converters, Inject into CustomConversionServiceFactory

I've got a MyAppConversionServiceFactoryBean which I'm registering like:
<bean id="conversionService" class="com.MyProject.MyAppConversionServiceFactoryBean">
<property name="messageSource" ref="messageSource"/>
<property name="converters">
<set>
<bean class="com.MyProject.XRepresentationConverter" />
<bean class="com.MyProject.YRepresentationConverter" />
<bean class="com.MyProject.ZRepresentationConverter" />
</set>
</property>
</bean>
I can continue to list every converter we write into this list, but I'd love to be able to configure it such that this isn't necessary and that converters will automatically register themselves somehow with my factory.
Sidebar 1: If that's not possible with a custom factory, is it possible with the default spring one?
Sidebar 2: If neither the first part nor Sidebar 1 is possible, is it possible to #Autowired the conversionService into the converters (so they can easily call one another)? Attempting to #Autowired ConversionService conversionService has previously given me issues due to not being able to wire the conversionService into an object while it's still busy creating the service.
Note: We're using Spring, but not Spring MVC. I have no control over that, so any solutions on that route will be unfortunately unusable. I can change pretty much anything else about the configuration and Java classes, just not the overarching tools.
#Vikdor's comment on the question pointed me in the right direction.
Spring is apparently capable (and no one I asked in person knew this) of gathering collections of beans through the scanning process with #Autowired annotations. Here's what I needed to achieve the same effect I got from the configuration in the post:
applicationContent.xml must have:
<context:component-scan base-package="com.MyProject"/>
<bean id="conversionService" class="com.MyProject.MyAppConversionServiceFactoryBean" />
MyAppConversionServiceFactoryBean.java:
public class MyAppConversionServiceFactoryBean implements
FactoryBean<ConversionService>, InitializingBean {
#Autowired
private Set<BaseConverter> converters;
}
And then all of my converters now have the #Component annotation.
Relevant Docs on #Autowired do briefly mention that it can be used to collect all beans of a type, but I wouldn't have known that it could be done into any collection type without this thread by Grzegorz Oledzki which addresses the generic form of my question, but takes it down a philosophical route.

request scoped beans in spring testing

I would like to make use of request scoped beans in my app. I use JUnit4 for testing. If I try to create one in a test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
public class TestScopedBeans {
protected final static Logger logger = Logger
.getLogger(TestScopedBeans.class);
#Resource
private Object tObj;
#Test
public void testBean() {
logger.debug(tObj);
}
#Test
public void testBean2() {
logger.debug(tObj);
}
With the following bean definition:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="java.lang.Object" id="tObj" scope="request" />
</beans>
And I get:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gov.nasa.arc.cx.sor.query.TestScopedBeans': Injection of resource fields failed; nested exception is java.lang.IllegalStateException: No Scope registered for scope 'request'
<...SNIP...>
Caused by: java.lang.IllegalStateException: No Scope registered for scope 'request'
So I found this blog that seemed helpful:
http://www.javathinking.com/2009/06/no-scope-registered-for-scope-request_5.html
But I noticed he uses AbstractDependencyInjectionSpringContextTests which seems to be deprecated in Spring 3.0.
I use Spring 2.5 at this time, but thought it shouldn't be too hard to switch this method to use AbstractJUnit4SpringContextTests
as the docs suggest (ok the docs link to the 3.8 version but I'm using 4.4). So I change the
test to extend AbstractJUnit4SpringContextTests... same message. Same problem. And now the prepareTestInstance() method I want
to override is not defined. OK, maybe I'll put those registerScope calls somewhere else... So I read more about TestExecutionListeners and think that would be better since I don't want to have to inherit the spring package structure. So
I changed my Test to:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
#TestExecutionListeners({})
public class TestScopedBeans {
expecting I would have to create a custom listener but I when I ran it. It works! Great, but why? I don't see where any of the stock listeners
are registering request scope or session scope, and why would they? there's nothing to say I want that yet, this might not be a Test for Spring MVC code...
Solution for Spring 3.2 or newer
Spring starting with version 3.2 provides support for session/request scoped beans for integration testing.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
#WebAppConfiguration
public class SampleTest {
#Autowired WebApplicationContext wac;
#Autowired MockHttpServletRequest request;
#Autowired MockHttpSession session;
#Autowired MySessionBean mySessionBean;
#Autowired MyRequestBean myRequestBean;
#Test
public void requestScope() throws Exception {
assertThat(myRequestBean)
.isSameAs(request.getAttribute("myRequestBean"));
assertThat(myRequestBean)
.isSameAs(wac.getBean("myRequestBean", MyRequestBean.class));
}
#Test
public void sessionScope() throws Exception {
assertThat(mySessionBean)
.isSameAs(session.getAttribute("mySessionBean"));
assertThat(mySessionBean)
.isSameAs(wac.getBean("mySessionBean", MySessionBean.class));
}
}
Read more: Request and Session Scoped Beans
Solution for Spring before 3.2 with listener
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
#TestExecutionListeners({WebContextTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class})
public class SampleTest {
...
}
WebContextTestExecutionListener.java
public class WebContextTestExecutionListener extends AbstractTestExecutionListener {
#Override
public void prepareTestInstance(TestContext testContext) {
if (testContext.getApplicationContext() instanceof GenericApplicationContext) {
GenericApplicationContext context = (GenericApplicationContext) testContext.getApplicationContext();
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST,
new SimpleThreadScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION,
new SimpleThreadScope());
}
}
}
Solution for Spring before 3.2 with custom scopes
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class, locations = "test-config.xml")
public class SampleTest {
...
}
TestConfig.java
#Configuration
#ComponentScan(...)
public class TestConfig {
#Bean
public CustomScopeConfigurer customScopeConfigurer(){
CustomScopeConfigurer scopeConfigurer = new CustomScopeConfigurer();
HashMap<String, Object> scopes = new HashMap<String, Object>();
scopes.put(WebApplicationContext.SCOPE_REQUEST,
new SimpleThreadScope());
scopes.put(WebApplicationContext.SCOPE_SESSION,
new SimpleThreadScope());
scopeConfigurer.setScopes(scopes);
return scopeConfigurer
}
or with xml configuration
test-config.xml
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
<map>
<entry key="session">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Source code
Source code for all presented solutions:
https://github.com/mariuszs/spring-test-web
I've tried several solutions, including #Marius's solution with the "WebContextTestExecutionListener", but it didn't work for me, as this code loaded the application context before creating the request scope.
The answer that helped me in the end is not a new one, but it's good:
http://tarunsapra.wordpress.com/2011/06/28/junit-spring-session-and-request-scope-beans/
I simply added the following snippet to my (test) application context:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Good luck!
A solution, tested with Spring 4, for when you require request-scoped beans but aren't making any requests via MockMVC, etc.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(/* ... */)
public class Tests {
#Autowired
private GenericApplicationContext context;
#Before
public void defineRequestScope() {
context.getBeanFactory().registerScope(
WebApplicationContext.SCOPE_REQUEST, new RequestScope());
RequestContextHolder.setRequestAttributes(
new ServletRequestAttributes(new MockHttpServletRequest()));
}
// ...
The test passes because it isn't doing anything :)
When you omit the #TestExecutionListeners annotation, Spring registers 3 default listeners, including one called DependencyInjectionTestExecutionListener. This is the listener responsible for scanning your test class looking for things to inject, including #Resource annotations. This listener tried to inject tObj, and fails, because of the undefined scope.
When you declare #TestExecutionListeners({}), you suppress the registration of the DependencyInjectionTestExecutionListener, and so the test never gets tObj injected at all, and because your test is not checking for the existence of tObj, it passes.
Modify your test so that it does this, and it will fail:
#Test
public void testBean() {
assertNotNull("tObj is null", tObj);
}
So with your empty #TestExecutionListeners, the test passes because nothing happens.
Now, on to your original problem. If you want to try registering the request scope with your test context, then have a look at the source code for WebApplicationContextUtils.registerWebApplicationScopes(), you'll find the line:
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
You could try that, and see how you go, but there might be odd side-effects, because you're not really meant to do this in a test.
Instead, I would recommend rephrasing your test so that you don't need request scoped beans. This shouldn't be difficult, the lifecycle of the #Test shouldn't be any longer than the lifecycle of a request-scoped bean, if you write self-contained tests. Remember, there's no need to test the scoping mechanism, it's part of Spring and you can assume it works.
This is still an open issue:
https://jira.springsource.org/browse/SPR-4588
I was able to get this to work (mostly) by defining a custom context loader as outlined in
http://forum.springsource.org/showthread.php?p=286280
Test Request-Scoped Beans with Spring explains very well how to register and create a custom scope with Spring.
In a nutshell, as Ido Cohn explained, it's enough to add the following to the text context configuration:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Instead of using the predefined SimpleThreadScope, based on ThreadLocal, it's also easy to implement a Custom one, as explained in the article.
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class CustomScope implements Scope {
private final Map<String , Object> beanMap = new HashMap<String , Object>();
public Object get(String name, ObjectFactory<?> factory) {
Object bean = beanMap.get(name);
if (null == bean) {
bean = factory.getObject();
beanMap.put(name, bean);
}
return bean;
}
public String getConversationId() {
// not needed
return null;
}
public void registerDestructionCallback(String arg0, Runnable arg1) {
// not needed
}
public Object remove(String obj) {
return beanMap.remove(obj);
}
public Object resolveContextualObject(String arg0) {
// not needed
return null;
}
}
MariuszS' solution works, except I couldn't get the transaction committed properly.
It seems the newly released 3.2 has finally made testing request/session scoped beans first class citizens. Here's a couple of blogs for more details.
Rossen Stoyanchev's Spring Framework 3.2 RC1: Spring MVC Test Framework
Sam Brannen's Spring Framework 3.2 RC1: New Testing Features
NOT reading the docs sometimes drives one crazy. Almost.
If you are using shorter-lived beans (request scope for example), you most likely also need to change your lazy init default! Otherwise the WebAppContext will fail to load and tell you something about missing request scope, which is of course missing, because the context is still loading!
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-lazy-init
The Spring guys should definitely put that hint into their exception message...
If you don't want to change the default, there is also the annotation way: put "#Lazy(true)" after #Component etc. to make singletons initialize lazy and avoid instantiating request-scoped beans too early.

Categories

Resources