I'm using spring batch to perform some calcul, in a reader I have to get a large data to be treated in a processor / writer, and this process takes a lot of (RAM).
So I tried to split the step using the partitioner like below :
<batch:step id="MyStep.master" >
<partition step="MyStep" partitioner="MyPartitioner">
<handler grid-size="1" task-executor="TaskExecutor" />
</partition>
</batch:step>
<batch:step id="MyStep" >
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="MyReader" processor="MyProcessor"
writer="MyWriter" commit-interval="1000" skip-limit="1000">
<batch:skippable-exception-classes>
<batch:include class="...FunctionalException" />
</batch:skippable-exception-classes>
</batch:chunk>
</batch:tasklet>
</batch:step>
<bean id="MyPartitioner" class="...MyPartitioner" scope="step"/>
<bean id="TaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >
<bean name="MyReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
SELECT...
]]>
</value>
</property>
<property name="rowMapper" ref="MyRowMapper" />
</bean>
<bean id="MyRowMapper" class="...MyRowMapper" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver"/>
<property name="jdbcUrl" value="jdbc:postgresql://${database.host}/${database.name}"/>
<property name="user" value="${database.user}"/>
<property name="password" value="${database.password}"/>
<property name="acquireIncrement" value="1" />
<property name="autoCommitOnClose" value="true" />
<property name="minPoolSize" value="${min.pool.size}" /> <!-- min.pool.size=5 -->
<property name="maxPoolSize" value="${max.pool.size}" /> <!-- max.pool.size=15 -->
</bean>
But in vain the partitioning takes a lot of memory too, because the steps (slaves) are executed in parallel, what I want to do is to split the step and execute the thread successively (not in parallel) to reduce the memory usage (RAM), is that possible?
The question it's a bit old, so I'm not sure if this will be helpful now, probably you solved it yourself.
If you don't have a problem with the row execution order the solution would be to query your db in your partitioner bean, then pass to each partitions all the information to split in portions your table/s (start_key, end_key) this will reduce the ram usage (A LOT).
Some warnings:
Be aware that both the query used by partitioner bean and the reader must have the same "order by"
partitioned readers must be scope="step"
Don't use this method if you need to process the rows in a precise order
To tune the RAM try different combination of gridSize and taskExecutor maxCoreSize (that's why my gridSize is a jobParams)
Here an example:
XML Configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- JOB -->
<batch:job id="printPdf" job-repository="jobRepository"
restartable="false">
<batch:step id="MyStep">
<batch:partition step="MyStep.template"
partitioner="myPartitioner" handler="partitionHandler">
</batch:partition>
</batch:step>
</batch:job>
<!-- Partitioner -->
<bean id="myPartitioner" class="foo.MyPartitioner"
scope="step">
<property name="jdbcTemplate" ref="myJdbcTemplate" />
<property name="sql"
value="Select ...." />
<property name="rowMap">
<bean
class="foo.MyPartitionHandlerRowMapper" />
</property>
<property name="preparedStatementSetter">
<bean
class="org.springframework.batch.core.resource.ListPreparedStatementSetter">
<property name="parameters">
<list>
<value>#{jobParameters['param1']}</value>
</list>
</property>
</bean>
</property>
</bean>
<bean id="partitionHandler" scope="step"
class="org.springframework.batch.core.partition.support.TaskExecutorPartitionHandler">
<property name="taskExecutor" ref="customTaskExecutor" />
<property name="gridSize" value="#{jobParameters['gridSize']}" />
<property name="step" ref="MyStep.template" />
</bean>
<bean id="customTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="8" />
<property name="maxPoolSize" value="8" />
<property name="waitForTasksToCompleteOnShutdown" value="true" />
<property name="awaitTerminationSeconds" value="120" />
</bean>
<batch:step id="MyStep.tempate">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk commit-interval="2500" reader="myReader"
processor="myProcessor" writer="myWriter" skip-limit="2500">
<batch:skippable-exception-classes>
<batch:include class="...FunctionalException" />
</batch:skippable-exception-classes>
</batch:chunk>
</batch:tasklet>
</batch:step>
<!-- Beans -->
<!-- Processors -->
<bean id="myProcessor" class="foo.MyProcessor"
scope="step">
</bean>
<bean id="classLoaderVerifier"
class="it.addvalue.pkjwd.services.genbean.GenericStockKeysForNoDuplicate" />
<!-- Readers -->
<bean id="myReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="myDataSouce" />
<property name="sql"
value="select ... from ... where ID >= ? and ID <= ?" />
<property name="rowMapper">
<bean class="foo.MyReaderPartitionedRowMapper" />
</property>
<property name="preparedStatementSetter">
<bean
class="org.springframework.batch.core.resource.ListPreparedStatementSetter">
<property name="parameters">
<list>
<value>#{stepExecutionContext['START_ID']}</value>
<value>#{stepExecutionContext['END_ID']}</value>
</list>
</property>
</bean>
</property>
</bean>
<!-- Writers -->
<bean id="myWriter"
class="org.springframework.batch.item.database.JdbcBatchItemWriter">
<property name="assertUpdates" value="false" />
<property name="itemPreparedStatementSetter">
<bean class="foo.MyWriterStatementSetters" />
</property>
<property name="sql"
value="insert ..." />
<property name="dataSource" ref="myDataSouce" />
</bean>
</beans>
Your Partitioner Bean will look like this:
package foo;
import foo.model.MyTable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.springframework.batch.core.partition.support.Partitioner;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
public class MyPartitioner implements Partitioner
{
private JdbcTemplate jdbcTemplate;
private RowMapper<foo.model.MyTable> rowMap;
private String sql;
private PreparedStatementSetter preparedStatementSetter;
public JdbcTemplate getJdbcTemplate()
{
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate)
{
this.jdbcTemplate = jdbcTemplate;
}
public RowMapper<foo.model.MyTable> getRowMap()
{
return rowMap;
}
public void setRowMap(RowMapper<PkjwdPolizzePartition> rowMap)
{
this.rowMap = rowMap;
}
public String getSql()
{
return sql;
}
public void setSql(String sql)
{
this.sql = sql;
}
public PreparedStatementSetter getPreparedStatementSetter()
{
return preparedStatementSetter;
}
public void setPreparedStatementSetter(PreparedStatementSetter preparedStatementSetter)
{
this.preparedStatementSetter = preparedStatementSetter;
}
#Override
public Map<String, ExecutionContext> partition(int gridSize)
{
Map<String, ExecutionContext> map = new HashMap<String, ExecutionContext>();
try
{
List<PkjwdPolizzePartition> lstMyRows = jdbcTemplate.query(sql, preparedStatementSetter ,rowMap);
if ( lstMyRows.size() > 0 )
{
int total = lstMyRows.size();
int rowsPerPartition = total / gridSize;
int leftovers = total % gridSize;
total = lstMyRows.size() - 1;
int startPos = 0;
int endPos = rowsPerPartition - 1;
int i = 0;
while (endPos <= (total))
{
ExecutionContext context = new ExecutionContext();
if ( endPos + leftovers == total )
{
endPos = total;
}
else if ( endPos >= (total) )
{
endPos = total;
}
context.put("START_ID", lstMyRows.get(startPos).getId());
context.put("END_ID", lstMyRows.get(endPos).getId());
map.put("PART_" + StringUtils.leftPad("" + i, ("" + gridSize).length(), '0'), context);
i++;
startPos = endPos + 1;
endPos = endPos + rowsPerPartition;
}
}
}
catch ( Exception e )
{
e.printStackTrace();
}
return map;
}
}
Related
I've been trying to deploy my project on my tomcat server but apparently, every time I try to start the server, I get this error in my catalina logs:
Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name
'mnpTranslationServiceImpl' defined in URL [file:/opt/tomcat/webapps/axis2/WEB-INF/springjdbc.xml]:
Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'mnpTranslationDAO' of bean class [com.e_horizon.jdbc.mnpTranslation.mnpTranslationServiceImpl]:
Bean property 'mnpTranslationDAO' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
here is my bean.xml which i named springjdbc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://192.168.XX.X1:3306/msdp" />
<property name="user" value="root" />
<property name="password" value="ehorizon" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="500" />
<property name="numHelperThreads" value="5" />
<property name="initialPoolSize" value="2" />
<property name="autoCommitOnClose" value="true" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="maxIdleTime" value="1200" />
<property name="acquireRetryAttempts" value="2" />
</bean>
<bean id="dataSourceHD" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://192.168.XX.X2:3306/hd" />
<property name="user" value="teligent" />
<property name="password" value="teligent" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="500" />
<property name="numHelperThreads" value="5" />
<property name="initialPoolSize" value="2" />
<property name="autoCommitOnClose" value="true" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="maxIdleTime" value="1200" />
<property name="acquireRetryAttempts" value="2" />
</bean>
<bean id="mpp2SubscribersDAO" class="com.e_horizon.jdbc.mpp2Subscriber.Mpp2SubscribersDAO">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<bean id="mpp2SubscribersService"
class="com.e_horizon.jdbc.mpp2Subscriber.Mpp2SubscribersServiceImpl">
<property name="mpp2SubscribersDAO">
<ref bean="mpp2SubscribersDAO" />
</property>
</bean>
<bean id="mnpTranslationDAO" class="com.e_horizon.jdbc.mnpTranslation.mnpTranslationDAO">
<property name="dataSource">
<ref bean="dataSourceHD" />
</property>
</bean>
<bean id="mnpTranslationServiceImpl" class="com.e_horizon.jdbc.mnpTranslation.mnpTranslationServiceImpl">
<property name="mnpTranslationDAO">
<ref bean="mnpTranslationDAO" />
</property>
</bean>
`
Here's the DAO file which the error says as not writable:
package com.e_horizon.jdbc.mnpTranslation;
import java.util.HashMap;
import java.util.Map;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import com.e_horizon.www.jdbc.common.BaseDAO;
public class mnpTranslationDAO extends BaseDAO {
private SimpleJdbcInsert jdbcCall;
public static String TABLE = "MNP_TRANSLATION";
public static String FIELD_MSISDN = "msisdn";
public static String FIELD_ROUTING_NUMBER = "routing_number";
public static String FIELD_LAST_UPDATE = "last_update";
public static String FIELD_ACTION = "action";
protected RowMapper getObjectMapper () {
return new mnpTranslationMapper();
}
public void save (mnpTranslation mnp) {
this.jdbcCall = this.getSimpleJdbcInsert().withTableName(TABLE);
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put(FIELD_MSISDN, mnp.getMsisdn());
parameters.put(FIELD_ROUTING_NUMBER, mnp.getRouting_number());
parameters.put(FIELD_LAST_UPDATE, mnp.getLast_update());
parameters.put(FIELD_ACTION, mnp.getAction());
jdbcCall.execute(parameters);
}
}
Adding my model which contains the setters and getters (mnpTranslation.java):
package com.e_horizon.jdbc.mnpTranslation;
import java.sql.Timestamp;
public class mnpTranslation {
private String msisdn;
private String routing_number;
private Timestamp last_update;
private String action;
public void setMsisdn (String msisdn) {
this.msisdn = msisdn;
}
public String getMsisdn () {
return this.msisdn;
}
public void setRouting_number (String routing_number) {
this.routing_number = routing_number;
}
public String getRouting_number () {
return this.routing_number;
}
public void setLast_update (Timestamp last_update) {
this.last_update = last_update;
}
public Timestamp getLast_update () {
return this.last_update;
}
public void setAction (String action) {
this.action = action;
}
public String getAction () {
return this.action;
}
}
[edit] I'm adding the rest of my java files so you could see more what I'm dealing with right now. Thanks a lot for checking this.
mnpTranslationService.java:
package com.e_horizon.jdbc.mnpTranslation;
public abstract interface mnpTranslationService {
public abstract void save (mnpTranslation mnp);
}
mnpTranslationServiceImpl.java:
package com.e_horizon.jdbc.mnpTranslation;
public class mnpTranslationServiceImpl {
private mnpTranslationDAO mnpTranslationDAO;
public void save (mnpTranslation mnp) {
this.mnpTranslationDAO.save(mnp);
}
}
mnpTranslationMapper.java:
package com.e_horizon.jdbc.mnpTranslation;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class mnpTranslationMapper implements RowMapper {
public Object mapRow (ResultSet result, int dex) throws SQLException {
mnpTranslation mnp = new mnpTranslation();
mnp.setMsisdn(result.getString("msisdn"));
mnp.setRouting_number(result.getString("routing_number"));
mnp.setLast_update(result.getTimestamp("last_update"));
mnp.setAction(result.getString("action"));
return mnp;
}
}
Please let me know if there's anything else you need to see. I'd gladly post it right away. Any help will be much appreciated.
The error message states that the problem is with your mnpTranslationServiceImpl class.
See error message
Bean property 'mnpTranslationDAO' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
I am importing a spring xml configuration file from another project using import resource. The resource is apparently found since there is no error message saying it isn't, but none of the beans get loaded. Is there something I'm doing wrong here? I should note that when I run my integration tests from within eclipse it all works fine, but it blows up when running the same integration test from within a maven build.
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<import resource="classpath*:httpclient-4x.xml" />
<context:component-scan base-package="com.mycompany.data" />
<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:properties/client.${deployment.env}.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="false" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchSystemEnvironment" value="true" />
</bean>
<bean id="jacksonObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
</beans>
httpclient-4x.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="secureRestTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<ref bean="secureClientHttpRequestFactory" />
</constructor-arg>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<util:constant static-field="org.springframework.http.MediaType.APPLICATION_XML" />
<util:constant static-field="org.springframework.http.MediaType.APPLICATION_JSON" />
<util:constant static-field="org.springframework.http.MediaType.TEXT_PLAIN" />
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<util:constant static-field="org.springframework.http.MediaType.MULTIPART_FORM_DATA" />
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</list>
</property>
</bean>
<bean id="secureClientHttpRequestFactory" class="com.cobalt.inventory.security.PreemptiveBasicAuthClientHttpRequestFactory">
<constructor-arg>
<ref bean="secureCloseableHttpClient" />
</constructor-arg>
</bean>
<bean id="secureCloseableHttpClient" factory-bean="secureHttpClientBuilder" factory-method="build" />
<bean id="secureHttpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create">
<property name="defaultCredentialsProvider" ref="credentialsProvider" />
<property name="defaultSocketConfig" ref="socketConfig" />
<property name="defaultRequestConfig" ref="requestConfig" />
<property name="userAgent" value="${http.user.agent: CDK Cobalt invJava}" />
<property name="maxConnPerRoute" value="${http.max.connections.per.route:100}" />
<property name="maxConnTotal" value="${http.max.connections:100}" />
</bean>
<bean id="credentialsProvider" class="org.apache.http.impl.client.BasicCredentialsProvider" />
<bean id="credentials" class="org.apache.http.auth.UsernamePasswordCredentials">
<constructor-arg index="0" name="userName" value="validUsername" />
<constructor-arg index="1" name="password" value="validPassword" />
</bean>
<bean id="socketConfigBuilder" class="org.apache.http.config.SocketConfig.Builder">
<property name="soKeepAlive" value="true" />
<property name="soTimeout" value="${http.socket.timeout:10000}" />
</bean>
<bean id="socketConfig" factory-bean="socketConfigBuilder" factory-method="build" />
<bean id="requestConfigBuilder" class="org.apache.http.client.config.RequestConfig.Builder">
<property name="connectTimeout" value="${http.connection.timeout:10000}" />
<property name="socketTimeout" value="${http.socket.timeout:10000}" />
<property name="cookieSpec">
<util:constant static-field="org.apache.http.client.config.CookieSpecs.IGNORE_COOKIES" />
</property>
</bean>
<bean id="requestConfig" factory-bean="requestConfigBuilder" factory-method="build" />
<!-- For accessing common authorization service -->
<bean id="commonAuthzSecureRestTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<ref bean="commonAuthzSecureClientHttpRequestFactory" />
</constructor-arg>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<util:constant static-field="org.springframework.http.MediaType.APPLICATION_XML" />
<util:constant static-field="org.springframework.http.MediaType.APPLICATION_JSON" />
<util:constant static-field="org.springframework.http.MediaType.TEXT_PLAIN" />
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<util:constant static-field="org.springframework.http.MediaType.MULTIPART_FORM_DATA" />
</list>
</property>
</bean>
<bean id="jsonMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter ">
<property name="supportedMediaTypes">
<list>
<bean id="jsonMediaTypeApplicationJson" class="org.springframework.http.MediaType">
<constructor-arg value="application" />
<constructor-arg value="json" />
</bean>
</list>
</property>
</bean>
</list>
</property>
</bean>
<bean id="commonAuthzSecureClientHttpRequestFactory" class="com.mycompany.security.PreemptiveBasicAuthClientHttpRequestFactory">
<constructor-arg>
<ref bean="commonAuthzSecureCloseableHttpClient" />
</constructor-arg>
</bean>
<bean id="commonAuthzSecureCloseableHttpClient" factory-bean="commonAuthzSecureHttpClientBuilder" factory-method="build" />
<bean id="commonAuthzSecureHttpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create">
<property name="defaultCredentialsProvider" ref="commonAuthzCredentialsProvider" />
<property name="defaultSocketConfig" ref="commonAuthzSocketConfig" />
<property name="defaultRequestConfig" ref="commonAuthzRequestConfig" />
<property name="userAgent" value="${http.user.agent: userAgent}" />
<property name="maxConnPerRoute" value="${http.max.connections.per.route:100}" />
<property name="maxConnTotal" value="${http.max.connections:100}" />
</bean>
<bean id="commonAuthzCredentialsProvider" class="org.apache.http.impl.client.BasicCredentialsProvider" />
<bean id="commonAuthzCredentials" class="org.apache.http.auth.UsernamePasswordCredentials">
<constructor-arg index="0" name="userName" value="${common_services.iam.remoting.user:user" />
<constructor-arg index="1" name="password" value="${common_services.iam.remoting.password:password}" />
</bean>
<bean id="commonAuthzSocketConfigBuilder" class="org.apache.http.config.SocketConfig.Builder">
<property name="soKeepAlive" value="true" />
<property name="soTimeout" value="${http.socket.timeout:10000}" />
</bean>
<bean id="commonAuthzSocketConfig" factory-bean="commonAuthzSocketConfigBuilder" factory-method="build" />
<bean id="commonAuthzRequestConfigBuilder" class="org.apache.http.client.config.RequestConfig.Builder">
<property name="connectTimeout" value="${http.connection.timeout:10000}" />
<property name="socketTimeout" value="${http.socket.timeout:10000}" />
<property name="cookieSpec">
<util:constant static-field="org.apache.http.client.config.CookieSpecs.IGNORE_COOKIES" />
</property>
</bean>
<bean id="commonAuthzRequestConfig" factory-bean="commonAuthzRequestConfigBuilder" factory-method="build" />
<util:list id="spel-configuration">
<value>#{credentialsProvider.setCredentials(T(org.apache.http.auth.AuthScope).ANY, credentials)}</value>
<value>#{commonAuthzCredentialsProvider.setCredentials(T(org.apache.http.auth.AuthScope).ANY, commonAuthzCredentials)} </value>
</util:list>
</beans>
I turned debug logging on for Spring and found the following log entries:
[2015-04-13 10:46:56,818][DEBUG][org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loaded 0 bean definitions from location pattern [classpath*:httpclient-4x.xml]
[2015-04-13 10:46:56,818][DEBUG][org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader] - Imported 0 bean definitions from URL location [classpath*:httpclient-4x.xml]
I do not have control of the content of httpclient-4x.xml I just reference it.
By request, here is CvsDataClientImpl:
package com.mycompany.data;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.mycompany.utility.ConditionalUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
#Component
public class CvsDataClientImpl implements CvsDataClient<JsonNode> {
private static final int MAX_PAGE_SIZE = 100;
private static final String V1 = "/rest/v1.0/vehicles";
#Resource(name = "secureRestTemplate")
private RestTemplate restTemplate;
#Resource(name = "jsonVehiclesNodeMerger")
private JsonVehiclesNodeMerger nodeMerger;
#Value("${inv.vehicle-app.context:inventoryWebApp}")
private String vehicleAppContextPath;
#Value("${services.remoting.url:http://localhost:10080}")
private String remotingUrl;
#Override
public JsonNode read(String urlParameters) {
validateUrlParameters(urlParameters);
ResponseEntity<JsonNode> response = restTemplate.getForEntity(buildBaseRestUrl() + "?" + urlParameters, JsonNode.class);
return response.getBody();
}
#Override
public JsonNode readAll(String urlParameters) {
return readAll(urlParameters, null);
}
public JsonNode readAll(String urlParameters, Integer pageSize) {
int offset = 0;
int actualPageSize = pageSize != null ? pageSize : MAX_PAGE_SIZE;
JsonNode latestResult = null;
JsonNode baseResult = null;
do {
String extendedUrlParameters = buildUrlParameters(urlParameters, offset, actualPageSize);
latestResult = read(extendedUrlParameters);
baseResult = merge(latestResult, baseResult);
offset += actualPageSize;
} while (latestResult.get("searchResult").get("vehicles") != null);
return baseResult;
}
private JsonNode merge(JsonNode latestResult, JsonNode baseResult) {
JsonNode merged = null;
if (baseResult == null) {
merged = latestResult;
} else {
merged = nodeMerger.merge(latestResult, baseResult);
}
JsonNode searchResult = merged.get("searchResult");
((ObjectNode) searchResult).remove("summary");
return merged;
}
private String buildUrlParameters(String urlParameters, int offset, int pageSize) {
String parameters = urlParameters;
parameters += "&limit=" + pageSize + "&offset=" + offset;
return parameters;
}
private String buildBaseRestUrl() {
return remotingUrl + vehicleAppContextPath + V1;
}
private void validateUrlParameters(String urlParameters) {
if (ConditionalUtils.isNullOrEmpty(urlParameters) || urlParameters.indexOf("inventoryOwner") < 0 && urlParameters.indexOf("storeId") < 0) {
throw new IllegalArgumentException("Must provide at least an inventory owner or store ID");
}
}
void setVehicleAppContextPath(String contextPath) {
vehicleAppContextPath = contextPath;
}
void setRemotingUrl(String remotingUrl) {
this.remotingUrl = remotingUrl;
}
}
After some discussion on chat, minion and I found that httpclient-4x.xml is not included in the external project's build jar for some reason. Now I need to investigate that, but it's another story. :-)
What would be the best way to set custom parameters to JdbcPagingItemReader query?
My custom JdbcPagingItemReader implementation:
public class CustomItemReader extends JdbcPagingItemReader<Long> {
public CustomItemReader(DataSource dataSource) throws Exception {
SqlPagingQueryProviderFactoryBean queryProvider = new SqlPagingQueryProviderFactoryBean();
queryProvider.setDataSource(dataSource);
queryProvider.setSelectClause("SELECT t1.id");
queryProvider.setFromClause("FROM table1 t1 LEFT JOIN table2 t2 ON t2.fk_table1_id = t1.id");
queryProvider.setWhereClause("WHERE (t1.col1 = :param1) AND ((t2.id IS NULL) OR (t2.col3 = :param2))");
queryProvider.setSortKey("t1.id");
setDataSource(dataSource);
setFetchSize(10);
setRowMapper(new RowMapper<Long>() {
#Override
public Long mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getLong(1);
}
});
setQueryProvider(queryProvider.getObject());
}
}
To pass parameters from the job's ExecutionContext to the JdbcPagingItemReader, you'd configure the writer as follows:
<bean id="itemReader" class="org.springframework.batch.item.database.JdbcPagingItemReader" scope="step">
<property name="dataSource" ref="dataSource" />
<property name="rowMapper">
<bean class="MyRowMapper" />
</property>
<property name="queryProvider">
<bean class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="sortKeys">
<map>
<entry key="t1.id" value="ASCENDING"/>
</map>
</property>
<property name="selectClause" value="SELECT t1.id" />
<property name="fromClause" value="FROM table1 t1 LEFT JOIN table2 t2 ON t2.fk_table1_id = t1.id" />
<property name="whereClause" value=""WHERE (t1.col1 = :param1) AND ((t2.id IS NULL) OR (t2.col3 = :param2))" />
</bean>
</property>
<property name="pageSize" value="10" />
<property name="parameterValues">
<map>
<entry key="param1" value="#{jobExecutionContext[param1]}" />
<entry key="param2" value="#{jobExecutionContext[param2]}" />
</map>
</property>
</bean>
You can view a configuration like this in action in the Spring Batch examples here: https://github.com/spring-projects/spring-batch/tree/master/spring-batch-samples
My spring batch process has some bad SQL error in the console.......
I am using spring batch job from URL..At this time of error I am not able to see any errors on my webpage but there is some error in my console.....
I want to see some kind of error on my webpage...like(i.e 500...)
here is my job.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:batch="http://www.springframework.org/schema/batch" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<batch:job id="subrogJob" incrementer="incrementer">
<batch:step id="subrn" next="readFromDataBase">
<batch:tasklet ref="filetransferTasklet">
<batch:listeners>
<batch:listener ref="setCurrentFile" />
</batch:listeners>
</batch:tasklet>
</batch:step>
<batch:step id="readFromDataBase" next="hasMoreFilesStep">
<batch:tasklet>
<batch:chunk reader="databaseReader" processor="Processor"
writer="dbToFileItemWriter" commit-interval="1" />
</batch:tasklet>
<batch:listeners>
<batch:listener ref="setCurrentFile1" />
</batch:listeners>
</batch:step>
<batch:decision id="hasMoreFilesStep" decider="hasMoreFilesDecider">
<batch:fail on="FAILED" />
<batch:next on="CONTINUE" to="subrn" />
<batch:end on="COMPLETED" />
</batch:decision>
</batch:job>
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations"
value="file:${UNIQUE_DIR}/${APP_NAME}/batch/nonadj_batch.properties" />
</bean>
<bean id="incrementer"
class="org.springframework.batch.core.launch.support.RunIdIncrementer" ></bean>
<bean id="setCurrentFile"
class="com.hcsc.ccsp.nonadj.batch.InputFolderScanner"
scope="step">
<property name="collectionParameter" value="inputFiles" />
<property name="outputFolder" value="${OutputFolder}" />
<property name="inputFolder" value="${InputFolder}" />
<property name="archiveFolder" value="${ArchiveFolder}" />
</bean>
<bean id="subrogationsetCurrentFile1"
class="com.hcsc.ccsp.nonadj.subrogation.batch.SubrogationInputFolderScanner1"
scope="step">
</bean>
<bean id="filetransferTasklet"
class="com.hcsc.ccsp.nonadj.integration.FileTransferTasklet"
scope="step">
<property name="inputfile" value="file:#{jobExecutionContext['inputFile']}" />
<property name="outputfile" value="file:#{jobExecutionContext['outputFile']}" />
</bean>
<bean id="Processor"
class="com.hcsc.ccsp.nonadj.processor.Processor">
</bean>
<bean id="dbToFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter"
scope="step">
<property name="resource" value="file:#{jobExecutionContext['outputFile']}" />
<property name="lineAggregator">
<bean
class="com.hcsc.ccsp.nonadj.writer.SubrogationLineAggregator" />
</property>
<property name="footerCallback" ref="HeaderFooterWriter" />
<property name="headerCallback" ref="HeaderFooterWriter" />
<property name="transactional" value="true" />
<property name="appendAllowed" value="true" />
<property name="saveState" value="true" />
</bean>
<bean id="HeaderFooterWriter"
class="com.hcsc.ccsp.nonadj.writer.HeaderFooterWriter">
<property name="delegate" ref="dbToFileItemWriter" />
</bean>
<bean id="hasMoreFilesDecider"
class="com.hcsc.ccsp.nonadj.subrogation.batch.CollectionEmptyDecider">
<property name="collectionParameter" value="inputFiles" />
<property name="archiveFolder" value="${nArchiveFolder}" />
<property name="errorFolder" value="${ErrorFolder}" />
<property name="DeleteFilesOlderthanXDays" value="${DeleteFilesOlderthanXDays}" />
</bean>
<context:component-scan base-package="com.hcsc.ccsp.nonadj.batch"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Component" />
</context:component-scan>
<context:annotation-config />
<bean id="databaseReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="DataSource" />
<property name="sql"
value="SELECT Query" />
<property name="rowMapper">
<bean
class="com.hcsc.ccsp.nonadj.integration.FieldSetMapper" />
</property>
</bean>
</beans>
and here is my jobLauncher class...
package com.hcsc.ccsp.nonadj.batch.web;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
#Controller
public class JobLauncherController {
private Logger LOG = LogManager.getLogger(JobLauncherController.class);
private static final String JOB_PARAM = "job";
private JobLauncher jobLauncher;
private JobRegistry jobRegistry;
private String param;
private String paramValue;
#Autowired
public JobLauncherController(JobLauncher jobLauncher, JobRegistry jobRegistry) {
super();
this.jobLauncher = jobLauncher;
this.jobRegistry = jobRegistry;
DateFormat dateFormat = new SimpleDateFormat("ss");
Date date = new Date();
this.paramValue = dateFormat.format(date);
LOG.info("Param Start Value :: " + dateFormat.format(date));
}
#RequestMapping(value="launcher",method=RequestMethod.GET)
#ResponseStatus(HttpStatus.ACCEPTED)
public void launch(#RequestParam String job, HttpServletRequest request) throws Exception {
JobParametersBuilder builder = extractParameters(request);
jobLauncher.run(jobRegistry.getJob(request.getParameter(JOB_PARAM)), builder.toJobParameters());
}
private JobParametersBuilder extractParameters(HttpServletRequest request) {
JobParametersBuilder builder = new JobParametersBuilder();
this.param = "param" + this.paramValue;
if(!JOB_PARAM.equals(this.param)) {
builder.addString(this.param,this.paramValue);
}
LOG.info("Param :: " + this.param);
LOG.info("Param Value :: " + this.paramValue);
int paramVal = Integer.parseInt(this.paramValue) + 1;
this.paramValue = paramVal + "";
LOG.info("Incremented Param Value :: " + this.paramValue);
return builder;
}
}
Thanks in advance...
Just check if execution was not COMPLETED; get all exceptions list and log them then throw exception or return 500 or whatever you like.
Your launch() should be something like this:
public void launch(#RequestParam String job, HttpServletRequest request) throws Exception {
JobParametersBuilder builder = extractParameters(request);
JobExecution execution = jobLauncher.run(jobRegistry.getJob(request.getParameter(JOB_PARAM)), builder.toJobParameters());
if(BatchStatus.COMPLETED != execution.getStatus()){
List<Throwable> throwList = execution.getAllFailureExceptions();
for (Throwable throwable : throwList) {
logger.error("error",throwable);
}
throw new Exception();
}
}
I am looking for a guidance/solution with Spring batch integration. I have a directory to which external application will send xml files. My application should read the file content and move the file to another directory.
The application should be able to process the files in parallel.
Thanks in advance.
You can use Spring Integration ftp / sftp combined with Spring Batch:
1.Spring Integration Ftp Configuration :
<bean id="ftpClientFactory"
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${host.name}" />
<property name="port" value="${host.port}" />
<property name="username" value="${host.username}" />
<property name="password" value="${host.password}" />
<property name="bufferSize" value="100000"/>
</bean>
<int:channel id="ftpChannel" />
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel" remote-directory="/yourremotedirectory/" session-factory="ftpClientFactory" use-temporary-file-name="false" />
2.Create your reader and autowire a service to provide your items if needed :
#Scope("step")
public class MajorItemReader implements InitializingBean{
private List<YourItem> yourItems= null;
#Autowired
private MyService provider;
public YourItem read() {
if ((yourItems!= null) && (yourItems.size() != 0)) {
return yourItems.remove(0);
}
return null;
}
//Reading Items from Service
private void reloadItems() {
this.yourItems= new ArrayList<YourItem>();
// use the service to provide your Items
if (yourItems.isEmpty()) {
yourItems= null;
}
}
public MyService getProvider() {
return provider;
}
public void setProvider(MyService provider) {
this.provider = provider;
}
#Override
public void afterPropertiesSet() throws Exception {
reloadItems();
}
}
3. Create Your Own Item Processor
public class MyProcessor implements
ItemProcessor<YourItem, YourItem> {
#Override
public YourItem process(YourItem arg0) throws Exception {
// Apply any logic to your Item before transferring it to the writer
return arg0;
}
}
4. Create Your Own Writer :
public class MyWriter{
#Autowired
#Qualifier("ftpChannel")
private MessageChannel messageChannel;
public void write(YourItem pack) throws IOException {
//create your file and from your Item
File file = new File("the_created_file");
// Sending the file via Spring Integration Ftp Channel
Message<File> message = MessageBuilder.withPayload(file).build();
messageChannel.send(message);
}
5.Batch Configuration :
<bean id="dataSourcee"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="" />
<property name="url" value="" />
<property name="username" value="" />
<property name="password" value="" />
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="dataSourcee" />
<property name="transactionManager" ref="transactionManagerrr" />
<property name="databaseType" value="" />
</bean>
<bean id="transactionManagerrr"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
6.Another ApplicationContext file to Configure Your Job :
<context:annotation-config />
<bean id="provider" class="mypackage.MyService" />
<context:component-scan base-package="mypackage" />
<bean id="myReader" class="mypackage.MyReader"
<property name="provider" ref="provider" />
</bean>
<bean id="myWriter" class="mypackage.MyWriter" />
<bean id="myProcessor" class="mypackage.MyProcessor" />
<bean id="mReader"
class="org.springframework.batch.item.adapter.ItemReaderAdapter">
<property name="targetObject" ref="myReader" />
<property name="targetMethod" value="read" />
</bean>
<bean id="mProcessor"
class="org.springframework.batch.item.adapter.ItemProcessorAdapter">
<property name="targetObject" ref="myProcessor" />
<property name="targetMethod" value="process" />
</bean>
<bean id="mWriter"
class="org.springframework.batch.item.adapter.ItemWriterAdapter">
<property name="targetObject" ref="myWriter" />
<property name="targetMethod" value="write" />
</bean>
<batch:job id="myJob">
<batch:step id="step01">
<batch:tasklet>
<batch:chunk reader="mReader" writer="mWriter"
processor="mProcessor" commit-interval="1">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="myRunScheduler" class="mypackage.MyJobLauncher" />
<task:scheduled-tasks>
<task:scheduled ref="myJobLauncher" method="run"
cron="0 0/5 * * * ?" />
<!-- this will maker the job runs every 5 minutes -->
</task:scheduled-tasks>
7.Finally Configure A launcher to launch your job :
public class MyJobLauncher {
#Autowired
private JobLauncher jobLauncher;
#Autowired
#Qualifier("myJob")
private Job job;
public void run() {
try {
String dateParam = new Date().toString();
JobParameters param = new JobParametersBuilder().addString("date",
dateParam).toJobParameters();
JobExecution execution = jobLauncher.run(job, param);
execution.stop();
} catch (Exception e) {
e.printStackTrace();
}
}