enhance remove 'final' keyword from org.apache.wicket.Resource to support
builder style content generation
----------------------------------------------------------------------------------------------------------
Key: WICKET-964
URL: https://issues.apache.org/jira/browse/WICKET-964
Project: Wicket
Issue Type: Improvement
Components: wicket
Reporter: Peter Ertl
I run into the following problem recently:
In order to create ad-hoc PDF documents I decided on using the iText library
(http://www.lowagie.com/iText).
iText is capable of producing easily a 10000 pages PDF document on the fly
without getting a scary OutOfMemoryException on the web server. This is because
it's not buffering the result of creating a PDF but instantly writing the PDF
data stream _while_ you build your PDF.
Code looks something like this:
Document doc = new Document();
PdfWriter.getInstance(doc, outputStream);
doc.addAuthor("Mr. Foobar");
doc.open();
doc.add(...); // some stuff -> will write directly to stream
doc.add(...); // even more stuff -> will again write directly to stream
doc.close();
Of course I wanted to use org.apache.wicket.Resource to deliver the content to
the browser in the very wicket way. However, Resource and all it's children
have one similar problem:
You need to implement
IResourceStream getResourceStream()
which in turn demands you to implement
InputStream getInputStream() throws ResourceStreamNotFoundException
So, whatever resource you would like to implement, you need an
java.io.InputStream !
However, as iText immediately writes to an java.io.OutputStream you would have
to do one of these to successfully implement a Resource:
(1) iText -> ByteArrayOutputStream -> byte[] -> ByteArrayInputStream ->
Resource::getInputStream
(2) iText -> FileOutputStream -> temp file -> FileInputStream ->
Resource::getInputStream
(3) iText -> PipedOutputStream (thread #1) -> PipedInputStream (thread #2) ->
Resource::getInputStream
However I don't like any of these (1)-(3) because this requirement is purely
caused by the current design but technically you could just write direcly to
the ServletOutputStream
(4) PdfWriter.getInstance(doc,
getWebResponse().getOutputStream(outputStream)));
this would be the best performing and low memory solution, but Resource stream
will no cooperate and still wants an InputStream.
I wrote a simple proof-of-concept class called ResponseWriterResource (which
currently will not work unless the issue gets resolved):
public abstract class ResponseWriterResource extends Resource
{
@Override
public void onResourceRequested()
{
RequestCycle.get().setRequestTarget(new IRequestTarget()
{
public void respond(RequestCycle requestCycle)
{
write(requestCycle.getResponse());
}
public void detach(RequestCycle requestCycle)
{
}
});
}
@Override
public IResourceStream getResourceStream()
{
// ignore
return null;
}
protected abstract void write(Response response);
}
in the case of creating a pdf the code would look like this:
Resource resource = new ResponseWriterResource()
{
write(Response response)
{
Document doc = new Document();
PdfWriter.getInstance(doc, response.getOutputStream());
//
// ... create the pdf here
// ... (and write it directly! to the servlet output stream)
}
}
So the resource could immediately be rendered to the client without the need of
buffering the response first.
There's only one tiny change that I would need inside wicket:
In order to override
@Override
public void onResourceRequested()
I would need the removal of the final keyword from onResourceRequested()
class Resource
{
// ...
public final(*) void onResourceRequested() // (*) please remove the
final keyword
// ...
}
The benefit of this would be that any kind of very large resource could be
created in the web application without the need of ugly solutions like (1)-(3).
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.