Agent to facesContext stream: I'm missing the connection
Sep 27, 2011
http://www.bleedyellow.com/blogs/DominoHerald/entry/agent_to_facescontext_stream_i_m_missing_the_connection?lang=en_us


Stephan has a great post on using facesContext to handle the output of an agent. But I'm missing how you hook the agent output to this. I'm not a deep Java guy, and I've been looking for it and not finding it.
What I want to do is have an agent where I create a document (a PDF at this point, so Java) and create a file then serve the file to the browser. All the examples I'm finding are showing how to do it all in via the writer where you write the text in (like the example in the xPages cheat sheet). I'm sure it's real simple, but I'm not seeing where I tell the code in the xpage I don't render to get the output of the agent I trigger. 
I'm thinking I could create a NotesStream, get my file and hook it up it up to the "getResponse", but I can't find how to do that. Stephan says to use the stream, I just can't find a sample of how that goes from soup to nuts.
If someone gives me a working sample, I'll buy dinner.
Cheers,
Brian

 


Add a Comment 
More Actions 
Comments (6)


1 Tim Tripcony commented Sep 28 2011 Permalink Recommendations 0
Brian, Stephan's post describes using the facesContext *instead* of an agent, not as the output for a traditional agent... many of us have taken to referring to this technique as "XAgent": doing some of the kinds of things we used to do in an agent, but in an XPage instead.

 


In other words, the URL to trigger the PDF generation would be:
 
/somedb.nsf/somexpage.xsp
 
...not...
 
/somedb.nsf/someagent?OpenAgent
 
The XPage does all the work... and, nearly always, in less time, since all XPages run inside a single JVM, whereas a new one has to be spun up for every agent invocation.
 
For information about accessing the binary stream (instead of simply sending text content, like HTML, XML, or JSON), check out Stephan's followup post:
 
http://www.wissel.net/blog/d6plinks/SHWL-8248MT
 
Two lines in particular:
 
var out = response.getOutputStream(); 
... 
facesContext.responseComplete();

 
In an XAgent, you can send a response using either the response writer OR the response stream... not both. So when you're sending binary content, like PDF, the code must run in the beforeRenderResponse event. This is because, even if the rendered attribute is set to false, the XPage runtime still attempts to establish a handle on the response writer during the render phase of the JSF lifecycle. As a result, if your code is in the afterRenderResponse event, it's too late to get a handle on the response stream, because a response writer was already accessed. Similarly, the responseComplete method of the facesContext object tells the render phase to skip accessing the writer, because you've already sent everything that needs to be sent... if you forget this line, it sends the content properly, but then still blows up anyway.
 
The response headers he's setting in his example are optional but recommended, because they tell the browser exactly what type of content to expect. In your case, however, the content type and file extension would of course indicate PDF instead of ODF.
 
Hope that helps...


2 Paul T. Calhoun commented Sep 28 2011 Permalink Recommendations 0
Here is the SSJS source of an XPage's beforeRenderResponse event that will write the binary output. Make sure to set the xpages render property to "false"

 


 
// The Faces Context global object provides access to the servlet environment via the external content 
var extCont = facesContext.getExternalContext(); 
// The servlet's response object provides control to the response object 
var pageResponse = extCont.getResponse(); 
//Get the output stream to send the data 
var pageOutput = pageResponse.getOutputStream();

 
//Set the file headers. 
// For Example text/html, text/xml, application/json, excel, pdf, etc 
pageResponse.setContentType("MIMETYPE"); 
pageResponse.setHeader("Cache-Control", "no-cache"); 
pageResponse.setHeader("ANY OTHER NEEDED HEADER");

 
//Perform the binary output "write" functionality here
 
pageOutput.close();
 
// Tell the browser the content is complete 
//pageOutput.endDocument();

 
// Terminates the request processing lifecycle. 
facesContext.responseComplete();


3 Brian M Moore commented Sep 28 2011 Permalink Recommendations 0
@Tim - Thanks, I don't think I've been seeing the *instead* portion.

 


can I write to the output steam like a NotesStream? like this:
 
var nStream:NotesStream = session.createStream(); 
var writer = nStream;

 
Oh, and I can actually buy you dinner, I'm in your area fairly regularly.
 
Cheers, 
Brian


4 Brian M Moore commented Sep 28 2011 Permalink Recommendations 0
@Paul, Do you have an example of writing the binary data? I know I'm being dense, but I'm looking for a sample (and I have been looking, not just sitting hoping someone will deliver it to me).

 


Cheers, 
Brian


5 Paul T. Calhoun commented Sep 29 2011 Permalink Recommendations 0
Brian,

 


The actual writing of the binary data is specific to whatever library (POI versus iText versus xxx). So for example the POI api has a workbook.write method that does that output.
 
So the template code I gave you will support any output to the stream where the api you are using wii write to an output stream.
 
Paul T. Calhoun


6 Brian M Moore commented Oct 1 2011 Permalink Recommendations 0
When I try this:

 


// The Faces Context global object provides access to the servlet environment via the external content 
var extCont = facesContext.getExternalContext(); 
// The servlet's response object provides control to the response object 
var pageResponse = extCont.getResponse(); 
//Get the output stream to send the data 
var pageOutput = pageResponse.getOutputStream();

 
//Set the file headers. 
// For Example text/html, text/xml, application/json, excel, pdf, etc 
response.setContentType("text/xml"); 
response.setHeader("Cache-Control", "no-cache"); 
response.setHeader( "Content-Disposition", "attachment; filename=\"textout.xml\"");

 
var out:OutputStreamWriter = new OutputStreamWriter(pageOutput);
 
out.write("\r\n"); 
out.write("<Fibonacci Numbers="Numbers">\r\n"); 
for (i = 1; i <= 10; i++) { 
out.write(" <fibonacci index="" i="i">"); 
out.write(low.toString()); 
out.write("</fibonacci>\r\n"); 
temp:Number = high; 
high = high.add(low); 
low = temp; 

out.write("</Fibonacci>\r\n");

 
out.flush(); 
out.close();

 
// Tell the browser the content is complete 
//pageOutput.endDocument();

 
// Terminates the request processing lifecycle. 
facesContext.responseComplete();

 
I get: 
Can't get a Writer while an OutputStream is already in use.

 
I'm confused :/
 
Cheers, 
Brian
This is an archive of my previous blog http://www.bleedyellow.com/blogs/DominoHerald attachments are in the download control