diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java index c24c927305..2ad41622bf 100644 --- a/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java +++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AjaxRequestHandler.java @@ -51,6 +51,8 @@ import org.apache.wicket.util.string.Strings; import org.apache.wicket.util.visit.IVisit; import org.apache.wicket.util.visit.IVisitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A request target that produces ajax response envelopes used on the client side to update @@ -81,6 +83,7 @@ */ public class AjaxRequestHandler implements AjaxRequestTarget { + private static final Logger log = LoggerFactory.getLogger(AjaxRequestHandler.class); /** * Collector of page updates. @@ -106,7 +109,7 @@ public class AjaxRequestHandler implements AjaxRequestTarget * Constructor * * @param page - * the currently active page + * the currently active page */ public AjaxRequestHandler(final Page page) { @@ -140,7 +143,7 @@ protected void onBeforeRespond(final Response response) * events will have been fired by now. * * @param response - * the response to write to + * the response to write to */ @Override protected void onAfterRespond(final Response response) @@ -150,7 +153,8 @@ protected void onAfterRespond(final Response response) // invoke onafterresponse event on listeners if (listeners != null) { - final Map components = Collections.unmodifiableMap(markupIdToComponent); + final Map components = Collections + .unmodifiableMap(markupIdToComponent); // create response that will be used by listeners to append // javascript @@ -159,7 +163,8 @@ protected void onAfterRespond(final Response response) @Override public void addJavaScript(String script) { - writeNormalEvaluations(response, Collections.singleton(script)); + writeNormalEvaluations(response, + Collections. singleton(script)); } }; @@ -228,14 +233,27 @@ public void add(Component... components) "Cannot update component that does not have setOutputMarkupId property set to true. Component: " + component.toString()); } - else if (component.getPage() != getPage()) + Page page = component.findParent(Page.class); + if (page != getPage()) { - throw new IllegalArgumentException( - "Cannot update component because its page is not the same as " + - "the one this handler has been created for. Component: " + - component.toString()); + String msg = "Cannot update component because its page is not the same as " + + "the one this handler has been created for. Component: " + component.toString(); + IllegalArgumentException error = new IllegalArgumentException(msg); + if (Application.get().usesDevelopmentConfig()) + { + throw error; + } + else + { + // log the error to the application log, but don't block the user of the + // application (which was the behavior in Wicket <= 7. + log.error(msg, error); + } + } + else + { + add(component, component.getMarkupId()); } - add(component, component.getMarkupId()); } } diff --git a/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPage$LastSelectedPanel.html b/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPage$LastSelectedPanel.html new file mode 100644 index 0000000000..5f505482c5 --- /dev/null +++ b/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPage$LastSelectedPanel.html @@ -0,0 +1,6 @@ + + + +Refresh + + diff --git a/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPage.html b/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPage.html new file mode 100644 index 0000000000..a2432f6a83 --- /dev/null +++ b/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPage.html @@ -0,0 +1,15 @@ + + + +Stateless Wicket + + +
    +
  • + +
  • +
+
+ + diff --git a/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPage.java b/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPage.java new file mode 100644 index 0000000000..5d06ea3db8 --- /dev/null +++ b/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPage.java @@ -0,0 +1,89 @@ +/* + * 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.ajax.markup.html; + +import java.util.Arrays; + +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.markup.html.panel.EmptyPanel; +import org.apache.wicket.markup.html.panel.Panel; + +/** + * Test page for triggering the component is not on page error from AjaxRequestHandler. + */ +public class ComponentNotOnPage extends WebPage +{ + private static final long serialVersionUID = 1L; + + @Override + protected void onInitialize() + { + super.onInitialize(); + + add(new EmptyPanel("refresher")); + + add(new ListView("listview", Arrays.asList(1, 2, 3, 4, 5)) + { + private static final long serialVersionUID = 1L; + + @Override + protected void populateItem(ListItem item) + { + item.setOutputMarkupId(true); + + item.add(new AjaxLink("link") + { + private static final long serialVersionUID = 1L; + + @Override + public void onClick(AjaxRequestTarget target) + { + LastSelectedPanel lastSelected = new LastSelectedPanel("refresher", item); + ComponentNotOnPage.this.replace(lastSelected); + target.add(lastSelected); + } + }); + } + }); + } + + public static class LastSelectedPanel extends Panel + { + private static final long serialVersionUID = 1L; + + public LastSelectedPanel(String id, WebMarkupContainer refresher) + { + super(id); + + setOutputMarkupId(true); + add(new AjaxLink("refresh") + { + private static final long serialVersionUID = 1L; + + @Override + public void onClick(AjaxRequestTarget target) + { + target.add(refresher); + } + }); + } + } +} diff --git a/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPageTest.java b/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPageTest.java new file mode 100644 index 0000000000..14e8255225 --- /dev/null +++ b/wicket-core/src/test/java/org/apache/wicket/ajax/markup/html/ComponentNotOnPageTest.java @@ -0,0 +1,103 @@ +/* + * 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.ajax.markup.html; + +import org.apache.wicket.RuntimeConfigurationType; +import org.apache.wicket.mock.MockApplication; +import org.apache.wicket.protocol.http.WebApplication; +import org.apache.wicket.util.tester.WicketTestCase; +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests for checking whether or not a component is attached to the component hierarchy when + * updating through AJAX. + */ +public class ComponentNotOnPageTest extends WicketTestCase +{ + private RuntimeConfigurationType configuration = RuntimeConfigurationType.DEVELOPMENT; + + /** + * Overrides the application factory to enable changing the configuration type of Wicket. + */ + @Override + protected WebApplication newApplication() + { + return new MockApplication() + { + @Override + public RuntimeConfigurationType getConfigurationType() + { + return configuration; + } + }; + } + + /** + * When running in development mode Wicket should trigger an exception signaling the error on + * the developers part that a component that is not part of the page is being refreshed in the + * AJAX response, resulting in a no-op (which is not the intended result). + */ + @Test(expected = IllegalArgumentException.class) + public void responseTargetInDevelopmentModeShouldFail() + { + configuration = RuntimeConfigurationType.DEVELOPMENT; + + try + { + // this should not fail + ComponentNotOnPage page = tester.startPage(ComponentNotOnPage.class); + tester.clickLink("listview:0:link", true); + tester.startPage(page); + } + catch (Exception e) + { + Assert.fail("Unexpected exception: " + e); + } + + // this should fail + tester.clickLink("refresher:refresh", true); + } + + /** + * When running in deployment mode Wicket should not trigger an exception signaling the + * error on the developers part that a component that is not part of the page is being refreshed + * in the AJAX response, resulting in a no-op (which is not the intended result). Instead Wicket + * should signal the error in the log, but not prevent the user of the application to continue + * (which happened in Wicket 7). + */ + @Test + public void responseTargetInDeploymentModeShouldNotFail() + { + configuration = RuntimeConfigurationType.DEPLOYMENT; + + try + { + // this should not fail + ComponentNotOnPage page = tester.startPage(ComponentNotOnPage.class); + tester.clickLink("listview:0:link", true); + tester.startPage(page); + } + catch (Exception e) + { + Assert.fail("Unexpected exception: " + e); + } + + // this shouldn't fail as well + tester.clickLink("refresher:refresh", true); + } +}