[
https://issues.apache.org/jira/browse/WICKET-6823?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17187289#comment-17187289
]
Thorsten Schöning commented on WICKET-6823:
-------------------------------------------
I've attached a quickstart demonstrating the issue by creating two very simple
transformers to change the version number rendered on runtime. When attaching
only one transformer, the textual phrase "</html>" is found in the rendered
output, otherwise it's not. This demonstrates the overall problem, that neither
the output of the transformer itself is available, e..g the phrase "bar" in
this test, nor any markup after the component the two transformers are applied
to.
{noformat}
String version = getApplication().getFrameworkSettings().getVersion();
Label label = new Label("version", version);
label.add(new FooTrans());
//label.add(new BarTrans());
{noformat}
Just change the comment about "BarTrans" to make the test succeed or fail. If
it fails, the stacktrace already shows where the HTML-output stops as well:
{noformat}
junit.framework.AssertionFailedError: pattern '</html>' not found in:
<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
<head>
<meta charset="utf-8" />
<title>Apache Wicket Quickstart</title>
<link
href='https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:regular,bold'
rel='stylesheet' type='text/css' />
<link rel="stylesheet" href="style.css" type="text/css"
media="screen" title="Stylesheet"/>
</head>
<body>
<div id="hd">
<div id="logo">
<img src="logo.png" width="50px" height="50px"
alt="Wicket Logo"/>
<h1>Apache Wicket</h1>
</div>
</div>
<div id="bd">
<h3>Reporting a bug</h3>
<p>
Help us help you:
</p>
<ol>
<li>reproduce the bug with the
<strong>least</strong> amount of code</li>
<li>create a unit test that shows the bug</li>
<li>fix the bug and create a patch</li>
<li>attach the result of step 1, 2 or 3 to a <a
href="https://issues.apache.org/jira/browse/WICKET" target="_blank">JIRA
issue</a></li>
<li>profit!</li>
</ol>
<p>
Please mention the correct Wicket version:
<wicket:container wicket:id="version">8.9.0</wicket:container> foo
at
org.apache.wicket.util.tester.WicketTester.assertResult(WicketTester.java:798)
at
org.apache.wicket.util.tester.WicketTester.assertContains(WicketTester.java:365)
at
de.am_soft.TestHomePage.homepageRendersSuccessfully(TestHomePage.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at
org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at
org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at
org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
{noformat}
> 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
> 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)