0

I'm working on a java method that receive a list of items, generate a report (PDF file) of each of them, and finally zip all these PDF files into a zip file.

This is my simplified method:

List<File> pdfs = new ArrayList<File>();

public void generateZipOfItems(List<Item> items) {
  for(Item item : items) {
    ReportThread report = generatePDFReport(item);

    report.addListener(new ReportExecuteListener() {
      @Override
      public void finishReport(ReportExecutionEvent reportExecutionEvent) {
         // when report is finished, add the PDF file on arrayList
         pdfs.add(reportExecutionEvent.getReport());
      }
  }

  createZipFromFileList(pdfs); // this method just zip the files, and it is working very well

}

In a perfect scenario, the method should generate the report of item A, add it on "pdfs" array and repeat for item B, C, and so on. Finally, with all pdfs on array, it should zip the file.

Problem is, the loop is not waiting for the listener to finish generating the report files, and the zip file is being created without items.

The loop should wait for the listener before iterate. How can I do that?

aseolin
  • 1,184
  • 3
  • 17
  • 35
  • "inside a loop" has no meaning here, since the listeners have been added to reports, and will finish long after the loop is done. One solution is to use a count down latch and only create the zip file when the count has completed. – Hovercraft Full Of Eels Aug 08 '18 at 19:00

1 Answers1

-1

I would use a parallel stream. Something like:

 createZipFileFromFileList(items.parallelStream()
                                .map(item -> generatePDFReport(item))
                                .collect(Collectors.toList()));

EDIT: In this refactoring, generatePDFReport() would return the resulting PDF object itself, rather than a ReportThread.

Kevin Swan
  • 147
  • 12
  • 1
    that's not how the code works. Where is the listener in the code you posted? – f1sh Aug 08 '18 at 19:10
  • The listener is unnecessary. In the OP's sample code, the listener only exists in order to add the finished PDF to the collection once it's been processed. Such functionality is "free" using parallel streams. The lamdba function is invoked in the context of the threads. In addition, the original poster's code had a problem in that if the processing completed before the listener could be registered, the listener would never be invoked. – Kevin Swan Aug 08 '18 at 19:13
  • your invocation ``collect`` returns a ``List``, not a ``List``. – f1sh Aug 08 '18 at 19:23
  • You're correct, it's possible I oversimplified my answer. In my refactoring, there'd be no need for threads at all, and `generatePDFReport()` could instead return the resulting PDF document itself, rather than a thread. I've edited my answer accordingly. – Kevin Swan Aug 08 '18 at 19:25
  • but that's not how the code works. OP works with a thread model and you get notified about the thread's result using the listener. What you call a "refactoring" is simply changing what OP is required to work with. – f1sh Aug 08 '18 at 19:42
  • The apparent purpose of the OP's code is to employ multiple threads to farm out the "grunt work" of rendering PDF documents in an effort to improve efficiency. My solution leverages built-in parallelism of the core Java library to achieve the same thing, with much less code on the front end. – Kevin Swan Aug 08 '18 at 19:44
  • Your solution simply offers using a stream but does not solve the problem of "how do I wait for all threads to finish to gather a list of results" by simply saying "the asynchronicity is nothing i want to care about" which is not how programming works – f1sh Aug 08 '18 at 19:55