I have learned that JasperViewer (default preview component of JasperReports) is a Swing component, so is there any way to convert or embed it in a web application? Some say I should use Java Web Start, but from what i have learned from this link JWS is useful to download and install an application on client machine and this is not our case. the other work around that it may work (maybe just in theory) is converting jFrame to jApplet as briefly described in this link
Have you tried any of these solutions and did they work?
Do you know any other solution to this problem?
If you know how to generate a report, you can easily do it inside a servlet and send the generated file to the client. Using a JWS application or an Applet would most likely mean that the report is generated client-side and that the raw data plus all the dependencies are also available to the client.
The code below assumes that you're generating a PDF file
public class ReportServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// initialize your report objects here
JasperReport jasperReport =
JasperPrint print =
JRPdfExporter exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, resp.getOutputStream());
resp.setContentType("application/pdf");
exporter.exportReport();
} catch (Exception e) {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error generating report : " + e.getClass() + " " + e.getMessage());
}
}
You can extend the example above to support multiple export formats by setting the correct content type and using the matching JRXYZExporter (JRHtmlExporter, JExcelApiExporter,...)
If you need something more customizable, you might also want to look into Jasper Server
Related
I am using JasperReport to export a report to a PDF. The code runs fine with no exception messages showing up in the console/log. However, the report does not export to the browser. In other words, the report is being created, I just cannot download or gain access to it.
Here is the export code:
public void generatePDFReport(Map<String, Object> parameters, JRDataSource jrDataSource, String resource, String filename)
{
OutputStream os = null;
try{
FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
os = response.getOutputStream();
InputStream reportTemplate = this.getClass().getClassLoader().getResourceAsStream(resource);
byte[] pdf = null;
try {
JasperDesign masterDesign = JRXmlLoader.load(reportTemplate);
masterReport = JasperCompileManager.compileReport(masterDesign);
masterReport.setWhenNoDataType(WhenNoDataTypeEnum.ALL_SECTIONS_NO_DETAIL);
JasperPrint masterPrint = JasperFillManager.fillReport(masterReport, parameters, jrDataSource);
pdf = JasperExportManager.exportReportToPdf(masterPrint);
} catch (JRException e) {
log.error(e);
}
response.setContentType("application/pdf");
response.setContentLength(pdf.length);
response.setHeader("Content-disposition", "attachment; filename=\""+filename+"\"");
context.responseComplete();
os.write(pdf);
pdf = null;
}catch(Exception e){
log.error(e);
}finally{
try{
os.flush();
os.close();
}catch(IOException e){
log.error(e);
}
}
}
I am almost 100% certain that there is nothing wrong with the code as it works fine for different reports (I run the same export code for several other reports and it works as expected for all of them except for this one).
Knowing this, I figured it must have something to do with the report itself. The report is a jrxml JasperReport file. The report was created using iReport. However, I modified the above code to simply save it to the downloads folder and the report is being created perfectly fine.
So, the problem is that the report is successfully being created in the backend but it is not being sent to the front-end (browser) as expected.
I am open to any suggestions as to why this report would not be working.
Running the code inside a bean is problematic because:
only one call to getOutputStream is allowed per HTTP request
the web framework (J2EE/JSF) has likely already written HTTP headers
the JSF page has likely already been written as HTML inside a temporary buffer (flushed upon calling responseComplete()).
the headers could be reset, but that won't help with the getOutputStream issue
calling responseComplete() flushes any HTML along with PDF content to the browser
Use a servlet. The send method of the servlet needn't be any more complex than:
protected void send(final byte[] content) throws IOException {
setContentLength(content.length);
try (final OutputStream out = getOutputStream()) {
out.write(content);
}
}
Also consider setting the cache so that stale reports are not possible:
protected void disableCache() {
// https://tools.ietf.org/html/rfc7234#section-7.1.3
setHeader(CACHE_CONTROL, "private, no-store, no-cache, must-revalidate");
// https://tools.ietf.org/html/rfc7234#section-5.3
setHeader(EXPIRES, "Thu, 01 Dec 1994 16:00:00 GMT");
// https://tools.ietf.org/html/rfc7234#section-5.4
setHeader(PRAGMA, "no-cache");
// https://tools.ietf.org/html/rfc7232#section-2.2
setHeader(LAST_MODIFIED, getServerTimestamp());
}
private String getServerTimestamp() {
final SimpleDateFormat rfc1123 = new SimpleDateFormat(DATE_RFC_1123, getDefault());
rfc1123.setTimeZone(getTimeZone("GMT"));
final Calendar calendar = Calendar.getInstance();
return rfc1123.format(calendar.getTime());
}
This implies, for example:
#WebServlet(
name = "ReportServlet",
urlPatterns = {PATH_SERVLET + "ReportServlet"}
)
public class ReportServlet extends AbstractServlet {
}
And then use a regular anchor link:
<h:outputLink value="/app/path/servlet/Reportservlet">Run Report</h:outputLink>
In summary, don't send binary report data by intercepting a request to a JSF page; use a servlet, instead.
Communications between servlets and JSF pages can be made via:
Session variables (HTTPSession on the servlet side)
URL parameters
Servlets have the advantage that the JSF overhead is completely avoided, which will make the report run faster from the user's perspective. Also, don't compile the report -- use the .jasper file directly, which will also have performance improvements. (I did not mean to imply using the .jrxml file was the problem, merely that it isn't a necessary step.)
I figured out a solution to my problem. Ultimately, I found that there was nothing wrong with the report generation code or the reports, but there was an ajax issue that was preventing the outputstream from exporting the report to the browser.
I've created a servlet which creates an XSSFWorkbook and writes it to the response's outputStream. Strangely enough, when I try to test the functionality in the browser (Chrome v54.0.2840.98) I'm only able to get the xlsx file once (the file opens up without any formatting issues and has the expected content as well) but if I navigate away from the page where this feature is available with the 'back' button in the browser and go immediately back to same page and try to get the same file again I'm not getting anything in the response. Additionally, my other servlets stop working too until I open a new tab. I've given it a shot in a different browser (Safari v9.1.2 (11601.7.7)) and everything is working as expected, no issues whatsoever.
Here's the code that I use:
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
DateTime now = new DateTime();
Workbook workbook = createWorkbook(); //creates an XSSFWorkbook
response.setContentType("application/vnd.ms-excel");
response.setHeader(
"Content-Disposition",
"attachment; filename=\"excel-export-" + now.toString("yyyy-MMM-dd") + ".xlsx\""
);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(response.getOutputStream());
workbook.write(bufferedOutputStream);
}
When I'm running the code in the development env I don't get any exception, the status is 200 but still nothing gets downloaded. Ocassionally I get a
org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException:Fail to save: an error occurs while saving the package : The part /docProps/core.xml fail to be saved in the stream with marshaller org.apache.poi.openxml4j.opc.internal.marshallers.ZipPackagePropertiesMarshaller
Which, after extensive debugging, I can reproduce by passing a null to the workbook.write() function:
workbook.write(null);
Any help is appreciated, thank you for reading!
Javax Servlet API v2.5
Apache-POI v3.15
Java 8 JDK(1.8.0_111)
UPDATE
If I get an exception it looks like this(stacktrace):
https://gist.githubusercontent.com/darkstar85/b151e53b64498e1fb476d0f6f8ea4eaf/raw/ffb078c54b850922fcd4e467a6ebf9695aeb7354/gistfile1.txt
When looking at the code of Apache POI, this can only happen if StreamHelper.saveXmlInStream(xmlDoc, out) returns false. Additionally this only returns false if XML-Transformation fails at the line trans.transform(xmlSource, outputTarget);.
However it just does a identity-transformation (i.e. a simple copy) here, so this can only fail, if the XML Parser that is available in your application somehow does not work correctly.
Therefore I would check which JDK you are using and if there are any additional XML Parsers added in your application, e.g. Xerces or any other and see if you can remove them.
I read here that one should not save the file in the server anyway as it is not portable, transactional and requires external parameters. However, given that I need a tmp solution for tomcat (7) and that I have (relative) control over the server machine I want to know :
What is the best place to save the file ? Should I save it in /WEB-INF/uploads (advised against here) or someplace under $CATALINA_BASE (see here) or ... ? The JavaEE 6 tutorial gets the path from the user (:wtf:). NB : The file should not be downloadable by any means.
Should I set up a config parameter as detailed here ? I'd appreciate some code (I'd rather give it a relative path - so it is at least Tomcat portable) - Part.write() looks promising - but apparently needs a absolute path
I'd be interested in an exposition of the disadvantages of this approach vs a database/JCR repository one
Unfortunately the FileServlet by #BalusC concentrates on downloading files, while his answer on uploading files skips the part on where to save the file.
A solution easily convertible to use a DB or a JCR implementation (like jackrabbit) would be preferable.
Store it anywhere in an accessible location except of the IDE's project folder aka the server's deploy folder, for reasons mentioned in the answer to Uploaded image only available after refreshing the page:
Changes in the IDE's project folder does not immediately get reflected in the server's work folder. There's kind of a background job in the IDE which takes care that the server's work folder get synced with last updates (this is in IDE terms called "publishing"). This is the main cause of the problem you're seeing.
In real world code there are circumstances where storing uploaded files in the webapp's deploy folder will not work at all. Some servers do (either by default or by configuration) not expand the deployed WAR file into the local disk file system, but instead fully in the memory. You can't create new files in the memory without basically editing the deployed WAR file and redeploying it.
Even when the server expands the deployed WAR file into the local disk file system, all newly created files will get lost on a redeploy or even a simple restart, simply because those new files are not part of the original WAR file.
It really doesn't matter to me or anyone else where exactly on the local disk file system it will be saved, as long as you do not ever use getRealPath() method. Using that method is in any case alarming.
The path to the storage location can in turn be definied in many ways. You have to do it all by yourself. Perhaps this is where your confusion is caused because you somehow expected that the server does that all automagically. Please note that #MultipartConfig(location) does not specify the final upload destination, but the temporary storage location for the case file size exceeds memory storage threshold.
So, the path to the final storage location can be definied in either of the following ways:
Hardcoded:
File uploads = new File("/path/to/uploads");
Environment variable via SET UPLOAD_LOCATION=/path/to/uploads:
File uploads = new File(System.getenv("UPLOAD_LOCATION"));
VM argument during server startup via -Dupload.location="/path/to/uploads":
File uploads = new File(System.getProperty("upload.location"));
*.properties file entry as upload.location=/path/to/uploads:
File uploads = new File(properties.getProperty("upload.location"));
web.xml <context-param> with name upload.location and value /path/to/uploads:
File uploads = new File(getServletContext().getInitParameter("upload.location"));
If any, use the server-provided location, e.g. in JBoss AS/WildFly:
File uploads = new File(System.getProperty("jboss.server.data.dir"), "uploads");
Either way, you can easily reference and save the file as follows:
File file = new File(uploads, "somefilename.ext");
try (InputStream input = part.getInputStream()) {
Files.copy(input, file.toPath());
}
Or, when you want to autogenerate an unique file name to prevent users from overwriting existing files with coincidentally the same name:
File file = File.createTempFile("somefilename-", ".ext", uploads);
try (InputStream input = part.getInputStream()) {
Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
How to obtain part in JSP/Servlet is answered in How to upload files to server using JSP/Servlet? and how to obtain part in JSF is answered in How to upload file using JSF 2.2 <h:inputFile>? Where is the saved File?
Note: do not use Part#write() as it interprets the path relative to the temporary storage location defined in #MultipartConfig(location). Also make absolutely sure that you aren't corrupting binary files such as PDF files or image files by converting bytes to characters during reading/writing by incorrectly using a Reader/Writer instead of InputStream/OutputStream.
See also:
How to save uploaded file in JSF (JSF-targeted, but the principle is pretty much the same)
Simplest way to serve static data from outside the application server in a Java web application (in case you want to serve it back)
How to save generated file temporarily in servlet based web application
I post my final way of doing it based on the accepted answer:
#SuppressWarnings("serial")
#WebServlet("/")
#MultipartConfig
public final class DataCollectionServlet extends Controller {
private static final String UPLOAD_LOCATION_PROPERTY_KEY="upload.location";
private String uploadsDirName;
#Override
public void init() throws ServletException {
super.init();
uploadsDirName = property(UPLOAD_LOCATION_PROPERTY_KEY);
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// ...
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Collection<Part> parts = req.getParts();
for (Part part : parts) {
File save = new File(uploadsDirName, getFilename(part) + "_"
+ System.currentTimeMillis());
final String absolutePath = save.getAbsolutePath();
log.debug(absolutePath);
part.write(absolutePath);
sc.getRequestDispatcher(DATA_COLLECTION_JSP).forward(req, resp);
}
}
// helpers
private static String getFilename(Part part) {
// courtesy of BalusC : http://stackoverflow.com/a/2424824/281545
for (String cd : part.getHeader("content-disposition").split(";")) {
if (cd.trim().startsWith("filename")) {
String filename = cd.substring(cd.indexOf('=') + 1).trim()
.replace("\"", "");
return filename.substring(filename.lastIndexOf('/') + 1)
.substring(filename.lastIndexOf('\\') + 1); // MSIE fix.
}
}
return null;
}
}
where :
#SuppressWarnings("serial")
class Controller extends HttpServlet {
static final String DATA_COLLECTION_JSP="/WEB-INF/jsp/data_collection.jsp";
static ServletContext sc;
Logger log;
// private
// "/WEB-INF/app.properties" also works...
private static final String PROPERTIES_PATH = "WEB-INF/app.properties";
private Properties properties;
#Override
public void init() throws ServletException {
super.init();
// synchronize !
if (sc == null) sc = getServletContext();
log = LoggerFactory.getLogger(this.getClass());
try {
loadProperties();
} catch (IOException e) {
throw new RuntimeException("Can't load properties file", e);
}
}
private void loadProperties() throws IOException {
try(InputStream is= sc.getResourceAsStream(PROPERTIES_PATH)) {
if (is == null)
throw new RuntimeException("Can't locate properties file");
properties = new Properties();
properties.load(is);
}
}
String property(final String key) {
return properties.getProperty(key);
}
}
and the /WEB-INF/app.properties :
upload.location=C:/_/
HTH and if you find a bug let me know
I have a "simple" problem downloading a PDF, Zip or XLSX auto-generated file in Spring MVC.
I have an Exporter component that is capable of exporting a dataset into CSV,PDF,XLSX (plain or compressed) formats directly to a generic OutputStream.
Running it in JUnit with temporary files as target OutputStreams succeeds. However, if I run that component using Spring MVC's auto-wired response OutputStream then the response gets corrupted
#RequestMapping("/export")
public void export(#RequestBody ..., HttpServletResponse response)
throws IOException, ExportException
{
webExportService.export(MyEntity.class, exportRequest, sessionFactory, response.getOutputStream(), response);
}
public <T> void export(Class<T> clazz, RequestBean exportInfo, SessionFactory sessionFactory, OutputStream outputStream,
HttpServletResponse httpResponse) {
httpResponse.setContentType(getMimeType());
httpResponse.setHeader("Content-Disposition", "attachment; filename=" + getFileName() + "");
httpResponse.setHeader("Pragma", "public");
httpResponse.setHeader("Cache-control", "must-revalidate");
export(clazz, exportInfo, sessionFactory, outputStream); //this will write to the outputStream
}
[Edit] MAJOR REPHRASE REQUIRED. Sorry now that I re-read the question I found I phrased it bad
When I try to open files of ZIP, PDF and XLSX types, those files cannot be opened by their default editors. Opening them with Notepad++, compared to a temporary folder copy, shows something interesting. The binary characters are different in the two files, like in the example below:
Good
Bad
Output stream is type org.apache.catalina.connector.CoyoteOutputStream.
It looks like it is a content encoding problem. How do I find out the correct encoding for those files, if leaving to default is not working?
Side note: I initially posted that the files were "truncated", because opening them (with Notepad++, not mentioned in early question) showed up just a few dozen lines. That looked being correct, I was misleaded by the small size of the file and thought that a double newline was cutting the response entity
I am working on extjs 4.0.2a along with Java. I am using some framework with java such as Hibernate/JPA and Spring. Now I want to use a Jasper Report designing tool i.e. iReport to generate my reports in different formats (.xls,.pdf) etc. I am familiar with iReport. I generated the iReport file in .jrxml and .jasper format using the jdbc connection.
Now i want to integrate my generated report with JAVA so I can get the report in either .pdf or .xls format. When I click on pdf icon I can download the report generated in .pdf format.
I am using Ext js 4.0.2a mvc architecture. Anyone having idea how to proceed can help me
My solution for this problem is simple. As you are using ExtJs I think your controller is in JavaScript.
Do a request for the java server to create your report in pdf, xls, whatever
Save the report in your session with a key(can be the time in mileseconds)
Return the key to the view and open a new window to ask the server for your report
The new windows should contain a URL like this http://localhost:8080/myApplication/report?key=312312313
If you want to download to xsl you can try this:
private void exportToExcel(HttpServletResponse resp, JasperPrint jasperPrint) throws IOException {
String reportfilename = tagreport("file") + "repor.xls";
JExcelApiExporter exporterXLS = new JExcelApiExporter();
exporterXLS.setParameter(JRXlsExporterParameter.JASPER_PRINT,jasperPrint);
exporterXLS.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE,Boolean.TRUE);
exporterXLS.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
exporterXLS.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
exporterXLS.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, resp.getOutputStream());
resp.setHeader("Content-Disposition", "inline;filename="+ reportfilename);
resp.setContentType("application/vnd.ms-excel");
try {
exporterXLS.exportReport();
} catch (JRException e) {
e.printStackTrace();
}
}