I am upgrading from Mule 3.4 to 3.7. I have applications that handle exceptions by alerting administrators by email. In 3.4, I used to have the mule expression #[exception.getSummaryMessage()] for printing and emailing the information. It used to provide me the complete exception message with the exception stack as below:
Message : groovy.lang.MissingPropertyException: No such property: z for class: Script1 (javax.script.ScriptException)
Type : org.mule.api.transformer.TransformerException
Code : MULE_ERROR--2
JavaDoc : http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/transformer/TransformerException.html
Transformer : ScriptTransformer{this=11810917, name='ScriptTransformer', ignoreBadInput=false, returnClass=SimpleDataType{type=java.lang.Object, mimeType='/', encoding='null'}, sourceTypes=[]}
Exception stack is:
1. No such property: z for class: Script1 (groovy.lang.MissingPropertyException)
org.codehaus.groovy.runtime.ScriptBytecodeAdapter:53 (null)
2. groovy.lang.MissingPropertyException: No such property: z for class: Script1 (javax.script.ScriptException)
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl:326 (http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/script/ScriptException.html)
3. groovy.lang.MissingPropertyException: No such property: z for class: Script1 (javax.script.ScriptException) (org.mule.api.transformer.TransformerException)
org.mule.module.scripting.transformer.ScriptTransformer:56 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/transformer/TransformerException.html)
In 3.7, the same expression only gives me the exception message as below:
groovy.lang.MissingPropertyException: No such property: z for class: Script1 (javax.script.ScriptException). Message payload is of type: NullPayload (org.mule.api.transformer.TransformerMessagingException). Message payload is of type: NullPayload
Could someone guide me as to how I can retrieve the exception in the previous format or how this can be achieved with Mule 3.7?
I had to do something similar so I wrote a piece of Groovy code to extract the error message from the exception object:
def log = LogFactory.getLog(logCategory)
def cleanRootErrorMessage = null
// no matter what, we don't want Exceptions getting out of here!
try {
log.debug("Extracting exception root cause...")
def tmpException = exception
while (tmpException.cause != null && (tmpException.cause instanceof NullPointerException) == false) {
tmpException = tmpException.cause
}
cleanRootErrorMessage = tmpException.message
log.debug("Root cause: [$cleanRootErrorMessage]")
if (cleanRootErrorMessage ==~/^.*(Exception: |Error: )(.*)$/) {
cleanRootErrorMessage = (cleanRootErrorMessage =~/^.*(Exception: |Error: )(.*)$/).replaceAll('$2')
}
if(cleanRootErrorMessage == null) {
log.debug("Capturing exception.message...")
cleanRootErrorMessage = exception.message
}
log.debug("Filtered root cause: [$cleanRootErrorMessage]")
} catch (Exception e) {
log.error("Exception thrown ***inside*** the Root Error Message extraction code...", e)
cleanRootErrorMessage = 'Unknown Error'
}
return cleanRootErrorMessage
I hope it helps, if you want further help, feel free to find me on my twitter account from my profile.
Can you try #[exception.cause.message]
Related
I just involved the new project for API test for our service by using Gatling. At this point, I want to search query, below is the code:
def chnSendToRender(testData: FeederBuilderBase[String]): ChainBuilder = {
feed(testData)
exec(api.AdvanceSearch.searchAsset(s"{\"all\":[{\"all:aggregate:text\":{\"contains\":\"#{edlAssetId}_Rendered\"}}]}", "#{authToken}")
.check(status.is(200).saveAs("searchStatus"))
.check(jsonPath("$..asset:id").findAll.optional.saveAs("renderedAssetList"))
)
.doIf(session => session("searchStatus").as[Int] == 200) {
exec { session =>
printConsoleLog("Rendered Asset ID List: " + session("renderedAssetList").as[String], "INFO")
session
}
}
}
I declared the feeder already in the simulation scala file:
class GVRERenderEditor_new extends Simulation {
private val edlToRender = csv("data/render/edl_asset_ids.csv").queue
private val chnPostRender = components.notifications.notice.JobsPolling_new.chnSendToRender(edlToRender)
private val scnSendEDLForRender = scenario("Search Post Render")
.exitBlockOnFail(exec(preSimAuth))
.exec(chnPostRender)
setUp(
scnSendEDLForRender.inject(atOnceUsers(1)).protocols(httpProtocol)
)
.maxDuration(sessionDuration.seconds)
.assertions(global.successfulRequests.percent.is(100))
}
But Gatling test failed to run, showing this error: Exception in thread "main" java.lang.UnsupportedOperationException: There were no requests sent during the simulation, reports won't be generated
If I hardcode the #{edlAssetId} (put the real edlAssetId in that query), I will get result. I think I passed the parameter wrongly in this case. I've tried to print the output in console log but no luck. What's wrong with this code? I would appreciate your help. Thanks!
feed(testData)
exec(api.AdvanceSearch.searchAsset(s"{\"all\":[{\"all:aggregate:text\":{\"contains\":\"#{edlAssetId}_Rendered\"}}]}", "#{authToken}")
.check(status.is(200).saveAs("searchStatus"))
.check(jsonPath("$..asset:id").findAll.optional.saveAs("renderedAssetList"))
)
You're missing a . (dot) before the exec to attach it to the feed.
As a result, your method is returning the last instruction, ie the exec only.
I am trying to send email from the flow and stored all the email addresses in the yaml file like below
# Email
email:
toEmail: "abc.123#gg.org,def.456#gg.org"
fromEmail: "ms-dev#gg.org"
ccAddress: "abc123#gmail.com"
I am trying use the above values in to the send email connector like
<email:send doc:name="Send" doc:id="fd09c56f-eaed-44c4-ab06-aa0417f2fdbf" config-ref="Email_SMTP" subject="Error with SOW integration between D365 and Salesforce " fromAddress='#[p("email.fromEmail")]' toAddresses='#[p("email.toEmail") splitBy ","]' ccAddresses='#[p("email.ccAddress")]'>
<email:body contentType="text/html">
<email:content ><![CDATA[#[vars.emailBody]]]></email:content>
</email:body>
</email:send>
But when debugging Iam getting the error like below
org.mule.runtime.core.internal.exception.OnErrorPropagateHandler:
********************************************************************************
Message : "Cannot coerce String { class: java.lang.String } ("abc123#gmail.com" as String {class: "java.lang.String"}) to Array" evaluating expression: "p("email.ccAddress")".
Element : salesforce-proc-SendEmail_Flow/processors/1 # salesforce-proc:salesforce-proc-implementation.xml:566 (Send)
Element DSL : <email:send doc:name="Send" doc:id="fd09c56f-eaed-44c4-ab06-aa0417f2fdbf" config-ref="Email_SMTP" subject="Error with SOW integration between D365 and Salesforce " fromAddress="#[p("email.fromEmail")]" toAddresses="#[p("email.toEmail") splitBy ","]" ccAddresses="#[p("email.ccAddress")]">
<email:body contentType="text/html">
<email:content><![CDATA[
#[vars.emailBody]
]]></email:content>
</email:body>
</email:send>
Error type : MULE:EXPRESSION
FlowStack : at salesforce-proc-SendEmail_Flow(salesforce-proc-SendEmail_Flow/processors/1 # salesforce-proc:salesforce-proc-implementation.xml:566 (Send))
at listener-flow(listener-flow/errorHandler/0/processors/2 # salesforce-proc:salesforce-proc-implementation.xml:547 (Flow Reference))\
After fixing ccAddresses error I get below error
caf9-11ec-b461-025041000001] org.mule.runtime.core.internal.exception.OnErrorPropagateHandler:
********************************************************************************
Message : Error while sending email: Exception reading response
Element : salesforce-proc-SendEmail_Flow/processors/1 # salesforce-proc:salesforce-proc-implementation.xml:566 (Send)
Element DSL : <email:send doc:name="Send" doc:id="fd09c56f-eaed-44c4-ab06-aa0417f2fdbf" config-ref="Email_SMTP" subject="Error with SOW integration between D365 and Salesforce " fromAddress="#[p("email.fromEmail")]" toAddresses="#[p("email.toEmail") splitBy ","]" ccAddresses="#[p("email.ccAddress") splitBy ","]">
<email:body contentType="text/html">
<email:content><![CDATA[
#[vars.emailBody]
]]></email:content>
</email:body>
</email:send>
Error type : EMAIL:SEND
FlowStack : at salesforce-proc-SendEmail_Flow(salesforce-proc-SendEmail_Flow/processors/1 # salesforce-proc:salesforce-proc-implementation.xml:566 (Send))
at listener-flow(listener-flow/errorHandler/0/processors/2 # salesforce-proc:salesforce-proc-implementation.xml:547 (Flow Reference))
Can anyone please suggest what is that I am missing here.
The problem is that ccAddresses expects an array. Because you are using an expression to configure that attribute you need to convert the string value from the configuration file explicitly to an array, for example using the splitBy() function as you did in toAddresses.
Or if you will only use one address simply remove the expression and use a property placeholder (ccAddresses="${email.ccAddress}").
While running pyspark3 with pandas 1.1.5 and pyarrow 2.0.0 getting the below error:
Spark Code:
import pyarrow
import pandas as pd
df = pd.DataFrame({'col1' : [1,2,3], 'col2': [4,5,6]})
df_sp = spark.createDataFrame(df)
df_sp.cache().count()
schema = df_sp.schema
def dummy_udf(data):
return data
res = df_sp.groupby('col1').applyInPandas(dummy_udf, schema=schema)
print(res.cache().count())
print(res.toPandas())
Exception:
21/09/17 07:28:10 ERROR util.Utils: Uncaught exception in thread stdout writer for python3
java.lang.NoSuchMethodError: com.google.flatbuffers.FlatBufferBuilder.createString(Ljava/lang/CharSequence;)I
at org.apache.arrow.vector.types.pojo.Field.getField(Field.java:204)
at org.apache.arrow.vector.types.pojo.Schema.getSchema(Schema.java:178)
at org.apache.arrow.vector.ipc.message.MessageSerializer.serializeMetadata(MessageSerializer.java:187)
at org.apache.arrow.vector.ipc.message.MessageSerializer.serialize(MessageSerializer.java:165)
at org.apache.arrow.vector.ipc.ArrowWriter.ensureStarted(ArrowWriter.java:159)
at org.apache.arrow.vector.ipc.ArrowWriter.start(ArrowWriter.java:112)
at org.apache.spark.sql.execution.python.ArrowPythonRunner$$anon$1.$anonfun$writeIteratorToStream$1(ArrowPythonRunner.scala:86)
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
at org.apache.spark.util.Utils$.tryWithSafeFinally(Utils.scala:1439)
at org.apache.spark.sql.execution.python.ArrowPythonRunner$$anon$1.writeIteratorToStream(ArrowPythonRunner.scala:103)
at org.apache.spark.api.python.BasePythonRunner$WriterThread.$anonfun$run$1(PythonRunner.scala:397)
at org.apache.spark.util.Utils$.logUncaughtExceptions(Utils.scala:1996)
at org.apache.spark.api.python.BasePythonRunner$WriterThread.run(PythonRunner.scala:232)
21/09/17 07:28:10 ERROR util.SparkUncaughtExceptionHandler: Uncaught exception in thread Thread[stdout writer for python3,5,main]
java.lang.NoSuchMethodError: com.google.flatbuffers.FlatBufferBuilder.createString(Ljava/lang/CharSequence;)I
at org.apache.arrow.vector.types.pojo.Field.getField(Field.java:204)
at org.apache.arrow.vector.types.pojo.Schema.getSchema(Schema.java:178)
at org.apache.arrow.vector.ipc.message.MessageSerializer.serializeMetadata(MessageSerializer.java:187)
at org.apache.arrow.vector.ipc.message.MessageSerializer.serialize(MessageSerializer.java:165)
at org.apache.arrow.vector.ipc.ArrowWriter.ensureStarted(ArrowWriter.java:159)
at org.apache.arrow.vector.ipc.ArrowWriter.start(ArrowWriter.java:112)
at org.apache.spark.sql.execution.python.ArrowPythonRunner$$anon$1.$anonfun$writeIteratorToStream$1(ArrowPythonRunner.scala:86)
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
at org.apache.spark.util.Utils$.tryWithSafeFinally(Utils.scala:1439)
at org.apache.spark.sql.execution.python.ArrowPythonRunner$$anon$1.writeIteratorToStream(ArrowPythonRunner.scala:103)
at org.apache.spark.api.python.BasePythonRunner$WriterThread.$anonfun$run$1(PythonRunner.scala:397)
at org.apache.spark.util.Utils$.logUncaughtExceptions(Utils.scala:1996)
at org.apache.spark.api.python.BasePythonRunner$WriterThread.run(PythonRunner.scala:232)
21/09/17 07:28:10 WARN storage.BlockManager: Putting block rdd_25_69 failed due to exception org.apache.spark.SparkException: Python worker exited unexpectedly (crashed).
21/09/17 07:28:10 INFO memory.MemoryStore: MemoryStore cleared
21/09/17 07:28:10 INFO storage.BlockManager: BlockManager stopped
21/09/17 07:28:10 INFO util.ShutdownHookManager: Shutdown hook called
While checking createString() method implementations in FlatBufferBuilder.java class, it has two methods one is accepting CharSequence and another one is accepting the ByteBuffer as a argument.
public int createString(CharSequence s) {
}
public int createString(ByteBuffer s) {
}
While checking getField() method implementation in Field.java class, here it is passed String value.
public class Field {
private final String name;
public int getField(FlatBufferBuilder builder) {
int nameOffset = name == null ? -1 : builder.createString(name);
}
}
To fix this issue, we need to pass either CharSequence or ByteBuffer as the argument in getField() method.
Solution:
public int getField(FlatBufferBuilder builder) {
java.nio.ByteBuffer bb = java.nio.ByteBuffer.wrap(name.getBytes());
int nameOffset = name == null ? -1 : builder.createString(bb);
.......
}
When I try again in Spring Cloud Stream Reactive, a situation that I don't understand arises, so I ask a question.
In case of sending String type data per second, after processing in s-c-stream Function, I intentionally caused RuntimeException according to conditions.
#Bean
fun test(): Function<Flux<String>, Flux<String>?> = Function{ input ->
input.map { sellerId ->
if(sellerId == "I-ZICGO")
throw RuntimeException("intentional")
else
log.info("do normal: {}", sellerId)
sellerId
}.retryWhen(Retry.from { companion ->
companion.map { rs ->
if (rs.totalRetries() < 3) { // retrying 3 times
log.info("retry!!!: {}", rs.totalRetries())
rs.totalRetries()
}
else
throw Exceptions.propagate(rs.failure())
}
})
}
However, the result of running the above logic is:
2021-02-25 16:14:29.319 INFO 36211 --- [container-0-C-1] o.s.c.s.m.DirectWithAttributesChannel : Channel 'consumer.processingSellerItem-in-0' has 0 subscriber(s).
2021-02-25 16:14:29.322 INFO 36211 --- [container-0-C-1] k.c.m.c.service.impl.ItemServiceImpl : retry!!!: 0
2021-02-25 16:14:29.322 INFO 36211 --- [container-0-C-1] o.s.c.s.m.DirectWithAttributesChannel : Channel 'consumer.processingSellerItem-in-0' has 1 subscriber(s).
Retry is processed only once.
Should I change from reactive to imperative to fix this?
In short, yes. The retry settings are meaningless for reactive functions. You can see a more details explanation on the similar SO question here
I'm new to grails so hoping someone will be patient and give me a hand. I have a controller that creates a PDF. If the user clicks more then one time before the PDF is created I get the following error. Below is the code for the creation of the PDF.
2016-03-09 09:32:11,549 ERROR errors.GrailsExceptionResolver - SocketException occurred when processing request: [GET] /wetlands-form/assessment/f3458c91-3435-4714-a0e0-3b24de238671/assessment/pdf
Connection reset by peer: socket write error. Stacktrace follows:
java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:113)
at java.net.SocketOutputStream.write(SocketOutputStream.java:159)
at mdt.wetlands.AssessmentController$_closure11$$EPeyAg3t.doCall(AssessmentController.groovy:300)
at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
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)
2016-03-09 09:32:11,549 ERROR errors.GrailsExceptionResolver - IllegalStateException occurred when processing request: [GET] /wetlands-form/assessment/f3458c91-3435-4714-a0e0-3b24de238671/assessment/pdf
getOutputStream() has already been called for this response. Stacktrace follows:
org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException: Error processing GroovyPageView: getOutputStream() has already been called for this response
at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
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)
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
at C__MDTDATA_gg_workspace_new_wetlands_grails_app_views_error_gsp.run(error.gsp:1)
... 5 more
2016-03-09 09:32:11,549 ERROR [/wetlands-form].[grails] - Servlet.service() for servlet grails threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
PDF CODE VIA rendering plugin
def pdf = {
def assessment = lookupAssessment()
if (!assessment){
return
}
// Trac 219 Jasper report for PDF output
Map reportParams = [:]
def report = params.report
def printType = params.printType
def mitigationType = params.mitigationType
def fileName
def fileType
fileType = 'PDF'
def reportDir =
grailsApplication.mainContext.servletContext.getRealPath(""+File.separatorChar+"reports"+File.separatorChar)
def resolver = new SimpleFileResolver(new File(reportDir))
reportParams.put("ASSESS_ID", assessment.id)
reportParams.put("RUN_DIR", reportDir+File.separatorChar)
reportParams.put("JRParameter.REPORT_FILE_RESOLVER", resolver)
reportParams.put("_format", fileType)
reportParams.put("_file", "assessment")
println params
def reportDef = jasperService.buildReportDefinition(reportParams, request.getLocale(), [])
def file = jasperService.generateReport(reportDef).toByteArray()
// Non-inline reports (e.g. PDF)
if (!reportDef.fileFormat.inline && !reportDef.parameters._inline)
{
response.setContentType("APPLICATION/OCTET-STREAM")
response.setHeader("Content-disposition", "attachment; filename=" + assessment.name + "." + reportDef.fileFormat.extension);
response.contentType = reportDef.fileFormat.mimeTyp
response.characterEncoding = "UTF-8"
response.outputStream << reportDef.contentStream.toByteArray()
}
else
{
// Inline report (e.g. HTML)
render(text: reportDef.contentStream, contentType: reportDef.fileFormat.mimeTyp, encoding: reportDef.parameters.encoding ? reportDef.parameters.encoding : 'UTF-8');
}
}
This is the WORD code.
def word = {
def assessment = lookupAssessment()
if (!assessment){
return
}
// get the assessment's data as xml
def assessmentXml = g.render(template: 'word', model: [assessment:assessment]).toString()
// open the Word template
def loader = new LoadFromZipNG()
def template = servletContext.getResourceAsStream('/word/template.docx')
WordprocessingMLPackage wordMLPackage = (WordprocessingMLPackage)loader.get(template)
// get custom xml piece from Word template
String itemId = '{44f68b34-ffd4-4d43-b59d-c40f7b0a2880}' // have to pull up part by ID. Watch out - this may change if you muck with the template!
CustomXmlDataStoragePart customXmlDataStoragePart = wordMLPackage.getCustomXmlDataStorageParts().get(itemId)
CustomXmlDataStorage data = customXmlDataStoragePart.getData()
// and replace it with our assessment's xml
ByteArrayInputStream bs = new ByteArrayInputStream(assessmentXml.getBytes())
data.setDocument(bs) // needs java.io.InputStream
// that's it! the data is in the Word file
// but in order to do the highlighting, we have to manipulate the Word doc directly
// gather the list of cells to highlight
def highlights = assessment.highlights()
// get the main document from the Word file as xml
MainDocumentPart mainDocPart = wordMLPackage.getMainDocumentPart()
def xml = XmlUtils.marshaltoString(mainDocPart.getJaxbElement(), true)
// use the standard Groovy tools to handle the xml
def document = new XmlSlurper(keepWhitespace:true).parseText(xml)
// for each value in highlight list - find node, shade cell and add bold element
highlights.findAll{it != null}.each{highlight ->
def tableCell = document.body.tbl.tr.tc.find{it.sdt.sdtPr.alias.'#w:val' == highlight}
tableCell.tcPr.shd[0].replaceNode{
'w:shd'('w:fill': 'D9D9D9') // shade the cell
}
def textNodes = tableCell.sdt.sdtContent.p.r.rPr
textNodes.each{
it.appendNode{
'w:b'() // bold element
}
}
}
// here's a good way to print out xml for debugging
// System.out.println(new StreamingMarkupBuilder().bindNode(document.body.tbl.tr.tc.find{it.sdt.sdtPr.alias.#'w:val' == '12.1.1'}).toString())
// or save xml to file for study
// File testOut = new File("C:/MDTDATA/wetlands-trunk/xmlout.xml")
// testOut.setText(new StreamingMarkupBuilder().bindNode(document).toString())
// get the updated xml back in the Word doc
Object obj = XmlUtils.unmarshallFromTemplate(new StreamingMarkupBuilder().bindNode(document).toString(), null);
mainDocPart.setJaxbElement((Object)obj)
File file = File.createTempFile('wordexport-', '.docx')
wordMLPackage.save(file)
response.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document;')
response.setHeader('Content-Disposition', "attachment; filename=${assessment.name.encodeAsURL()}.docx")
response.setHeader('Content-Length', "${file.size()}")
response.outputStream << file.readBytes()
response.outputStream.flush()
file.delete()
}
// for checking XML during development
def word2 = {
def assessment = lookupAssessment()
if (!assessment){
return
}
render template: 'word', model: [assessment:assessment]
}
You need to catch the exception, if you wish to not do anything with it then as below in the catch nothing going on.. after it has gone through try and catch if still no file we know something has gone wrong so we render another or same view with error this time. After this it returns so it won't continue to your other bit which checks report type i.e. pdf or html
..
//declare file (def means it could be any type of object)
def file
//Now when you expect unexpected behaviour capture it with a try/catch
try {
file = jasperService.generateReport(reportDef).toByteArray()
}catch (Exception e) {
//log.warn (e)
//println "${e} ${e.errors}"
}
//in your scenario or 2nd click the user will hit the catch segment
//and have no file produced that would be in the above try block
//this now says if file == null or if file == ''
// in groovy !file means capture if there nothing defined for file
if (!file) {
//render something else
render 'a message or return to page with error that its in use or something gone wrong'
//return tells your controller to stop what ever else from this point
return
}
//so what ever else would occur will not occur since no file was produced
...
Now a final note try/catches are expensive and should not be used everywhere. If you are expecting something then deal with the data. In scenarios typically like this third party api where you have no control i.e. to make the unexpected expected then you fall back to these methods
1- Client Side : Better is to disable button on first click and wait for response from Server.
2- Catch Exception and do nothing or just print error log.
// get/set parameters
def file
def reportDef
try{
reportDef = jasperService.buildReportDefinition(reportParams, request.getLocale(), [])
file = jasperService.generateReport(reportDef).toByteArray()
}catch(Exception e){
// print log or do nothing
}
if (file){
// render file according to your conditions
}
else {
// render , return appropriate message.
}
Instead of catching Exception, Its better to catch IOException. Otherwise you will be eating all other exceptions as well. Here is how i handled it.
private def streamFile(File file) {
def outputStream
try {
response.contentType = "application/pdf"
response.setHeader "Content-disposition", "inline; filename=${file.name}"
outputStream = response.outputStream
file.withInputStream {
response.contentLength = it.available()
outputStream << it
}
outputStream.flush()
}
catch (IOException e){
log.info 'Probably User Cancelled the download!'
}
finally {
if (outputStream != null){
try {
outputStream.close()
} catch (IOException e) {
log.info 'Exception on close'
}
}
}
}