You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by "Sven Meier (Jira)" <ji...@apache.org> on 2020/08/30 21:52:00 UTC

[jira] [Comment Edited] (WICKET-6823) Two instances of AbstractTransformerBehavior on the same component results in incomplete output.

    [ https://issues.apache.org/jira/browse/WICKET-6823?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17187338#comment-17187338 ] 

Sven Meier edited comment on WICKET-6823 at 8/30/20, 9:51 PM:
--------------------------------------------------------------

Wicket 9.x tests all work fine with this change though.


was (Author: svenmeier):
Wicket tests all work fine with this change though.

> 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)