开发者

How to use Apache FOP with Velocity in Spring MVC web-app?

I have simple configuration of Velocity in Spring context (according to an official Spring documentation) and works ok. How to configure/integrate this with Apache FOP and generate pdf documents ? I would be grateful for some examples.

<!-- velocity -->
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
    <property name="resourceLoaderPath" value="/WEB-INF/velocity/"/>
</bean>
<bean id="velocityViewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
    <property name="cache" value="true"/>
    <property name="prefix" value=""/>
    <property name="suffix" value=".vm"/>
</bean>

Test controller:

@Controller
@RequestMapping("/doc")
public class DocumentController {

    @RequestMapping("/test")
    public ModelAndView velocityTest() {
        List<String> xmens = new ArrayList<String>();
        xmens.add("Professor X");
        xmens.add("Cyclops");
        xmens.add("Iceman");
        xmens.add("Archangel");
        xmens.add("Beast");
        xmens.add("Phoenix");
        Map<String, List<String>> model = new HashMap<String, List<String>>();
        model.put("xmens", xmens);
     开发者_如何学Python   return new ModelAndView("testdoc", model);      
    }
}

/WEB-INF/velocity/test.vm

<html>
    <body>
        <ul>
        #foreach ($xmen in $xmens)
            <li>$xmen</li>
        #end
        </ul>
    </body>
</html>


I did it this way, but I think it is certainly a more elegant solution (testpdf.vm and testpdf.xsl are in /WEB-INF/velocity).

@Controller
@RequestMapping("/doc")
public class DocumentController {

    @Autowired
    private PdfReportService pdfReportService;

    @RequestMapping("/pdf")
    public void testPdf(HttpServletResponse response) throws IOException {
        Map<String, Object> model = new HashMap<String,Object>(); 
        model.put("message", "Hello World!");
        pdfReportService.generatePdf("testpdf", model, response.getOutputStream());
    }

}

PdfReportService:

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.view.velocity.VelocityConfigurer;

@Service("pdfReportService")
public class PdfReportService {

    private final Logger log = Logger.getLogger(getClass());

    @Autowired
    private VelocityConfigurer velocityConfig;

    @Autowired
    private ServletContext servletContext; 

    public void generatePdf(String templateName, Map<String,Object>model, OutputStream out) {

        // get an engine
        final VelocityEngine engine = velocityConfig.getVelocityEngine();

        // get the Template
        Template template = engine.getTemplate(templateName+".vm");

        // create a context and add data
        VelocityContext context = new VelocityContext();
        context.put("model", model);

        // render the template into a StringWriter
        StringWriter writer = new StringWriter();
        template.merge(context, writer);

        FopFactory fopFactory = FopFactory.newInstance();

        try {
            //Setup FOP
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);

            //Setup Transformer
            InputStream xstlIn = servletContext.getResourceAsStream("/WEB-INF/velocity/"+templateName+".xsl");
            TransformerFactory tFactory = TransformerFactory.newInstance();
            Transformer transformer = tFactory.newTransformer(new StreamSource(xstlIn));

            //Make sure the XSL transformation's result is piped through to FOP
            Result res = new SAXResult(fop.getDefaultHandler());

            //Setup input
            byte[] bytes = writer.toString().getBytes("UTF-8");
            Source src = new StreamSource(new ByteArrayInputStream(bytes));

            //Start XSLT transformation and FOP processing
            transformer.transform(src, res);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

}


I did it exactly as marioosh. Later on we had issues (OutOfMemory ecxeptions) in a big web application when we had a lot of users doing PDF conversation at the same time since both the VelocityEngine and Apache FOP need some (a lot of) memory and when you have many concurrent users, this sums up.

We changed the approach to streaming. Velocity streams the XSL-FO to Apache FOP now. FOP streams the result to the client. We did this with PipedReader / PipedWriter. Please note that this solution needs an extra thread. I cannot share this code as we did it for a customer of us.

Meantime I found an existing solution for streaming on the web, see the archive. But note that this solution creates an extra thread via new Thread(worker).start(); In an application server, you should rather use a WorkManager instead. See also http://www.devx.com/java/Article/28815/1954

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜