Quellcode durchsuchen

Iterate over copies of listener lists (#17477)

Allows adding/removing view change listeners from listeners.

Change-Id: Idb2227e1423c0297887f01f6df03b74e633ad917
tags/7.4.5
Henri Sara vor 9 Jahren
Ursprung
Commit
6e46ed68d6

+ 9
- 2
server/src/com/vaadin/navigator/Navigator.java Datei anzeigen

@@ -32,6 +32,7 @@ package com.vaadin.navigator;
*/

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -597,7 +598,10 @@ public class Navigator implements Serializable {
* block the navigation operation
*/
protected boolean fireBeforeViewChange(ViewChangeEvent event) {
for (ViewChangeListener l : listeners) {
// a copy of the listener list is needed to avoid
// ConcurrentModificationException as a listener can add/remove
// listeners
for (ViewChangeListener l : new ArrayList<ViewChangeListener>(listeners)) {
if (!l.beforeViewChange(event)) {
return false;
}
@@ -647,7 +651,10 @@ public class Navigator implements Serializable {
* view change event (not null)
*/
protected void fireAfterViewChange(ViewChangeEvent event) {
for (ViewChangeListener l : listeners) {
// a copy of the listener list is needed to avoid
// ConcurrentModificationException as a listener can add/remove
// listeners
for (ViewChangeListener l : new ArrayList<ViewChangeListener>(listeners)) {
l.afterViewChange(event);
}
}

+ 100
- 0
uitest/src/com/vaadin/tests/navigator/NavigatorListenerModifiesListeners.java Datei anzeigen

@@ -0,0 +1,100 @@
package com.vaadin.tests.navigator;

import com.vaadin.navigator.Navigator;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;

public class NavigatorListenerModifiesListeners extends AbstractTestUI {

private Navigator navigator;

protected static final String LABEL_MAINVIEW_ID = "LABEL_MAINVIEW_ID";
protected static final String LABEL_ANOTHERVIEW_ID = "LABEL_ANOTHERVIEW_ID";

// NOP view change listener
private class MyViewChangeListener implements ViewChangeListener {
@Override
public boolean beforeViewChange(ViewChangeEvent event) {
navigator.removeViewChangeListener(listener1);
navigator.addViewChangeListener(listener2);
return true;
}

@Override
public void afterViewChange(ViewChangeEvent event) {
navigator.removeViewChangeListener(listener2);
navigator.addViewChangeListener(listener1);
}
}

private MyViewChangeListener listener1 = new MyViewChangeListener();
private MyViewChangeListener listener2 = new MyViewChangeListener();

@Override
protected void setup(VaadinRequest request) {
navigator = new Navigator(this, this);
navigator.addView(MainView.NAME, new MainView());
navigator.addView(AnotherView.NAME, new AnotherView());
navigator.addViewChangeListener(listener1);
navigator.navigateTo(MainView.NAME);
}

class MainView extends VerticalLayout implements View {

public static final String NAME = "mainview";

public MainView() {
Label label = new Label("MainView content");
label.setId(LABEL_MAINVIEW_ID);
addComponent(label);

Button buttonNavToAnotherView = new Button(
"Navigate to another view", new ClickListener() {

@Override
public void buttonClick(ClickEvent event) {
navigator.navigateTo(AnotherView.NAME);
}
});
addComponent(buttonNavToAnotherView);
}

@Override
public void enter(ViewChangeEvent event) {
}

}

class AnotherView extends VerticalLayout implements View {

public static final String NAME = "another";

public AnotherView() {
Label label = new Label("AnotherView content");
label.setId(LABEL_ANOTHERVIEW_ID);
addComponent(label);
}

@Override
public void enter(ViewChangeEvent event) {
}
}

@Override
protected String getTestDescription() {
return "Adding and removing view change listeners from view change listeners should not cause a ConcurrentModificationException";
}

@Override
protected Integer getTicketNumber() {
return 17477;
}
}

+ 68
- 0
uitest/src/com/vaadin/tests/navigator/NavigatorListenerModifiesListenersTest.java Datei anzeigen

@@ -0,0 +1,68 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed 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 com.vaadin.tests.navigator;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import com.vaadin.testbench.elements.ButtonElement;
import com.vaadin.tests.tb3.SingleBrowserTest;

public class NavigatorListenerModifiesListenersTest extends SingleBrowserTest {

@Test
public void testIfConfirmBack() {
openTestURL();

// keep URL of main view
final String initialUrl = driver.getCurrentUrl();

// do it 2 times to verify that this is not broken after first time
for (int i = 0; i < 2; i++) {
// go to prompted view
WebElement button = $(ButtonElement.class).first();
button.click();

// verify we are in another view and url is correct
waitForElementPresent(By
.id(NavigatorListenerModifiesListeners.LABEL_ANOTHERVIEW_ID));
String currentUrl = driver.getCurrentUrl();
assertEquals(
"Current URL should be equal to another view URL",
initialUrl
.replace(
NavigatorListenerModifiesListeners.MainView.NAME,
NavigatorListenerModifiesListeners.AnotherView.NAME),
currentUrl);

// click back button
driver.navigate().back();

// verify we are in main view and url is correct
// without the fix for #17477, we get
// ConcurrentModificationException
waitForElementPresent(By
.id(NavigatorListenerModifiesListeners.LABEL_MAINVIEW_ID));
currentUrl = driver.getCurrentUrl();
assertEquals("Current URL should be equal to the initial view URL",
initialUrl, currentUrl);
}
}

}

Laden…
Abbrechen
Speichern