aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/Application.java
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/com/vaadin/Application.java')
-rw-r--r--server/src/com/vaadin/Application.java341
1 files changed, 183 insertions, 158 deletions
diff --git a/server/src/com/vaadin/Application.java b/server/src/com/vaadin/Application.java
index fb2691c6d3..b1a9ae1d26 100644
--- a/server/src/com/vaadin/Application.java
+++ b/server/src/com/vaadin/Application.java
@@ -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) {
@@ -236,6 +237,19 @@ 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>
* Note that this theme can be overridden for a specific UI with
@@ -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;
}
/**
@@ -2003,53 +2043,22 @@ public class Application implements Terminal.ErrorListener, Serializable {
}
/**
- * 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;
+ }
}