[ 
https://issues.apache.org/jira/browse/WICKET-964?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_12527064
 ] 

Peter Ertl commented on WICKET-964:
-----------------------------------

Johan,

you are absolutely right and I don't like InputStream being null either.

I think the best design decision would be to not use an InputStream for 
generating resource data

   InputStream getInputStream() throws ResourceStreamNotFoundException 

but instead something like

  ResourceData getResourceData() throws ResourceStreamNotFoundException 

// suggestion //

public interface ResourceData extends IClusterable
{
  write(OutputStream);
}

public class InputStreamResourceData implements ResourceData
{
  private final InputStream input;

  public class InputStreamResourceData(InputStream input)
  {
    this.input = input:
  }
  
  public void write(OutputStream output) 
  {
     Streams.copy(this.input, output);
  }
}

ResourceData would care about putting the data into the response stream, 
whereever it comes from (input stream, byte array, builder, database result 
set, ...(

ResourceStreamRequestTarget would need only little change to make this work.

However changing that one interface method will cause quite some changes of 
code that depend on this method. Maybe you guys think of a better solution that 
is more backward compatible.

However *imho* it's quite essential being able to access the response stream 
directly to produce unbuffered response data because:

- when resource data is really, really large (like 10-100 MB) memory could 
exhaust easily
- the overall memory requirements of the server could be lowered significantly
- zillions of clients access your resources and again memory could exhaust just 
to easy
- it's faster!


> 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
>            Assignee: Johan Compagner
>         Attachments: ResponseWriterResource.java
>
>
> 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.

Reply via email to