Help

Controls

PermLinkWikiLink

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

Forum: Seam Users Forum ListTopic List
06. Mar 2008, 12:15 CET | Link

Hi,

i have created a pdf with itext directly in my action class and iText is offering me some outputstream options (e.g. ByteArrayOutputStream, ...)

Now i would like to send this pdf (stream) to a browser without using a jsf (.xhtml) page. Or is there a tag to this ? I do not want to store the pdf on disc if i can avoid it.

Is this possible ?

Any ideas are welcome.

Best regards.

mki

17 Replies:
06. Mar 2008, 14:29 CET | Link

Easy. Use the byteArrayOutputStream and hook it up to a servlet. You can keep the byteArrayOutputStream in memory in a conversation/session scoped Bean until the servlet needs it. Use Component.getInstance() in your servlet to access the Bean.

Cheers,

Damian.

06. Mar 2008, 15:27 CET | Link

mki,

Possibly an even easier way that doesn't require writing a custom servlet....write a Seam component that serves up the report - this is the approach we took for the application we demoed at JbossWorld.


@Name("reportAction")
public class ReportAction
{
    @In
    private FacesContext facesContext;
    
    public void run() 
    { 

final HttpServletResponse resp = (HttpServletResponse) facesContext.getExternalContext().getResponse();
// run the report            
// write the report to resp.getOutputStream()
            facesContext.responseComplete();
    }
}

Then in xhtml


<h:commandLink action="#{reportAction.run}"/>

or


<s:link action="#{reportAction.run}"/>

etc.

Good luck!

Rating:  * * * * *
06. Mar 2008, 22:03 CET | Link

Thanks Keith,

that was exactly the solution i was looking for. :-)

08. Mar 2008, 17:42 CET | Link

Keith,

I gave this a go and it's very cool and easier to implement than the servlet. The only thing that escapes me at the moment is how to assign a filename to the download. With the servlet I used a servlet URL pattern so I could put any name in the link.

What is the JSF equivalent of that?

Cheers,

Damian.

08. Mar 2008, 19:38 CET | Link

That's easy, you simply have to set the Content-Disposition in the header:


		final HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
		
		response.setContentType("application/pdf");
		response.setHeader("Content-Disposition", " attachment; filename=invoice.pdf");

Greetings,

Manuel

09. Mar 2008, 11:44 CET | Link

Genius. Thanks Manuel & Keith. Much appreciated.

Cheers,

Damian.

08. Mar 2008, 23:58 CET | Link

Damian, Manuel has it dead on. If I remember correctly IE behaves slightly differently than the other browsers so you may or may not see the right filename in all browsers. I just found this article that describes IE's flakiness.

I would have replied sooner, but we just had a record snowfall of 16 inches and I've spent a good part of the day digging out of it!

Keith

09. Mar 2008, 19:06 CET | Link

I have used exactly that technique a couple of times, but recently I got notice from quite a few customers that it did not work.

The problem was Safari (3+ something, whatever ships with their newest cat-release), where this approch does not work. So we also had to go with the custom servlet approach, which works fine.

20. Mar 2008, 16:03 CET | Link

I implemented the solution described by Keith Naas on 3/6, and it got it working. I'm just learning, so it took a little while. Now that I have it, I would like to expose the servlet so that it is available by direct URL, instead of by JSF commandLink. Is that possible? How would I go about doing that?

 

Speak good words, Think good thoughts, Walk a good road.

20. Mar 2008, 16:15 CET | Link

Ken,

you can define a link to your action in pages.xml.


<page view-id="/pdf.xhtml" action="#{pdfgeneratorAction.generate}"/> 

Manuel

20. Mar 2008, 20:12 CET | Link

Thanks Manuel. Is there a way to configure it so that the page I use (/pdf.xhtml in your example) doesn't actually have to exist? I can certainly put the page in place, but it seems sort of kludgy, since it never gets displayed, and never should.

 

Speak good words, Think good thoughts, Walk a good road.

20. Mar 2008, 20:38 CET | Link

Thanks for your help. I am new at this, so I'm certain I'm asking questions that would seem stupid to anyone who knows this stuff, but anyway, I have one more thing. I was hoping to pass form information in the request, and use that to manipulate the PDF created. After setting up access through pages.xml, I'm not seeing any parameters in the HttpServletRequest object. I promise I'm not trying to get you to write my code for me, but I'm up against the boundaries of my knowledge.

The HTML code that calls this looks like this:

<html>
<head>
<title>Connector test</title>
</head>
<body>

<p>&nbsp;</p>
<form action="http://localhost/hbi/services/pdf/test.seam" method="post">
<input name="content" id="content" type="text" size=20>
<div><input type="submit" value="PDF"></div>
</form>

</body>
</html>

My code looks like this, and I'm finding nothing in my parameterMap:

public class PrintServer extends SeamResourceServlet {
	private static final long serialVersionUID = 1278807414184735439L;

	@In
    private FacesContext facesContext;
	
	@Logger
	private static Log log;
	
	
	public void test( String message )
 		throws ServletException, IOException, DocumentException
	{
		final HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
		final HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();

		log.info( "context path: " + request.getContextPath());
		log.info( "method: " + request.getMethod());
		log.info( "URI: " + request.getRequestURI());
		
		Map parameterMap = request.getParameterMap();
		
		if( parameterMap==null)
		{
			log.info( "Can't get parameter names");
		}
		else
		{
			
			log.info("Request Parameters: " + parameterMap.toString());
			Collection <Map.Entry> paramCollection =  parameterMap.entrySet();
			java.util.Iterator <Map.Entry> paramIterator = paramCollection.iterator();
			
			while( paramIterator.hasNext())
			{
				Map.Entry me = paramIterator.next();
				String [] sa = (String [] ) me.getValue();
				String sValue = "";
				
				for( int i=0; i<sa.length; ++i )
				{
					sValue += sa[i] + ",";
				}
				log.info("Request Parameter " + me.getKey() + " size " + sValue);
			}
			log.info("Request Parameters size: " + parameterMap.size());
		}
		log.info("Request Querystring: " + request.getQueryString());

		facesContext.responseComplete();
		
		ServletOutputStream out = response.getOutputStream();
		
		Document document = new Document();
		PdfWriter.getInstance(document, out);
		document.open();
		document.add(new Paragraph("Form message: " + message));
		document.close();
		
		response.setContentType("application/pdf");
		response.setHeader("Content-Disposition", " attachment; filename=PrintTest.pdf");		            
		
		log.info("Run GraphServer.test");
		return;
	}
}
 

Speak good words, Think good thoughts, Walk a good road.

21. Mar 2008, 14:48 CET | Link

Ken,

the beauty of the solution in 3 is, that it does even not use a servlet to deliver the PDF, so you can do everything in a standard seam way by calling an action, create the pdf and deliver it to the browser.

Inside this action you have easy access to all form values.

So why going the hard way ?

Manuel

21. Mar 2008, 17:29 CET | Link

Thanks for your help Manuel.

Am I going the hard way? I thought I was replicating the solution above. I do see that my code extends the SeamResourceServlet, but I'm not using that interface - it was left over from my servlet approach, and I forgot to remove it.

I restarted my JBoss server (the whole machine, in fact) and now I'm seeing the form information. There must have been something left in my deployment that was interfering, and it got cleaned out by the restart. So the code I provided above works after all.

As a last question, is there a better/simpler way to retrieve the form information than what I do above?

Thanks again.

 

Speak good words, Think good thoughts, Walk a good road.

23. Mar 2008, 16:21 CET | Link

I have another question about this. If I use same aproach as above to show images stored in a database as a blob, how do I map the fake image url to this ActionClass (ReportAction).

Let's I want all my image urls to be: http://localhost:8080/project/images/foo.jpg

How do I do the mapping from the url so it's mapped to the ActionClass?

Thanks,

/Jacob

23. Mar 2008, 16:37 CET | Link

If I used a Custom servlet I would have to add this to the web.xml


<servlet>
    <servlet-name>CustromServlet</servlet-name>
    <servlet-class>test.CustomServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>CustromServlet</servlet-name>
    <url-pattern>/image/*</url-pattern>
</servlet-mapping>

But the above aproach is not a servlet, how or is it possible? :)

/Jacob

23. Mar 2008, 17:13 CET | Link

You could try with pages.xml:

    <page view-id="/image/*"> <!-- This is a dummy page - it don't exist ! -->
        <action execute="#{<component name>.<action method>}"/>
       ...
       ...
     </page>