However, it could be very convenient in some situations. For example, you do not want to add a new servlet to the project, and you want to use servlet DWR well suited for this. Or you want to use its convenient interface to access the methods of Java, in which the data for downloading could be formed, as in my case.
Anyway, after a few agonizing moments I have found a way to implement such a mechanism.
So what is required?
First, of course, we need to write java code that will build the required binary data and write it into output of server response. According to DWR Rules, a java-method will generate these data, which we will call from browser. The method will not return binary data. It will return a simple string marker, but more on that later.
So this is a code of our test class:
package test;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import uk.ltd.getahead.dwr.WebContextFactory;
public class ExcelGenerator {
public String getExcelTest() throws IOException {
final HSSFWorkbook wb = new HSSFWorkbook();
final HSSFSheet sheet = wb.createSheet("sheet_1");
HSSFRow tempRow = sheet.createRow((short) 0);
HSSFCell tempCell = tempRow.createCell((short) 0);
HSSFRichTextString tempCellValue = new HSSFRichTextString(
"Hello World!");
tempCell.setCellValue(tempCellValue);
HttpServletResponse response = WebContextFactory.get().getResponse();
response.setContentType("application/vnd.ms-excel");
response.setHeader("pragma", "public");
response.setHeader("pragma", "no-cache");
response.setHeader("Cache-Control", "cache");
response.setHeader("Cache-Control", "must-revalidate");
response.setHeader("Content-Disposition",
"download;filename=\"helloworld.xls\"");
ServletOutputStream servletOutputStream = response.getOutputStream();
wb.write(servletOutputStream);
servletOutputStream.flush();
servletOutputStream.close();
return "END_RESPONSE";
}
}
This code uses the Apache POI to generate a MS Excel workbook, which is our binary data. String END_RESPONSE is an indication of the fact that you do not need to write anything more to the output and it is used in the future.
Initially, DWR will attempt to return the string to the browser, but it is not desirable for us. In addition, after a call to our method of generating DWR will attempt to re-open the output to its own output, which would lead to an exceptional situation.
In order to avoid it, we will use the Plug-in Points for DWR. In the project file web.xml, in the section describing the servlet DWR, we add the initialization parameter in order to change org.directwebremoting.dwrp.PlainCallMarshaller used in response process:
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
TestApp
dwr-invoker
org.directwebremoting.servlet.DwrServlet
org.directwebremoting.dwrp.PlainCallMarshaller
test.HelloPlainCallMarshaller
activeReverseAjaxEnabled
true
initApplicationScopeCreatorsAtStartup
true
maxWaitAfterWrite
-1
allowScriptTagRemoting
true
1
dwr-invoker
/dwr/*
Here are highlighted lines in web.xml file that are important to us now. That lines mean that the behaviour of the class org.directwebremoting.dwrp.PlainCallMarshaller will be assigned to the class test.HelloPlainCallMarshaller. Therefore the class test.HelloPlainCallMarshaller would participate in the chain of calls, if the method getExcelTest(or another) was called through DWR. This class extends the class PlainCallMarshaller and its method marshallOuntbout is performed after the method getExcelTest.
package test;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.directwebremoting.extend.Replies;
public class HelloPlainCallMarshaller extends org.directwebremoting.dwrp.PlainCallMarshaller{
public void marshallOutbound(Replies replies, HttpServletRequest request, HttpServletResponse response) throws IOException{
if(replies.getReplyCount() != 1 || !replies.getReply(0).getReply().toString().
equals("END_RESPONSE"))
super.marshallOutbound(replies, request, response);
}
}
The sole purpose of our method marshallOuntbout is to check that, whether we want to interrupt the chain of further challenges DWR or continue it. We need it because otherwise we would get an exception that the response output stream has already been opened. The method getExcelTest returns END_RESPONSE and our method marshallOutbound knows this and terminates further execution.
Just do not forget to add the class ExcelGenerator configuration DWR as usual, for example, through the file dwr.xml!
Client settings
Now that we have configured the server part of our application, we still have to deal with how to call a method from a browser. The problem is that we will not get this file if we used a standard call to method through DWR, because DWR uses AJAX to access the server. The solution is that when we invoke a method we can extract all the parameters that DWR sends in the request and then paste them into a dynamically created form. Then after submitting of the form, we have a chance to get our Excel file!
We can write a method that would be taken as an argument a call of the method to the server, but instead of that would implement AJAX call, made the above operations.
var DWRUtils = new Object();
DWRUtils.directPostDWR = function(genCall){
var form = document.createElement("form");
form.method = "POST";
document.body.appendChild(form);
dwr.engine.beginBatch();
genCall();
var map = dwr.engine._batch.map;
form.action = dwr.engine._defaultPath + dwr.engine._ModePlainCall +
map["c0-scriptName"]+ "." + map["c0-methodName"] + ".dwr";
map["batchId"] = dwr.engine._nextBatchId;
for(var param in map){
var input = document.createElement('input');
input.type = 'hidden';
input.name = param;
input.value = map[param];
form.appendChild(input);
}
form.submit();
if(typeof form.removeNode != "undefined")form.removeNode(true); // IE
else document.body.removeChild(form); // FF
dwr.engine._batch = null;
}
Now we can download like this!
DWRUtils.directPostDWR(function(){
ExcelGenerator.getExcelTest();
}
);