]> source.dussan.org Git - vaadin-framework.git/commitdiff
Move UI class info querying to UIProvider (#9402)
authorLeif Åstrand <leif@vaadin.com>
Tue, 4 Sep 2012 08:15:53 +0000 (11:15 +0300)
committerLeif Åstrand <leif@vaadin.com>
Wed, 5 Sep 2012 08:39:31 +0000 (11:39 +0300)
server/src/com/vaadin/Application.java
server/src/com/vaadin/server/AbstractUIProvider.java
server/src/com/vaadin/server/BootstrapHandler.java
server/src/com/vaadin/server/UIProvider.java
server/src/com/vaadin/ui/UI.java
uitest/src/com/vaadin/tests/application/ThreadLocalInstances.java
uitest/src/com/vaadin/tests/components/ui/LazyInitUIs.java
uitest/src/com/vaadin/tests/minitutorials/v7a1/DifferentFeaturesForDifferentClients.java

index 9498534b4a598974ae8e602f5e2fcb26547511fa..36a8db83a1371e9f5093327b5a423483903f2fec 100644 (file)
@@ -18,7 +18,6 @@ package com.vaadin;
 
 import java.io.IOException;
 import java.io.Serializable;
-import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 import java.net.SocketException;
 import java.net.URL;
@@ -40,15 +39,12 @@ import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-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;
 import com.vaadin.data.util.converter.DefaultConverterFactory;
 import com.vaadin.event.EventRouter;
 import com.vaadin.server.AbstractErrorMessage;
+import com.vaadin.server.AbstractUIProvider;
 import com.vaadin.server.ApplicationContext;
 import com.vaadin.server.BootstrapFragmentResponse;
 import com.vaadin.server.BootstrapListener;
@@ -193,6 +189,47 @@ public class Application implements Terminal.ErrorListener, Serializable {
             this.mainWindow = mainWindow;
         }
 
+        @Override
+        public void start(ApplicationStartEvent event) {
+            super.start(event);
+            addUIProvider(new AbstractUIProvider() {
+                @Override
+                public Class<? extends UI> getUIClass(Application application,
+                        WrappedRequest request) {
+                    if (application == LegacyApplication.this) {
+                        UI uiInstance = getUIInstance(request);
+                        if (uiInstance != null) {
+                            return uiInstance.getClass();
+                        }
+                    }
+                    return null;
+                }
+
+                @Override
+                public UI createInstance(Application application,
+                        Class<? extends UI> type, WrappedRequest request) {
+                    return getUIInstance(request);
+                }
+
+                @Override
+                public String getThemeForUI(WrappedRequest request,
+                        Class<? extends UI> uiClass) {
+                    return theme;
+                }
+
+                @Override
+                public String getPageTitleForUI(WrappedRequest request,
+                        Class<? extends UI> uiClass) {
+                    UI uiInstance = getUIInstance(request);
+                    if (uiInstance != null) {
+                        return uiInstance.getCaption();
+                    } else {
+                        return super.getPageTitleForUI(request, uiClass);
+                    }
+                }
+            });
+        }
+
         /**
          * Gets the mainWindow of the application.
          * 
@@ -210,19 +247,6 @@ 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
-        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;
@@ -260,19 +284,6 @@ 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}
-         */
-        @Override
-        public Class<? extends UI> getUIClass(WrappedRequest request) {
-            return getUIInstance(request).getClass();
-        }
-
         /**
          * Sets the application's theme.
          * <p>
@@ -301,18 +312,6 @@ public class Application implements Terminal.ErrorListener, Serializable {
             return theme;
         }
 
-        /**
-         * This implementation returns the theme that has been set using
-         * {@link #setTheme(String)}
-         * <p>
-         * {@inheritDoc}
-         */
-        @Override
-        public String getThemeForUI(WrappedRequest request,
-                Class<? extends UI> uiClass) {
-            return theme;
-        }
-
         /**
          * <p>
          * Gets a UI by name. Returns <code>null</code> if the application is
@@ -1601,23 +1600,8 @@ public class Application implements Terminal.ErrorListener, Serializable {
      * @since 7.0
      */
     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 uiClass;
-            }
-        }
-
-        throw new RuntimeException(
-                "No UI provider returned an UI class for request");
+        UIProvider uiProvider = getUiProvider(request, null);
+        return uiProvider.getUIClass(this, request);
     }
 
     /**
@@ -1639,6 +1623,59 @@ public class Application implements Terminal.ErrorListener, Serializable {
      */
     protected <T extends UI> T createUIInstance(WrappedRequest request,
             Class<T> uiClass) {
+        UIProvider uiProvider = getUiProvider(request, uiClass);
+        return uiClass.cast(uiProvider.createInstance(this, uiClass, request));
+    }
+
+    /**
+     * Gets the {@link UIProvider} that should be used for a request. The
+     * selection can further be restricted by also requiring the UI provider to
+     * support a specific UI class.
+     * 
+     * @see UIProvider
+     * @see #addUIProvider(UIProvider)
+     * 
+     * @param request
+     *            the request for which to get an UI provider
+     * @param uiClass
+     *            the UI class for which a provider is required, or
+     *            <code>null</code> to use the first UI provider supporting the
+     *            request.
+     * @return an UI provider supporting the request (and the UI class if
+     *         provided).
+     * 
+     * @since 7.0.0
+     */
+    public UIProvider getUiProvider(WrappedRequest request, Class<?> uiClass) {
+        UIProvider provider = (UIProvider) request
+                .getAttribute(UIProvider.class.getName());
+        if (provider != null) {
+            // Cached provider found, verify that it's a sensible selection
+            Class<? extends UI> providerClass = provider.getUIClass(this,
+                    request);
+            if (uiClass == null && providerClass != null) {
+                // Use it if it gives any answer if no specific class is
+                // required
+                return provider;
+            } else if (uiClass == providerClass) {
+                // Use it if it gives the expected UI class
+                return provider;
+            } else {
+                // Don't keep it cached if it doesn't match the expectations
+                request.setAttribute(UIProvider.class.getName(), null);
+            }
+        }
+
+        // Iterate all current providers if no matching cached provider found
+        provider = doGetUiProvider(request, uiClass);
+
+        // Cache the found provider
+        request.setAttribute(UIProvider.class.getName(), provider);
+
+        return provider;
+    }
+
+    private UIProvider doGetUiProvider(WrappedRequest request, Class<?> uiClass) {
         int providersSize = uiProviders.size();
         if (providersSize == 0) {
             throw new IllegalStateException("There are no UI providers");
@@ -1649,108 +1686,25 @@ public class Application implements Terminal.ErrorListener, Serializable {
 
             Class<? extends UI> providerClass = provider.getUIClass(this,
                     request);
+            // If we found something
             if (providerClass != null) {
-                if (providerClass != uiClass) {
+                if (uiClass == null) {
+                    // Not looking for anything particular -> anything is ok
+                    return provider;
+                } else if (providerClass == uiClass) {
+                    // Looking for a specific provider -> only use if matching
+                    return provider;
+                } else {
                     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;
+                    // Continue looking
                 }
-                return uiClass.cast(provider.createInstance(this, uiClass,
-                        request));
             }
         }
 
-        throw new RuntimeException(
-                "No UI provider created an UI instance for request");
-    }
-
-    /**
-     * Finds the theme to use for a specific UI. If no specific theme is
-     * required, <code>null</code> is returned.
-     * 
-     * TODO Tell what the default implementation does once it does something.
-     * 
-     * @param uI
-     *            the UI to get a theme for
-     * @return the name of the theme, or <code>null</code> if the default theme
-     *         should be used
-     * 
-     * @since 7.0
-     */
-    public String getThemeForUI(WrappedRequest request,
-            Class<? extends UI> uiClass) {
-        Theme uiTheme = getAnnotationFor(uiClass, Theme.class);
-        if (uiTheme != null) {
-            return uiTheme.value();
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * 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.
-     * 
-     * @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(WrappedRequest request,
-            Class<? extends UI> uiClass) {
-        Widgetset uiWidgetset = getAnnotationFor(uiClass, Widgetset.class);
-        if (uiWidgetset != null) {
-            return uiWidgetset.value();
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Helper to get an annotation for a class. If the annotation is not present
-     * on the target class, it's superclasses and implemented interfaces are
-     * also searched for the annotation.
-     * 
-     * @param type
-     *            the target class from which the annotation should be found
-     * @param annotationType
-     *            the annotation type to look for
-     * @return an annotation of the given type, or <code>null</code> if the
-     *         annotation is not present on the class
-     */
-    private static <T extends Annotation> T getAnnotationFor(Class<?> type,
-            Class<T> annotationType) {
-        // Find from the class hierarchy
-        Class<?> currentType = type;
-        while (currentType != Object.class) {
-            T annotation = currentType.getAnnotation(annotationType);
-            if (annotation != null) {
-                return annotation;
-            } else {
-                currentType = currentType.getSuperclass();
-            }
-        }
-
-        // Find from an implemented interface
-        for (Class<?> iface : type.getInterfaces()) {
-            T annotation = iface.getAnnotation(annotationType);
-            if (annotation != null) {
-                return annotation;
-            }
-        }
-
-        return null;
+        throw new RuntimeException("No UI provider found for request");
     }
 
     /**
@@ -1999,7 +1953,7 @@ public class Application implements Terminal.ErrorListener, Serializable {
 
         ui.doInit(request, uiId.intValue());
 
-        if (isUiPreserved(request, uiClass)) {
+        if (getUiProvider(request, uiClass).isUiPreserved(request, uiClass)) {
             // Remember this UI
             String windowName = request.getBrowserDetails().getWindowName();
             if (windowName == null) {
@@ -2034,25 +1988,6 @@ public class Application implements Terminal.ErrorListener, Serializable {
         return uiId;
     }
 
-    /**
-     * 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(WrappedRequest request,
-            Class<? extends UI> uiClass) {
-        PreserveOnRefresh preserveOnRefresh = getAnnotationFor(uiClass,
-                PreserveOnRefresh.class);
-        return preserveOnRefresh != null;
-    }
-
     /**
      * Gets all the uIs of this application. This includes uIs that have been
      * requested but not yet initialized. Please note, that uIs are not
@@ -2270,13 +2205,4 @@ 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();
-        }
-    }
 }
index 59ce31891d19b30ce5294834d6fc4a36e1b3a0ac..49f8e3ec778287b61e816c9e25c7fca5289b291c 100644 (file)
 
 package com.vaadin.server;
 
+import java.lang.annotation.Annotation;
+
 import com.vaadin.Application;
+import com.vaadin.annotations.PreserveOnRefresh;
+import com.vaadin.annotations.Theme;
+import com.vaadin.annotations.Title;
+import com.vaadin.annotations.Widgetset;
 import com.vaadin.ui.UI;
 
 public abstract class AbstractUIProvider implements UIProvider {
@@ -32,4 +38,81 @@ public abstract class AbstractUIProvider implements UIProvider {
             throw new RuntimeException("Could not access root class", e);
         }
     }
+
+    /**
+     * Helper to get an annotation for a class. If the annotation is not present
+     * on the target class, it's superclasses and implemented interfaces are
+     * also searched for the annotation.
+     * 
+     * @param type
+     *            the target class from which the annotation should be found
+     * @param annotationType
+     *            the annotation type to look for
+     * @return an annotation of the given type, or <code>null</code> if the
+     *         annotation is not present on the class
+     */
+    protected static <T extends Annotation> T getAnnotationFor(Class<?> type,
+            Class<T> annotationType) {
+        // Find from the class hierarchy
+        Class<?> currentType = type;
+        while (currentType != Object.class) {
+            T annotation = currentType.getAnnotation(annotationType);
+            if (annotation != null) {
+                return annotation;
+            } else {
+                currentType = currentType.getSuperclass();
+            }
+        }
+
+        // Find from an implemented interface
+        for (Class<?> iface : type.getInterfaces()) {
+            T annotation = iface.getAnnotation(annotationType);
+            if (annotation != null) {
+                return annotation;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public String getThemeForUI(WrappedRequest request,
+            Class<? extends UI> uiClass) {
+        Theme uiTheme = getAnnotationFor(uiClass, Theme.class);
+        if (uiTheme != null) {
+            return uiTheme.value();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String getWidgetsetForUI(WrappedRequest request,
+            Class<? extends UI> uiClass) {
+        Widgetset uiWidgetset = getAnnotationFor(uiClass, Widgetset.class);
+        if (uiWidgetset != null) {
+            return uiWidgetset.value();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean isUiPreserved(WrappedRequest request,
+            Class<? extends UI> uiClass) {
+        PreserveOnRefresh preserveOnRefresh = getAnnotationFor(uiClass,
+                PreserveOnRefresh.class);
+        return preserveOnRefresh != null;
+    }
+
+    @Override
+    public String getPageTitleForUI(WrappedRequest request,
+            Class<? extends UI> uiClass) {
+        Title titleAnnotation = getAnnotationFor(uiClass, Title.class);
+        if (titleAnnotation == null) {
+            return null;
+        } else {
+            return titleAnnotation.value();
+        }
+    }
 }
index a1438312b6cde261ce6bc9ca4c02747758dfdd0d..4d200b50634c3fda638550f30c05b1ae117138a8 100644 (file)
@@ -218,8 +218,9 @@ public abstract class BootstrapHandler implements RequestHandler {
         head.appendElement("meta").attr("http-equiv", "X-UA-Compatible")
                 .attr("content", "chrome=1");
 
-        String title = context.getApplication().getPageTitleForUI(
-                context.getRequest(), context.getUIClass());
+        String title = context.getApplication()
+                .getUiProvider(context.getRequest(), context.getUIClass())
+                .getPageTitleForUI(context.getRequest(), context.getUIClass());
         if (title != null) {
             head.appendElement("title").appendText(title);
         }
@@ -270,8 +271,9 @@ public abstract class BootstrapHandler implements RequestHandler {
     public String getWidgetsetForUI(BootstrapContext context) {
         WrappedRequest request = context.getRequest();
 
-        String widgetset = context.getApplication().getWidgetsetForUI(
-                context.getRequest(), context.getUIClass());
+        String widgetset = context.getApplication()
+                .getUiProvider(context.getRequest(), context.getUIClass())
+                .getWidgetsetForUI(context.getRequest(), context.getUIClass());
         if (widgetset == null) {
             widgetset = request.getDeploymentConfiguration()
                     .getConfiguredWidgetset(request);
@@ -497,8 +499,9 @@ public abstract class BootstrapHandler implements RequestHandler {
      * @return
      */
     public String getThemeName(BootstrapContext context) {
-        return context.getApplication().getThemeForUI(context.getRequest(),
-                context.getUIClass());
+        return context.getApplication()
+                .getUiProvider(context.getRequest(), context.getUIClass())
+                .getThemeForUI(context.getRequest(), context.getUIClass());
     }
 
     /**
index ea73a705ea2bebf300e930b010c5cff0a9aafc96..60b79cdbb99602a8d2656328e10d374584de95f7 100644 (file)
@@ -17,6 +17,7 @@
 package com.vaadin.server;
 
 import com.vaadin.Application;
+import com.vaadin.annotations.Widgetset;
 import com.vaadin.ui.UI;
 
 public interface UIProvider {
@@ -25,4 +26,56 @@ public interface UIProvider {
 
     public UI createInstance(Application application, Class<? extends UI> type,
             WrappedRequest request);
+
+    public String getPageTitleForUI(WrappedRequest request,
+            Class<? extends UI> uiClass);
+
+    /**
+     * 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(WrappedRequest request,
+            Class<? extends UI> uiClass);
+
+    /**
+     * 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.
+     * 
+     * @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
+     * 
+     */
+    public String getWidgetsetForUI(WrappedRequest request,
+            Class<? extends UI> uiClass);
+
+    /**
+     * Finds the theme to use for a specific UI. If no specific theme is
+     * required, <code>null</code> is returned.
+     * 
+     * TODO Tell what the default implementation does once it does something.
+     * 
+     * @param uI
+     *            the UI to get a theme for
+     * @return the name of the theme, or <code>null</code> if the default theme
+     *         should be used
+     * 
+     */
+    public String getThemeForUI(WrappedRequest request,
+            Class<? extends UI> uiClass);
+
 }
index ee4cb9fd2cedcbe958274784ef35c8ff1557fbb8..7ae4e6bda34450e92c7f6439fe108cb50470f968 100644 (file)
@@ -940,7 +940,8 @@ public abstract class UI extends AbstractComponentContainer implements
             throw new IllegalStateException("UI id has already been defined");
         }
         this.uiId = uiId;
-        theme = getApplication().getThemeForUI(request, getClass());
+        theme = getApplication().getUiProvider(request, getClass())
+                .getThemeForUI(request, getClass());
 
         getPage().init(request);
 
index 0f576a0f6941c9e74fa161f019cd78ce6afe267b..bad5b53478197d72ce0d1ba82ed4b799368653a5 100644 (file)
@@ -1,9 +1,9 @@
 package com.vaadin.tests.application;
 
 import com.vaadin.Application;
+import com.vaadin.server.AbstractUIProvider;
 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,7 +73,7 @@ public class ThreadLocalInstances extends AbstractTestApplication {
     @Override
     public void init() {
         reportCurrentStatus("app init");
-        addUIProvider(new UIProvider() {
+        addUIProvider(new AbstractUIProvider() {
             @Override
             public UI createInstance(Application application,
                     Class<? extends UI> type, WrappedRequest request) {
index 3980cbd4deec4d50c4f8b466324b60f2bdef713a..f33037f171b7e02fc5ab787bff84a98dc2c91bc9 100644 (file)
@@ -1,8 +1,8 @@
 package com.vaadin.tests.components.ui;
 
 import com.vaadin.Application;
+import com.vaadin.server.AbstractUIProvider;
 import com.vaadin.server.ExternalResource;
-import com.vaadin.server.UIProvider;
 import com.vaadin.server.WrappedRequest;
 import com.vaadin.shared.ui.label.ContentMode;
 import com.vaadin.tests.components.AbstractTestApplication;
@@ -22,7 +22,7 @@ public class LazyInitUIs extends AbstractTestApplication {
 
     @Override
     public void init() {
-        addUIProvider(new UIProvider() {
+        addUIProvider(new AbstractUIProvider() {
 
             @Override
             public UI createInstance(Application application,
index 9e4c71983076e8de0e9faadfc7735d4500c18d3f..8c2a816e1cd90b0e7a6da2477ab51642f912e861 100644 (file)
@@ -17,7 +17,7 @@
 package com.vaadin.tests.minitutorials.v7a1;
 
 import com.vaadin.Application;
-import com.vaadin.server.UIProvider;
+import com.vaadin.server.AbstractUIProvider;
 import com.vaadin.server.WebBrowser;
 import com.vaadin.server.WrappedRequest;
 import com.vaadin.ui.Label;
@@ -36,7 +36,7 @@ public class DifferentFeaturesForDifferentClients extends Application {
     @Override
     public void init() {
         super.init();
-        addUIProvider(new UIProvider() {
+        addUIProvider(new AbstractUIProvider() {
             @Override
             public Class<? extends UI> getUIClass(Application application,
                     WrappedRequest request) {