Issue Details (XML | Word | Printable)

Key: WW-3195
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Major Major
Assignee: Unassigned
Reporter: Jasper Rosenberg
Votes: 0
Watchers: 2
Operations

If you were logged in you would be able to see more operations.
Struts 2

When FreemarkerResult isn't writing directly to Response, s.action tag is broken

Created: 17/Jul/09 07:02 PM   Updated: 27/Oct/09 05:04 PM
Return to search
Component/s: None
Affects Version/s: 2.1.8
Fix Version/s: 2.2.0

Issue Links:
Duplicate
 
Reference
 

Flags: Patch


 Description  « Hide
In https://issues.apache.org/struts/browse/WW-1808, the FreemarkerResult was improved to support not writing to the response until it had completed processing.

Unfortunately, if you are using an s.action tag, then that means the s.action output ends up before the main template output rather than inline.

One way to fix this (which I did locally) is to add a thread local that keeps track of the "parent" writer such that if there is one present the child writes to that at the end rather than the response's writer.

Something like:

    /** Thread local for the current writer. */
    private static final ThreadLocal<CharArrayWriter> WRITER_THREAD_LOCAL
        = new ThreadLocal<CharArrayWriter>();

...
   // Inside the case where we are writing upon completion

            CharArrayWriter parentCharArrayWriter = WRITER_THREAD_LOCAL.get();
            try {
                // Process the template with the normal writer since it was available
                // But delay writing in case exception
                CharArrayWriter charArrayWriter = new CharArrayWriter(1024);
                
                if (parentCharArrayWriter == null) {
                    WRITER_THREAD_LOCAL.set(charArrayWriter);
                }
                
                template.process(model, charArrayWriter);

                charArrayWriter.flush();

                // Only the template that created the writer can actually write
                // to the response. This is necessary to support the s.action tag.
                if (parentCharArrayWriter == null) {
                    charArrayWriter.writeTo(writer); // Write to the response writer
                } else {
                    // Copying to parent enmass so don't get partially rendered s.actions
                    charArrayWriter.writeTo(parentCharArrayWriter);
                }
            } finally {
                if (parentCharArrayWriter == null) {
                    WRITER_THREAD_LOCAL.remove();
                }
            }




 All   Comments   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Jasper Rosenberg added a comment - 22/Jul/09 11:57 AM
Just discovered the code above doesn't work when s.actions are nested 2 deep. Can't have the template.process write to a temporary buffer as it is the same problem then as the original :) The code below will work better.

CharArrayWriter parentCharArrayWriter = WRITER_THREAD_LOCAL.get();
            try {
                // Process the template with the normal writer since it was available
                // But delay writing in case exception
                CharArrayWriter charArrayWriter;
                if (parentCharArrayWriter == null) {
                    charArrayWriter = new CharArrayWriter(1024);
                    WRITER_THREAD_LOCAL.set(charArrayWriter);
                } else {
                    charArrayWriter = parentCharArrayWriter;
                }
                
                template.process(model, charArrayWriter);

                // Only the template that created the writer can actually write
                // to the response. This is necessary to support the s.action tag.
                if (parentCharArrayWriter == null) {
                    // Process the template
                    Writer writer = getWriter();
    
                    charArrayWriter.flush();
                    charArrayWriter.writeTo(writer);
                }
            } finally {
                if (parentCharArrayWriter == null) {
                    WRITER_THREAD_LOCAL.remove();
                }
            }

Musachy Barroso added a comment - 12/Aug/09 10:14 PM
I am very uncomfortable making this change and risking breaking normal results just to fix the "action" tag when using freemarker, I will move this

Jasper Rosenberg added a comment - 20/Aug/09 07:27 PM
I think this change is less scary than you may have originally read it.

A. It only effects FreemarkerResult
B. Is only for the case: if (isWriteIfCompleted() || configuration.getTemplateExceptionHandler() == TemplateExceptionHandler.RETHROW_HANDLER)
Which was only introduced at the begining of this year in the 2.1 tree.



Dushyant Pandya added a comment - 26/Oct/09 11:07 PM
Is there any plan to fix this defect?

Musachy Barroso added a comment - 26/Oct/09 11:46 PM
I am looking at it, I got a junit test case to reproduce it.

Musachy Barroso added a comment - 27/Oct/09 05:04 PM
Fixed in trunk. Tests added as well. Please give it a try and let me know