spring-projects / spring-batch

Spring Batch is a framework for writing batch applications using Java and Spring
http://projects.spring.io/spring-batch/
Apache License 2.0
2.73k stars 2.35k forks source link

Adding FilePart to the Job Parameter is not working #4476

Open premcody opened 1 year ago

premcody commented 1 year ago

I've tried to add FilePart Object to the JobParameterBuilder but getting the error "No Suitable Convertor to perform conversion from DefaultFilePart to String".

org.springframework.core.convert.converternotfoundexception: no converter found capable of converting from type [org.springframework.http.codec.multipart.defaultparts$defaultfilepart] to type [java.lang.string] + spring batch 6.0.2

Here is the code snippet :

 FilePart filePart = (FilePart) partMap.get("importFile");
   JobParameters jobParameters = new JobParametersBuilder()

                            .addString("batchId", batchId.toString())

                            .addString("some1", some)
                            .addString("app", appCodePart.value())
                            .addString("dskey", datasourceKeyPart.value())
                            .addString("table", tableNamePart.value())
                            .addString("emailAddress", emailAddressPart.value())
                            .addString("fileName", filePart.filename())
                            .addString("mode",uploadMode)
                            .addJobParameter("filePart",filePart,FilePart.class)

                            .toJobParameters();
Spring batch version: spring-batch-core-5.0.2
Spring boot: 3
spring-core-6.0.11

This was working atleast before (Spring-batch-core 4x versions) as i'd added FilePart as jobparameter using addparamter method but now failing with spring-batch-core 5.0.2 and spring-core-6.0.11.

I think the problem is here the filepart is really not assignable to/from String as per the Spring-core code(JdbcJobExecutionDao,GenericConversionService & TypeDescriptor).

Adding the Stacktrace.

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.http.codec.multipart.DefaultParts$DefaultFilePart] to type [java.lang.String]
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322) ~[spring-core-6.0.11.jar:6.0.11]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
    *__checkpoint ⇢ org.springframework.web.cors.reactive.CorsWebFilter [DefaultWebFilterChain]
    *__checkpoint ⇢ HTTP POST "/import/insert" [ExceptionHandlingWebHandler]
Original Stack Trace:
        at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322) ~[spring-core-6.0.11.jar:6.0.11]
        at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195) ~[spring-core-6.0.11.jar:6.0.11]
        at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175) ~[spring-core-6.0.11.jar:6.0.11]
        at org.springframework.batch.core.repository.dao.JdbcJobExecutionDao.insertParameter(JdbcJobExecutionDao.java:443) ~[spring-batch-core-5.0.2.jar:5.0.2]
        at org.springframework.batch.core.repository.dao.JdbcJobExecutionDao.lambda$insertJobParameters$0(JdbcJobExecutionDao.java:429) ~[spring-batch-core-5.0.2.jar:5.0.2]
        at org.springframework.jdbc.core.JdbcTemplate.lambda$batchUpdate$5(JdbcTemplate.java:1139) ~[spring-jdbc-6.0.11.jar:6.0.11]
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:656) ~[spring-jdbc-6.0.11.jar:6.0.11]
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:699) ~[spring-jdbc-6.0.11.jar:6.0.11]
        at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:1133) ~[spring-jdbc-6.0.11.jar:6.0.11]
        at org.springframework.batch.core.repository.dao.JdbcJobExecutionDao.insertJobParameters(JdbcJobExecutionDao.java:426) ~[spring-batch-core-5.0.2.jar:5.0.2]
        at org.springframework.batch.core.repository.dao.JdbcJobExecutionDao.saveJobExecution(JdbcJobExecutionDao.java:247) ~[spring-batch-core-5.0.2.jar:5.0.2]
        at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:178) ~[spring-batch-core-5.0.2.jar:5.0.2]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.11.jar:6.0.11]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.0.11.jar:6.0.11]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.0.11.jar:6.0.11]
        at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-6.0.11.jar:6.0.11]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391) ~[spring-tx-6.0.11.jar:6.0.11]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.0.11.jar:6.0.11]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.11.jar:6.0.11]
        at org.springframework.batch.core.repository.support.AbstractJobRepositoryFactoryBean$1.invoke(AbstractJobRepositoryFactoryBean.java:207) ~[spring-batch-core-5.0.2.jar:5.0.2]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.11.jar:6.0.11]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:244) ~[spring-aop-6.0.11.jar:6.0.11]
        at jdk.proxy4.$Proxy87.createJobExecution(Unknown Source) ~[na:na]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:145) ~[spring-batch-core-5.0.2.jar:5.0.2]
        at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:59) ~[spring-batch-core-5.0.2.jar:5.0.2]
fmbenhassine commented 1 year ago

This is related to the new way of persisting job parameters in Spring Batch 5, see What’s New in Spring Batch 5.0 -> Support for any type as a job parameter.

By default, Spring Batch uses the DefaultConversionService from Spring Framework, which does not support converting FilePart to/from String out-of-the-box. So you need to register a converter for that type. If you use @EnableBatchProcessing, you can register a bean of type ConfigurableConversionService and add enrich the default conversion service with converters for the custom type:

@Configuration
@EnableBatchProcessing
class MyJobConfiguration {

    @Bean
    public ConfigurableConversionService conversionService() {
        DefaultConversionService conversionService = new DefaultConversionService();
        // add converter from String to FilePart and vice-versa in conversionService
        return conversionService;
    }

}

That said, any reason for using the FilePart object as job parameter instead of the file name or path (as String)?

premcody commented 1 year ago

I really dont want to do file processing (read and write i/o) cause some performance in the batch processing. Its bummer that we couldn't able to retain something that was working before. We had few items others got broken due to this conversion.

fmbenhassine commented 1 year ago

You can get the same functionality if you provide a converter for FilePart. Otherwise, as part of the migration to v5, I would recommend to take the opportunity to improve the design of your application's job parameters by using the file name/path as a job parameter instead of the file itself (so somehow, it is good that the new way of handling job parameters in Spring Batch 5 makes it difficult to implement a bad practice 😉).