Jasper Plugin PDF File Sometimes Not Returned

We are using Servoy Version: 2023.3.6.3848_LTS, developing on Windows and deploying to Linux.

All of our reports are returned to the user as PDFs. Sometimes, although the plugin appears to be creating the required document it is not returned to the user. This problem happens if the user has left the Servoy solution running without interaction for two hours or more and then clicks a Print button.

We are using this method of the plugin:

 @param {Object} reportDataSource the server name or foundset to run the report on
 @param {String} report the report file (relative to the reports directory)
 @param {Object} outputOptions the output file (must specify an absolute path) or null if not needed
 @param {String} outputType the output format; use the constants node for available output formats
 @param {Object} parameters a parameter map to be used when running the report
 @return {byte[]} the generated reported as a byte array
byte[] runReport(reportDataSource:Object, report:String, outputOptions:Object, outputType:String, parameters:Object)

Our call from Servoy is:

plugins.jasperPluginRMI.runReport( goFieldsDS, sJasperTemplate, null, plugins.jasperPluginRMI.OUTPUT_FORMAT.PDF, oParams );

Note that we don’t send anything for the third argument: outputOptions.

Debugging has shown that the code spends the same amount of time in the Jasper plugin whether or not a PDF file is returned, which implies that the PDF is being created but we do not know where.

Looking through the source code for the JasperPluginRMI I see:

// in JasperReportsWebViewer.show 61

    if(application instanceof IWebClientPluginAccess)
    {
      IWebClientPluginAccess wapp = (IWebClientPluginAccess) application;
      IPageContributor pc = wapp.getPageContributor();
      if (pc != null) {
        String url = wapp.serveResource(getFixedFileName(file, ext), jsp, mimeType);
        wapp.showURL(url, "_self", null, 0);
      }
    }

At this point the value of “file” is still null.

The getFixedFileName(file, ext) call looks like this:

    if (file == null || file.length() == 0)
    {
      Calendar cal = Calendar.getInstance(TimeZone.getDefault());
      String DATE_FORMAT = "yyyyMMddHHmmss";
      java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(DATE_FORMAT);
      sdf.setTimeZone(TimeZone.getDefault());
      fixedFileName = "report_" + sdf.format(cal.getTime()) + "." + ext;
    }

So that creates a file name with no path. For example: report_20240702083040.pdf

The key to understanding what happens in:

        String url = wapp.serveResource(getFixedFileName(file, ext), jsp, mimeType);
        wapp.showURL(url, "_self", null, 0);

depends on:

      IWebClientPluginAccess wapp = (IWebClientPluginAccess) application;

The most recent Javadocs I can find are in https://developer.servoy.com/docs/publi … index.html

There it says:

Methods inherited from interface com.servoy.j2db.plugins.IAllWebClientPluginAccess
serveResource, serveResource, showURL, showURL, showURL

And those are precisely the methods whose details I need to see.

But unlike the other interfaces inherited by IWebClientPluginAccess there is no information given there on IAllWebClientPluginAccess!

So, I am unable to figure out just what this does:

        String url = wapp.serveResource(getFixedFileName(file, ext), jsp, mimeType);
        wapp.showURL(url, "_self", null, 0);

What actually happens when wapp.serveResource(getFixedFileName(file, ext), jsp, mimeType) is called?

Is the PDF file being created and written to a folder on the server?

If so, where?

If not, is it being created in-memory?

Any help would be greatly appreciated.

You might consider adjusting your code so you can have more control over what’s happening. For example…

Get the bytes and stream it to the user’s browser

var reportBytes = plugins.jasperPluginRMI.runReport( goFieldsDS, sJasperTemplate, null, plugins.jasperPluginRMI.OUTPUT_FORMAT.PDF, oParams );
plugins.file.writeFile("report.pdf", reportBytes, "application/pdf");

Get the bytes, save a copy on the server and stream it to the user’s browser

var reportBytes = plugins.jasperPluginRMI.runReport( goFieldsDS, sJasperTemplate, null, plugins.jasperPluginRMI.OUTPUT_FORMAT.PDF, oParams );
plugins.file.writeFile(plugins.file.convertToJSFile("c:/archive/report.pdf"), reportBytes, "application/pdf"); //server copy
plugins.file.writeFile("report.pdf", reportBytes, "application/pdf");

Save it to disk and stream it to the user’s browser or open it in PDF viewer

var fileName = "report.pdf";
var reportFile = plugins.file.getDefaultUploadLocation() + "/" + fileName;
plugins.jasperPluginRMI.runReport( goFieldsDS, sJasperTemplate, reportFile, plugins.jasperPluginRMI.OUTPUT_FORMAT.PDF, oParams );
var remoteFile = plugins.file.convertToRemoteJSFile("/" + fileName);
var fileURL = plugins.file.getUrlForRemoteFile(remoteFile);
application.showUrl(fileURL, "_blank"); //or pass the url into Servoy's PDF Viewer

Greatly appreciated Scott. I’ll give that a try.

Terry

Unfortunately convertToRemoteJSFile() requires a file path that starts with “/”. But I am developing on Windows so the file path starts with "C:" and that fails.

So I don’t see how your call to:

plugins.file.convertToJSFile(“c:/archive/report.pdf”)

works.

Any suggestions?

thanks.

Hi Terry,

What type of Servoy-client do you use, and where do you expect the pdf file to be written to?

I have made this work but wonder if this is right.

I now write the PDF file to the DefaultUploadLocation and then I take the bytes of the report and set them into a RemoteFile, which seems redundant to me. But I can’t make this work if I still pass null for the third plugins.jasperPluginRMI.runReport() argument.

I have no idea what a RemoteFile is. Is this a file on the file system? Is it some kind of in-memory file?

And when I call plugins.file.convertToRemoteJSFile( “/” + sReportFilename ) that uses only the file’s name not its path so it is not clear to me what is happening here and therefor what may need to be cleaned up later. Certainly I need at some point to delete the PDF files from the DefaultUploadLocation but do I also need to delete the RemoteFile? If so from where?

  var sReportFilename = makeReportFilename( );
  var sReportFilepath = plugins.file.getDefaultUploadLocation( ) + java.io.File.separator + sReportFilename;

  var oBytes = plugins.jasperPluginRMI.runReport( goFieldsDS, sJasperTemplate, sReportFilepath, plugins.jasperPluginRMI.OUTPUT_FORMAT.PDF, oParams );

  var oRemoteFile = plugins.file.convertToRemoteJSFile( "/" + sReportFilename );
  oRemoteFile.setBytes( oBytes, true );
  var sFileURL = plugins.file.getUrlForRemoteFile( oRemoteFile );
  application.showURL( sFileURL, "_blank" );

thanks,

Terry

In response to Marc Boegem’s questions:

We are running Servoy Version: 2023.3.6.3848_LTS in the NG mode but not in Titanium yet. We develop on Windows and deploy to Linux.

Originally we used:

plugins.jasperPluginRMI.runReport( goFieldsDS, sJasperTemplate, null, plugins.jasperPluginRMI.OUTPUT_FORMAT.PDF, oParams );

This depends on Servoy to create a report name then return the PDF to the user. But when a Servoy form with a Print button on it is left for more than two hours, when the user clicks the Print button Servoy acts as if it is creating the PDF but then returns nothing to the user. This does not stop Servoy from working. We have no idea where or even if Servoy is writing that file.

Following suggestions from Scott Butler we are now creating our own report filename. Servoy’s name included year, day, hour, minute and seconds. We have added milliseconds. We then construct a file path to Sevoy’s DefaultUploadLocation and pass that with the appended filename.

We will deploy that to our Linux server soon to see if it fixes the problem.

thanks,

Terry

Hi Terry,

I haven’t heard reports from users not able to generate their reports.
Anyway, the function below is pretty simple and shows 3 ways of handling the byteArray:

  1. Download it to the users download folder
  2. Store it on the server in the default upload folder in a subfolder called ‘UserReports’
  3. Preview it in the PDFjs component within your application. (using modal dialog)
var _aBytes = plugins.jasperPluginRMI.runReport( goFieldsDS, sJasperTemplate, null, plugins.jasperPluginRMI.OUTPUT_FORMAT.PDF, oParams );
	var _sFileName = 'myReportName.pdf'
	
	// download
	plugins.file.writeFile(_sFileName, _aBytes);
	
	// store on server
	var _sPath = plugins.file.getDefaultUploadLocation() + '/UserReports/' + _sFileName;
	plugins.file.writeFile(plugins.file.convertToJSFile(_sPath), _aBytes);
	
	// preview in PDFjs
	forms.preview_pdf.showFID(_aBytes);

The previewer is nice because from there, users are able to print directly, or use the download button.

The preview form should be easy:

  1. create a form with the PDFjs component
  2. create a form variable ‘$aBytes’ and use that as the dataprovider on the component.
  3. create a function showFID(_aBytes), that will set the incoming byteArray into the form variable and open the form as modal dialog.

Hope this helps in order to choose the best way of handling your report.