]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add reinit method for preserve-on-refresh UIs (#12191)
authorJohannes Dahlström <johannesd@vaadin.com>
Wed, 19 Mar 2014 15:29:45 +0000 (17:29 +0200)
committerVaadin Code Review <review@vaadin.com>
Mon, 31 Mar 2014 08:39:15 +0000 (08:39 +0000)
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

server/src/com/vaadin/annotations/PreserveOnRefresh.java
server/src/com/vaadin/server/Page.java
server/src/com/vaadin/server/UIProvider.java
server/src/com/vaadin/server/communication/UIInitHandler.java
server/src/com/vaadin/ui/UI.java
server/tests/src/com/vaadin/ui/UIInitReinitTest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/ui/UIReinit.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/ui/UIReinitTest.java [new file with mode: 0644]

index 0b503b8c3f40e9ecd1df6649056b37288bd70b51..801c1e78f28469ef844c6b8966091d667755ba2c 100644 (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
index 3b96ec2ee19e495f145f98a3bc2fba33b2a36b24..70b8306bc3ea9258a43cec7b74b38a161e646885 100644 (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) {
index 3e7c85aea9d56e2ebb1719a7085f753d56fef040..a76f3967679e875f7f18ba61c56bfb09afc85a6f 100644 (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
index 6ab9d9dc58bbebe80c076550eb3716e44c0f3237..5dc44208d1353ddd91c0bd79db0c66686f3571e1 100644 (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);
     }
 
     /**
index b3004e9ad20d2438559b963f5ccba43a495da9fd..2b2e7736011fe6a28036339928219a47eb2a6524 100644 (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
diff --git a/server/tests/src/com/vaadin/ui/UIInitReinitTest.java b/server/tests/src/com/vaadin/ui/UIInitReinitTest.java
new file mode 100644 (file)
index 0000000..f8ec0e6
--- /dev/null
@@ -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);
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/ui/UIReinit.java b/uitest/src/com/vaadin/tests/components/ui/UIReinit.java
new file mode 100644 (file)
index 0000000..bef7a5a
--- /dev/null
@@ -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);
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/ui/UIReinitTest.java b/uitest/src/com/vaadin/tests/components/ui/UIReinitTest.java
new file mode 100644 (file)
index 0000000..82132d3
--- /dev/null
@@ -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();
+    }
+}