开发者

Piped Stream Issue in Freemarker

I need to load and process a template in freemarker. I am using a piped stream to read back the generated result by freemarker.

Sample code:

PipedInputStream pi = new PipedInputStream();
PipedOutputStream po 开发者_JAVA百科= new PipedOutputStream(pi);
Writer writer = new OutputStreamWriter(po);
configuration.getTemplate("xx").process(rootMap, writer);

The issue is that sometimes it's freezing inside freemarker procsss method. No Error, no Exception, but it's not returning back from the process method.

If I convert the piped stream to a ByteArray stream, it works fine.

Am I using piped stream in correct way?


No, piped streams are designed to pass data between two threads. There is only a small buffer between the ends of the pipe. If you write into the piped output stream, your thread will be blocked if the buffer is full until another thread will read from the corresponding piped input stream. This will not work with just one thread.

From the Javadoc:

Typically, data is read from a PipedInputStream object by one thread and data is written to the corresponding PipedOutputStream by some other thread.

So for small templates just use a StringWriter, for large ones you may use a FileWriter on a temp file created by File.createTempFile().


As Arne writes, the amount of buffer space in a piped stream is fairly small. If you can't use a buffer that can hold all of the data (whether in memory or on disk) then one thing you could try is to see if you can run the template processing in another thread with a piped stream sending the results back to the main thread where you're doing this.

PipedInputStream pi = new PipedInputStream();
final Writer writer = new OutputStreamWriter(new PipedOutputStream(pi));
Thread worker = new Thread(new Runnable() {
    public void run() {
        configuration.getTemplate("xx").process(rootMap, writer);
    }
});
worker.start();

You might need to add final keywords to other variables to make this work in your real code. It depends on whether the variable configuration, the argument to getTemplate or the rootMap variable are local variables or instance (or class) variables.

(I could have subclassed Thread when specifying the thread's behavior of course, but I prefer to instantiate an interface – Runnable in this case – for such things.)


This is how I made it work.

final String uploadReportAsCsv = FreeMarkerTemplateUtils.processTemplateIntoString(
                        fileUploadReportTemplate, modelMap);

message.addAttachment("fileUploadProcessedReport.csv",
    new InputStreamSource() {
        //InputStreamResource from Spring is always returning an open stream,
        // thus we need to create this anonymous class.
        @Override
        public InputStream getInputStream() throws IOException {
            return new StringInputStream(uploadReportAsCsv);
        }
    }
);
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜