Browse Source

Add reinit method for preserve-on-refresh UIs (#12191)

UI.reinit() is now called when an existing, preserved UI is shown after a
browser reload of the current page. The default implementation is empty.
The browser window size and location are up to date in UI.reinit();
window resize and URI fragment listeners, if any, will be called after
returning from UI.reinit().

Change-Id: Ie7aa670aaecf8e0e1510c91325b2a137b41263af
tags/7.2.0.beta1
Johannes Dahlström 10 years ago
parent
commit
e4a50934a2

+ 5
- 0
server/src/com/vaadin/annotations/PreserveOnRefresh.java View File

@@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.vaadin.server.UIProvider;
import com.vaadin.ui.UI;

/**
* Marks a UI that should be retained when the user refreshed the browser
@@ -30,6 +31,10 @@ import com.vaadin.server.UIProvider;
* adding this annotation to a UI class, the framework will instead reuse the
* current UI instance when a reload is detected.
* <p>
* Whenever a request is received that reloads a preserved UI, the UI's
* {@link UI#reinit(com.vaadin.server.VaadinRequest) reinit} method is invoked
* by the framework.
* <p>
* By using
* {@link UIProvider#isPreservedOnRefresh(com.vaadin.server.UICreateEvent)}, the
* decision can also be made dynamically based on other parameters than only

+ 61
- 11
server/src/com/vaadin/server/Page.java View File

@@ -32,6 +32,7 @@ import com.vaadin.shared.ui.ui.PageClientRpc;
import com.vaadin.shared.ui.ui.PageState;
import com.vaadin.shared.ui.ui.UIConstants;
import com.vaadin.shared.ui.ui.UIState;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.JavaScript;
import com.vaadin.ui.LegacyWindow;
import com.vaadin.ui.Link;
@@ -635,6 +636,9 @@ public class Page implements Serializable {
}

public void init(VaadinRequest request) {
// NOTE: UI.reinit makes assumptions about the semantics of this method.
// It should be kept in sync if this method is changed.

// Extract special parameter sent by vaadinBootstrap.js
String location = request.getParameter("v-loc");
String clientWidth = request.getParameter("v-cw");
@@ -676,28 +680,51 @@ public class Page implements Serializable {
}

/**
* Updates the internal state with the given values. Does not resize the
* Page or browser window.
*
* For internal use only. Updates the internal state with the given values.
* Does not resize the Page or browser window.
*
* @deprecated As of 7.2, use
* {@link #updateBrowserWindowSize(int, int, boolean)} instead.
*
* @param width
* The new width
* the new browser window width
* @param height
* The new height
* the new browse window height
*/
@Deprecated
public void updateBrowserWindowSize(int width, int height) {
boolean fireEvent = false;
updateBrowserWindowSize(width, height, true);
}

/**
* For internal use only. Updates the internal state with the given values.
* Does not resize the Page or browser window.
*
* @since 7.2
*
* @param width
* the new browser window width
* @param height
* the new browser window height
* @param fireEvents
* whether to fire {@link BrowserWindowResizeEvent} if the size
* changes
*/
public void updateBrowserWindowSize(int width, int height,
boolean fireEvents) {
boolean sizeChanged = false;

if (width != browserWindowWidth) {
browserWindowWidth = width;
fireEvent = true;
sizeChanged = true;
}

if (height != browserWindowHeight) {
browserWindowHeight = height;
fireEvent = true;
sizeChanged = true;
}

if (fireEvent) {
if (fireEvents && sizeChanged) {
fireEvent(new BrowserWindowResizeEvent(this, browserWindowWidth,
browserWindowHeight));
}
@@ -917,14 +944,37 @@ public class Page implements Serializable {
/**
* For internal use only. Used to update the server-side location when the
* client-side location changes.
*
* @deprecated As of 7.2, use {@link #updateLocation(String, boolean)}
* instead.
*
* @param location
* the new location URI
*/
@Deprecated
public void updateLocation(String location) {
updateLocation(location, true);
}

/**
* For internal use only. Used to update the server-side location when the
* client-side location changes.
*
* @since 7.2
*
* @param location
* the new location URI
* @param fireEvents
* whether to fire {@link UriFragmentChangedEvent} if the URI
* fragment changes
*/
public void updateLocation(String location, boolean fireEvents) {
try {
String oldUriFragment = this.location.getFragment();
this.location = new URI(location);
String newUriFragment = this.location.getFragment();
if (newUriFragment == null ? oldUriFragment != null
: !newUriFragment.equals(oldUriFragment)) {
if (fireEvents
&& !SharedUtil.equals(oldUriFragment, newUriFragment)) {
fireEvent(new UriFragmentChangedEvent(this, newUriFragment));
}
} catch (URISyntaxException e) {

+ 5
- 0
server/src/com/vaadin/server/UIProvider.java View File

@@ -129,6 +129,11 @@ public abstract class UIProvider implements Serializable {
* detect that the application is opened in a browser window where it has
* previously been open. The framework attempts to discover this by checking
* the value of window.name in the browser.
* <p>
* Whenever a preserved UI is reused, its
* {@link UI#reinit(com.vaadin.server.VaadinRequest) reinit} method is
* invoked by the framework first.
*
*
* @param event
* the UI create event with information about the UI and the

+ 1
- 6
server/src/com/vaadin/server/communication/UIInitHandler.java View File

@@ -266,12 +266,7 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
*/
private void reinitUI(UI ui, VaadinRequest request) {
UI.setCurrent(ui);

// Fire fragment change if the fragment has changed
String location = request.getParameter("v-loc");
if (location != null) {
ui.getPage().updateLocation(location);
}
ui.doReinit(request);
}

/**

+ 55
- 2
server/src/com/vaadin/ui/UI.java View File

@@ -16,6 +16,7 @@

package com.vaadin.ui;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -27,6 +28,7 @@ import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.event.Action;
import com.vaadin.event.Action.Handler;
import com.vaadin.event.ActionManager;
@@ -161,7 +163,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements
public void resize(int viewWidth, int viewHeight, int windowWidth,
int windowHeight) {
// TODO We're not doing anything with the view dimensions
getPage().updateBrowserWindowSize(windowWidth, windowHeight);
getPage().updateBrowserWindowSize(windowWidth, windowHeight, true);
}

@Override
@@ -361,7 +363,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements
if (variables.containsKey(UIConstants.LOCATION_VARIABLE)) {
String location = (String) variables
.get(UIConstants.LOCATION_VARIABLE);
getPage().updateLocation(location);
getPage().updateLocation(location, true);
}
}

@@ -659,6 +661,57 @@ public abstract class UI extends AbstractSingleComponentContainer implements
*/
protected abstract void init(VaadinRequest request);

/**
* Internal reinitialization method, should not be overridden.
*
* @since 7.2
* @param request
* the request that caused this UI to be reloaded
*/
public void doReinit(VaadinRequest request) {
// This is a horrible hack. We want to have the most recent location and
// browser window size available in reinit(), but we want to call
// listeners, if any, only after reinit(). So we momentarily assign the
// old values back before setting the new values again to ensure the
// events are properly fired.

Page page = getPage();

URI oldLocation = page.getLocation();
int oldWidth = page.getBrowserWindowWidth();
int oldHeight = page.getBrowserWindowHeight();

page.init(request);

reinit(request);

URI newLocation = page.getLocation();
int newWidth = page.getBrowserWindowWidth();
int newHeight = page.getBrowserWindowHeight();

page.updateLocation(oldLocation.toString(), false);
page.updateBrowserWindowSize(oldWidth, oldHeight, false);

page.updateLocation(newLocation.toString(), true);
page.updateBrowserWindowSize(newWidth, newHeight, true);
}

/**
* Reinitializes this UI after a browser refresh if the UI is set to be
* preserved on refresh, typically using the {@link PreserveOnRefresh}
* annotation. This method is intended to be overridden by subclasses if
* needed; the default implementation is empty.
* <p>
* The {@link VaadinRequest} can be used to get information about the
* request that caused this UI to be reloaded.
*
* @since 7.2
* @param request
* the request that caused this UI to be reloaded
*/
protected void reinit(VaadinRequest request) {
}

/**
* Sets the thread local for the current UI. This method is used by the
* framework to set the current application whenever a new request is

+ 116
- 0
server/tests/src/com/vaadin/ui/UIInitReinitTest.java View File

@@ -0,0 +1,116 @@
/*
* Copyright 2000-2013 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.ui;

import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.server.Page.BrowserWindowResizeEvent;
import com.vaadin.server.Page.BrowserWindowResizeListener;
import com.vaadin.server.Page.UriFragmentChangedEvent;
import com.vaadin.server.Page.UriFragmentChangedListener;
import com.vaadin.server.VaadinRequest;

public class UIInitReinitTest {

private boolean initCalled;
private boolean reinitCalled;
private boolean fragmentChangeCalled;
private boolean browserWindowResizeCalled;

private class TestUI extends UI implements UriFragmentChangedListener,
BrowserWindowResizeListener {
@Override
protected void init(VaadinRequest request) {
getPage().addBrowserWindowResizeListener(this);
getPage().addUriFragmentChangedListener(this);

initCalled = true;

Assert.assertEquals("foo", getPage().getUriFragment());
Assert.assertEquals(100, getPage().getBrowserWindowWidth());
Assert.assertEquals(100, getPage().getBrowserWindowHeight());

Assert.assertFalse(fragmentChangeCalled);
Assert.assertFalse(browserWindowResizeCalled);
}

@Override
protected void reinit(VaadinRequest request) {
reinitCalled = true;

Assert.assertEquals("bar", getPage().getUriFragment());
Assert.assertEquals(200, getPage().getBrowserWindowWidth());
Assert.assertEquals(200, getPage().getBrowserWindowHeight());

Assert.assertFalse(fragmentChangeCalled);
Assert.assertFalse(browserWindowResizeCalled);
}

@Override
public void browserWindowResized(BrowserWindowResizeEvent event) {
Assert.assertEquals(200, event.getWidth());
Assert.assertEquals(200, event.getHeight());
browserWindowResizeCalled = true;
}

@Override
public void uriFragmentChanged(UriFragmentChangedEvent event) {
Assert.assertEquals("bar", event.getUriFragment());
fragmentChangeCalled = true;
}
};

@Before
public void setUp() {
initCalled = reinitCalled = fragmentChangeCalled = browserWindowResizeCalled = false;
}

@Test
public void testListenersCalled() {
IMocksControl control = EasyMock.createNiceControl();

VaadinRequest initRequest = control.createMock(VaadinRequest.class);
EasyMock.expect(initRequest.getParameter("v-loc")).andReturn(
"http://example.com/#foo");
EasyMock.expect(initRequest.getParameter("v-cw")).andReturn("100");
EasyMock.expect(initRequest.getParameter("v-ch")).andReturn("100");

VaadinRequest reinitRequest = control.createMock(VaadinRequest.class);
EasyMock.expect(reinitRequest.getParameter("v-loc")).andReturn(
"http://example.com/#bar");
EasyMock.expect(reinitRequest.getParameter("v-cw")).andReturn("200");
EasyMock.expect(reinitRequest.getParameter("v-ch")).andReturn("200");

control.replay();

UI ui = new TestUI();
ui.doInit(initRequest, 0, "");

Assert.assertTrue(initCalled);
Assert.assertFalse(fragmentChangeCalled);
Assert.assertFalse(browserWindowResizeCalled);

ui.doReinit(reinitRequest);

Assert.assertTrue(reinitCalled);
Assert.assertTrue(fragmentChangeCalled);
Assert.assertTrue(browserWindowResizeCalled);
}
}

+ 49
- 0
uitest/src/com/vaadin/tests/components/ui/UIReinit.java View File

@@ -0,0 +1,49 @@
/*
* Copyright 2000-2013 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.components.ui;

import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Label;

@PreserveOnRefresh
public class UIReinit extends AbstractTestUI {

public static final String REINIT_ID = "reinit";

@Override
protected void setup(VaadinRequest request) {
}

@Override
protected void reinit(VaadinRequest request) {
Label l = new Label("Reinit!");
l.setId(REINIT_ID);
addComponent(l);
}

@Override
public String getTestDescription() {
return "UI reinit after refresh";
}

@Override
protected Integer getTicketNumber() {
return Integer.valueOf(12191);
}
}

+ 39
- 0
uitest/src/com/vaadin/tests/components/ui/UIReinitTest.java View File

@@ -0,0 +1,39 @@
/*
* Copyright 2000-2013 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.components.ui;

import org.junit.Assert;
import org.junit.Test;

import com.vaadin.testbench.By;
import com.vaadin.tests.tb3.MultiBrowserTest;

public class UIReinitTest extends MultiBrowserTest {

@Test
public void testUIReinit() {
openTestURL();
Assert.assertFalse(reinitLabelExists());
// Reload the page; UI.reinit should be invoked
openTestURL();
Assert.assertTrue(reinitLabelExists());
}

private boolean reinitLabelExists() {
return !getDriver().findElements(By.id(UIReinit.REINIT_ID)).isEmpty();
}
}

Loading…
Cancel
Save