Catch block not called on Groovy exception in NiFi ExecuteScript processor - groovy

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

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).

Related

Spring Integration Java DSL: How to continue after error situation with the split and the aggregate methods?

My program does the following in the high level
Task 1
get the data from the System X
the Java DSL split
post the data to the System Y
post the reply data to the X
the Java DSL aggregate
Task 2
get the data from the System X
the Java DSL split
post the data to the System Y
post the reply data to the X
the Java DSL aggregate
...
The problem is that when one post the data to the System Y sub task fails, the error message is correctly send back to the System X, but after that any other sub tasks or tasks are not executed.
My error handler does this:
...
Message<String> newMessage = MessageBuilder.withPayload("error occurred")
.copyHeadersIfAbsent(message.getPayload().getFailedMessage().getHeaders()).build();
...
Set some extra headers etc.
...
return newMessage;
What could be the problem?
Edit:
I debugged the Spring Integration. In the error situation only first error message comes to the method AbstractCorrelatingMessageHandler.handleMessageInternal. Other successfull and failing messages not come to the method.
If there are not errors all the messages come to the method and finally the group is released.
What could be wrong in my program?
Edit 2:
This is working:
Added the advice for the Http.outboundGateway:
.handle(Http.outboundGateway(...,
c -> c.advice(myAdvice()))
and the myAdvice bean
#Bean
private Advice myAdvice() {
return new MyAdvice();
}
and the MyAdvice class
public class MyAdvice<T> extends AbstractRequestHandlerAdvice {
#SuppressWarnings("unchecked")
#Override
protected Object doInvoke(final ExecutionCallback callback, final Object target, final Message<?> message)
throws Exception {
...
try {
result = (MessageBuilder<T>) callback.execute();
} catch (final MessageHandlingException e) {
take the exception cause for the new payload
}
return new message with the old headers and replyChannel header and result.payload or the exception cause as a payload
}
}
There is nothing wrong with your program. That's exactly how regular loop works in Java. To catch exception for each iteration and continue with other remaining item you definitely need a try..catch in the Java loop. So, something similar you need to apply here for the splitter. It can be achieved with the ExpressionEvaluatingRequestHandlerAdvice, an ExectutorChannel as an output from the splitter or with the gateway call via service activator on the splitter output channel.
Since the story is about an aggregator afterward, you still need to finish a group somehow and this can be done only with some error compensation message to be emitted from the error handling to return back to the aggregator's input channel. In this case you need to ensure to copy request headers from the failedMessage of the MessagingException thrown to the error flow. After aggregation of the group you would need to sever messages with error from the normal ones. That can be done only with the special payload or you may just have an exception as a payload for the proper distinguishing errors from normal messages in the final result from the aggregator.

C++/winRT coroutine try/catch not working

I have a C++/winRT coroutine that reads a file. The following 'gets' the file:
StorageFile _fileDoubles = co_await _turboCalc.GetFileAsync(L"FileDoubles.dbo");
Which executes properly because the file exists. If I change its name to a nonexistent file, my app fails with this error message:
Exception thrown at 0x76903AC2 in IBuffer.exe: Microsoft C++ exception : winrt::hresult_error at memory location 0x02FBE338.
If I enclose it in the following try/catch, My app still fails and with the same error message.
try {
StorageFile _fileDoubles = co_await_turboCalc.GetFileAsync(L"FileDoublesZZZ.dbo"); //doesn't exist
}
catch (hresult_error const & e) {
printf("error: %ls\n", e.message().c_str());
}
What is the proper way to use try/catch in a couroutine?

Multithreading code testing by Spock

I want to verify exception throwing in one of my running threads. This is piece of my test code:
then:
def e = thrown(RequestFormatException)
e.message == "Incorrect first line: INCORRECT LINE"
When I run this I get next messages:
Exception in thread "Thread-1" by.westside.staircase.core.exception.RequestFormatException: Incorrect first line: INCORRECT LINE
at by.westside.staircase.core.util.HttpUtil.parseHttpRequest(HttpUtil.kt:19)
at by.westside.staircase.core.server.ServerThread.run(ServerThread.kt:26)
at java.lang.Thread.run(Thread.java:745)
Expected exception of type 'by.westside.staircase.core.exception.RequestFormatException', but no exception was thrown
at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:79)
at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:66)
at by.westside.staircase.core.server.SyncServerSpec.should throw exception in incorrect first line case(SyncServerSpec.groovy:26)
Spock, like JUnit, can only assert on exceptions thrown from the thread executing the test, not "any thread in the application". Your exceptions are not caught by spock, and can't be asserted.
You can play with Thread.uncaughtExceptionHandler but you should probably unit-test the runnable executed in your thread - or implement some error handling in your business logic, and test this part of the code.
I think another option is to actually catch the exception in your test case and assert on that. here is a snippet of my code (written in Groovy Spock):
def exceptionThrown = false
def exceptionMessage
def thread = new Thread( {_ ->
try{
//code you are testing
} catch(Exception e) { // change this to the exception you want to catch
exceptionThrown = true
exceptionMessage = e.getMessage()
}
})
then: "the right exception should be thrown"
exceptionThrown
exceptionMessage = "I am thrown" //this should be your error message
I ran into the same issue and took a simple, hokey route. In the spirit of "good software is testable software" I added a flag and asserted on that, labeling it: // only for testing. Which, of course, will be ignored down the road.
thrown(RequestFormatException)
this should be in your first line after then: as this is the constraint imposed by spock.
Whenever thrown or notThrown is called in it should be the first statement.
Note: thrown and notThrown both return true and hence there should be no comparison operator as well.
Hence, In your case , it should be like below:
then:
thrown(RequestFormatException)

Exception handling with sql.rows from a Quartz job

I am using a groovy.sql.Sql.rows(sql, parameters) inside a quartz job to call a stored procedure. At times the stored proc times out but it doesn't propagate the exception to the calling code. For ex in the following code the exception never makes it to the try/catch block-
try{
res = db.rows(sql, parameter) //db is of type groovy.sql.Sql
}
catch(Exception e) {
log.error("error in calling the proc", e)
}
The only thing I see in the logs is a warning from sql.SQL which looks like this -
[quartzScheduler_Worker-1] WARN sql.Sql - Failed to execute: { call stored_prod_name(?, ?, ?, ?) } because: JZ006: Caught IOException: java.net.SocketTimeoutException: Read timed out
After this my entire JVM hangs! I would like to recover from a timeout but I can't seem to catch the exception. What am I doing wrong?

play file wav j2me - IllegalArgumentException at Manager.createPlayer()

I got problem about play wav file in my application.
This is my error:
java.lang.IllegalArgumentException
at javax.microedition.media.Manager.createPlayer(), bci=8
at Tajwid.Tajwid.run(Tajwid.java:649)
at Tajwid.Tajwid.actionPerformed(Tajwid.java:186)
at com.sun.lwuit.util.EventDispatcher.fireActionSync(), bci=19
at com.sun.lwuit.util.EventDispatcher.fireActionEvent(EventDispatcher.java:257)
This is my code:
public void run() {
try {
InputStream is = getClass().getResourceAsStream("/tes.wav");
player = Manager.createPlayer(is, "audio/x-wav");
player.realize();
// get volume control for player and set volume to max
vc = (VolumeControl) player.getControl("VolumeControl");
if (vc != null) {
vc.setLevel(100);
}
player.prefetch();
player.start();
} catch (Exception e) {
e.printStackTrace();
}
Device Configuration : CLDC-1.1
Device Profile MIDP 2.0
Error message you've got has sufficient information to figure what went wrong in the code.
Look at it a bit closer:
java.lang.IllegalArgumentException
at javax.microedition.media.Manager.createPlayer()...
It says something went wrong in Manager.createPlayer(). From your code, it is apparent that you use method Manager.createPlayer(java.io.InputStream stream, java.lang.String type).
If you look into API documentation for the method you use (available online), you'll find the explanation when this exception occurs:
Throws:
java.lang.IllegalArgumentException - Thrown if stream is null.
Above means that stream parameter (is in your code) passed to the method is null.
You could add some logging right after initialization of the is to debug this issue easier:
InputStream is = getClass().getResourceAsStream("/tes.wav");
// add some logging to see if initialization was OK or not:
System.out.println("input stream is null: [" + (is == null) + "]");
That way, when running your MIDlet in emulator, you will see whether is was initialized as expected or not.
Actually, looking at the code I would guess that you made a typo in file name passed to getResourceAsStream: "/tes.wav" looks like a mis-typed "/test.wav".

Resources