]> source.dussan.org Git - vaadin-framework.git/commitdiff
Refactor UI bootstrap (#9443)
authorLeif Åstrand <leif@vaadin.com>
Fri, 31 Aug 2012 09:02:51 +0000 (12:02 +0300)
committerLeif Åstrand <leif@vaadin.com>
Fri, 31 Aug 2012 16:00:00 +0000 (19:00 +0300)
27 files changed:
WebContent/VAADIN/vaadinBootstrap.js
server/src/com/vaadin/Application.java
server/src/com/vaadin/UIRequiresMoreInformationException.java [deleted file]
server/src/com/vaadin/annotations/PreserveOnRefresh.java [new file with mode: 0644]
server/src/com/vaadin/annotations/Title.java [new file with mode: 0644]
server/src/com/vaadin/server/AbstractApplicationPortlet.java
server/src/com/vaadin/server/AbstractCommunicationManager.java
server/src/com/vaadin/server/BootstrapFragmentResponse.java
server/src/com/vaadin/server/BootstrapHandler.java
server/src/com/vaadin/server/BootstrapPageResponse.java
server/src/com/vaadin/server/BootstrapResponse.java
server/src/com/vaadin/server/CommunicationManager.java
server/src/com/vaadin/server/DefaultUIProvider.java
server/src/com/vaadin/server/PortletCommunicationManager.java
server/src/com/vaadin/server/UIProvider.java
server/src/com/vaadin/server/WrappedRequest.java
server/src/com/vaadin/ui/UI.java
server/tests/src/com/vaadin/tests/server/component/root/CustomUIClassLoader.java
uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java
uitest/src/com/vaadin/tests/application/RefreshStatePreserve.java
uitest/src/com/vaadin/tests/application/ThreadLocalInstances.java
uitest/src/com/vaadin/tests/components/loginform/LoginFormWithMultipleWindows.java
uitest/src/com/vaadin/tests/components/ui/LazyInitUIs.java
uitest/src/com/vaadin/tests/components/ui/UIsInMultipleTabs.java
uitest/src/com/vaadin/tests/minitutorials/v7a1/CreatingPreserveState.java
uitest/src/com/vaadin/tests/minitutorials/v7a1/DifferentFeaturesForDifferentClients.java
uitest/src/com/vaadin/tests/vaadincontext/TestAddonContextListener.java

index 1f5f3fa9734a318f181df2bba2aac89278ec1771..36cf2ec8ebe39121411d8da78552b03b1f2c87e8 100644 (file)
                        
                        var bootstrapApp = function(mayDefer) {
                                var themeUri = getConfig('themeUri');
-                               if (themeUri) {
-                                       loadTheme(themeUri);
-                               }
+                               loadTheme(themeUri);
                                
                                var widgetsetBase = getConfig('widgetsetBase');
                                var widgetset = getConfig('widgetset');
-                               if (widgetset && widgetsetBase) {
-                                       loadWidgetset(widgetsetBase, widgetset);
-                               }
+                               loadWidgetset(widgetsetBase, widgetset);
                                
                                if (getConfig('uidl') === undefined) {
                                        if (mayDefer) {
index fb2691c6d331bd6ca1232bc7fcdc79b67dcbe5d3..b1a9ae1d26ce5ea63edb839d6e399ce80361714a 100644 (file)
@@ -28,7 +28,6 @@ import java.util.Collections;
 import java.util.Enumeration;
 import java.util.EventObject;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -36,14 +35,15 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
-import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import com.vaadin.annotations.EagerInit;
+import com.vaadin.annotations.PreserveOnRefresh;
 import com.vaadin.annotations.Theme;
+import com.vaadin.annotations.Title;
 import com.vaadin.annotations.Widgetset;
 import com.vaadin.data.util.converter.Converter;
 import com.vaadin.data.util.converter.ConverterFactory;
@@ -210,15 +210,16 @@ public class Application implements Terminal.ErrorListener, Serializable {
          * This implementation simulates the way of finding a window for a
          * request by extracting a window name from the requested path and
          * passes that name to {@link #getWindow(String)}.
-         * 
+         * <p>
          * {@inheritDoc}
-         * 
-         * @see #getWindow(String)
-         * @see Application#getUI(WrappedRequest)
          */
-
         @Override
-        public UI.LegacyWindow getUI(WrappedRequest request) {
+        protected <T extends UI> T createUIInstance(WrappedRequest request,
+                Class<T> uiClass) {
+            return uiClass.cast(getUIInstance(request));
+        }
+
+        private UI getUIInstance(WrappedRequest request) {
             String pathInfo = request.getRequestPathInfo();
             String name = null;
             if (pathInfo != null && pathInfo.length() > 0) {
@@ -235,6 +236,19 @@ public class Application implements Terminal.ErrorListener, Serializable {
             return mainWindow;
         }
 
+        /**
+         * This implementation simulates the way of finding a window for a
+         * request by extracting a window name from the requested path and
+         * passes that name to {@link #getWindow(String)}.
+         * 
+         * <p>
+         * {@inheritDoc}
+         */
+        @Override
+        public Class<? extends UI> getUIClass(WrappedRequest request) {
+            return getUIInstance(request).getClass();
+        }
+
         /**
          * Sets the application's theme.
          * <p>
@@ -269,9 +283,9 @@ public class Application implements Terminal.ErrorListener, Serializable {
          * <p>
          * {@inheritDoc}
          */
-
         @Override
-        public String getThemeForUI(UI uI) {
+        public String getThemeForUI(WrappedRequest request,
+                Class<? extends UI> uiClass) {
             return theme;
         }
 
@@ -476,15 +490,6 @@ public class Application implements Terminal.ErrorListener, Serializable {
 
     private final EventRouter eventRouter = new EventRouter();
 
-    /**
-     * Keeps track of which uIs have been inited.
-     * <p>
-     * TODO Investigate whether this might be derived from the different states
-     * in getUIForRrequest.
-     * </p>
-     */
-    private Set<Integer> initedUIs = new HashSet<Integer>();
-
     private List<UIProvider> uiProviders = new LinkedList<UIProvider>();
 
     private GlobalResourceHandler globalResourceHandler;
@@ -1577,67 +1582,98 @@ public class Application implements Terminal.ErrorListener, Serializable {
     }
 
     /**
-     * Gets a UI for a request for which no UI is already known. This method is
-     * called when the framework processes a request that does not originate
-     * from an existing UI instance. This typically happens when a host page is
-     * requested.
-     * 
+     * Gets the UI class for a request for which no UI is already known. This
+     * method is called when the framework processes a request that does not
+     * originate from an existing UI instance. This typically happens when a
+     * host page is requested.
      * <p>
      * Subclasses of Application may override this method to provide custom
-     * logic for choosing how to create a suitable UI or for picking an already
-     * created UI. If an existing UI is picked, care should be taken to avoid
-     * keeping the same UI open in multiple browser windows, as that will cause
-     * the states to go out of sync.
-     * </p>
-     * 
+     * logic for choosing what kind of UI to use.
      * <p>
-     * If {@link BrowserDetails} are required to create a UI, the implementation
-     * can throw a {@link UIRequiresMoreInformationException} exception. In this
-     * case, the framework will instruct the browser to send the additional
-     * details, whereupon this method is invoked again with the browser details
-     * present in the wrapped request. Throwing the exception if the browser
-     * details are already available is not supported.
-     * </p>
+     * The default implementation in {@link Application} uses the
+     * {@value #UI_PARAMETER} parameter from web.xml for finding the name of the
+     * UI class. If {@link DeploymentConfiguration#getClassLoader()} does not
+     * return <code>null</code>, the returned {@link ClassLoader} is used for
+     * loading the UI class. Otherwise the {@link ClassLoader} used to load this
+     * class is used.
      * 
-     * <p>
-     * The default implementation in {@link Application} creates a new instance
-     * of the UI class returned by {@link #getUIClassName(WrappedRequest)},
-     * which in turn uses the {@value #UI_PARAMETER} parameter from web.xml. If
-     * {@link DeploymentConfiguration#getClassLoader()} for the request returns
-     * a {@link ClassLoader}, it is used for loading the UI class. Otherwise the
-     * {@link ClassLoader} used to load this class is used.
      * </p>
      * 
      * @param request
      *            the wrapped request for which a UI is needed
      * @return a UI instance to use for the request
-     * @throws UIRequiresMoreInformationException
-     *             may be thrown by an implementation to indicate that
-     *             {@link BrowserDetails} are required to create a UI
      * 
-     * @see #getUIClassName(WrappedRequest)
      * @see UI
-     * @see UIRequiresMoreInformationException
      * @see WrappedRequest#getBrowserDetails()
      * 
      * @since 7.0
      */
-    protected UI getUI(WrappedRequest request)
-            throws UIRequiresMoreInformationException {
-
-        // Iterate in reverse order - test check newest provider first
-        for (int i = uiProviders.size() - 1; i >= 0; i--) {
+    public Class<? extends UI> getUIClass(WrappedRequest request) {
+        // Iterate in reverse order - check newest provider first
+        int providersSize = uiProviders.size();
+        if (providersSize == 0) {
+            throw new IllegalStateException("There are no UI providers");
+        }
+        for (int i = providersSize - 1; i >= 0; i--) {
             UIProvider provider = uiProviders.get(i);
 
             Class<? extends UI> uiClass = provider.getUIClass(this, request);
 
             if (uiClass != null) {
-                return provider.instantiateUI(this, uiClass, request);
+                return uiClass;
+            }
+        }
+
+        throw new RuntimeException(
+                "No UI provider returned an UI class for request");
+    }
+
+    /**
+     * Creates an UI instance for a request for which no UI is already known.
+     * This method is called when the framework processes a request that does
+     * not originate from an existing UI instance. This typically happens when a
+     * host page is requested.
+     * <p>
+     * Subclasses of Application may override this method to provide custom
+     * logic for choosing how to create a suitable UI or for picking an already
+     * created UI. If an existing UI is picked, care should be taken to avoid
+     * keeping the same UI open in multiple browser windows, as that will cause
+     * the states to go out of sync.
+     * </p>
+     * 
+     * @param request
+     * @param uiClass
+     * @return
+     */
+    protected <T extends UI> T createUIInstance(WrappedRequest request,
+            Class<T> uiClass) {
+        int providersSize = uiProviders.size();
+        if (providersSize == 0) {
+            throw new IllegalStateException("There are no UI providers");
+        }
+
+        for (int i = providersSize - 1; i >= 0; i--) {
+            UIProvider provider = uiProviders.get(i);
+
+            Class<? extends UI> providerClass = provider.getUIClass(this,
+                    request);
+            if (providerClass != null) {
+                if (providerClass != uiClass) {
+                    getLogger().warning(
+                            "Mismatching UI classes. Expected " + uiClass
+                                    + " but got " + providerClass + " from "
+                                    + provider);
+                    // Try with next provider if we didn't get the expected
+                    // class
+                    continue;
+                }
+                return uiClass.cast(provider.instantiateUI(this, uiClass,
+                        request));
             }
         }
 
         throw new RuntimeException(
-                "No UI providers available or providers are not able to find UI instance");
+                "No UI provider created an UI instance for request");
     }
 
     /**
@@ -1653,8 +1689,9 @@ public class Application implements Terminal.ErrorListener, Serializable {
      * 
      * @since 7.0
      */
-    public String getThemeForUI(UI uI) {
-        Theme uiTheme = getAnnotationFor(uI.getClass(), Theme.class);
+    public String getThemeForUI(WrappedRequest request,
+            Class<? extends UI> uiClass) {
+        Theme uiTheme = getAnnotationFor(uiClass, Theme.class);
         if (uiTheme != null) {
             return uiTheme.value();
         } else {
@@ -1665,18 +1702,22 @@ public class Application implements Terminal.ErrorListener, Serializable {
     /**
      * Finds the widgetset to use for a specific UI. If no specific widgetset is
      * required, <code>null</code> is returned.
+     * <p>
+     * The default implementation uses the @{@link Widgetset} annotation if it's
+     * defined for the UI class.
      * 
-     * TODO Tell what the default implementation does once it does something.
-     * 
-     * @param uI
-     *            the UI to get a widgetset for
+     * @param request
+     *            the wrapped request for which to get a widgetset
+     * @param uiClass
+     *            the UI class to get a widgetset for
      * @return the name of the widgetset, or <code>null</code> if the default
      *         widgetset should be used
      * 
      * @since 7.0
      */
-    public String getWidgetsetForUI(UI uI) {
-        Widgetset uiWidgetset = getAnnotationFor(uI.getClass(), Widgetset.class);
+    public String getWidgetsetForUI(WrappedRequest request,
+            Class<? extends UI> uiClass) {
+        Widgetset uiWidgetset = getAnnotationFor(uiClass, Widgetset.class);
         if (uiWidgetset != null) {
             return uiWidgetset.value();
         } else {
@@ -1818,8 +1859,6 @@ public class Application implements Terminal.ErrorListener, Serializable {
      */
     private static final ThreadLocal<Application> currentApplication = new ThreadLocal<Application>();
 
-    private boolean uiPreserved = false;
-
     /**
      * Gets the currently used application. The current application is
      * automatically defined when processing requests to the server. In other
@@ -1894,17 +1933,12 @@ public class Application implements Terminal.ErrorListener, Serializable {
      * @param request
      *            the request for which a UI is desired
      * @return a UI belonging to the request
-     * @throws UIRequiresMoreInformationException
-     *             if no existing UI could be found and creating a new UI
-     *             requires additional information from the browser
      * 
-     * @see #getUI(WrappedRequest)
-     * @see UIRequiresMoreInformationException
+     * @see #createUI(WrappedRequest)
      * 
      * @since 7.0
      */
-    public UI getUIForRequest(WrappedRequest request)
-            throws UIRequiresMoreInformationException {
+    public UI getUIForRequest(WrappedRequest request) {
         UI uI = UI.getCurrent();
         if (uI != null) {
             return uI;
@@ -1917,69 +1951,75 @@ public class Application implements Terminal.ErrorListener, Serializable {
                     && browserDetails.getUriFragment() != null;
 
             uI = uIs.get(uiId);
+            Class<? extends UI> uiClass = null;
+
+            if (uI == null && hasBrowserDetails
+                    && !retainOnRefreshUIs.isEmpty()) {
+                uiClass = getUIClass(request);
 
-            if (uI == null && isUiPreserved()) {
                 // Check for a known UI
-                if (!retainOnRefreshUIs.isEmpty()) {
 
-                    Integer retainedUIId;
-                    if (!hasBrowserDetails) {
-                        throw new UIRequiresMoreInformationException();
-                    } else {
-                        String windowName = browserDetails.getWindowName();
-                        retainedUIId = retainOnRefreshUIs.get(windowName);
-                    }
+                @SuppressWarnings("null")
+                String windowName = browserDetails.getWindowName();
+                Integer retainedUIId = retainOnRefreshUIs.get(windowName);
 
-                    if (retainedUIId != null) {
+                if (retainedUIId != null) {
+                    UI retainedUI = uIs.get(retainedUIId);
+                    // We've had the same UI instance in a window with this
+                    // name, but should we still use it?
+                    if (retainedUI.getClass() == uiClass) {
                         uiId = retainedUIId;
-                        uI = uIs.get(uiId);
+                        uI = retainedUI;
+                    } else {
+                        getLogger().info(
+                                "Not using retained UI in " + windowName
+                                        + " because retained UI was of type "
+                                        + retainedUIId.getClass() + " but "
+                                        + uiClass
+                                        + " is expected for the request.");
                     }
                 }
             }
 
-            if (uI == null) {
-                // Throws exception if UI can not yet be created
-                uI = getUI(request);
+        } // end synchronized block
 
-                // Initialize some fields for a newly created UI
-                if (uI.getApplication() == null) {
-                    uI.setApplication(this);
-                }
-                if (uI.getUIId() < 0) {
+        UI.setCurrent(uI);
 
-                    if (uiId == null) {
-                        // Get the next id if none defined
-                        uiId = Integer.valueOf(nextUIId++);
-                    }
-                    uI.setUIId(uiId.intValue());
-                    uIs.put(uiId, uI);
-                }
-            }
+        return uI;
+    }
 
-            // Set thread local here so it is available in init
-            UI.setCurrent(uI);
+    public UI createUI(WrappedRequest request) {
+        Class<? extends UI> uiClass = getUIClass(request);
 
-            if (!initedUIs.contains(uiId)) {
-                boolean initRequiresBrowserDetails = isUiPreserved()
-                        || !uI.getClass().isAnnotationPresent(EagerInit.class);
-                if (!initRequiresBrowserDetails || hasBrowserDetails) {
-                    uI.doInit(request);
+        UI ui = createUIInstance(request, uiClass);
 
-                    // Remember that this UI has been initialized
-                    initedUIs.add(uiId);
+        // Initialize some fields for a newly created UI
+        if (ui.getApplication() == null) {
+            ui.setApplication(this);
+        }
+        // Get the next id
+        Integer uiId = Integer.valueOf(nextUIId++);
 
-                    // init() might turn on preserve so do this afterwards
-                    if (isUiPreserved()) {
-                        // Remember this UI
-                        String windowName = request.getBrowserDetails()
-                                .getWindowName();
-                        retainOnRefreshUIs.put(windowName, uiId);
-                    }
-                }
+        uIs.put(uiId, ui);
+
+        // Set thread local here so it is available in init
+        UI.setCurrent(ui);
+
+        ui.doInit(request, uiId.intValue());
+
+        if (isUiPreserved(request, uiClass)) {
+            // Remember this UI
+            String windowName = request.getBrowserDetails().getWindowName();
+            if (windowName == null) {
+                getLogger().warning(
+                        "There is no window.name available for UI " + uiClass
+                                + " that should be preserved.");
+            } else {
+                retainOnRefreshUIs.put(windowName, uiId);
             }
-        } // end synchronized block
+        }
 
-        return uI;
+        return ui;
     }
 
     /**
@@ -2002,54 +2042,23 @@ public class Application implements Terminal.ErrorListener, Serializable {
         return uiId;
     }
 
-    /**
-     * Sets whether the same UI state should be reused if the framework can
-     * 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>
-     * NOTE that you should avoid turning this feature on/off on-the-fly when
-     * the UI is already shown, as it might not be retained as intended.
-     * </p>
-     * 
-     * @param uiPreserved
-     *            <code>true</code>if the same UI instance should be reused e.g.
-     *            when the browser window is refreshed.
-     */
-    public void setUiPreserved(boolean uiPreserved) {
-        this.uiPreserved = uiPreserved;
-        if (!uiPreserved) {
-            retainOnRefreshUIs.clear();
-        }
-    }
-
     /**
      * Checks whether the same UI state should be reused if the framework can
      * 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.
      * 
+     * @param request
+     * @param uiClass
+     * 
      * @return <code>true</code>if the same UI instance should be reused e.g.
      *         when the browser window is refreshed.
      */
-    public boolean isUiPreserved() {
-        return uiPreserved;
-    }
-
-    /**
-     * Checks whether there's a pending initialization for the UI with the given
-     * id.
-     * 
-     * @param uiId
-     *            UI id to check for
-     * @return <code>true</code> of the initialization is pending,
-     *         <code>false</code> if the UI id is not registered or if the UI
-     *         has already been initialized
-     * 
-     * @see #getUIForRequest(WrappedRequest)
-     */
-    public boolean isUIInitPending(int uiId) {
-        return !initedUIs.contains(Integer.valueOf(uiId));
+    public boolean isUiPreserved(WrappedRequest request,
+            Class<? extends UI> uiClass) {
+        PreserveOnRefresh preserveOnRefresh = getAnnotationFor(uiClass,
+                PreserveOnRefresh.class);
+        return preserveOnRefresh != null;
     }
 
     /**
@@ -2268,4 +2277,20 @@ public class Application implements Terminal.ErrorListener, Serializable {
 
         return globalResourceHandler;
     }
+
+    public String getPageTitleForUI(WrappedRequest request,
+            Class<? extends UI> uiClass) {
+        Title titleAnnotation = getAnnotationFor(uiClass, Title.class);
+        if (titleAnnotation == null) {
+            return null;
+        } else {
+            return titleAnnotation.value();
+        }
+    }
+
+    public boolean isEagerInit(WrappedRequest request,
+            Class<? extends UI> uiClass) {
+        EagerInit eagerInit = getAnnotationFor(uiClass, EagerInit.class);
+        return eagerInit != null;
+    }
 }
diff --git a/server/src/com/vaadin/UIRequiresMoreInformationException.java b/server/src/com/vaadin/UIRequiresMoreInformationException.java
deleted file mode 100644 (file)
index 76a31d8..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2011 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;
-
-import com.vaadin.server.WrappedRequest;
-import com.vaadin.server.WrappedRequest.BrowserDetails;
-
-/**
- * Exception that is thrown to indicate that creating or initializing the UI
- * requires information detailed from the web browser ({@link BrowserDetails})
- * to be present.
- * 
- * This exception may not be thrown if that information is already present in
- * the current WrappedRequest.
- * 
- * @see Application#getUI(WrappedRequest)
- * @see WrappedRequest#getBrowserDetails()
- * 
- * @since 7.0
- */
-public class UIRequiresMoreInformationException extends Exception {
-    // Nothing of interest here
-}
diff --git a/server/src/com/vaadin/annotations/PreserveOnRefresh.java b/server/src/com/vaadin/annotations/PreserveOnRefresh.java
new file mode 100644 (file)
index 0000000..59c4abb
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PreserveOnRefresh {
+
+}
diff --git a/server/src/com/vaadin/annotations/Title.java b/server/src/com/vaadin/annotations/Title.java
new file mode 100644 (file)
index 0000000..fcd5d98
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.vaadin.ui.UI;
+
+/**
+ * Defines the HTML page title for a {@link UI}.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Title {
+    /**
+     * Gets the HTML title that should be used if the UI is used on it's own.
+     * 
+     * @return a page title string
+     */
+    public String value();
+}
index e47e00577bd60fa300e49cc7b07de86fb2960a63..e8151462aa32f068ca0d3a7c2da47def497880d8 100644 (file)
@@ -56,7 +56,6 @@ import com.liferay.portal.kernel.util.PropsUtil;
 import com.vaadin.Application;
 import com.vaadin.Application.ApplicationStartEvent;
 import com.vaadin.Application.SystemMessages;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.server.AbstractCommunicationManager.Callback;
 import com.vaadin.ui.UI;
 
@@ -501,12 +500,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
                             // Both action requests and render requests are ok
                             // without a UI as they render the initial HTML
                             // and then do a second request
-                            try {
-                                uI = application
-                                        .getUIForRequest(wrappedRequest);
-                            } catch (UIRequiresMoreInformationException e) {
-                                // Ignore problem and continue without UI
-                            }
+                            uI = application.getUIForRequest(wrappedRequest);
                             break;
                         case BROWSER_DETAILS:
                             // Should not try to find a UI here as the
@@ -902,7 +896,6 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
             throws PortletException {
         try {
             final Application application = getApplicationClass().newInstance();
-            application.setUiPreserved(true);
             return application;
         } catch (final IllegalAccessException e) {
             throw new PortletException("getNewApplication failed", e);
index 08ad48ff3d9e8d7e427310f124ceb5d1b312049d..72406e629d89cdbf06acb0ba99e35ac170b88757 100644 (file)
@@ -60,13 +60,11 @@ import javax.servlet.http.HttpServletResponse;
 
 import com.vaadin.Application;
 import com.vaadin.Application.SystemMessages;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.annotations.JavaScript;
 import com.vaadin.annotations.StyleSheet;
 import com.vaadin.external.json.JSONArray;
 import com.vaadin.external.json.JSONException;
 import com.vaadin.external.json.JSONObject;
-import com.vaadin.server.BootstrapHandler.BootstrapContext;
 import com.vaadin.server.ComponentSizeValidator.InvalidLayout;
 import com.vaadin.server.RpcManager.RpcInvocationException;
 import com.vaadin.server.StreamVariable.StreamingEndEvent;
@@ -1512,7 +1510,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
     }
 
     private String getTheme(UI uI) {
-        String themeName = uI.getApplication().getThemeForUI(uI);
+        String themeName = uI.getTheme();
         String requestThemeName = getRequestTheme();
 
         if (requestThemeName != null) {
@@ -2410,36 +2408,22 @@ public abstract class AbstractCommunicationManager implements Serializable {
             WrappedResponse response, Application application)
             throws IOException {
 
-        // if we do not yet have a currentUI, it should be initialized
-        // shortly, and we should send the initial UIDL
-        boolean sendUIDL = UI.getCurrent() == null;
+        assert UI.getCurrent() == null;
 
         try {
             CombinedRequest combinedRequest = new CombinedRequest(request);
 
-            UI uI = application.getUIForRequest(combinedRequest);
             response.setContentType("application/json; charset=UTF-8");
 
-            // Use the same logic as for determined UIs
-            BootstrapHandler bootstrapHandler = getBootstrapHandler();
-            BootstrapContext context = bootstrapHandler.createContext(
-                    combinedRequest, response, application, uI.getUIId());
-
-            String widgetset = context.getWidgetsetName();
-            String theme = context.getThemeName();
-            String themeUri = bootstrapHandler.getThemeUri(context, theme);
+            UI uI = application.getUIForRequest(combinedRequest);
+            if (uI == null) {
+                uI = application.createUI(combinedRequest);
+            }
 
-            // TODO These are not required if it was only the init of the UI
-            // that was delayed
             JSONObject params = new JSONObject();
-            params.put("widgetset", widgetset);
-            params.put("themeUri", themeUri);
-            // UI id might have changed based on e.g. window.name
             params.put(UIConstants.UI_ID_PARAMETER, uI.getUIId());
-            if (sendUIDL) {
-                String initialUIDL = getInitialUIDL(combinedRequest, uI);
-                params.put("uidl", initialUIDL);
-            }
+            String initialUIDL = getInitialUIDL(combinedRequest, uI);
+            params.put("uidl", initialUIDL);
 
             // NOTE! GateIn requires, for some weird reason, getOutputStream
             // to be used instead of getWriter() (it seems to interpret
@@ -2452,10 +2436,6 @@ public abstract class AbstractCommunicationManager implements Serializable {
             // NOTE GateIn requires the buffers to be flushed to work
             outWriter.flush();
             out.flush();
-        } catch (UIRequiresMoreInformationException e) {
-            // Requiring more information at this point is not allowed
-            // TODO handle in a better way
-            throw new RuntimeException(e);
         } catch (JSONException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
index 16f7bc653b93f90638756e5b7179504e301e64ab..149f59e7a5221908411dcda7536427cf1317f909 100644 (file)
@@ -21,6 +21,7 @@ import java.util.List;
 import org.jsoup.nodes.Node;
 
 import com.vaadin.Application;
+import com.vaadin.ui.UI;
 
 /**
  * A representation of a bootstrap fragment being generated. The bootstrap
@@ -37,7 +38,7 @@ public class BootstrapFragmentResponse extends BootstrapResponse {
      * Crate a new bootstrap fragment response.
      * 
      * @see BootstrapResponse#BootstrapResponse(BootstrapHandler,
-     *      WrappedRequest, Application, Integer)
+     *      WrappedRequest, Application, Class)
      * 
      * @param handler
      *            the bootstrap handler that is firing the event
@@ -47,16 +48,16 @@ public class BootstrapFragmentResponse extends BootstrapResponse {
      * @param application
      *            the application for which the bootstrap page should be
      *            generated
-     * @param uiId
-     *            the generated id of the UI that will be displayed on the page
+     * @param uiClass
+     *            the class of the UI that will be displayed on the page
      * @param fragmentNodes
      *            a mutable list containing the DOM nodes that will make up the
      *            application HTML
      */
     public BootstrapFragmentResponse(BootstrapHandler handler,
-            WrappedRequest request, Application application, Integer uiId,
-            List<Node> fragmentNodes) {
-        super(handler, request, application, uiId);
+            WrappedRequest request, Application application,
+            Class<? extends UI> uiClass, List<Node> fragmentNodes) {
+        super(handler, request, application, uiClass);
         this.fragmentNodes = fragmentNodes;
     }
 
index 280372a5e50a30d266304b1bf9f8a4802f43f111..2b31d5d3bc959cccce1ee442c3df129a3e22e275 100644 (file)
@@ -37,12 +37,10 @@ import org.jsoup.nodes.Node;
 import org.jsoup.parser.Tag;
 
 import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.external.json.JSONException;
 import com.vaadin.external.json.JSONObject;
 import com.vaadin.shared.ApplicationConstants;
 import com.vaadin.shared.Version;
-import com.vaadin.shared.ui.ui.UIConstants;
 import com.vaadin.ui.UI;
 
 public abstract class BootstrapHandler implements RequestHandler {
@@ -74,30 +72,20 @@ public abstract class BootstrapHandler implements RequestHandler {
             return bootstrapResponse.getApplication();
         }
 
-        public Integer getUIId() {
-            return bootstrapResponse.getUIId();
-        }
-
-        public UI getUI() {
-            return bootstrapResponse.getUI();
+        public Class<? extends UI> getUIClass() {
+            return bootstrapResponse.getUiClass();
         }
 
         public String getWidgetsetName() {
             if (widgetsetName == null) {
-                UI uI = getUI();
-                if (uI != null) {
-                    widgetsetName = getWidgetsetForUI(this);
-                }
+                widgetsetName = getWidgetsetForUI(this);
             }
             return widgetsetName;
         }
 
         public String getThemeName() {
             if (themeName == null) {
-                UI uI = getUI();
-                if (uI != null) {
-                    themeName = findAndEscapeThemeName(this);
-                }
+                themeName = findAndEscapeThemeName(this);
             }
             return themeName;
         }
@@ -120,23 +108,11 @@ public abstract class BootstrapHandler implements RequestHandler {
             WrappedRequest request, WrappedResponse response)
             throws IOException {
 
-        // TODO Should all urls be handled here?
-        Integer uiId = null;
         try {
-            UI uI = application.getUIForRequest(request);
-            if (uI == null) {
-                writeError(response, new Throwable("No UI found"));
-                return true;
-            }
+            Class<? extends UI> uiClass = application.getUIClass(request);
 
-            uiId = Integer.valueOf(uI.getUIId());
-        } catch (UIRequiresMoreInformationException e) {
-            // Just keep going without uiId
-        }
-
-        try {
             BootstrapContext context = createContext(request, response,
-                    application, uiId);
+                    application, uiClass);
             setupMainDiv(context);
 
             BootstrapFragmentResponse fragmentResponse = context
@@ -166,8 +142,8 @@ public abstract class BootstrapHandler implements RequestHandler {
             Map<String, Object> headers = new LinkedHashMap<String, Object>();
             Document document = Document.createShell("");
             BootstrapPageResponse pageResponse = new BootstrapPageResponse(
-                    this, request, context.getApplication(), context.getUIId(),
-                    document, headers);
+                    this, request, context.getApplication(),
+                    context.getUIClass(), document, headers);
             List<Node> fragmentNodes = fragmentResponse.getFragmentNodes();
             Element body = document.body();
             for (Node node : fragmentNodes) {
@@ -242,10 +218,11 @@ public abstract class BootstrapHandler implements RequestHandler {
         head.appendElement("meta").attr("http-equiv", "X-UA-Compatible")
                 .attr("content", "chrome=1");
 
-        UI uI = context.getUI();
-        String title = ((uI == null || uI.getCaption() == null) ? "" : uI
-                .getCaption());
-        head.appendElement("title").appendText(title);
+        String title = context.getApplication().getPageTitleForUI(
+                context.getRequest(), context.getUIClass());
+        if (title != null) {
+            head.appendElement("title").appendText(title);
+        }
 
         head.appendElement("style").attr("type", "text/css")
                 .appendText("html, body {height:100%;margin:0;}");
@@ -267,11 +244,12 @@ public abstract class BootstrapHandler implements RequestHandler {
         body.addClass(ApplicationConstants.GENERATED_BODY_CLASSNAME);
     }
 
-    public BootstrapContext createContext(WrappedRequest request,
-            WrappedResponse response, Application application, Integer uiId) {
+    private BootstrapContext createContext(WrappedRequest request,
+            WrappedResponse response, Application application,
+            Class<? extends UI> uiClass) {
         BootstrapContext context = new BootstrapContext(response,
-                new BootstrapFragmentResponse(this, request, application, uiId,
-                        new ArrayList<Node>()));
+                new BootstrapFragmentResponse(this, request, application,
+                        uiClass, new ArrayList<Node>()));
         return context;
     }
 
@@ -290,10 +268,10 @@ public abstract class BootstrapHandler implements RequestHandler {
     protected abstract String getApplicationId(BootstrapContext context);
 
     public String getWidgetsetForUI(BootstrapContext context) {
-        UI uI = context.getUI();
         WrappedRequest request = context.getRequest();
 
-        String widgetset = uI.getApplication().getWidgetsetForUI(uI);
+        String widgetset = context.getApplication().getWidgetsetForUI(
+                context.getRequest(), context.getUIClass());
         if (widgetset == null) {
             widgetset = request.getDeploymentConfiguration()
                     .getConfiguredWidgetset(request);
@@ -413,14 +391,9 @@ public abstract class BootstrapHandler implements RequestHandler {
     protected JSONObject getApplicationParameters(BootstrapContext context)
             throws JSONException, PaintException {
         Application application = context.getApplication();
-        Integer uiId = context.getUIId();
 
         JSONObject appConfig = new JSONObject();
 
-        if (uiId != null) {
-            appConfig.put(UIConstants.UI_ID_PARAMETER, uiId);
-        }
-
         if (context.getThemeName() != null) {
             appConfig.put("themeUri",
                     getThemeUri(context, context.getThemeName()));
@@ -433,18 +406,18 @@ public abstract class BootstrapHandler implements RequestHandler {
 
         appConfig.put("widgetset", context.getWidgetsetName());
 
-        if (uiId == null || application.isUIInitPending(uiId.intValue())) {
-            appConfig.put("initialPath", context.getRequest()
-                    .getRequestPathInfo());
-
-            Map<String, String[]> parameterMap = context.getRequest()
-                    .getParameterMap();
-            appConfig.put("initialParams", parameterMap);
-        } else {
+        if (application.isEagerInit(context.getRequest(), context.getUIClass())) {
+            throw new RuntimeException(
+                    "Eager UI init is currently not supported");
             // write the initial UIDL into the config
-            appConfig.put("uidl",
-                    getInitialUIDL(context.getRequest(), context.getUI()));
+            // appConfig.put("uidl",
+            // getInitialUIDL(context.getRequest(), context.getUI()));
         }
+        appConfig.put("initialPath", context.getRequest().getRequestPathInfo());
+
+        Map<String, String[]> parameterMap = context.getRequest()
+                .getParameterMap();
+        appConfig.put("initialParams", parameterMap);
 
         return appConfig;
     }
@@ -532,7 +505,8 @@ public abstract class BootstrapHandler implements RequestHandler {
      * @return
      */
     public String getThemeName(BootstrapContext context) {
-        return context.getApplication().getThemeForUI(context.getUI());
+        return context.getApplication().getThemeForUI(context.getRequest(),
+                context.getUIClass());
     }
 
     /**
@@ -561,21 +535,4 @@ public abstract class BootstrapHandler implements RequestHandler {
         response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                 e.getLocalizedMessage());
     }
-
-    /**
-     * Gets the initial UIDL message to send to the client.
-     * 
-     * @param request
-     *            the originating request
-     * @param ui
-     *            the UI for which the UIDL should be generated
-     * @return a string with the initial UIDL message
-     * @throws PaintException
-     *             if an exception occurs while painting the components
-     * @throws JSONException
-     *             if an exception occurs while formatting the output
-     */
-    protected abstract String getInitialUIDL(WrappedRequest request, UI ui)
-            throws PaintException, JSONException;
-
 }
index d6df145728227918dc4e8d2aac6a27c8ce4a779f..a5fdfe470708a15df79ddaf97bba21829f45882a 100644 (file)
@@ -21,6 +21,7 @@ import java.util.Map;
 import org.jsoup.nodes.Document;
 
 import com.vaadin.Application;
+import com.vaadin.ui.UI;
 
 /**
  * A representation of a bootstrap page being generated. The bootstrap page
@@ -39,7 +40,7 @@ public class BootstrapPageResponse extends BootstrapResponse {
      * Crate a new bootstrap page response.
      * 
      * @see BootstrapResponse#BootstrapResponse(BootstrapHandler,
-     *      WrappedRequest, Application, Integer)
+     *      WrappedRequest, Application, Class)
      * 
      * @param handler
      *            the bootstrap handler that is firing the event
@@ -49,17 +50,18 @@ public class BootstrapPageResponse extends BootstrapResponse {
      * @param application
      *            the application for which the bootstrap page should be
      *            generated
-     * @param uiId
-     *            the generated id of the UI that will be displayed on the page
+     * @param uiClass
+     *            the class of the UI that will be displayed on the page
      * @param document
      *            the DOM document making up the HTML page
      * @param headers
      *            a map into which header data can be added
      */
     public BootstrapPageResponse(BootstrapHandler handler,
-            WrappedRequest request, Application application, Integer uiId,
-            Document document, Map<String, Object> headers) {
-        super(handler, request, application, uiId);
+            WrappedRequest request, Application application,
+            Class<? extends UI> uiClass, Document document,
+            Map<String, Object> headers) {
+        super(handler, request, application, uiClass);
         this.headers = headers;
         this.document = document;
     }
index 962b48dc312e948b3d5605616edbc32b6e4e5a69..3173569059e2b04c9c43ee1d250b58de023e1070 100644 (file)
@@ -19,7 +19,6 @@ package com.vaadin.server;
 import java.util.EventObject;
 
 import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.ui.UI;
 
 /**
@@ -32,7 +31,7 @@ import com.vaadin.ui.UI;
 public abstract class BootstrapResponse extends EventObject {
     private final WrappedRequest request;
     private final Application application;
-    private final Integer uiId;
+    private final Class<? extends UI> uiClass;
 
     /**
      * Creates a new bootstrap event.
@@ -45,15 +44,15 @@ public abstract class BootstrapResponse extends EventObject {
      * @param application
      *            the application for which the bootstrap page should be
      *            generated
-     * @param uiId
-     *            the generated id of the UI that will be displayed on the page
+     * @param uiClass
+     *            the class of the UI that will be displayed on the page
      */
     public BootstrapResponse(BootstrapHandler handler, WrappedRequest request,
-            Application application, Integer uiId) {
+            Application application, Class<? extends UI> uiClass) {
         super(handler);
         this.request = request;
         this.application = application;
-        this.uiId = uiId;
+        this.uiClass = uiClass;
     }
 
     /**
@@ -89,32 +88,13 @@ public abstract class BootstrapResponse extends EventObject {
     }
 
     /**
-     * Gets the UI id that has been generated for this response. Please note
-     * that if {@link Application#isUiPreserved()} is enabled, a previously
-     * created UI with a different id might eventually end up being used.
+     * Gets the class of the UI that will be displayed on the generated
+     * bootstrap page.
      * 
-     * @return the UI id
+     * @return the class of the UI
      */
-    public Integer getUIId() {
-        return uiId;
+    public Class<? extends UI> getUiClass() {
+        return uiClass;
     }
 
-    /**
-     * Gets the UI for which this page is being rendered, if available. Some
-     * features of the framework will postpone the UI selection until after the
-     * bootstrap page has been rendered and required information from the
-     * browser has been sent back. This method will return <code>null</code> if
-     * no UI instance is yet available.
-     * 
-     * @see Application#isUiPreserved()
-     * @see Application#getUI(WrappedRequest)
-     * @see UIRequiresMoreInformationException
-     * 
-     * @return The UI that will be displayed in the page being generated, or
-     *         <code>null</code> if all required information is not yet
-     *         available.
-     */
-    public UI getUI() {
-        return UI.getCurrent();
-    }
 }
index 9b56e20fd449ce6c13a16e3fd299f845b2418358..3c594eaf02ba2283904eaefe40a1223f42183f64 100644 (file)
@@ -22,7 +22,6 @@ import java.net.URL;
 import javax.servlet.ServletContext;
 
 import com.vaadin.Application;
-import com.vaadin.external.json.JSONException;
 import com.vaadin.ui.UI;
 
 /**
@@ -107,12 +106,6 @@ public class CommunicationManager extends AbstractCommunicationManager {
                 }
                 return themeName;
             }
-
-            @Override
-            protected String getInitialUIDL(WrappedRequest request, UI uI)
-                    throws PaintException, JSONException {
-                return CommunicationManager.this.getInitialUIDL(request, uI);
-            }
         };
     }
 
index d4490b164236e3822d04ae1c96e77d3be984d842..93128aad2898089cb5428c12b0981e9f2073dae8 100644 (file)
 package com.vaadin.server;
 
 import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.ui.UI;
 
 public class DefaultUIProvider extends AbstractUIProvider {
 
     @Override
     public Class<? extends UI> getUIClass(Application application,
-            WrappedRequest request) throws UIRequiresMoreInformationException {
+            WrappedRequest request) {
         Object uiClassNameObj = application
                 .getProperty(Application.UI_PARAMETER);
 
index f7d9371022aa96b9349c22d0813ab21bac41a227..697095d6914de66988e4fed0236e575a0f80f470 100644 (file)
@@ -137,13 +137,6 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
                         null);
             }
 
-            @Override
-            protected String getInitialUIDL(WrappedRequest request, UI uI)
-                    throws PaintException, JSONException {
-                return PortletCommunicationManager.this.getInitialUIDL(request,
-                        uI);
-            }
-
             @Override
             protected JSONObject getApplicationParameters(
                     BootstrapContext context) throws JSONException,
index 890809fdd5779cfa689edb68fd4179c3c934ae5c..36bb164845d30a84a682e18c0f5d3e04d0c0792f 100644 (file)
 package com.vaadin.server;
 
 import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.ui.UI;
 
 public interface UIProvider {
     public Class<? extends UI> getUIClass(Application application,
-            WrappedRequest request) throws UIRequiresMoreInformationException;
+            WrappedRequest request);
 
     public UI instantiateUI(Application application, Class<? extends UI> type,
             WrappedRequest request);
index 8ae5335763da58bf7e00a384d729da4e5fd9a617..e95b2d599b98b76d1517474e315377abe05a68a2 100644 (file)
@@ -26,8 +26,6 @@ import javax.portlet.PortletRequest;
 import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 
-import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.annotations.EagerInit;
 import com.vaadin.ui.UI;
 
@@ -218,11 +216,8 @@ public interface WrappedRequest extends Serializable {
      * for instance using javascript in the browser.
      * 
      * This information is only guaranteed to be available in some special
-     * cases, for instance when
-     * {@link Application#getUIForRequest(WrappedRequest)} is called again after
-     * throwing {@link UIRequiresMoreInformationException} or in
-     * {@link UI#init(WrappedRequest)} for a UI class not annotated with
-     * {@link EagerInit}
+     * cases, for instance in {@link UI#init(WrappedRequest)} for a UI class not
+     * annotated with {@link EagerInit}
      * 
      * @return the browser details, or <code>null</code> if details are not
      *         available
index d86d46c155df278717f915e3bf6e7d6fb9025b12..b6ac27194253651a1ef3c5fe2f02142d481ebc1c 100644 (file)
@@ -85,7 +85,7 @@ import com.vaadin.tools.ReflectTools;
  * </p>
  * 
  * @see #init(WrappedRequest)
- * @see Application#getUI(WrappedRequest)
+ * @see Application#createUI(WrappedRequest)
  * 
  * @since 7.0
  */
@@ -98,7 +98,6 @@ public abstract class UI extends AbstractComponentContainer implements
      * window in Vaadin 6 with {@link com.vaadin.Application.LegacyApplication}
      */
     @Deprecated
-    @EagerInit
     public static class LegacyWindow extends UI {
         private String name;
 
@@ -709,28 +708,6 @@ public abstract class UI extends AbstractComponentContainer implements
         }
     }
 
-    /**
-     * Sets the id of this UI within its application. The UI id is used to route
-     * requests to the right UI.
-     * <p>
-     * This method is mainly intended for internal use by the framework.
-     * </p>
-     * 
-     * @param uiId
-     *            the id of this UI
-     * 
-     * @throws IllegalStateException
-     *             if the UI id has already been set
-     * 
-     * @see #getUIId()
-     */
-    public void setUIId(int uiId) {
-        if (this.uiId != -1) {
-            throw new IllegalStateException("UI id has already been defined");
-        }
-        this.uiId = uiId;
-    }
-
     /**
      * Gets the id of the UI, used to identify this UI within its application
      * when processing requests. The UI id should be present in every request to
@@ -748,8 +725,8 @@ public abstract class UI extends AbstractComponentContainer implements
      * Adds a window as a subwindow inside this UI. To open a new browser window
      * or tab, you should instead use {@link open(Resource)} with an url
      * pointing to this application and ensure
-     * {@link Application#getUI(WrappedRequest)} returns an appropriate UI for
-     * the request.
+     * {@link Application#createUI(WrappedRequest)} returns an appropriate UI
+     * for the request.
      * 
      * @param window
      * @throws IllegalArgumentException
@@ -831,6 +808,8 @@ public abstract class UI extends AbstractComponentContainer implements
 
     private boolean resizeLazy = false;
 
+    private String theme;
+
     /**
      * This method is used by Component.Focusable objects to request focus to
      * themselves. Focus renders must be handled at window level (instead of
@@ -959,8 +938,16 @@ public abstract class UI extends AbstractComponentContainer implements
      * 
      * @param request
      *            the initialization request
+     * @param uiId
+     *            the id of the new ui
      */
-    public void doInit(WrappedRequest request) {
+    public void doInit(WrappedRequest request, int uiId) {
+        if (this.uiId != -1) {
+            throw new IllegalStateException("UI id has already been defined");
+        }
+        this.uiId = uiId;
+        theme = getApplication().getThemeForUI(request, getClass());
+
         getPage().init(request);
 
         // Call the init overridden by the application developer
@@ -1352,4 +1339,13 @@ public abstract class UI extends AbstractComponentContainer implements
     public void setLastUidlRequestTime(long lastUidlRequest) {
         this.lastUidlRequest = lastUidlRequest;
     }
+
+    /**
+     * Gets the theme that was used when the UI was initialized.
+     * 
+     * @return the theme name
+     */
+    public String getTheme() {
+        return theme;
+    }
 }
index 906d6ccebdc54dbe4cfee088d99d95050563bebf..89da55a31f163d29c01beaca8032ef9f505785d7 100644 (file)
@@ -10,7 +10,6 @@ import org.easymock.EasyMock;
 
 import com.vaadin.Application;
 import com.vaadin.Application.ApplicationStartEvent;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.server.DefaultUIProvider;
 import com.vaadin.server.DeploymentConfiguration;
 import com.vaadin.server.WrappedRequest;
@@ -56,8 +55,11 @@ public class CustomUIClassLoader extends TestCase {
         application.start(new ApplicationStartEvent(null,
                 createConfigurationMock(), null));
 
-        UI uI = application.getUIForRequest(createRequestMock(null));
-        assertTrue(uI instanceof MyUI);
+        DefaultUIProvider uiProvider = new DefaultUIProvider();
+        Class<? extends UI> uiClass = uiProvider.getUIClass(application,
+                createRequestMock(null));
+
+        assertEquals(MyUI.class, uiClass);
     }
 
     private static DeploymentConfiguration createConfigurationMock() {
@@ -101,9 +103,11 @@ public class CustomUIClassLoader extends TestCase {
         application.start(new ApplicationStartEvent(null,
                 createConfigurationMock(), null));
 
-        UI uI = application
-                .getUIForRequest(createRequestMock(loggingClassLoader));
-        assertTrue(uI instanceof MyUI);
+        DefaultUIProvider uiProvider = new DefaultUIProvider();
+        Class<? extends UI> uiClass = uiProvider.getUIClass(application,
+                createRequestMock(loggingClassLoader));
+
+        assertEquals(MyUI.class, uiClass);
         assertEquals(1, loggingClassLoader.requestedClasses.size());
         assertEquals(MyUI.class.getName(),
                 loggingClassLoader.requestedClasses.get(0));
@@ -112,10 +116,6 @@ public class CustomUIClassLoader extends TestCase {
 
     private Application createStubApplication() {
         return new Application() {
-            {
-                addUIProvider(new DefaultUIProvider());
-            }
-
             @Override
             public String getProperty(String name) {
                 if (name.equals(UI_PARAMETER)) {
@@ -124,14 +124,6 @@ public class CustomUIClassLoader extends TestCase {
                     return super.getProperty(name);
                 }
             }
-
-            @Override
-            public UI getUIForRequest(WrappedRequest request)
-                    throws UIRequiresMoreInformationException {
-                // Always create a new root for testing (can't directly use
-                // getRoot as it's protected)
-                return getUI(request);
-            }
         };
     }
 }
index e05979ede099ad43ac640103461167cb60ee7d2e..bceecaf35aa91436f15e56f0eea87ec373e4b066 100644 (file)
@@ -30,7 +30,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.server.AbstractApplicationServlet;
 import com.vaadin.server.AbstractUIProvider;
 import com.vaadin.server.WrappedHttpServletRequest;
@@ -116,8 +115,7 @@ public class ApplicationRunnerServlet extends AbstractApplicationServlet {
 
                     @Override
                     public Class<? extends UI> getUIClass(
-                            Application application, WrappedRequest request)
-                            throws UIRequiresMoreInformationException {
+                            Application application, WrappedRequest request) {
                         return (Class<? extends UI>) classToRun;
                     }
                 });
index 3013659ed78f8b619c217ec01a3c454c47681953..8962f5de9af9a6cbd7a9b22f560a5ef5839f8076 100644 (file)
@@ -1,7 +1,7 @@
 package com.vaadin.tests.application;
 
 import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
+import com.vaadin.annotations.PreserveOnRefresh;
 import com.vaadin.server.AbstractUIProvider;
 import com.vaadin.server.WrappedRequest;
 import com.vaadin.tests.components.AbstractTestApplication;
@@ -9,6 +9,7 @@ import com.vaadin.ui.Label;
 import com.vaadin.ui.UI;
 
 public class RefreshStatePreserve extends AbstractTestApplication {
+    @PreserveOnRefresh
     public static class RefreshStateUI extends UI {
         @Override
         public void init(WrappedRequest request) {
@@ -22,12 +23,10 @@ public class RefreshStatePreserve extends AbstractTestApplication {
     @Override
     public void init() {
         super.init();
-        setUiPreserved(true);
         addUIProvider(new AbstractUIProvider() {
             @Override
             public Class<? extends UI> getUIClass(Application application,
-                    WrappedRequest request)
-                    throws UIRequiresMoreInformationException {
+                    WrappedRequest request) {
                 return RefreshStateUI.class;
             }
         });
index 0e7dd1b242bc8363b99c0f15adb122a9d0c152cd..89cbf5d3ffb1ca7a4a6bea6b872bbb8d58a1012f 100644 (file)
@@ -1,9 +1,9 @@
 package com.vaadin.tests.application;
 
 import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.server.DownloadStream;
 import com.vaadin.server.PaintException;
+import com.vaadin.server.UIProvider;
 import com.vaadin.server.WrappedRequest;
 import com.vaadin.tests.components.AbstractTestApplication;
 import com.vaadin.tests.integration.FlagSeResource;
@@ -73,12 +73,19 @@ public class ThreadLocalInstances extends AbstractTestApplication {
     @Override
     public void init() {
         reportCurrentStatus("app init");
-    }
+        addUIProvider(new UIProvider() {
+            @Override
+            public UI instantiateUI(Application application,
+                    Class<? extends UI> type, WrappedRequest request) {
+                return mainWindow;
+            }
 
-    @Override
-    protected UI getUI(WrappedRequest request)
-            throws UIRequiresMoreInformationException {
-        return mainWindow;
+            @Override
+            public Class<? extends UI> getUIClass(Application application,
+                    WrappedRequest request) {
+                return mainWindow.getClass();
+            }
+        });
     }
 
     @Override
index ac4c3c8ea32ad6421161678ce37cee98947f6727..84c14763ab0baf5072984a0ef1f4be6d383bb835 100644 (file)
@@ -1,6 +1,7 @@
 package com.vaadin.tests.components.loginform;
 
 import com.vaadin.Application;
+import com.vaadin.server.AbstractUIProvider;
 import com.vaadin.server.WrappedRequest;
 import com.vaadin.ui.LoginForm;
 import com.vaadin.ui.LoginForm.LoginEvent;
@@ -12,11 +13,17 @@ import com.vaadin.ui.UI.LegacyWindow;
 public class LoginFormWithMultipleWindows extends Application {
 
     @Override
-    protected UI getUI(WrappedRequest request) {
-        return new LoginFormWindow();
+    public void init() {
+        addUIProvider(new AbstractUIProvider() {
+            @Override
+            public Class<? extends UI> getUIClass(Application application,
+                    WrappedRequest request) {
+                return LoginFormWindow.class;
+            }
+        });
     }
 
-    public class LoginFormWindow extends LegacyWindow {
+    public static class LoginFormWindow extends LegacyWindow {
         public LoginFormWindow() {
             super();
 
index 34bf8f67150167062aab470a00c76433563ab0d8..4cd786593ab2d05af4bfa00d5b630d8e9fdb948f 100644 (file)
@@ -1,10 +1,10 @@
 package com.vaadin.tests.components.ui;
 
-import com.vaadin.UIRequiresMoreInformationException;
+import com.vaadin.Application;
 import com.vaadin.annotations.EagerInit;
 import com.vaadin.server.ExternalResource;
+import com.vaadin.server.UIProvider;
 import com.vaadin.server.WrappedRequest;
-import com.vaadin.server.WrappedRequest.BrowserDetails;
 import com.vaadin.shared.ui.label.ContentMode;
 import com.vaadin.tests.components.AbstractTestApplication;
 import com.vaadin.ui.Label;
@@ -22,23 +22,33 @@ public class LazyInitUIs extends AbstractTestApplication {
     }
 
     @Override
-    public UI getUI(WrappedRequest request)
-            throws UIRequiresMoreInformationException {
+    public void init() {
+        addUIProvider(new UIProvider() {
+
+            @Override
+            public UI instantiateUI(Application application,
+                    Class<? extends UI> type, WrappedRequest request) {
+                return getUI(request);
+            }
+
+            @Override
+            public Class<? extends UI> getUIClass(Application application,
+                    WrappedRequest request) {
+                return getUI(request).getClass();
+            }
+        });
+    }
+
+    private UI getUI(WrappedRequest request) {
         if (request.getParameter("lazyCreate") != null) {
             // UI created on second request
-            BrowserDetails browserDetails = request.getBrowserDetails();
-            if (browserDetails == null
-                    || browserDetails.getUriFragment() == null) {
-                throw new UIRequiresMoreInformationException();
-            } else {
-                UI uI = new UI() {
-                    @Override
-                    protected void init(WrappedRequest request) {
-                        addComponent(getRequestInfo("LazyCreateUI", request));
-                    }
-                };
-                return uI;
-            }
+            UI uI = new UI() {
+                @Override
+                protected void init(WrappedRequest request) {
+                    addComponent(getRequestInfo("LazyCreateUI", request));
+                }
+            };
+            return uI;
         } else if (request.getParameter("eagerInit") != null) {
             // UI inited on first request
             return new EagerInitUI();
index 022db1bf3e5dae2e6018b968b0fa557110fff9be..fe2fe16d938ce9cbff3a20090ac4d44c4a46dfd5 100644 (file)
@@ -1,7 +1,6 @@
 package com.vaadin.tests.components.ui;
 
 import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
 import com.vaadin.server.AbstractUIProvider;
 import com.vaadin.server.WrappedRequest;
 import com.vaadin.tests.components.AbstractTestApplication;
@@ -26,8 +25,7 @@ public class UIsInMultipleTabs extends AbstractTestApplication {
         addUIProvider(new AbstractUIProvider() {
             @Override
             public Class<? extends UI> getUIClass(Application application,
-                    WrappedRequest request)
-                    throws UIRequiresMoreInformationException {
+                    WrappedRequest request) {
                 return TabUI.class;
             }
         });
index a96b8957c6feb3fbb4b3e5373388ef5d69e36a88..cfb24c732fcb7b79fad302380103f370a3914248 100644 (file)
 
 package com.vaadin.tests.minitutorials.v7a1;
 
+import com.vaadin.annotations.PreserveOnRefresh;
 import com.vaadin.server.WrappedRequest;
-import com.vaadin.ui.UI;
 import com.vaadin.ui.TextField;
+import com.vaadin.ui.UI;
 
 /**
  * Mini tutorial code for
@@ -28,6 +29,7 @@ import com.vaadin.ui.TextField;
  * @author Vaadin Ltd
  * @since 7.0.0
  */
+@PreserveOnRefresh
 public class CreatingPreserveState extends UI {
     private static int windowCounter = 0;
 
@@ -36,7 +38,6 @@ public class CreatingPreserveState extends UI {
         TextField tf = new TextField("Window #" + (++windowCounter));
         tf.setImmediate(true);
         getContent().addComponent(tf);
-        getApplication().setUiPreserved(true);
     }
 
 }
index ba3042c48e965a5b857198c2827378cf09fc036f..54cf8a94e07ccdb0f533ae926f15384852935292 100644 (file)
 package com.vaadin.tests.minitutorials.v7a1;
 
 import com.vaadin.Application;
-import com.vaadin.UIRequiresMoreInformationException;
+import com.vaadin.server.UIProvider;
 import com.vaadin.server.WebBrowser;
 import com.vaadin.server.WrappedRequest;
-import com.vaadin.server.WrappedRequest.BrowserDetails;
 import com.vaadin.ui.Label;
 import com.vaadin.ui.UI;
 
@@ -35,21 +34,32 @@ import com.vaadin.ui.UI;
 public class DifferentFeaturesForDifferentClients extends Application {
 
     @Override
-    protected UI getUI(WrappedRequest request)
-            throws UIRequiresMoreInformationException {
-        BrowserDetails browserDetails = request.getBrowserDetails();
-        // This is a limitation of 7.0.0.alpha1 that there is no better way to
-        // check if WebBrowser has been fully initialized
-        if (browserDetails.getUriFragment() == null) {
-            throw new UIRequiresMoreInformationException();
-        }
+    public void init() {
+        super.init();
+        addUIProvider(new UIProvider() {
+            @Override
+            public Class<? extends UI> getUIClass(Application application,
+                    WrappedRequest request) {
+                // could also use browser version etc.
+                if (request.getHeader("user-agent").contains("mobile")) {
+                    return TouchRoot.class;
+                } else {
+                    return DefaultRoot.class;
+                }
+            }
 
-        // could also use screen size, browser version etc.
-        if (browserDetails.getWebBrowser().isTouchDevice()) {
-            return new TouchRoot();
-        } else {
-            return new DefaultRoot();
-        }
+            // Must override as default implementation isn't allowed to
+            // instantiate our non-public classes
+            @Override
+            public UI instantiateUI(Application application,
+                    Class<? extends UI> type, WrappedRequest request) {
+                try {
+                    return type.newInstance();
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
     }
 }
 
index 9f3019b21c2fc06915dd955511f1f814fe19b951..1f50110330172d85dda3e58b5242bc1cae9b198b 100644 (file)
@@ -42,9 +42,8 @@ public class TestAddonContextListener implements AddonContextListener {
             }
 
             private boolean shouldModify(BootstrapResponse response) {
-                UI uI = response.getUI();
-                boolean shouldModify = uI != null
-                        && uI.getClass() == BootstrapModifyUI.class;
+                Class<? extends UI> uiClass = response.getUiClass();
+                boolean shouldModify = uiClass == BootstrapModifyUI.class;
                 return shouldModify;
             }