This is an automated email from the ASF dual-hosted git repository. mgrigorov pushed a commit to branch wicket-8.x in repository https://gitbox.apache.org/repos/asf/wicket.git
commit 2c46ad329e25056a32fb765e642457fa176f82c5 Author: Martin Tzvetanov Grigorov <[email protected]> AuthorDate: Mon Apr 19 16:32:00 2021 +0300 WICKET-6877 Removing component's MetaData during detach phase from within a Behavior causes issue Reset the iteration index and upper bound while detaching Component's behaviors if the Behavior modifies the Component's data anyhow (cherry picked from commit e11bc07c1b147c55766638ecaba899956505857b) --- .../src/main/java/org/apache/wicket/Behaviors.java | 16 ++++ .../src/main/java/org/apache/wicket/Component.java | 2 +- .../org/apache/wicket/BehaviorsDetachTest.java | 37 +++++++++ .../apache/wicket/BehavioursDetachTestPage.html | 16 ++++ .../apache/wicket/BehavioursDetachTestPage.java | 90 ++++++++++++++++++++++ 5 files changed, 160 insertions(+), 1 deletion(-) diff --git a/wicket-core/src/main/java/org/apache/wicket/Behaviors.java b/wicket-core/src/main/java/org/apache/wicket/Behaviors.java index fc99c6c..ed0298b 100644 --- a/wicket-core/src/main/java/org/apache/wicket/Behaviors.java +++ b/wicket-core/src/main/java/org/apache/wicket/Behaviors.java @@ -156,6 +156,22 @@ final class Behaviors behavior.detach(component); + final int currentLength = component.data_length(); + if (len != currentLength) + { + // if the length has changed then reset 'i' and 'len' + for (int j = start; j < currentLength; j++) + { + // find the new index of the current behavior by identity + if (behavior == component.data_get(j)) + { + i = j; + len = currentLength; + break; + } + } + } + if (behavior.isTemporary(component)) { internalRemove(component, behavior); diff --git a/wicket-core/src/main/java/org/apache/wicket/Component.java b/wicket-core/src/main/java/org/apache/wicket/Component.java index c1ae312..76a0533 100644 --- a/wicket-core/src/main/java/org/apache/wicket/Component.java +++ b/wicket-core/src/main/java/org/apache/wicket/Component.java @@ -2884,7 +2884,7 @@ public abstract class Component MetaDataEntry<?>[] old = getMetaData(); Object metaData = null; - MetaDataEntry<?>[] metaDataArray = key.set(getMetaData(), object); + MetaDataEntry<?>[] metaDataArray = key.set(old, object); if (metaDataArray != null && metaDataArray.length > 0) { metaData = (metaDataArray.length > 1) ? metaDataArray : metaDataArray[0]; diff --git a/wicket-core/src/test/java/org/apache/wicket/BehaviorsDetachTest.java b/wicket-core/src/test/java/org/apache/wicket/BehaviorsDetachTest.java new file mode 100644 index 0000000..90c1640 --- /dev/null +++ b/wicket-core/src/test/java/org/apache/wicket/BehaviorsDetachTest.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.wicket; + +import org.apache.wicket.util.tester.WicketTestCase; +import org.junit.Test; + +/** + * Tests for {@link Behaviors#detach(Component)} method + */ +public class BehaviorsDetachTest extends WicketTestCase { + + /** + * https://issues.apache.org/jira/browse/WICKET-6877 + */ + @Test + public void detach_whenRemovingTheLastMetaData_thenUpdateComponentDataAndDetachAllFollowingBehaviors() { + final BehavioursDetachTestPage page = tester.startPage(BehavioursDetachTestPage.class); + assertFalse("The LoadableDetachableModel should be in detached state!", page.theModel.isAttached()); + } + + +} diff --git a/wicket-core/src/test/java/org/apache/wicket/BehavioursDetachTestPage.html b/wicket-core/src/test/java/org/apache/wicket/BehavioursDetachTestPage.html new file mode 100644 index 0000000..46b6cd4 --- /dev/null +++ b/wicket-core/src/test/java/org/apache/wicket/BehavioursDetachTestPage.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html xmlns:wicket="http://wicket.apache.org"> + <head> + <meta charset="utf-8" /> + <title>Apache Wicket Quickstart</title> + </head> + <body> + <div id="hd"> + <p> + <a wicket:id="link">Link</a> + </p> + </div> + <div id="ft"> + </div> + </body> +</html> diff --git a/wicket-core/src/test/java/org/apache/wicket/BehavioursDetachTestPage.java b/wicket-core/src/test/java/org/apache/wicket/BehavioursDetachTestPage.java new file mode 100644 index 0000000..65191a2 --- /dev/null +++ b/wicket-core/src/test/java/org/apache/wicket/BehavioursDetachTestPage.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.wicket; + +import org.apache.wicket.behavior.Behavior; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.request.mapper.parameter.PageParameters; + +public class BehavioursDetachTestPage extends WebPage { + private static final long serialVersionUID = 1L; + + /** + * This model should be detached after page rendering + */ + final LoadableDetachableModel<String> theModel; + + public BehavioursDetachTestPage(final PageParameters parameters) { + super(parameters); + + // Bug seems to only occur on stateful pages, + // hence a Link to force it to be stateful in this quickstart + Link<String> link = new Link<String>("link") { + @Override + public void onClick() {/*NoOp*/} + }; + queue(link); + + // A behavior that causes the problem + link.add(new VisibilityBehavior()); + + theModel = LoadableDetachableModel.of(() -> "attribute value"); + + // AttributeModifier and thus its LoadableDetachableModel don't get detached + link.add(AttributeModifier.replace("some-random-attribute", theModel)); + } + + /** + * A behavior that modifies the component's data by removing the last MetaDataEntry + * in its detach() method + */ + public static class VisibilityBehavior extends Behavior { + + private static final MetaDataKey<Boolean> META_DATA_KEY = new MetaDataKey<Boolean>() {}; + + @Override + public void onConfigure(Component component) { + super.onConfigure(component); + + // If no other Behavior of this kind deemed the component to be hidden yet, + // calculate its visibilty now + Boolean calculatdVisibilitySoFar = component.getMetaData(META_DATA_KEY); + if (!Boolean.FALSE.equals(calculatdVisibilitySoFar)) { + boolean calculatedVisibility = calculateVisibility(); + component.setVisibilityAllowed(calculatedVisibility); + component.setMetaData(META_DATA_KEY, calculatedVisibility); + } + } + + // usually abstract and gets overridden in subclasses with more meaningful code + protected boolean calculateVisibility() { + return true; + } + + @Override + public void detach(Component component) { + // Reset visibility information so it's not being used in the next request anymore. + + // ==> Setting the MetaData to "null" here causes the issue + component.setMetaData(META_DATA_KEY, null); + super.detach(component); + } + } + +}
