4

I am essentially trying to convert a tiff file to pdf using itext, which is fairly simple. But from what I can see TiffImage.getTiffImage is taking a lot of time to execute for larger files.

My requirement is to use FutureTask and ExecutorService to provide a multi threaded solution. Here is my current code:

import java.util.concurrent.Callable;

import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.RandomAccessFileOrArray;
import com.itextpdf.text.pdf.codec.TiffImage;

public class ProcessTiffImage implements Callable<Image>{

    RandomAccessFileOrArray tiffFile;
    int pageNo;

    public ProcessTiffImage(RandomAccessFileOrArray tiffFile, int pageNo){
        this.tiffFile = tiffFile;
        this.pageNo = pageNo;
    }

    public Image call() throws Exception {
        Image image = TiffImage.getTiffImage(tiffFile, pageNo);

        return image;

    }

}

and the convert method is

public boolean convert(Document document) {

        int numOfThreads = Runtime.getRuntime().availableProcessors()  ;
        ExecutorService service = Executors.newFixedThreadPool(numOfThreads );
        List<FutureTask<Image>> taskList = new ArrayList<FutureTask<Image>>();
        List<Image> imageList = new ArrayList<Image>();

        for (int page = 1; page <= numOfPages; page++) {
            FutureTask<Image> futureTask = new FutureTask<Image>(new ProcessTiffImage(tiffFile, page));
            taskList.add(futureTask);
            service.execute(futureTask);
        }

        try {
            // Wait until all results are available
            for (FutureTask<Image> future : taskList) {

                imageList.add(future.get());
            }

        } catch (InterruptedException ex) {
            ex.printStackTrace();
        } catch (ExecutionException ex) {
            ex.printStackTrace();
        }

        service.shutdown();


        boolean success = generatePdf(document, imageList);
        return success;
    }

But I'm getting a NullPointerException at future.get(). The problem is the execution is not waiting for TiffImage.getTiffImage(tiffFile, pageNo) to finish. Hence I'm unable to create the Image List. Any help would be very much appreciated.

Stack Trace

java.util.concurrent.ExecutionException: java.lang.NullPointerException
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:188)
    at com.app.convertor.TiffParser.convert(TiffParser.java:107)
    at com.app.start.TiffToPdf.main(TiffToPdf.java:40)
Caused by: java.lang.NullPointerException
Caused by: java.lang.NullPointerException
    at com.itextpdf.text.pdf.codec.TIFFDirectory.getFieldAsLong(TIFFDirectory.java:467)
    at com.itextpdf.text.pdf.codec.TIFFDirectory.getFieldAsLong(TIFFDirectory.java:477)
    at com.itextpdf.text.pdf.codec.TiffImage.getTiffImage(TiffImage.java:124)
    at com.itextpdf.text.pdf.codec.TiffImage.getTiffImage(TiffImage.java:106)
    at com.app.processor.ProcessTiffImage.call(ProcessTiffImage.java:20)
    at com.app.processor.ProcessTiffImage.call(ProcessTiffImage.java:1)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
beingsuplab
  • 155
  • 1
  • 1
  • 12

2 Answers2

3

Don't create the Future yourself, ask the executor to create it for you,(ie: submit a Callable)

List<Future<Image>> taskList = new ArrayList<Future<Image>>();

 for (int page = 1; page <= numOfPages; page++) {
     Future<Image> futureTask = service.submit(new ProcessTiffImage(tiffFile, page));
     taskList.add(futureTask);
 }

for (Future<Image> future : taskList) {
   imageList.add(future.get());
}

Note: consider using ExecutorCompletionService instead.

Sleiman Jneidi
  • 22,907
  • 14
  • 56
  • 77
  • java.util.concurrent.ExecutionException: ExceptionConverter: java.io.EOFException at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:188) at com.app.convertor.TiffParser.convert(TiffParser.java:118) at com.app.start.TiffToPdf.main(TiffToPdf.java:40) – beingsuplab Oct 20 '15 at 12:01
  • 1
    Yes, this is sensible advice, but I don't think it solves OP's problem. – chiastic-security Oct 20 '15 at 12:03
1

This seems to be happening because RandomAccessFileOrArray is not thread safe.

The exception you're getting in the future.get() call is an underlying exception in the TiffImage.getTiffImage() call, and that seems to be happening because that method isn't happy with the TIFF data it's being passed. In particular, it's looking to get some field out, and it seems it's not there. Possibly image height or width?

But that's happening because the RandomAccessFileOrArray is being consumed in multiple threads. Every time one thread moves the pointer in the file, it screws up what the other threads are doing.

You need to use RandomAccessFileOrArray.createView() to get a new view of the file to pass to each thread:

Image image = TiffImage.getTiffImage(tiffFile.createView(), pageNo);
chiastic-security
  • 20,430
  • 4
  • 39
  • 67
  • I understand your point, but what's weird is that if I debug this code in any standard IDE is works perfectly and is happy with the data I pass to the getTiffImage(). But running the same, is causing the issue. Thanks for the help though, will try putting in a try/catch to monitor the data. – beingsuplab Oct 20 '15 at 12:14