Jasper Report integration in Primefaces - java

I am posting a code snippet which worked well on other forms to print Jasperreport as Pdf.
<p:commandButton id="cmdPrint" value="Print"
actionListener="#{receiptMB.print()}"
disabled="#{receiptMB.chkSave == false}" process="#this" />
Backing Bean code snippet :
public void print(){
try
{
JRBeanCollectionDataSource beanCollectionDataSource = new JRBeanCollectionDataSource(getObjPrintList());
String reportPath= FacesContext.getCurrentInstance().getExternalContext().getRealPath("/reports/Receipt.jasper");
JasperPrint jasperPrint=JasperFillManager.fillReport(reportPath,getReportParameters(), beanCollectionDataSource);
HttpServletResponse httpServletResponse = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
httpServletResponse.setContentType("application / pdf");
httpServletResponse.addHeader("Content-disposition", "inline; filename=Receipt_" +objPrint.getDateNp()+".pdf");
ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
servletOutputStream.write(JasperExportManager.exportReportToPdf(jasperPrint));
servletOutputStream.flush();
servletOutputStream.close();
FacesContext.getCurrentInstance().renderResponse();
FacesContext.getCurrentInstance().responseComplete();
}
catch(JRException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
The above code is working well on other forms but it is not working in a particular form which is a Dialog.It gives no error but, also it doesn't give any output.
I went around with some solutions but no work.

Related

how to deal with exceptions on a Spring controller while generating a file

I've implemented a Spring controller that generates a PDF file, consuming my service layer. The problem is that when some exception is thrown during the execution of the method, even if I get the exception with a try-catch block, spring tries to resolve the view based on the current URL.
I honestly have no idea on what is causing this behavior.
#RequestMapping("/cliente")
public void relatorioCliente(EdicaoMovimentacaoWrapper wrapper, #AuthenticationPrincipal Usuario usuario) {
try {
gerarReportService.relatorioParaCliente(wrapper.getContaId(), usuario);
} catch (Exception e) {
e.printStackTrace();
// TODO alguma coisa
}
}
the relatorioParaCliente method generates the PDF and exports it to the response's OutputStream.
Expected: the exception gets caught, the stack trace gets printed and nothing happens to the user.
Actual result: Spring redirects the user to views/cliente.jsp
UPDATE
I've tried changing the return type of the method so it looks like this now:
#RequestMapping("/cliente")
public ModelAndView relatorioCliente(EdicaoCadastroMovimentacaoWrapper wrapper, #AuthenticationPrincipal Usuario usuario) {
try {
gerarReportService.relatorioParaCliente(wrapper.getContaId(), usuario);
} catch (Exception e) {
e.printStackTrace();
return new ModelAndView("/contas/" + wrapper.getContaId());
}
return null;
}
But this has no effect on my code.
I suppose that this does not affect the code because the outputStream gets used on the service. take a look:
#Service
public class ExportarReportPdfService {
#Autowired
private HttpServletResponse res;
public void exportar(List<JasperPrint> jasperPrintList) throws IOException {
JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList));
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(res.getOutputStream()));
SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
configuration.setCreatingBatchModeBookmarks(true);
exporter.setConfiguration(configuration);
res.setContentType("application/x-pdf");
res.setHeader("Content-disposition", "inline; filename=relatorio.pdf" );
try {
exporter.exportReport();
} catch (Exception e) {
e.printStackTrace();
throw new CriacaoReportException("Erro ao exportar para PDF");
} finally {
OutputStream out = res.getOutputStream();
out.flush();
out.close();
}
}
Here is what I did to solve the problem:
instead of Autowiring the response on the service and exporting the pdf from there, I generate a byteArray and return it to the controller:
#Service
public class ExportarReportPdfService {
public byte[] exportar(List<JasperPrint> jasperPrintList) {
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList));
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(outputStream));
[OMITED CODE]
try {
exporter.exportReport();
return outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
throw new CriacaoReportException("Erro ao exportar para PDF");
}
}
}
And the controller sends the response as a ResponseEntity :
#RequestMapping("/geral")
public ResponseEntity<InputStreamResource> relatorioAdministrador(EdicaoCadastroMovimentacaoWrapper wrapper, #AuthenticationPrincipal Usuario usuario) {
byte[] arquivo = gerarReportService.relatorioParaAdmin(wrapper.getContaId(), usuario);
return ResponseEntity
.ok()
.contentLength(arquivo.length)
.contentType(MediaType.parseMediaType("application/pdf"))
.header("Content-Disposition", "attachment; filename=relatorio.pdf")
.body(new InputStreamResource(new ByteArrayInputStream(arquivo)));
}
So if any exception occurs it will be caught in the ExceptionHandler as #cmoetzing explained in the comments:
#ExceptionHandler(CriacaoReportException.class)
public ModelAndView trataGeracaoRelatorio(CriacaoReportException exception) {
return new ModelAndView("redirect:/");
}

Apache POI 3.14 Writing SXSSF to ServletResponse OutputStream is corrupting workbook JSF

In my webapp, I'm building SXSSFWorkbook objects to generate reports with roughly 30-60k records each. I kick off a process through a request to first fetch and build each SXSSFWorkbook. I'm able to generate the report and open a FileOutputStream to export my object to my desktop(locally of course). However, I want to let user choose which report to download through a request(JSF)to the server. When I feed the OutputStream from the servlet response, I can download the .xlsx file but it tells me it's been corrupted. I've done some research, tried some different workarounds all with no results. Posted is my code. I'm kind of at a loss for what's going on here.
p.s. I was previously generating HSSFWorkbook objects and downloading them but they were starting to causing heap space issues. Hence, the switch to SXSSFWorkbook.
My command button
<h:commandButton value="Download Report" styleClass="secondary-button"
action="#{backingBean.getLatestReport}"
id="thirdReportButton">
</h:commandButton>
My action
public void getLatestReport() throws Exception {
FacesContext faces = FacesContext.getCurrentInstance();
String templateName = "Report.xlsx";
HttpServletResponse response = null;
OutputStream outputStream = null;
//workbookForLatestReport is a SXSSFWorkbook object
try {
if (workbookForLatestReport != null) {
response = (HttpServletResponse) faces.getExternalContext()
.getResponse();
response.reset();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
"attachment; filename=\"" + templateName + "\"");
outputStream = response.getOutputStream();
workbookForLatestReport.write(outputStream);
outputStream.close();
workbookForLatestReport.dispose();
}
faces.renderResponse();
} catch (Exception e) {
throw e;
}
}
Recently I successfully accomplished similar task so I might be able to help you.
I've just ran your code (on Payara 4.1 using Chrome as browser) adding part that you omitted in your post
#ManagedBean(name = "backingBean")
#ViewScoped
public class BackingBean {
//your command button should call getLatestReport and not getSecondReport() as in your original post
public void getLatestReport() throws Exception {
FacesContext faces = FacesContext.getCurrentInstance();
String templateName = "Report.xlsx";
HttpServletResponse response = null;
OutputStream outputStream = null;
//workbookForLatestReport is a SXSSFWorkbook object
//MY ADDITION START
//I've created SXSSFWorkbook object since your post did't contain this part
//100K rows, 100 columns
SXSSFWorkbook workbookForLatestReport = new SXSSFWorkbook(SXSSFWorkbook.DEFAULT_WINDOW_SIZE);
workbookForLatestReport.setCompressTempFiles(true);
SXSSFSheet sheet = workbookForLatestReport.createSheet();
for (int rowNumber = 0; rowNumber < 100000; rowNumber++) {
SXSSFRow row = sheet.createRow(rowNumber);
for (int columnNumber = 0; columnNumber < 100; columnNumber++) {
SXSSFCell cell = row.createCell(columnNumber);
cell.setCellValue("ROW " + rowNumber + " COLUMN " + columnNumber);
}
}
//MY ADDITION END
try {
if (workbookForLatestReport != null) {
response = (HttpServletResponse) faces.getExternalContext()
.getResponse();
response.reset();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
"attachment; filename=\"" + templateName + "\"");
outputStream = response.getOutputStream();
workbookForLatestReport.write(outputStream);
outputStream.close();
workbookForLatestReport.dispose();
}
faces.renderResponse();
} catch (Exception e) {
throw e;
}
}
}
It was working just fine and as expected.
I have 2 suggestions:
your command button "calls" action="#{backingBean.getSecondReport}"
but your managed bean action is named public void getLatestReport.
Check out if it is typing error or not.
compare your SXSSFWorkbook object creation code with my example. Are
there any crucial differences?
I was able to come to a solution with the aid of omifaces and changing some code around.
After I create my workbook, I use a ByteArrayStream to set a byte[] attribute on my model bean that can be referenced when the download listener from the jsf is clicked.
ByteArrayOutputStream bos;
byte[] workbookForLatestBytes;
XSSFXWorkbook workbookForLatestReport;
.
.
.
workbookForLatestReport = <generate the report here>
if(workbookForLatestReport != null){
bos = new ByteArrayOutputStream();
workbookForLatestReport.write(bos);
workbookForLatestBytes = bos.toByteArray();
workbookForPreviousReport.dispose();
}
Here is the action being fired from my JSF code.
<h:commandButton value="Download Report"
styleClass="secondary-button"
action="#{productRuleAuditCompareBackingBean.getSecondReport}"
id="thirdReportButton"
rendered="#{not empty productRuleAuditCompareModelBean.workbookForLatestBytes}">
</h:commandButton>
My backing bean action is as follows. I reset the HTTP Response before writing the byte array to the response output stream.
public void getSecondReport() {
FacesContext faces = FacesContext.getCurrentInstance();
try {
faces.getExternalContext().responseReset();
this.prAuditCompareModelBean.getLatestReport();
faces.responseComplete();
} catch (Exception e) {
PODBException.wrap(e, PODBExceptionInformation.create(
PODBExceptionType.ERROR,
ProductRuleAuditCompareBackingBean.class.getName(),
"getting first report",
"Error while getting second report for download", "1"));
}
}
Here I'm using Omniface Faces#sendFile method to write the workbook to the response outputstream.
public void getLatestReport() throws Exception {
try {
if (getWorkbookForLatestBytes() != null) {
Faces.sendFile(getWorkbookForLatestBytes(), reportName, true);
}
} catch (Exception e) {
throw e;
}
}

Upload file using Spark framework and FreeMarker

I'm trying to make a method to upload files using the spark framework and freemarker but I seem to be hitting a brick wall at the .getPart method. My current freemarker code looks as follows:
<form method='post' enctype='multipart/form-data'>
<div>
<input type='file' name='uploadedFile'>
<button>Upload csv</button>
</div>
</form>
and my spark java post method code looks as follows:
post(new Route("/pdf", "multipart/form-data") {
#Override
public Object handle(Request request, Response response) {
String name = null;
File upLoadM = new File("messages/");
Path tempFile = null;
Part file = null;
try {
tempFile = Files.createTempFile(upLoadM.toPath(), "", "");
System.out.println(tempFile);
}
catch (IOException e1) {
e1.printStackTrace();
}
request.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/temp"));
try {
file = request.raw().getPart("uploadedFile");
System.out.println(file);
}
catch (IOException | ServletException e1) {
e1.printStackTrace();
}
try (InputStream input = file.getInputStream()) {
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
}
catch (IOException e) {
e.printStackTrace();
}
response.status(201);
response.redirect("/pdf");
return "";
}
});
When i hit the upload button I get a 500 Internal Error. Not sure what the reason it's crashing at the .getPart method is. Any help would be appreciated.
Turns out it is working if i tweek the line of code:
request.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/temp"));
to
request.attribute("org.eclipse.multipartConfig", new MultipartConfigElement("/temp"));

The Jasper Reports servlet stopped working after calling response.getOutputStream()

I have code such as below. The program stopped working at line servletOutputStream = response.getOutputStream();. I don't know how to resolve this? Can anybody help me with this problem?
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, SQLException, JRException, ParserConfigurationException, SAXException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
out.println ("<html>");
out.println (" <head>");
out.println (" <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>");
out.println (" <title>JSP Page</title>");
out.println (" </head>");
out.println (" <body>");
out.println (" <h1>Hello iReport!</h1>");
String resourceName = "D:/classic.jrxml";
response.setContentType("application/pdf");
ServletOutputStream servletOutputStream = null;
servletOutputStream = response.getOutputStream(); // <--
InputStream reportStream = getServletConfig().getServletContext().getResourceAsStream(resourceName);
try {
Driver driver = new org.gjt.mm.mysql.Driver();
DriverManager.registerDriver(driver);
String conString = "jdbc:mysql://localhost:3306/quanlynhasach";
Properties info = new Properties();
info.setProperty("characterEncoding", "utf8");
info.setProperty("user", "root");
info.setProperty("password", "");
Connection con = DriverManager.getConnection(conString, info);
JasperRunManager.runReportToPdfStream(reportStream, servletOutputStream,new HashMap<Object, Object>(), con);
con.close();
}catch(Exception e){
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
response.setContentType("text/plain");
response.getOutputStream().print(stringWriter.toString());
}
out.println (" </body>");
out.println ("</html>");
} finally {
out.close();
}
} // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
/**
* Handles the HTTP <code>GET</code> method.
* #param request servlet request
* #param response servlet response
* #throws ServletException if a servlet-specific error occurs
* #throws IOException if an I/O error occurs
*/
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
try {
processRequest(request, response);
} catch (ParserConfigurationException ex) {
Logger.getLogger(iReport.class.getName()).log(Level.SEVERE, null, ex);
} catch (SAXException ex) {
Logger.getLogger(iReport.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (SQLException ex) {
Logger.getLogger(iReport.class.getName()).log(Level.SEVERE, null, ex);
} catch (JRException ex) {
Logger.getLogger(iReport.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Handles the HTTP <code>POST</code> method.
* #param request servlet request
* #param response servlet response
* #throws ServletException if a servlet-specific error occurs
* #throws IOException if an I/O error occurs
*/
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
try {
processRequest(request, response);
} catch (ParserConfigurationException ex) {
Logger.getLogger(iReport.class.getName()).log(Level.SEVERE, null, ex);
} catch (SAXException ex) {
Logger.getLogger(iReport.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (SQLException ex) {
Logger.getLogger(iReport.class.getName()).log(Level.SEVERE, null, ex);
} catch (JRException ex) {
Logger.getLogger(iReport.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Returns a short description of the servlet.
* #return a String containing servlet description
*/
#Override
public String getServletInfo() {
return "Short description";
}// </editor-fold>
Look here:
PrintWriter out = response.getWriter();
// *snip*
servletOutputStream = response.getOutputStream();
You're getting both the Writer and OutputStream from the response. This is not allowed. Read their javadocs:
getOutputStream()
ServletOutputStream getOutputStream() throws java.io.IOException
Returns a ServletOutputStream suitable for writing binary data in the response. The servlet container does not encode the binary data.
Calling flush() on the ServletOutputStream commits the response. Either this method or getWriter() may be called to write the body, not both.
and
getWriter()
java.io.PrintWriter getWriter() throws java.io.IOException
Returns a PrintWriter object that can send character text to the client. The PrintWriter uses the character encoding returned by getCharacterEncoding(). If the response's character encoding has not been specified as described in getCharacterEncoding (i.e., the method just returns the default value ISO-8859-1), getWriter updates it to ISO-8859-1.
Calling flush() on the PrintWriter commits the response.
Either this method or getOutputStream() may be called to write the body, not both.
(emphasis mine)
The problem is in your particular case however much bigger. You're attempting to inline the PDF result of a Jasper Report between those HTML tags within a HTML response. I'm not sure what you thought or smoked while you wrote the code, but that is definitely not going to work. You need to rewrite the servlet that way so that it only returns the PDF and not that bunch of HTML noise. You should move all that HTML out the servlet into some JSP file. Then, you can call that servlet by a simple download link in the JSP
Download PDF
or inside an <iframe> (yes, in JSP)
<iframe src="yourServletUrl" style="width: 500px; height: 300px;"></iframe>
or in an <object> (also here, just in JSP)
<object data="yourServletUrl" type="application/pdf" width="500" height="300" />
Just put that HTML in a JSP page, open the JSP in browser and the webbrowser will take care that the servlet will be invoked and that the PDF will be represented the way you intended.
Your other problem is that the exception handling is not really good. You'll see completely nothing this way as the response buffer is not been resetted. You should instead be doing a
} catch (Exception e) {
throw new ServletException("descriptive message here", e);
}
as the container knows perfectly how to handle exceptions.
That both your doGet() and doPost() are doing exactly the same is by the way also a design smell. The JDBC driver which you used there is completely outdated and deprecated. The way how you registered the driver is clumsy. That the DB connection is not closed in finally is prone to resource leaking. Okay, I'll stop...
I presume that you are getting an IllegalStateException because you are calling getWriter() and getOutputStream() on the same response. Which you're not allowed to do.

Download a file with JSF? [duplicate]

This question already has answers here:
How to provide a file download from a JSF backing bean?
(5 answers)
Closed 7 years ago.
which is the right way to download a file using JSF?, just putting a link to the file ?? in that case how do i get the file URL??
i have seen one example using BufferedInputStream:
http://www.winstonprakash.com/articles/jsf/file_download_link.htm
What are the differences?
Thanks
If it's a simple file, just place in public webcontent (there where you put your static and JSF files) and create a link.
<h:outputLink value="/files/file.ext">link</h:outputLink>
The servletcontainer will worry about applying the correct headers.
If it's located outside the public webcontent for some specific reasons (e.g. in a fixed path at server machine, or in a database), then create a servlet which gets an InputStream of it and writes it to the OutputStream of the response along at least the Content-Type, Content-Disposition and Content-Length headers. You can find here a simple kickoff example. Also that can simply be linked on the servlet's url-pattern.
If it's to be dynamically generated and depending on the JSF specific request parameters, then you can also do so in a managed bean action which is bound by h:commandLink or h:commandButton, but you only need to ensure that you call FacesContext#responseComplete() at end of bean's action method to prevent JSF from taking the navigation in hands. The same kind of servlet code can be reused to stream the file. You can find a kickoff example in this answer.
I needed to make a similar code to download a file via JSF
That's my download button in my JSF page
<h:commandButton value="Download" action="#{helloBean.downloadFile}" />
And it's my Java Code
public void downloadFile() {
File file = new File("/home/marco/file.txt");
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.setHeader("Content-Disposition", "attachment;filename=file.txt");
response.setContentLength((int) file.length());
ServletOutputStream out = null;
try {
FileInputStream input = new FileInputStream(file);
byte[] buffer = new byte[1024];
out = response.getOutputStream();
int i = 0;
while ((i = input.read(buffer)) != -1) {
out.write(buffer);
out.flush();
}
FacesContext.getCurrentInstance().getResponseComplete();
} catch (IOException err) {
err.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException err) {
err.printStackTrace();
}
}
}
I have had an error on
FacesContext.getCurrentInstance().getResponseComplete();
from the type
java.lang.IllegalStateException: getOutputStream() has already been called for this response
and i solved it:
JSF page:
<h:commandButton action="#{bean.downloadFile}" id="downloadBtn" value="Download"/>
Bean method:
public void downloadFile(File file) {
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
response.setHeader("Content-Disposition", "attachment;filename=file.txt");
response.setContentLength((int) file.length());
FileInputStream input= null;
try {
int i= 0;
input = new FileInputStream(file);
byte[] buffer = new byte[1024];
while ((i = input.read(buffer)) != -1) {
response.getOutputStream().write(buffer);
response.getOutputStream().flush();
}
facesContext.responseComplete();
facesContext.renderResponse();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(input != null) {
input.close();
}
} catch(IOException e) {
e.printStackTrace();
}
}
}

Categories