[
https://issues.apache.org/jira/browse/WICKET-6823?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Sven Meier reassigned WICKET-6823:
----------------------------------
Assignee: Sven Meier
> Two instances of AbstractTransformerBehavior on the same component results in
> incomplete output.
> ------------------------------------------------------------------------------------------------
>
> Key: WICKET-6823
> URL: https://issues.apache.org/jira/browse/WICKET-6823
> Project: Wicket
> Issue Type: Bug
> Components: wicket-core
> Affects Versions: 8.9.0
> Reporter: Thorsten Schöning
> Assignee: Sven Meier
> Priority: Major
> Attachments: multiple_abstract_transformer_behavior.zip
>
>
> I'm using Wicket as a renderer for HTML-reports WITHOUT browser, web server
> or requests, only by using ComponentRenderer. There are two implementations
> of AbstractTransformerBehavior to update "colspan" attributes of table cells
> and IDs of arbitrary HTML nodes. Both are used on the same component:
> {noformat}
> resultsCont.add(new DvResultsCont.ColSpanUpdater());
> resultsCont.add(new MkIdReplacer
> (
> "th", "id", "td", "headers",
> String.format("%d.%s", itemIdx, kindOfDetail)
> ));
> {noformat}
> When only ONE of both behaviours is used, the page renders successfully and
> it doesn't make any difference which one is used. If both of those are used
> OTOH, the page seems to be rendered at all as well, but some rendered content
> is simply missing in the overall output in the end. My component
> "resultsCont" renders to a table, so the last markup I have is the following:
> {noformat}
> <table class="detailsMeters ui-expandable ui-expandable-collapsed missing">
> [...]
> </table>
> {noformat}
> In theory, after that table there should be additional content like foots,
> closing elements for HTML itself etc. So the current rendering is invalid.
> It's important to note, though, that I don't get any exception, the output
> simply seems to not contain all data. When enabling DEBUG logging during
> rendering, the logs make pretty much clear that Wicket really tries to
> continue rendering, but the output is simply missing.
> As no exceptions are thrown and output seems to simply be ignored at some
> point, I have the feeling the problem is in handling the response objects in
> "AbstractTransformerBehavior". All of those assigned transformers to some
> component are called before actually rendering anything and change the
> response. "Component.java" contains the following code:
> {noformat}
> /**
> * {@link Behavior#beforeRender(Component)} Notify all behaviors that are
> assigned to this
> * component that the component is about to be rendered.
> */
> private void notifyBehaviorsComponentBeforeRender()
> {
> for (Behavior behavior : getBehaviors())
> {
> if (isBehaviorAccepted(behavior))
> {
> behavior.beforeRender(this);
> }
> }
> }
> {noformat}
> Each call to "beforeRender" changes the response and all those changes are
> done one after another. This means that the current request to render two
> will be that one of the LAST applied transformer only.
> {noformat}
> @Override
> public void beforeRender(Component component)
> {
> super.beforeRender(component);
> final RequestCycle requestCycle = RequestCycle.get();
> // Temporarily replace the web response with a String response
> originalResponse = requestCycle.getResponse();
> WebResponse origResponse = (WebResponse)((originalResponse instanceof
> WebResponse)
> ? originalResponse : null);
> BufferedWebResponse tempResponse = newResponse(origResponse);
> // temporarily set StringResponse to collect the transformed output
> requestCycle.setResponse(tempResponse);
> }
> {noformat}
> Only after all those changes to the current request, content is rendered and
> transformed, using the current request AND changing it back to what each
> individual transformer believes to be the original request.
> {noformat}
> @Override
> public void afterRender(final Component component)
> {
> final RequestCycle requestCycle = RequestCycle.get();
> try
> {
> BufferedWebResponse tempResponse =
> (BufferedWebResponse)requestCycle.getResponse();
> if (component instanceof Page && originalResponse instanceof
> WebResponse)
> {
> tempResponse.writeMetaData((WebResponse)
> originalResponse);
> }
> // Transform the data
> CharSequence output = transform(component,
> tempResponse.getText());
> originalResponse.write(output);
> }
> catch (Exception ex)
> {
> throw new WicketRuntimeException("Error while transforming the
> output of component: " +
> component, ex);
> }
> finally
> {
> // Restore the original response object
> requestCycle.setResponse(originalResponse);
> }
> }
> {noformat}
> Because all transformers are executed in order at this stage, the FIRST
> executed transformer works on the content of the request of the LAST one
> which put requests into place. After applying its own transformation, it puts
> its own original request in place and the next transformer works on that and
> puts its own request into place etc.
> So the problem seems to be that AFTER the first transformer executed
> "afterReder", all subsequent ones are rendering into temporary responses of
> former transformers and those results are never written to the original
> response. It can't be, because only the first transformer has access to that
> response at all, but is unaware of the results of later transformers.
> So it needs to be decided if multiple transformers should be supported at all
> and if so, how to do it best with proper chaining of results. In my opinion
> it should be supported, as I have a valid use case and I couldn't find any
> docs that multiple of those transformers don't work. It simply seems the
> current implementation is too limited.
--
This message was sent by Atlassian Jira
(v8.3.4#803005)