2

I want to send a list from rest client to rest web service which will start a job in Spring Batch. Is that possible or must I save the list in database/flatfile before start the job and read the input values from database/flatfile? I guess someone pointed how do it in certain Jira issue (see below) but I couldn't figure out at least a basic idea how to move forward. I placed below my controller and how I am trying to cast it to JobParameter. I placed the Jira link and the possible direction perhaps I should take but I really didn't understand the suggestion in this Jira issue. I added below the SoapUi client and finally I pasted at the bottom my entire error log.

My Controller:

@RequestMapping(value = "runit/family", method = RequestMethod.POST)
       public void handle(@RequestBody List<Person> myFamily) throws Exception {
              System.out.println(myFamily); //until here, it is fine

              JobParameters jobParameters = new JobParametersBuilder()
                           .addParameter("parametersReceived", (JobParameter) myFamily)
                           .addLong("time", System.currentTimeMillis()).toJobParameters();

              jobLauncher.run(job, jobParameters);
       }

SoapUi (post):

[{"firstName":"aa","lastName":"bb"}]

Pojo

public class Person {

    private String lastName;
    private String firstName;
    ...getters and setters

Possible way to work but I am really confused about this Jira issue. Honestly, I was expecting some way more simple like I am trying with JobParameter

https://jira.spring.io/browse/BATCH-966 (this suggestion is from 2009. Probably some more up-to-date technique is available)
<bean class="MyItemReader">
<property name="dynamicValuesHashMap" ref="map"/>
...
</bean>

<bean class="MyLauncher">
<property name="dynamicValuesHashMap" ref="map"/>
...
</bean>

The entire error:

java.lang.ClassCastException: java.util.ArrayList cannot be cast to org.springframework.batch.core.JobParameter

       at hello.BatchController.handle(BatchController.java:42)

       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

       at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

       at java.lang.reflect.Method.invoke(Method.java:497)

       at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)

       at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)

       at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)

       at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776)

       at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705)

       at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)

       at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)

       at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)

       at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)

       at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869)

       at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)

       at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)

       at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)

       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)

       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

       at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)

       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

       at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)

       at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)

       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

       at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)

       at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)

       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

       at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)

       at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)

       at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)

       at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)

       at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)

       at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)

       at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)

       at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)

       at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)

       at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)

       at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)

       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

       at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

       at java.lang.Thread.run(Thread.java:745)
Jim C
  • 3,957
  • 25
  • 85
  • 162
  • Is this one time scenario ? Do you need to run job just once ? The parameter you are passing i.e. List of Person are fixed. What should happen if the job that is scheduled need to run again ? It seems to me its not a batch job. – Utkarsh Jul 25 '15 at 02:44
  • 1
    You can trigger the job from the webservice or controller. But I think you need to get the List of person from database during execution of job in your Job class. – Utkarsh Jul 25 '15 at 02:49
  • There is certain application A. An user will generate a report in this application A. This report is the input for the batch which is part of Application B domain. The user will have two ways to send this report from App A to App B: (1) just click in the button SEND so the reason I want to receive this input via rest service and start the job. (2) Edit the spreadsheet and save it in some folder which App B will read each 5 minutes to start the job. I can't think it is not a batch job. – Jim C Jul 25 '15 at 02:51
  • Utkarsh, so, in your opinion there is no way to pass a list as a jobParameter? If so, from design perspective, isn't a negative point related to Spring Batch because I need two events in chain: 1 - app B receives the list and persist it then 2 - app B triggers the job to start – Jim C Jul 25 '15 at 02:57
  • 1
    Add the above information in the question. Your point 1 Batch job cannot communicate like this. While Point 2 us completely feasible. You can either read file from your job class or instead store the spreadsheet sheet content in db table and read it from database – Utkarsh Jul 25 '15 at 02:58
  • 1
    By design itself batch jobs are not event based. They are time based. Its not a negative point. How batch job will know when the application B user is sending report? You can't add listeners because these are two different application. – Utkarsh Jul 25 '15 at 03:00
  • So do you mean that it is not a good desing expose the Spring Batch throw Rest Web Service for receive the input parameters? – Jim C Jul 25 '15 at 03:01
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/84237/discussion-between-jim-c-and-utkarsh). – Jim C Jul 25 '15 at 03:05
  • 1
    As I said before, You can trigger the job manually ( I think this is what you are trying to do from rest service). Its valid if your requirement are like that. But you can't pass list of person from rest service to batch job. – Utkarsh Jul 25 '15 at 03:06

2 Answers2

2

If you look at actual schema which stores meta data for spring batch you will see list of available types supported by job to be parameters (there is string_val, date_val, long_val, double_val). So when job is started each job parameter is persisted in DB with key_name as name of parameter and value stored in one of mentioned _val tables. type_cd gives a hint which type was used.

Also documentation for JobParameter gives a hint what can be used as job parameter:

Domain representation of a parameter to a batch job. Only the following types can be parameters: String, Long, Date, and Double. The identifying flag is used to indicate if the parameter is to be used as part of the identification of a job instance.

I think best way would be either to create table in DB which stores list of parameters and pass id of that record as JobParameter or to serialize list to json and pass it as String in job as JobParameter. If you go with second option be aware that string_val is stored in DB as varchar 250 so limit is 250 characters.

Nenad Bozic
  • 3,724
  • 19
  • 45
0

Batch jobs are meant to be Time based not Event based. So, you can't pass the information from Rest service to your Job instance. Before you trigger the job in your controller method either write the list in temporary flat file or store it in a database, from where your Job can read it.

Utkarsh
  • 589
  • 3
  • 19