I think I have found a bug. I have been digging into this and have come across the fact that the JdbcExecutionContextDao is not saving information to the BATCH_JOB_EXECUTION_CONTEXT table. The only way I could find to save information to that table is to use the JdbcExecutionContextDao directly.
Code:
String jobName = chunkContext.getStepContext().getJobName();
JobExecution jobExecution = getJobExecutionContext(jobName, chunkContext.getStepContext().getJobParameters());
ExecutionContext ec = jobExecution.getExecutionContext();
ec.put("TEST_ATTRIBUTE", "Test Attribute");
LOGGER.debug("Is Dirty = " + ec.isDirty());
LOGGER.debug("Is Empty = " + ec.isEmpty());
ExecutionContext ec2 = new ExecutionContext(ec);
jobExecution.setExecutionContext(ec2);
getEcd().updateExecutionContext(jobExecution);
What gets saved in the Table is this:
Code:
JOB_EXECUTION_ID SHORT_CONTEXT SERIALIZED_CONTEXT
199 {"map":""} (null)
What should have been saved is this:
Code:
UPDATE BATCH_JOB_EXECUTION_CONTEXT SET SHORT_CONTEXT = '{"map":{"entry":{"string":["TEST_ATTRIBUTE","Test Attribute"]}}}', SERIALIZED_CONTEXT = null WHERE JOB_EXECUTION_ID = 199
What the Logs are saying is this:
Code:
09:52:10,426 DEBUG main JdbcTemplate:790 - Executing prepared SQL update
09:52:10,426 DEBUG main JdbcTemplate:574 - Executing prepared SQL statement [UPDATE BATCH_JOB_EXECUTION_CONTEXT SET SHORT_CONTEXT = ?, SERIALIZED_CONTEXT = ? WHERE JOB_EXECUTION_ID = ?]
09:52:10,426 TRACE main TransactionSynchronizationManager:139 - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@6d4c4d61] for key [org.apache.commons.dbcp.BasicDataSource@5bf0cf51] bound to thread [main]
09:52:10,427 TRACE main TransactionSynchronizationManager:139 - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@6d4c4d61] for key [org.apache.commons.dbcp.BasicDataSource@5bf0cf51] bound to thread [main]
09:52:10,427 DEBUG main JdbcExecutionContextDao:182 - PreparedStatement Class = org.apache.commons.dbcp.DelegatingPreparedStatement
09:52:10,427 DEBUG main JdbcExecutionContextDao:183 - Short Context '{"map":{"entry":{"string":["TEST_ATTRIBUTE","Test Attribute"]}}}'
09:52:10,427 DEBUG main JdbcExecutionContextDao:184 - Long Context 'null'
09:52:10,427 DEBUG main JdbcExecutionContextDao:185 - Execution ID '199'
09:52:10,428 DEBUG main JdbcExecutionContextDao:197 - This is the SQL statement that should be ran:
09:52:10,428 DEBUG main JdbcExecutionContextDao:198 - com.mysql.jdbc.JDBC4PreparedStatement@6bf51e5c: UPDATE BATCH_JOB_EXECUTION_CONTEXT SET SHORT_CONTEXT = '{"map":{"entry":{"string":["TEST_ATTRIBUTE","Test Attribute"]}}}', SERIALIZED_CONTEXT = null WHERE JOB_EXECUTION_ID = 199
09:52:10,429 DEBUG main JdbcTemplate:800 - SQL update affected 1 rows
It looks like the Prepared Statement is creating the correct SQL, however, the data is not being saved. There seems to be a mismatch between what SQL should be executed and what is actually executed. Either that, or my configs are wrong.
data-source-context.xml
HTML Code:
<beans ....>
<!-- <bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="environment">
<property name="location" value="classpath:batch-mysql.properties" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="1" />
</bean> -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/feed_sandbox" />
<property name="username" value="spring_batch" />
<property name="password" value="spring_batch" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" lazy-init="true">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />
<bean id="incrementerParent" class="org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer">
<property name="dataSource" ref="dataSource" />
<property name="incrementerName" value="ID" />
<property name="columnName" value="ID"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg index="0" ref="dataSource"/>
</bean>
<bean id="jobInstanceDao" class="org.springframework.batch.core.repository.dao.JdbcJobInstanceDao">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
<property name="jobIncrementer" ref="incrementerParent" />
</bean>
<bean id="jobExecutionDao" class="org.springframework.batch.core.repository.dao.JdbcJobExecutionDao">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
<property name="jobExecutionIncrementer" ref="incrementerParent"/>
</bean>
<bean id="stepExecutionDao" class="org.springframework.batch.core.repository.dao.JdbcStepExecutionDao">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
<property name="stepExecutionIncrementer" ref="incrementerParent"/>
</bean>
<bean id="executionContextDao" class="org.springframework.batch.core.repository.dao.JdbcExecutionContextDao">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
simple-job-launcher-context.xml
HTML Code:
<beans ....>
<import resource="data-source-context.xml" />
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
p:databaseType="mysql"
p:dataSource-ref="dataSource"
p:transactionManager-ref="transactionManager"/>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="mapJobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"
p:transactionManager-ref="transactionManager" lazy-init="true" autowire-candidate="false" />
<bean id="jobExplorer" class="org.springframework.batch.core.explore.support.SimpleJobExplorer">
<constructor-arg index="0" ref="jobInstanceDao"/>
<constructor-arg index="1" ref="jobExecutionDao"/>
<constructor-arg index="2" ref="stepExecutionDao"/>
<constructor-arg index="3" ref="executionContextDao"/>
</bean>
<bean id="simpleJob" class="org.springframework.batch.core.job.SimpleJob" abstract="true">
<property name="jobRepository" ref="jobRepository" />
<property name="restartable" value="true" />
</bean>
<bean id="taskletStep" class="org.springframework.batch.core.step.tasklet.TaskletStep" abstract="true">
<property name="jobRepository" ref="jobRepository" />
<property name="allowStartIfComplete" value="true" />
</bean>
<bean id="simpleStep" class="org.springframework.batch.core.step.item.SimpleStepFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="jobRepository" ref="jobRepository" />
<property name="startLimit" value="100" />
<property name="commitInterval" value="1" />
</bean>
<bean id="skipLimitStep" class="org.springframework.batch.core.step.item.SkipLimitStepFactoryBean"
parent="simpleStep" abstract="true">
<property name="skipLimit" value="0" />
</bean>
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="int[]">
<bean class="org.springframework.batch.support.IntArrayPropertyEditor" />
</entry>
<entry key="org.springframework.batch.item.file.transform.Range[]">
<bean class="org.springframework.batch.item.file.transform.RangeArrayPropertyEditor" />
</entry>
<entry key="java.util.Date">
<bean class="org.springframework.beans.propertyeditors.CustomDateEditor">
<constructor-arg>
<bean class="java.text.SimpleDateFormat">
<constructor-arg value="yyyyMMdd" />
</bean>
</constructor-arg>
<constructor-arg value="false" />
</bean>
</entry>
</map>
</property>
</bean>
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean class="org.springframework.batch.core.configuration.support.AutomaticJobRegistrar">
<property name="applicationContextFactories">
<bean class="org.springframework.batch.core.configuration.support.ClassPathXmlApplicationContextFactory">
<property name="resource" value="classpath:/JobContext.xml" />
</bean>
</property>
<property name="jobLoader">
<bean class="org.springframework.batch.core.configuration.support.DefaultJobLoader">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
</property>
</bean>
</beans>