diff options
-rw-r--r-- | WebContent/WEB-INF/web.xml | 2 | ||||
-rw-r--r-- | server/src/com/vaadin/server/AbstractCommunicationManager.java | 98 | ||||
-rw-r--r-- | server/src/com/vaadin/server/BootstrapFragmentResponse.java | 7 | ||||
-rw-r--r-- | server/src/com/vaadin/server/BootstrapHandler.java | 65 | ||||
-rw-r--r-- | server/src/com/vaadin/server/BootstrapPageResponse.java | 6 | ||||
-rw-r--r-- | server/src/com/vaadin/server/BootstrapResponse.java | 17 | ||||
-rw-r--r-- | server/src/com/vaadin/server/CommunicationManager.java | 11 | ||||
-rw-r--r-- | server/src/com/vaadin/server/LegacyVaadinPortlet.java | 2 | ||||
-rw-r--r-- | server/src/com/vaadin/server/LegacyVaadinServlet.java | 2 | ||||
-rw-r--r-- | server/src/com/vaadin/server/ServletPortletHelper.java | 8 | ||||
-rw-r--r-- | server/src/com/vaadin/server/VaadinPortlet.java | 45 | ||||
-rw-r--r-- | server/src/com/vaadin/server/VaadinService.java | 196 | ||||
-rw-r--r-- | server/src/com/vaadin/server/VaadinServlet.java | 9 | ||||
-rw-r--r-- | server/src/com/vaadin/server/VaadinSession.java | 422 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/UI.java | 28 | ||||
-rw-r--r-- | uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java | 22 |
16 files changed, 513 insertions, 427 deletions
diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml index d752b8eb33..d8a8066edc 100644 --- a/WebContent/WEB-INF/web.xml +++ b/WebContent/WEB-INF/web.xml @@ -26,7 +26,7 @@ <servlet-name>Embed App 2</servlet-name> <servlet-class>com.vaadin.server.VaadinServlet</servlet-class> <init-param> - <param-name>ui</param-name> + <param-name>UI</param-name> <param-value>com.vaadin.tests.components.label.MarginsInLabels</param-value> </init-param> </servlet> diff --git a/server/src/com/vaadin/server/AbstractCommunicationManager.java b/server/src/com/vaadin/server/AbstractCommunicationManager.java index 49243a0589..eaef7485b3 100644 --- a/server/src/com/vaadin/server/AbstractCommunicationManager.java +++ b/server/src/com/vaadin/server/AbstractCommunicationManager.java @@ -68,6 +68,7 @@ import com.vaadin.server.StreamVariable.StreamingEndEvent; import com.vaadin.server.StreamVariable.StreamingErrorEvent; import com.vaadin.server.Terminal.ErrorEvent; import com.vaadin.server.Terminal.ErrorListener; +import com.vaadin.server.VaadinRequest.BrowserDetails; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.Connector; import com.vaadin.shared.JavaScriptConnectorState; @@ -2432,10 +2433,7 @@ public abstract class AbstractCommunicationManager implements Serializable { response.setContentType("application/json; charset=UTF-8"); - UI uI = session.getUIForRequest(combinedRequest); - if (uI == null) { - uI = session.createUI(combinedRequest); - } + UI uI = getBrowserDetailsUI(combinedRequest); JSONObject params = new JSONObject(); params.put(UIConstants.UI_ID_PARAMETER, uI.getUIId()); @@ -2461,6 +2459,98 @@ public abstract class AbstractCommunicationManager implements Serializable { } } + private UI getBrowserDetailsUI(VaadinRequest request) { + VaadinService vaadinService = request.getVaadinService(); + VaadinSession session = VaadinSession.getForSession(request + .getWrappedSession()); + + List<UIProvider> uiProviders = vaadinService.getUIProviders(session); + + UIProvider provider = null; + Class<? extends UI> uiClass = null; + for (UIProvider p : uiProviders) { + // Check if some UI provider has an existing UI available + UI existingUi = p.getExistingUI(request); + if (existingUi != null) { + UI.setCurrent(existingUi); + return existingUi; + } + + uiClass = p.getUIClass(request); + if (uiClass != null) { + provider = p; + break; + } + } + + if (provider == null || uiClass == null) { + return null; + } + + // Check for an existing UI based on window.name + BrowserDetails browserDetails = request.getBrowserDetails(); + boolean hasBrowserDetails = browserDetails != null + && browserDetails.getUriFragment() != null; + + Map<String, Integer> retainOnRefreshUIs = session + .getPreserveOnRefreshUIs(); + if (hasBrowserDetails && !retainOnRefreshUIs.isEmpty()) { + // Check for a known UI + + @SuppressWarnings("null") + String windowName = browserDetails.getWindowName(); + Integer retainedUIId = retainOnRefreshUIs.get(windowName); + + if (retainedUIId != null) { + UI retainedUI = session.getUIById(retainedUIId.intValue()); + if (uiClass.isInstance(retainedUI)) { + return 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."); + } + } + } + + // No existing UI found - go on by creating and initializing one + + // Explicit Class.cast to detect if the UIProvider does something + // unexpected + UI ui = uiClass.cast(provider.createInstance(request, uiClass)); + + // Initialize some fields for a newly created UI + if (ui.getSession() != session) { + // Session already set for LegacyWindow + ui.setSession(session); + } + Integer uiId = Integer.valueOf(session.getNextUIid()); + + // Set thread local here so it is available in init + UI.setCurrent(ui); + + ui.doInit(request, uiId.intValue()); + + session.addUI(ui); + + // Remember if it should be remembered + if (vaadinService.preserveUIOnRefresh(request, ui, provider)) { + // 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 { + session.getPreserveOnRefreshUIs().put(windowName, uiId); + } + } + + return ui; + } + /** * Generates the initial UIDL message that can e.g. be included in a html * page to avoid a separate round trip just for getting the UIDL. diff --git a/server/src/com/vaadin/server/BootstrapFragmentResponse.java b/server/src/com/vaadin/server/BootstrapFragmentResponse.java index 0269d1cfc0..8fc3183a7c 100644 --- a/server/src/com/vaadin/server/BootstrapFragmentResponse.java +++ b/server/src/com/vaadin/server/BootstrapFragmentResponse.java @@ -52,11 +52,14 @@ public class BootstrapFragmentResponse extends BootstrapResponse { * @param fragmentNodes * a mutable list containing the DOM nodes that will make up the * application HTML + * @param uiProvider + * the UI provider for the bootstrap */ public BootstrapFragmentResponse(BootstrapHandler handler, VaadinRequest request, VaadinSession session, - Class<? extends UI> uiClass, List<Node> fragmentNodes) { - super(handler, request, session, uiClass); + Class<? extends UI> uiClass, List<Node> fragmentNodes, + UIProvider uiProvider) { + super(handler, request, session, uiClass, uiProvider); this.fragmentNodes = fragmentNodes; } diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index 7e4fd653bf..8721550a41 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Serializable; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -115,10 +116,30 @@ public abstract class BootstrapHandler implements RequestHandler { VaadinResponse response) throws IOException { try { - Class<? extends UI> uiClass = session.getUIClass(request); + List<UIProvider> uiProviders = request.getVaadinService() + .getUIProviders(session); + + // Find UI provider and UI class + Class<? extends UI> uiClass = null; + UIProvider provider = null; + for (UIProvider p : uiProviders) { + uiClass = p.getUIClass(request); + // If we found something + if (uiClass != null) { + provider = p; + break; + } + } + + if (provider == null) { + // Can't generate bootstrap if no UI provider matches + return false; + } + + BootstrapContext context = new BootstrapContext(response, + new BootstrapFragmentResponse(this, request, session, + uiClass, new ArrayList<Node>(), provider)); - BootstrapContext context = createContext(request, response, - session, uiClass); setupMainDiv(context); BootstrapFragmentResponse fragmentResponse = context @@ -148,7 +169,8 @@ public abstract class BootstrapHandler implements RequestHandler { Document document = Document.createShell(""); BootstrapPageResponse pageResponse = new BootstrapPageResponse( this, request, context.getVaadinSession(), - context.getUIClass(), document, headers); + context.getUIClass(), document, headers, + fragmentResponse.getUIProvider()); List<Node> fragmentNodes = fragmentResponse.getFragmentNodes(); Element body = document.body(); for (Node node : fragmentNodes) { @@ -223,9 +245,8 @@ public abstract class BootstrapHandler implements RequestHandler { head.appendElement("meta").attr("http-equiv", "X-UA-Compatible") .attr("content", "chrome=1"); - String title = context.getVaadinSession() - .getUiProvider(context.getRequest(), context.getUIClass()) - .getPageTitle(context.getRequest(), context.getUIClass()); + String title = response.getUIProvider().getPageTitle( + context.getRequest(), context.getUIClass()); if (title != null) { head.appendElement("title").appendText(title); } @@ -250,15 +271,6 @@ public abstract class BootstrapHandler implements RequestHandler { body.addClass(ApplicationConstants.GENERATED_BODY_CLASSNAME); } - private BootstrapContext createContext(VaadinRequest request, - VaadinResponse response, VaadinSession application, - Class<? extends UI> uiClass) { - BootstrapContext context = new BootstrapContext(response, - new BootstrapFragmentResponse(this, request, application, - uiClass, new ArrayList<Node>())); - return context; - } - protected String getMainDivStyle(BootstrapContext context) { return null; } @@ -276,8 +288,7 @@ public abstract class BootstrapHandler implements RequestHandler { public String getWidgetsetForUI(BootstrapContext context) { VaadinRequest request = context.getRequest(); - String widgetset = context.getVaadinSession() - .getUiProvider(context.getRequest(), context.getUIClass()) + String widgetset = context.getBootstrapResponse().getUIProvider() .getWidgetset(context.getRequest(), context.getUIClass()); if (widgetset == null) { widgetset = request.getVaadinService().getConfiguredWidgetset( @@ -399,9 +410,9 @@ public abstract class BootstrapHandler implements RequestHandler { throws JSONException, PaintException { JSONObject appConfig = new JSONObject(); - if (context.getThemeName() != null) { - appConfig.put("themeUri", - getThemeUri(context, context.getThemeName())); + String themeName = context.getThemeName(); + if (themeName != null) { + appConfig.put("themeUri", getThemeUri(context, themeName)); } JSONObject versionInfo = new JSONObject(); @@ -412,8 +423,13 @@ public abstract class BootstrapHandler implements RequestHandler { appConfig.put("initialPath", context.getRequest().getRequestPathInfo()); - Map<String, String[]> parameterMap = context.getRequest() - .getParameterMap(); + Map<String, String[]> parameterMap = new HashMap<String, String[]>( + context.getRequest().getParameterMap()); + + // Include theme as a fake initial param so that UI can know its theme + // for serving CustomLayout templates + parameterMap.put("theme", new String[] { themeName }); + appConfig.put("initialParams", parameterMap); return appConfig; @@ -500,8 +516,7 @@ public abstract class BootstrapHandler implements RequestHandler { * @return */ public String getThemeName(BootstrapContext context) { - return context.getVaadinSession() - .getUiProvider(context.getRequest(), context.getUIClass()) + return context.getBootstrapResponse().getUIProvider() .getTheme(context.getRequest(), context.getUIClass()); } diff --git a/server/src/com/vaadin/server/BootstrapPageResponse.java b/server/src/com/vaadin/server/BootstrapPageResponse.java index 72bc2163e6..8a85765e97 100644 --- a/server/src/com/vaadin/server/BootstrapPageResponse.java +++ b/server/src/com/vaadin/server/BootstrapPageResponse.java @@ -55,12 +55,14 @@ public class BootstrapPageResponse extends BootstrapResponse { * the DOM document making up the HTML page * @param headers * a map into which header data can be added + * @param uiProvider + * the UI provider for the bootstrap */ public BootstrapPageResponse(BootstrapHandler handler, VaadinRequest request, VaadinSession session, Class<? extends UI> uiClass, Document document, - Map<String, Object> headers) { - super(handler, request, session, uiClass); + Map<String, Object> headers, UIProvider uiProvider) { + super(handler, request, session, uiClass, uiProvider); this.headers = headers; this.document = document; } diff --git a/server/src/com/vaadin/server/BootstrapResponse.java b/server/src/com/vaadin/server/BootstrapResponse.java index 86b37de22e..c6e5fdc2ad 100644 --- a/server/src/com/vaadin/server/BootstrapResponse.java +++ b/server/src/com/vaadin/server/BootstrapResponse.java @@ -31,6 +31,7 @@ public abstract class BootstrapResponse extends EventObject { private final VaadinRequest request; private final VaadinSession session; private final Class<? extends UI> uiClass; + private final UIProvider uiProvider; /** * Creates a new bootstrap event. @@ -44,13 +45,17 @@ public abstract class BootstrapResponse extends EventObject { * the session for which the bootstrap page should be generated * @param uiClass * the class of the UI that will be displayed on the page + * @param uiProvider + * the UI provider for the bootstrap */ public BootstrapResponse(BootstrapHandler handler, VaadinRequest request, - VaadinSession session, Class<? extends UI> uiClass) { + VaadinSession session, Class<? extends UI> uiClass, + UIProvider uiProvider) { super(handler); this.request = request; this.session = session; this.uiClass = uiClass; + this.uiProvider = uiProvider; } /** @@ -95,4 +100,14 @@ public abstract class BootstrapResponse extends EventObject { return uiClass; } + /** + * Gets the UI provider that is used to provide information about the + * bootstapped UI. + * + * @return the UI provider + */ + public UIProvider getUIProvider() { + return uiProvider; + } + } diff --git a/server/src/com/vaadin/server/CommunicationManager.java b/server/src/com/vaadin/server/CommunicationManager.java index e4029a847a..7e55396592 100644 --- a/server/src/com/vaadin/server/CommunicationManager.java +++ b/server/src/com/vaadin/server/CommunicationManager.java @@ -17,6 +17,7 @@ package com.vaadin.server; import java.io.InputStream; +import java.net.MalformedURLException; import java.net.URL; import javax.servlet.ServletContext; @@ -79,8 +80,14 @@ public class CommunicationManager extends AbstractCommunicationManager { // don't use server and port in uri. It may cause problems with // some // virtual server configurations which lose the server name - VaadinSession session = context.getVaadinSession(); - URL url = session.getURL(); + URL url; + + try { + url = context.getRequest().getVaadinService() + .getApplicationUrl(context.getRequest()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } String appUrl = url.getPath(); if (appUrl.endsWith("/")) { appUrl = appUrl.substring(0, appUrl.length() - 1); diff --git a/server/src/com/vaadin/server/LegacyVaadinPortlet.java b/server/src/com/vaadin/server/LegacyVaadinPortlet.java index de970d5c17..113cae6bf7 100644 --- a/server/src/com/vaadin/server/LegacyVaadinPortlet.java +++ b/server/src/com/vaadin/server/LegacyVaadinPortlet.java @@ -87,7 +87,7 @@ public class LegacyVaadinPortlet extends VaadinPortlet { private void onVaadinSessionStarted(VaadinPortletRequest request, VaadinPortletSession session) throws PortletException { - session.addUIProvider(provider); + getVaadinService().addUIProvider(session, provider); } protected boolean shouldCreateApplication(PortletRequest request) { diff --git a/server/src/com/vaadin/server/LegacyVaadinServlet.java b/server/src/com/vaadin/server/LegacyVaadinServlet.java index 69655eba34..18911dc96e 100644 --- a/server/src/com/vaadin/server/LegacyVaadinServlet.java +++ b/server/src/com/vaadin/server/LegacyVaadinServlet.java @@ -92,7 +92,7 @@ public class LegacyVaadinServlet extends VaadinServlet { private void onVaadinSessionStarted(VaadinRequest request, VaadinSession session) throws ServletException { - session.addUIProvider(provider); + getVaadinService().addUIProvider(session, provider); } } diff --git a/server/src/com/vaadin/server/ServletPortletHelper.java b/server/src/com/vaadin/server/ServletPortletHelper.java index efa3c62f6e..45c91d1970 100644 --- a/server/src/com/vaadin/server/ServletPortletHelper.java +++ b/server/src/com/vaadin/server/ServletPortletHelper.java @@ -123,13 +123,13 @@ class ServletPortletHelper implements Serializable { .getInitParameters().getProperty(VaadinSession.UI_PARAMETER); if (uiProperty != null) { verifyUIClass(uiProperty, vaadinService.getClassLoader()); - session.addUIProvider(new DefaultUIProvider()); + vaadinService.addUIProvider(session, new DefaultUIProvider()); } } - public static void checkUiProviders(VaadinSession session) - throws ServiceException { - if (session.getUIProviders().isEmpty()) { + public static void checkUiProviders(VaadinSession session, + VaadinService vaadinService) throws ServiceException { + if (vaadinService.getUIProviders(session).isEmpty()) { throw new ServiceException( "No UIProvider has been added and there is no \"" + VaadinSession.UI_PARAMETER + "\" init parameter."); diff --git a/server/src/com/vaadin/server/VaadinPortlet.java b/server/src/com/vaadin/server/VaadinPortlet.java index 83d152d653..5849d77c4d 100644 --- a/server/src/com/vaadin/server/VaadinPortlet.java +++ b/server/src/com/vaadin/server/VaadinPortlet.java @@ -246,6 +246,20 @@ public class VaadinPortlet extends GenericPortlet implements Constants { throws ServiceException { return new VaadinPortletSession(); } + + @Override + public String getServiceName() { + return getPortlet().getPortletName(); + } + + /** + * Always preserve UIs in portlets to make portlet actions work. + */ + @Override + public boolean preserveUIOnRefresh(VaadinRequest request, UI ui, + UIProvider provider) { + return true; + } } public static class VaadinHttpAndPortletRequest extends @@ -574,9 +588,6 @@ public class VaadinPortlet extends GenericPortlet implements Constants { if (vaadinSession == null) { return; } - VaadinSession.setCurrent(vaadinSession); - request.setAttribute(VaadinSession.class.getName(), - vaadinSession); PortletCommunicationManager communicationManager = (PortletCommunicationManager) vaadinSession .getCommunicationManager(); @@ -601,32 +612,8 @@ public class VaadinPortlet extends GenericPortlet implements Constants { // Finds the right UI UI uI = null; - vaadinSession.getLock().lock(); - try { - switch (requestType) { - case RENDER: - case ACTION: - // Both action requests and render requests are ok - // without a UI as they render the initial HTML - // and then do a second request - uI = vaadinSession.getUIForRequest(vaadinRequest); - break; - case BROWSER_DETAILS: - // Should not try to find a UI here as the - // combined request details might change the UI - break; - case FILE_UPLOAD: - // no window - break; - case APP: - // Not related to any UI - break; - default: - uI = vaadinSession.getUIForRequest(vaadinRequest); - } - // if window not found, not a problem - use null - } finally { - vaadinSession.getLock().unlock(); + if (requestType == RequestType.UIDL) { + uI = getVaadinService().findUI(vaadinRequest); } // TODO Should this happen before or after the transaction diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java index 766ae47fdb..e7bf14e258 100644 --- a/server/src/com/vaadin/server/VaadinService.java +++ b/server/src/com/vaadin/server/VaadinService.java @@ -23,7 +23,10 @@ import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; import java.util.Locale; import java.util.ServiceLoader; @@ -32,8 +35,10 @@ import javax.servlet.ServletContext; import javax.servlet.ServletException; import com.vaadin.LegacyApplication; +import com.vaadin.annotations.PreserveOnRefresh; import com.vaadin.event.EventRouter; import com.vaadin.server.VaadinSession.SessionStartEvent; +import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.ui.UI; import com.vaadin.util.CurrentInstance; import com.vaadin.util.ReflectTools; @@ -48,6 +53,69 @@ import com.vaadin.util.ReflectTools; */ public abstract class VaadinService implements Serializable { + /** + * Service specific data that is stored in VaadinSession separately for each + * VaadinService using that particular session. + * + * @author Vaadin Ltd + */ + public static class VaadinServiceData { + private final VaadinService vaadinService; + private LinkedList<UIProvider> uiProviders = new LinkedList<UIProvider>(); + + /** + * Create a new service data object for the given Vaadin service + * + * @param vaadinService + * the Vaadin service to which the data belongs + */ + public VaadinServiceData(VaadinService vaadinService) { + this.vaadinService = vaadinService; + } + + /** + * Gets a list of all the UI providers registered for a particular + * Vaadin service + * + * @see #addUIProvider(UIProvider) + * + * @return and unmodifiable list of UI providers + */ + public List<UIProvider> getUIProviders() { + return Collections.unmodifiableList(uiProviders); + } + + /** + * Adds a UI provider for a Vaadin service. + * + * @param uiProvider + * the UI provider to add + */ + public void addUIProvider(UIProvider uiProvider) { + uiProviders.addFirst(uiProvider); + } + + /** + * Removes a UI provider from a Vaadin service. + * + * @param uiProvider + * the UI provider to remove + */ + public void removeUIProvider(UIProvider uiProvider) { + uiProviders.remove(uiProvider); + } + + /** + * Gets the Vaadin service that this data belongs to. + * + * @return the Vaadin service that htis data belongs to + */ + public VaadinService getVaadinService() { + return vaadinService; + } + + } + private static final Method SESSION_INIT_METHOD = ReflectTools.findMethod( VaadinSessionInitializationListener.class, "vaadinSessionInitialized", VaadinSessionInitializeEvent.class); @@ -309,7 +377,26 @@ public abstract class VaadinService implements Serializable { */ public VaadinSession findVaadinSession(VaadinRequest request) throws ServiceException, SessionExpiredException { + VaadinSession vaadinSession = findOrCreateVaadinSession(request); + if (vaadinSession == null) { + return null; + } + if (!vaadinSession.hasVaadinServiceData(this)) { + vaadinSession.addVaadinServiceData(new VaadinServiceData(this)); + + ServletPortletHelper.initDefaultUIProvider(vaadinSession, this); + + onVaadinSessionStarted(request, vaadinSession); + } + + VaadinSession.setCurrent(vaadinSession); + request.setAttribute(VaadinSession.class.getName(), vaadinSession); + + return vaadinSession; + } + private VaadinSession findOrCreateVaadinSession(VaadinRequest request) + throws SessionExpiredException, ServiceException { boolean requestCanCreateSession = requestCanCreateSession(request); /* Find an existing session for this request. */ @@ -360,9 +447,6 @@ public abstract class VaadinService implements Serializable { throws ServiceException { VaadinSession session = createVaadinSession(request); - ServletPortletHelper.initDefaultUIProvider(session, this); - - session.setVaadinService(this); session.storeInSession(request.getWrappedSession()); URL applicationUrl; @@ -379,8 +463,6 @@ public abstract class VaadinService implements Serializable { getDeploymentConfiguration(), createCommunicationManager(session))); - onVaadinSessionStarted(request, session); - return session; } @@ -430,7 +512,7 @@ public abstract class VaadinService implements Serializable { eventRouter.fireEvent(new VaadinSessionInitializeEvent(this, session, request)); - ServletPortletHelper.checkUiProviders(session); + ServletPortletHelper.checkUiProviders(session, this); } private void closeSession(VaadinSession vaadinSession, @@ -551,4 +633,106 @@ public abstract class VaadinService implements Serializable { return CurrentInstance.get(VaadinResponse.class); } + /** + * Gets a unique name for this service. The name should be unique among + * different services of the same type but the same for corresponding + * instances running in different JVMs in a cluster. This is typically based + * on e.g. the configured servlet's or portlet's name. + * + * @return the unique name of this service instance. + */ + public abstract String getServiceName(); + + /** + * Gets all the UI providers from a session that are configured for this + * service. + * + * @param session + * the Vaadin session to get the UI providers from + * @return an unmodifiable list of UI providers + */ + public List<UIProvider> getUIProviders(VaadinSession session) { + return session.getVaadinServiceData(this).getUIProviders(); + } + + /** + * Finds the {@link UI} that belongs to the provided request. This is + * generally only supported for UIDL requests as other request types are not + * related to any particular UI or have the UI information encoded in a + * non-standard way. The returned UI is also set as the current UI ( + * {@link UI#setCurrent(UI)}). + * + * @param request + * the request for which a UI is desired + * @return the UI belonging to the request + * + */ + public UI findUI(VaadinRequest request) { + VaadinSession session = VaadinSession.getForSession(request + .getWrappedSession()); + + // Get UI id from the request + String uiIdString = request.getParameter(UIConstants.UI_ID_PARAMETER); + int uiId = Integer.parseInt(uiIdString); + + // Get lock before accessing data in session + session.getLock().lock(); + try { + UI ui = session.getUIById(uiId); + + UI.setCurrent(ui); + return ui; + } finally { + session.getLock().unlock(); + } + } + + /** + * Adds a UI provider to a Vaadin session and associates it with this Vaadin + * service. + * + * @param vaadinSession + * the Vaadin session to store the UI provider in + * @param uiProvider + * the UI provider that should be added + */ + public void addUIProvider(VaadinSession vaadinSession, UIProvider uiProvider) { + vaadinSession.getVaadinServiceData(this).addUIProvider(uiProvider); + } + + /** + * Removes a UI provider association for this service from a Vaadin session. + * + * @param vaadinSession + * the Vaadin session where the UI provider is stored + * @param uiProvider + * the UI provider that should be removed + */ + public void removeUIProvider(VaadinSession vaadinSession, + UIProvider uiProvider) { + vaadinSession.getVaadinServiceData(this).removeUIProvider(uiProvider); + } + + /** + * Check if the given UI should be associated with the + * <code>window.name</code> so that it can be re-used if the browser window + * is reloaded. This is typically determined by the UI provider which + * typically checks the @{@link PreserveOnRefresh} annotation but UI + * providers and ultimately VaadinService implementations may choose to + * override the defaults. + * + * @param request + * the Vaadin request used to initialize the UI + * @param ui + * the UI for which the preserve setting should be returned + * @param provider + * the UI provider responsible for the UI + * @return <code>true</code> if the UI should be preserved on refresh; + * <code>false</code> if a new UI instance should be initialized on + * refreshed. + */ + public boolean preserveUIOnRefresh(VaadinRequest request, UI ui, + UIProvider provider) { + return provider.isPreservedOnRefresh(request, ui.getClass()); + } } diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index 518d26070b..60f385146a 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -220,6 +220,11 @@ public class VaadinServlet extends HttpServlet implements Constants { throws ServiceException { return new VaadinServletSession(); } + + @Override + public String getServiceName() { + return getServlet().getServletName(); + } } private static class AbstractApplicationServletWrapper implements Callback { @@ -431,8 +436,6 @@ public class VaadinServlet extends HttpServlet implements Constants { if (vaadinSession == null) { return; } - request.setAttribute(VaadinSession.class.getName(), vaadinSession); - VaadinSession.setCurrent(vaadinSession); CommunicationManager communicationManager = (CommunicationManager) vaadinSession .getCommunicationManager(); @@ -458,7 +461,7 @@ public class VaadinServlet extends HttpServlet implements Constants { response); return; } else if (requestType == RequestType.UIDL) { - UI uI = vaadinSession.getUIForRequest(request); + UI uI = getVaadinService().findUI(request); if (uI == null) { throw new ServletException(ERROR_NO_UI_FOUND); } diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java index c3699bacfa..0bf5f8616b 100644 --- a/server/src/com/vaadin/server/VaadinSession.java +++ b/server/src/com/vaadin/server/VaadinSession.java @@ -25,7 +25,6 @@ import java.util.Collections; import java.util.EventObject; import java.util.HashMap; import java.util.LinkedList; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.locks.Lock; @@ -38,12 +37,12 @@ import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import com.vaadin.LegacyApplication; +import com.vaadin.annotations.PreserveOnRefresh; 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.VaadinRequest.BrowserDetails; -import com.vaadin.shared.ui.ui.UIConstants; +import com.vaadin.server.VaadinService.VaadinServiceData; import com.vaadin.ui.AbstractField; import com.vaadin.ui.Table; import com.vaadin.ui.UI; @@ -180,8 +179,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { private final EventRouter eventRouter = new EventRouter(); - private List<UIProvider> uiProviders = new LinkedList<UIProvider>(); - private GlobalResourceHandler globalResourceHandler; protected WebBrowser browser = new WebBrowser(); @@ -196,7 +193,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { private final Map<String, Object> attributes = new HashMap<String, Object>(); - private VaadinService vaadinService; + private Map<String, VaadinServiceData> serviceData = new HashMap<String, VaadinServiceData>(); /** * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent) @@ -213,22 +210,13 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { public void valueUnbound(HttpSessionBindingEvent event) { // If we are going to be unbound from the session, the session must be // closing - if (vaadinService != null) { - vaadinService.fireSessionDestroy(this); + // Notify all services that have used this session. + for (VaadinServiceData vaadinServiceData : serviceData.values()) { + vaadinServiceData.getVaadinService().fireSessionDestroy(this); } } /** - * Sets the Vaadin service to which this session belongs. - * - * @param vaadinService - * the Vaadin service. - */ - public void setVaadinService(VaadinService vaadinService) { - this.vaadinService = vaadinService; - } - - /** * Get the web browser associated with this session. * * @return @@ -609,137 +597,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { } /** - * 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. - * - * @param request - * the Vaadin request for which a UI is needed - * @return a UI instance to use for the request - * - * @see UI - * @see VaadinRequest#getBrowserDetails() - * - * @since 7.0 - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - public Class<? extends UI> getUIClass(VaadinRequest request) { - UIProvider uiProvider = getUiProvider(request, null); - return uiProvider.getUIClass(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. - * - * @param request - * @param uiClass - * @return - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - protected <T extends UI> T createUIInstance(VaadinRequest request, - Class<T> uiClass) { - UIProvider uiProvider = getUiProvider(request, uiClass); - return uiClass.cast(uiProvider.createInstance(request, uiClass)); - } - - /** - * 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 - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - public UIProvider getUiProvider(VaadinRequest 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(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; - } - - /** - * @param request - * @param uiClass - * @return - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - private UIProvider doGetUiProvider(VaadinRequest request, Class<?> 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(request); - // If we found something - if (providerClass != null) { - 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); - // Continue looking - } - } - } - - throw new RuntimeException("No UI provider found for request"); - } - - /** * Adds a request handler to this session. Request handlers can be added to * provide responses to requests that are not handled by the default * functionality of the framework. @@ -828,175 +685,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { CurrentInstance.setInheritable(VaadinSession.class, session); } - public void addUIProvider(UIProvider uIProvider) { - uiProviders.add(uIProvider); - } - - public void removeUIProvider(UIProvider uIProvider) { - uiProviders.remove(uIProvider); - } - - /** - * Finds the {@link UI} to which a particular request belongs. If the - * request originates from an existing UI, that UI is returned. In other - * cases, the method attempts to create and initialize a new UI and might - * throw a {@link UIRequiresMoreInformationException} if all required - * information is not available. - * <p> - * Please note that this method can also return a newly created - * <code>UI</code> which has not yet been initialized. You can use - * {@link #isUIInitPending(int)} with the UI's id ( {@link UI#getUIId()} to - * check whether the initialization is still pending. - * </p> - * - * @param request - * the request for which a UI is desired - * @return a UI belonging to the request - * - * @see #createUI(VaadinRequest) - * - * @since 7.0 - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - public UI getUIForRequest(VaadinRequest request) { - UI uI = UI.getCurrent(); - if (uI != null) { - return uI; - } - Integer uiId = getUIId(request); - getLock().lock(); - try { - uI = uIs.get(uiId); - - if (uI == null) { - uI = findExistingUi(request); - } - - } finally { - getLock().unlock(); - } - - UI.setCurrent(uI); - - return uI; - } - - /** - * @param request - * @return - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - private UI findExistingUi(VaadinRequest request) { - // Check if some UI provider has an existing UI available - for (int i = uiProviders.size() - 1; i >= 0; i--) { - UIProvider provider = uiProviders.get(i); - UI existingUi = provider.getExistingUI(request); - if (existingUi != null) { - return existingUi; - } - } - - BrowserDetails browserDetails = request.getBrowserDetails(); - boolean hasBrowserDetails = browserDetails != null - && browserDetails.getUriFragment() != null; - - if (hasBrowserDetails && !retainOnRefreshUIs.isEmpty()) { - // Check for a known UI - - @SuppressWarnings("null") - String windowName = browserDetails.getWindowName(); - Integer retainedUIId = retainOnRefreshUIs.get(windowName); - - if (retainedUIId != null) { - Class<? extends UI> expectedUIClass = getUIClass(request); - 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() == expectedUIClass) { - return retainedUI; - } else { - getLogger().info( - "Not using retained UI in " + windowName - + " because retained UI was of type " - + retainedUIId.getClass() + " but " - + expectedUIClass - + " is expected for the request."); - } - } - } - - return null; - } - - /** - * @param request - * @return - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - public UI createUI(VaadinRequest request) { - Class<? extends UI> uiClass = getUIClass(request); - - UI ui = createUIInstance(request, uiClass); - - // Initialize some fields for a newly created UI - if (ui.getSession() == null) { - ui.setSession(this); - } - // Get the next id - Integer uiId = Integer.valueOf(nextUIId++); - - uIs.put(uiId, ui); - - // Set thread local here so it is available in init - UI.setCurrent(ui); - - ui.doInit(request, uiId.intValue()); - - if (getUiProvider(request, uiClass).isPreservedOnRefresh(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); - } - } - - return ui; - } - - /** - * Internal helper to finds the UI id for a request. - * - * @param request - * the request to get the UI id for - * @return a UI id, or <code>null</code> if no UI id is defined - * - * @since 7.0 - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - private static Integer getUIId(VaadinRequest request) { - if (request instanceof CombinedRequest) { - // Combined requests has the uiId parameter in the second request - CombinedRequest combinedRequest = (CombinedRequest) request; - request = combinedRequest.getSecondRequest(); - } - String uiIdString = request.getParameter(UIConstants.UI_ID_PARAMETER); - Integer uiId = uiIdString == null ? null : new Integer(uiIdString); - return uiId; - } - /** * Gets all the UIs of this session. This includes UIs that have been * requested but not yet initialized. UIs that receive no heartbeat requests @@ -1239,10 +927,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { return globalResourceHandler; } - public Collection<UIProvider> getUIProviders() { - return Collections.unmodifiableCollection(uiProviders); - } - /** * Gets the lock that should be used to synchronize usage of data inside * this session. @@ -1360,4 +1044,98 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { } } + /** + * Checks whether there this session has any Vaadin service data for a + * particular Vaadin service. + * + * @see #addVaadinServiceData(VaadinServiceData) + * @see VaadinServiceData + * + * @param vaadinService + * the Vaadin service to check for + * @return <code>true</code> if there is a Vaadin service data object for + * the passed Vaadin service; otherwise <code>false</code> + */ + public boolean hasVaadinServiceData(VaadinService vaadinService) { + return getVaadinServiceData(vaadinService) != null; + } + + /** + * Gets the data stored for the passed Vaadin service. + * + * @see #addVaadinServiceData(VaadinServiceData) + * @see VaadinServiceData + * + * @param vaadinService + * the Vaadin service to get the data for + * @return the Vaadin service data for the provided Vaadin service; or + * <code>null</code> if there is no data for the service + */ + public VaadinServiceData getVaadinServiceData(VaadinService vaadinService) { + return serviceData.get(getServiceKey(vaadinService)); + } + + /** + * Adds Vaadin service specific data to this session. + * + * @see #getVaadinServiceData(VaadinService) + * @see VaadinServiceData + * + * @param serviceData + * the Vaadin service data to add + */ + public void addVaadinServiceData(VaadinServiceData serviceData) { + VaadinService vaadinService = serviceData.getVaadinService(); + assert !hasVaadinServiceData(vaadinService); + + this.serviceData.put(getServiceKey(vaadinService), serviceData); + } + + private static String getServiceKey(VaadinService vaadinService) { + String serviceKey = vaadinService.getClass().getName() + "." + + vaadinService.getServiceName(); + return serviceKey; + } + + /** + * Creates a new unique id for a UI. + * + * @return a unique UI id + */ + public int getNextUIid() { + return nextUIId++; + } + + /** + * Gets the mapping from <code>window.name</code> to UI id for UIs that are + * should be retained on refresh. + * + * @see VaadinService#preserveUIOnRefresh(VaadinRequest, UI, UIProvider) + * @see PreserveOnRefresh + * + * @return the mapping between window names and UI ids for this session. + */ + public Map<String, Integer> getPreserveOnRefreshUIs() { + return retainOnRefreshUIs; + } + + /** + * Adds an initialized UI to this session. + * + * @param ui + * the initialized UI to add. + */ + public void addUI(UI ui) { + if (ui.getUIId() == -1) { + throw new IllegalArgumentException( + "Can not add an UI that has not been initialized."); + } + if (ui.getSession() != this) { + throw new IllegalArgumentException( + "The UI belongs to a different session"); + } + + uIs.put(Integer.valueOf(ui.getUIId()), ui); + } + } diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index 5beb80ffcd..0d9fcd2c0a 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -41,8 +41,10 @@ import com.vaadin.server.Page.BrowserWindowResizeListener; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.server.Resource; +import com.vaadin.server.UIProvider; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinRequest.BrowserDetails; +import com.vaadin.server.VaadinService; import com.vaadin.server.VaadinServlet; import com.vaadin.server.VaadinSession; import com.vaadin.shared.EventId; @@ -66,10 +68,10 @@ import com.vaadin.util.ReflectTools; * </p> * <p> * When a new UI instance is needed, typically because the user opens a URL in a - * browser window which points to {@link VaadinServlet}, - * {@link VaadinSession#getUIForRequest(VaadinRequest)} is invoked to get a UI. - * That method does by default create a UI according to the - * {@value VaadinSession#UI_PARAMETER} parameter from web.xml. + * browser window which points to e.g. {@link VaadinServlet}, all + * {@link UIProvider}s registered to the current {@link VaadinSession} are + * queried for the UI class that should be used. The selection is by defaylt + * based on the {@value VaadinSession#UI_PARAMETER} parameter from web.xml. * </p> * <p> * After a UI has been created by the application, it is initialized using @@ -81,7 +83,7 @@ import com.vaadin.util.ReflectTools; * </p> * * @see #init(VaadinRequest) - * @see VaadinSession#createUI(VaadinRequest) + * @see UIProvider * * @since 7.0 */ @@ -466,7 +468,7 @@ public abstract class UI extends AbstractComponentContainer implements * which a request originates. A negative value indicates that the UI id has * not yet been assigned by the Application. * - * @see VaadinSession#nextUIId + * @see VaadinSession#getNextUIid() */ private int uiId = -1; @@ -748,8 +750,8 @@ public abstract class UI extends AbstractComponentContainer implements * 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 * the server that originates from this UI. - * {@link VaadinSession#getUIForRequest(VaadinRequest)} uses this id to find - * the route to which the request belongs. + * {@link VaadinService#findUI(VaadinRequest)} uses this id to find the + * route to which the request belongs. * * @return */ @@ -759,10 +761,7 @@ 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 VaadinSession#createUI(VaadinRequest)} returns an appropriate UI - * for the request. + * or tab, you should instead use a {@link UIProvider}. * * @param window * @throws IllegalArgumentException @@ -984,8 +983,9 @@ public abstract class UI extends AbstractComponentContainer implements throw new IllegalStateException("UI id has already been defined"); } this.uiId = uiId; - theme = getSession().getUiProvider(request, getClass()).getTheme( - request, getClass()); + + // Actual theme - used for finding CustomLayout templates + theme = request.getParameter("theme"); getPage().init(request); diff --git a/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java index 1323e247d9..971102495d 100644 --- a/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java +++ b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java @@ -35,11 +35,11 @@ import com.vaadin.server.DeploymentConfiguration; import com.vaadin.server.LegacyVaadinServlet; import com.vaadin.server.ServiceException; import com.vaadin.server.UIProvider; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinServletRequest; import com.vaadin.server.VaadinSession; import com.vaadin.server.VaadinSessionInitializationListener; import com.vaadin.server.VaadinSessionInitializeEvent; -import com.vaadin.server.VaadinServletRequest; -import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.TestBase; import com.vaadin.ui.UI; @@ -143,17 +143,19 @@ public class ApplicationRunnerServlet extends LegacyVaadinServlet { try { final Class<?> classToRun = getClassToRun(); if (UI.class.isAssignableFrom(classToRun)) { - session.addUIProvider(new AbstractUIProvider() { - - @Override - public Class<? extends UI> getUIClass(VaadinRequest request) { - return (Class<? extends UI>) classToRun; - } - }); + getVaadinService().addUIProvider(session, + new AbstractUIProvider() { + @Override + public Class<? extends UI> getUIClass( + VaadinRequest request) { + return (Class<? extends UI>) classToRun; + } + }); } else if (LegacyApplication.class.isAssignableFrom(classToRun)) { // Avoid using own UIProvider for legacy Application } else if (UIProvider.class.isAssignableFrom(classToRun)) { - session.addUIProvider((UIProvider) classToRun.newInstance()); + getVaadinService().addUIProvider(session, + (UIProvider) classToRun.newInstance()); } else { throw new ServiceException(classToRun.getCanonicalName() + " is neither an Application nor a UI"); |