1

I would like to be able to report a certain method's progress in Spring Boot. I used a seperate class which I store the current status in and return as the current view: It looks like this:

public class SearchTableReloadState {

    //STATIC STORAGE

    public static long TABLE_ROW_COUNT = 0;
    public static long CURRENT_OFFSET = 0;
    public static long CURRENT_LIMIT = 0;
    public static long DEFAULT_LIMIT = 20000;

    public static void reset() {
        TABLE_ROW_COUNT = 0;
        CURRENT_OFFSET = 0;
        CURRENT_LIMIT = DEFAULT_LIMIT;
    }

    public static void setDefaultLimit(long defaultLimit) {
        DEFAULT_LIMIT = defaultLimit;
    }

    // VIEWMODEL
    public long tableRowCount = 0;
    public long currentOffset = 0;
    public long currentLimit = 0;

    public static SearchTableReloadState getState() {
        SearchTableReloadState reloadState = new SearchTableReloadState();
        reloadState.tableRowCount = TABLE_ROW_COUNT;
        reloadState.currentOffset = CURRENT_OFFSET;
        reloadState.currentLimit = CURRENT_LIMIT;
        return reloadState;
    }
}

And the methods:

@RequestMapping(value = {"/manage/searchtable/reload/state"}, method = RequestMethod.GET)
public @ResponseBody SearchTableReloadState searchTableReloadState() {
    return SearchTableReloadState.getState();
} 

@ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = {"/manage/searchtable/reload"}, method = RequestMethod.GET)
    public void searchTableReload() throws ResourceAlreadyExistsException, ParameterMissingIdException {
     SearchTableReloadState.reset();
     SearchTableReloadState.TABLE_ROW_COUNT = productDataReferenceDao.countJobReferences();
     productDataReferenceDao.truncateSearchTable();
     while (SearchTableReloadState.CURRENT_OFFSET < SearchTableReloadState.TABLE_ROW_COUNT) {
          ... long running task
          ....
          SearchTableReloadState.CURRENT_OFFSET += SearchTableReloadState.CURRENT_LIMIT;
        }
}

The method with the /state would report the current state, so I could call these with Ajax on a site. Problem is, If I start the long running one, the state report request won't complete until the long running did not complete. I thought Spring uses separate threads for each request. Do I need to implement threading in Spring?

If I use the @Async annotation for the long running process, it works like I expected, but I still don't understand, why could two separate HTTP requests for a different method block each other!

szab.kel
  • 2,356
  • 5
  • 40
  • 74
  • nope i dont think it works like that , you have another issue somewhere else , but to confirm that it doesnt works with the way you mentioned , could you please open up the spring logs , and post them here. If you can add to the spring appender the thread id , in order to check that it will use another thread to process the request – AntJavaDev Aug 30 '16 at 09:23
  • How can I access Spring Boot's log? I am running it in and IDE, so you mean what the running process prints to the console window? – szab.kel Aug 30 '16 at 09:25
  • yes are you using log4j ? – AntJavaDev Aug 30 '16 at 09:27
  • I am using Spring Boot's default configuration, I did not configure another Logger – szab.kel Aug 30 '16 at 09:30
  • the default spring boot logger uses an `application.properties`. have you created one ? – AntJavaDev Aug 30 '16 at 09:32
  • Yes, I have two rows for API logging, `logging.file=API.LOG logging.path=D:/apilog` but the api.log file never got created – szab.kel Aug 30 '16 at 09:35
  • ok gimme 2 seconds ill create a proper configuration and share it , which spring boot version are you using ? – AntJavaDev Aug 30 '16 at 09:39
  • ` 1.3.5.RELEASE` – szab.kel Aug 30 '16 at 09:45
  • ok got it sorry for the delayed response , add this line to your application.properties file `logging.level.org.springframework=DEBUG` , and then try to make 2 http calls on the same time , you should see smth like `nio-8080-exec-3` and `nio-8080-exec-1` which means that your request is being processed by different threads , also you could simple log the `Thread.currentThread().getId();` inside the Controllers method , also check this answer here , it might be related with your issue [answer](http://stackoverflow.com/questions/25399400/maximum-client-request-thread-pool-size-in-spring) – AntJavaDev Aug 30 '16 at 11:44
  • but still i tried to replicate your issue by calling `Thread.sleep(500000);` and it operates properly , so i suppose you haven't posted all your code , so somewhere you perform a custom synchronization or all your threads in the connector's pool are unavailable due to the long task – AntJavaDev Aug 30 '16 at 11:55
  • I tried it, all I see is exec-1, even though I called the state method twice. – szab.kel Aug 30 '16 at 12:22
  • you called it twice on the same time , or twice after some time ? – AntJavaDev Aug 30 '16 at 12:46
  • While the long running was taking its time, I called the state method twice, one after another. Both got blocked and waited till the long task is completed, then got the result. – szab.kel Aug 30 '16 at 12:49
  • Are you, by any chance, using debugger breakpoint to emulate long running task? – chimmi Aug 30 '16 at 13:46
  • I use debug mode, but I did not place breakpoints into the task. It queries a big database table, so it takes long to complete. – szab.kel Aug 30 '16 at 13:58
  • ok could you try on last thing , close the logger(set the threshold back to ERROR) and at your controller , inside both methods , at their start , append this line `System.out.println(Thread.currentThread().getId())` surrounded by a try catch , and please share your log to see what it prints. Also i suppose you havent set a property like `server.tomcat.max-threads` ???? – AntJavaDev Aug 30 '16 at 16:18
  • Setting server.tomcat.max-threads to 150 did not solve the problem. I put the row you posted to the beginning of both of my methods. When I call the long one, it prints number 34 or 36, while it is working I called the state method but no number got printed, only after the long method succeeded. – szab.kel Aug 31 '16 at 12:10
  • I tried it with `@Async` annotation on the long method and worked. – szab.kel Sep 01 '16 at 05:50

1 Answers1

0

If I use the @Async annotation on the method that is supposed to take a long time, the HTTP Request calling it will get a response immediately and it will run in the background and I can call the state method as I expected. Even though it is working, I still don't know why it won't work without the asynchronous execution.

If you want to use the @Async annotation, you have to put the @EnableAsync annotation on the class you used the @SpringBootApplication and/or @EnableAutoConfiguration.

I hope someone can provide a better answer later.

szab.kel
  • 2,356
  • 5
  • 40
  • 74