0
votes

I'm using NiFi ExecuteScript to call a Groovy script that extracts text from PDFs. When it fails to extract, an Exception is supposed to be thrown and the flowfile is redirected to REL_FAILURE. Some PDFs go through fine, and some give the error:

ExecuteScript[id=9a39e0cb-ebcc-31e4-a169-575e367046e9] Failed to process session due to javax.script.ScriptException: javax.script.ScriptException: java.lang.IllegalStateException: StandardFlowFileRecord[uuid=2d6540f7-b7a2-48c7-8978-6b90bbfb0ff5,claim=StandardContentClaim [resourceClaim=StandardResourceClaim[id=1538596326047-12, container=default, section=12], offset=2134, length=930225],offset=0,name=1  i-9 INS rev 87   05-07-87.pdf,size=930225] already in use for an active callback or an OutputStream created by ProcessSession.write(FlowFile) has not been closed: org.apache.nifi.processor.exception.ProcessException: javax.script.ScriptException: javax.script.ScriptException: java.lang.IllegalStateException: StandardFlowFileRecord[uuid=2d6540f7-b7a2-48c7-8978-6b90bbfb0ff5,claim=StandardContentClaim [resourceClaim=StandardResourceClaim[id=1538596326047-12, container=default, section=12], offset=2134, length=930225],offset=0,name=1  i-9 INS rev 87   05-07-87.pdf,size=930225] already in use for an active callback or an OutputStream created by ProcessSession.write(FlowFile) has not been closed

My (simplified) code is below:

def flowFile = session.get()
if(!flowFile) return
flowFile = session.write(flowFile, { inputStream, outputStream ->
    try {
        // Load PDF from inputStream and parses text into a JSON string

        // If nothing can be extracted, throw an exception so the flowfile
        // can be routed to REL_FAILURE and processed further down the NiFi pipeline
        if(outputLength < 15) {
            throw new Exception('No output, send to REL_FAILURE')
        }

        // Write the string to the flowFile to be transferred
        outputStream.write(json.getBytes(StandardCharsets.UTF_8))
    } catch (Exception e){
        System.out.println(e.getMessage())
        session.transfer(flowFile, REL_FAILURE)
    }
} as StreamCallback)
session.transfer(flowFile, REL_SUCCESS)

It pretty closely follows the cookbook posted in the Hortonworks community forums, and the author even mentions that closing is handled automatically.

I think that the error is caused when a PDF fails to process. This throws an exception, which should be caught in the try{}catch{} and then be transferred to REL_FAILURE. Instead it appears that the catch{} is never getting called, so the outputStream object is never closed. It works as expected and gets caught just fine when I run the same Groovy code outside of NiFi.

If you want to try running it on your own server

NiFi template

full Groovy code.

Sample PDF

1

1 Answers

2
votes

The try/catch should be outside the session.write() call rather than in the callback. Inside the callback, throw an IOException rather than an Exception, that should be propagated up through the session.write() and should enter your catch clause outside. Then you can transfer the flow file to failure (you shouldn't be allowed to transfer the flow file while you're writing to it).