I'm using the following code to dynamically generate a download in Wicket, using the ResourceLink approach (since the download is not a static file, it needs to be generated on the fly, and I was told this was the correct approach):
IResource res = new AbstractResource() {
#Override
protected ResourceResponse newResourceResponse(Attributes attributes) {
ResourceResponse resourceResponse = new ResourceResponse();
resourceResponse.setContentType("application/pdf");
resourceResponse.setFileName("output.pdf");
resourceResponse.setContentDisposition(ContentDisposition.ATTACHMENT);
resourceResponse.setWriteCallback(new WriteCallback() {
#Override
public void writeData(Attributes attributes) throws IOException {
OutputStream outputStream = attributes.getResponse().getOutputStream();
try {
outputStream.write(generateDocument());
} catch (Exception e) {
//Generation failed... Here I'd like to either show a popup message or alter the current page to show an error somewhere in the page
}
}
});
return resourceResponse;
}
};
ResourceLink<Void> resLink = new ResourceLink<Void>("resLink", res);
myForm.add(resLink);
The comment in the code above shows where I'm having trouble. If the generation of the download fails (which can happen, if certain conditions are not met) I'd like to show an error message, either by showing a popup or altering the page to show some error text (but in either case I want to avoid leaving/reloading the entire page)
Is this possible?
Here's the link with the answer:
https://cwiki.apache.org/confluence/display/WICKET/AJAX+update+and+file+download+in+one+blow
Don't forger to use a try/catch with an error(e.getMessage()) inside the catch and a target.add(feedbackPanel) after catching the error.
I am not sure this is possible because you need to use non-Ajax request to be able to download as ATTACHMENT. But since it is non-Ajax request you will need to either reload the current page or redirect to another page in case of an error.
I try my best to describe my situation.
My wicket site contains list wicket component, where every list element has another list. Each element in lowest level list has ajax wicket link to download some file. All this works fine. I used to this AjaxBehaviour. Method startDownload of this behaviour is invoked within link onClick method.
public void startDownload(AjaxRequestTarget target) {
target.appendJavaScript("window.location.href='" + getCallbackUrl() +"'");
}
Method onRequest of this behaviour is:
#Override
public void onRequest() {
IRequestHandler fileTarget = new IRequestHandler() {
#Override
public void respond(IRequestCycle requestCycle) {
if (null != file) {
try {
FileInputStream inputStream = new FileInputStream(file);
WebResponse resp = (WebResponse) requestCycle.getResponse();
resp.setAttachmentHeader(fileName);
String contentType = FileUtils.getFileType(fileName);
if (contentType != null) {
resp.setContentType(contentType);
}
resp.setHeader("Pragma", "anytextexeptno-cache");
resp.setHeader("Cache-Control", "max-age=0");
Streams.copy(inputStream, requestCycle.getResponse().getOutputStream());
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
Now i need to reload model and refresh some components in the page after download file action. I tried to add entire page to the AjaxRequestTarget in method onclick, after code invoked startDownload method. Reload page works fine but window with file to download doesn`t show.
I think that i have to do reload page in other, separate request (maybe i'm mistaken? ), because in this request i call 'window.location.href=....', but i don`t know how i can to enforce second request to reload page.
Does have someone some ideas what I do wrong ? And how can I resolve my problem ?
Seems you need something like this:
https://cwiki.apache.org/confluence/display/WICKET/AJAX+update+and+file+download+in+one+blow
It seems that my implementation is simmilar to this from cwiki.apache.org website. In onRequest method i used getComponent().getRequestCycle().scheduleRequestHandlerAfterCurrent(handler), and despite of this doesn`t work.
Is possible that reason of this is component, which cause request is added to target (because i add to target entire page and component - ajaxLink in this example, is child of this page)
I'm using Apache Tapestry v5.3.7 and I already use the normal Tapestry upload component in a form. For a better user experience I try now to integrate Dropzone.js in a normal Tapestry page without any form. The JavaScript integration works fine. The uploaded file data are transferred to my server with a post request and I can access the request with all of its parameters.
My question is now how can I access the binary data of the uploaded file (maybe as InputStream) to save them in my system? I already injected the http request but getInputStream returns a empty stream.
Thanks for any suggestions
/** Code snippet of page java part */
...
#Inject
protected HttpServletRequest _request;
public void onActivate (String rowId) {
String fileName=_request.getParameter("file");
try {
InputStream is=_request.getInputStream();
// if I do read from is it returns -1
// :-(
doSomeSaveStuff(is); // dummy code
}
catch(Exception e) {
e.printStackTrace();
}
}
...
Here's one way to do it:
In template:
<t:form t:id="testForm" class="dropzone">
</t:form>
In page.java
#Inject
MultipartDecoder multipartDecoder;
#Component(id = "testForm")
private Form testForm;
#Inject
RequestGlobals requestGlobals;
void onSubmitFromTestForm() throws ManagerException {
System.out.println("test form invoked");
HttpServletRequest r = requestGlobals.getHTTPServletRequest();
UploadedFile u = multipartDecoder.getFileUpload("file");
The uploaded file contains what you uploaded and you can work with it the way you want.
Note: the HttpServletRequest::getParameterMap() , told me that the handle to to the file is called file which is how I know that passing file to getFileUpload makes the decoder correctly parse the multipart/post
I've got a pretty straightforward Java webapp that has been showing some very strange behavior on development systems. The problem starts with the registration handler, which is implented as follows:
//XXX: this shouldn't really be 'synchronized', but I've declared it as such
// for the sake of debugging this issue
public synchronized ModelAndView submitRegister(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String email = request.getParameter("email");
String pass = request.getParameter("pass");
String conf = request.getParameter("conf");
String name = request.getParameter("name");
EntityManager em = DatabaseUtil.getEntityManager(request);
//[make sure required fields are present and valid, etc.]
User user = getUserForEmail(email, em);
if (user != null) {
//[user already exists, go to error page]
}
//create the new user
em.getTransaction().begin();
try {
user = new User();
//[set fields, etc.]
em.persist(user);
//[generate e-mail message contents]
boolean validEmail = EmailUtility.sendEmail(admin, recip, subject, message, null, recip);
if (validEmail) {
em.getTransaction().commit();
//[go to 'registration successful' page]
}
em.getTransaction().rollback();
//[go to error page]
}
catch (Exception e) {
em.getTransaction().rollback();
//[go to error page]
}
}
The problem occurs on the EmailUtility.sendEmail() call. The code for this method is pretty straightforward:
public static boolean sendEmail(String fromAddress, String to, String subject, String message, String fromHeaderValue, String toHeaderValue) {
try {
Session session = getMailSession(to);
Message mailMessage = new MimeMessage(session);
mailMessage.setFrom(new InternetAddress(fromAddress));
if (fromHeaderValue != null) {
mailMessage.setHeader("From", fromHeaderValue);
}
if (toHeaderValue != null) {
mailMessage.setHeader("To", toHeaderValue);
}
mailMessage.setHeader("Date", new Date().toString());
mailMessage.setRecipients(RecipientType.TO, InternetAddress.parse(to, false));
mailMessage.setSubject(subject);
mailMessage.setContent(message, "text/html;charset=UTF-8");
Transport.send(mailMessage);
return true;
} catch (Throwable e) {
LOG.error("Failed to send e-mail!", e);
return false;
}
}
What happens is that when the code reaches the call for EmailUtility.sendEmail(), instead of calling that method execution recurses through submitRegister(). That's easily one of the most bizarre things I've ever seen.
For awhile I didn't even believe that was what's actually happening; but at this point I've confirmed it by synchronizing the method involved and adding print statements on every line of both methods. submitRegister() recurses, and sendEmail() is never called. I've got no idea how this is even possible.
Frustratingly, the exact same code runs just as it should on the production server. It's only on development systems that this problem appears.
Any suggestions regarding what might be causing this problem and what I can do to fix it are welcome.
You are right, This is not possible :)
I would suggest you strip away all other code, put in a lot of logging, if you don't like debugging and see what happens. Start with something like:
public synchronized ModelAndView submitRegister(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
LOG.debug("submitRegister: " + this.toString);
EmailUtility.sendEmail("a#x.y", "b#x.y", "subject", "message", "from", "to");
}
public static boolean sendEmail(String fromAddress, String to, String subject, String message, String fromHeaderValue, String toHeaderValue) {
LOG.debug("sendEmail: " + this.toString());
}
The toString will show you what classes are involved.
My guess would be that:
your first call fails, so sendEmail will never be invoked
submitRegister is triggered more than once by someone else, not by the EmailUtility.sendEmail statement.
If you get the stripped version to work, start putting back your code, one peace at a time to see where it all goes bad :)
Okay, I tracked this down to a few different issues working together:
On development systems, the classpath was missing javax.mail.Address. This caused the EmailUtility class to fail to initialize, and would throw a NoClassDefFoundError on the sendEmail() call, before any code from that method could execute.
The code in submitRegister() had a catch Exception block, but NoClassDefFoundError extends Error, not Exception. So it bypassed the catch Exception block entirely.
The Spring controller where the Error was actually caught had some of the most questionable "error-handling" code I've ever come across:
try {
Method serviceMethod = this.getControllerClass().getMethod(method, HttpServletRequest.class, HttpServletResponse.class);
if (this.doesMethodHaveAnnotation(serviceMethod, SynchronizedPerAccount.class)) {
synchronized(this.getAccountLock(request)) {
super.doService(request, response);
}
}
else {
//don't need to execute synchronously
super.doService(request, response);
}
}
catch (Throwable ignored) {
super.doService(request, response);
}
So the NoClassDefFoundError was propagating back up to the Spring controller, which was catching it and attempting to re-invoke the doService() method, which caused submitRegister() to be invoked again. It wasn't recursion (though there was no way to tell that by just looking at the debug output), it was the Spring controller calling it twice for the same request. It never got called more than twice for a given request, because there's no try/catch around the second doService() call.
Long story short, I patched up these issues and problem solved.
This is my download code. It just starts downloading the file without asking user. I've searched multiple forums and nothing seems to work. This is code is in a backing bean attached to a commandButton.
public void doDownloadFile() {
PrintWriter out = null;
try {
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=test.csv");
out = response.getWriter();
CSVWriter writer = new CSVWriter(out);
List<String[]> stringList = new ArrayList<String[]>();
for (User user : userList) {
String[] string = {user.getEmail(), user.getName(), user.getPassword()};
stringList.add(string);
}
writer.writeAll(stringList);
out.flush();
} catch (IOException ex) {
Logger.getLogger(ViewLines.class.getName()).log(Level.SEVERE, null, ex);
} finally {
out.close();
}
}
This is most likely due to the fact your browser is configured to download files of these types without prompt. The code has nothing to do with it.
The behavior of what to do with a download is 100% local, meaning it's the browser, not you, that determines what to do in that case. Whether the user's browser just dumps the file in a download folder or allows him to save it to a particular spot is entirely up to the browser.
Not much to be done.