diff options
author | John Ahlroos <john@vaadin.com> | 2012-09-24 11:42:58 +0300 |
---|---|---|
committer | John Ahlroos <john@vaadin.com> | 2012-09-24 11:42:58 +0300 |
commit | f9df64d3e490d0fa28d6f8fe31b0237009535804 (patch) | |
tree | 7814dfad8eadc527f36af61ce06587186da500ca | |
parent | bd576ade92607669fa7806fed18e90789027a80d (diff) | |
parent | e223589aae4ee77043ba156961a663b73ad3d8cb (diff) | |
download | vaadin-framework-f9df64d3e490d0fa28d6f8fe31b0237009535804.tar.gz vaadin-framework-f9df64d3e490d0fa28d6f8fe31b0237009535804.zip |
Merge branch 'master' into html5-doctype
22 files changed, 797 insertions, 517 deletions
diff --git a/server/src/com/vaadin/LegacyApplication.java b/server/src/com/vaadin/LegacyApplication.java index 3a6ffaa39c..3925c0b077 100644 --- a/server/src/com/vaadin/LegacyApplication.java +++ b/server/src/com/vaadin/LegacyApplication.java @@ -22,16 +22,13 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import com.vaadin.server.AbstractUIProvider; import com.vaadin.server.DefaultErrorListener; import com.vaadin.server.Terminal.ErrorEvent; import com.vaadin.server.Terminal.ErrorListener; import com.vaadin.server.VaadinSession; -import com.vaadin.server.WrappedRequest; import com.vaadin.ui.UI; +import com.vaadin.ui.UI.LegacyWindow; /** * A special application designed to help migrating applications from Vaadin 6 @@ -45,19 +42,20 @@ import com.vaadin.ui.UI; * @since 7.0 */ @Deprecated -public abstract class LegacyApplication extends AbstractUIProvider implements - ErrorListener { - /** - * Ignore initial / and then get everything up to the next / - */ - private static final Pattern WINDOW_NAME_PATTERN = Pattern - .compile("^/?([^/]+).*"); - +public abstract class LegacyApplication implements ErrorListener { private UI.LegacyWindow mainWindow; private String theme; private Map<String, UI.LegacyWindow> legacyUINames = new HashMap<String, UI.LegacyWindow>(); + private boolean isRunning = true; + + /** + * URL where the user is redirected to on application close, or null if + * application is just closed without redirection. + */ + private String logoutURL = null; + /** * Sets the main window of this application. Setting window as a main window * of this application also adds the window to this application. @@ -69,9 +67,7 @@ public abstract class LegacyApplication extends AbstractUIProvider implements if (this.mainWindow != null) { throw new IllegalStateException("mainWindow has already been set"); } - if (mainWindow.getSession() == null) { - mainWindow.setSession(VaadinSession.getCurrent()); - } else if (mainWindow.getSession() != VaadinSession.getCurrent()) { + if (mainWindow.getSession() != null) { throw new IllegalStateException( "mainWindow is attached to another application"); } @@ -80,7 +76,7 @@ public abstract class LegacyApplication extends AbstractUIProvider implements // no current UI -> set the main window as the current UI UI.setCurrent(mainWindow); } - mainWindow.setApplication(this); + addWindow(mainWindow); this.mainWindow = mainWindow; } @@ -91,36 +87,6 @@ public abstract class LegacyApplication extends AbstractUIProvider implements protected abstract void init(); - @Override - public Class<? extends UI> getUIClass(WrappedRequest request) { - UI uiInstance = getUIInstance(request); - if (uiInstance != null) { - return uiInstance.getClass(); - } - return null; - } - - @Override - public UI createInstance(WrappedRequest request, Class<? extends UI> type) { - return getUIInstance(request); - } - - @Override - public String getTheme(WrappedRequest request, Class<? extends UI> uiClass) { - return theme; - } - - @Override - public String getPageTitle(WrappedRequest request, - Class<? extends UI> uiClass) { - UI uiInstance = getUIInstance(request); - if (uiInstance != null) { - return uiInstance.getCaption(); - } else { - return super.getPageTitle(request, uiClass); - } - } - /** * Gets the mainWindow of the application. * @@ -138,43 +104,6 @@ public abstract class LegacyApplication extends AbstractUIProvider implements return mainWindow; } - private UI getUIInstance(WrappedRequest request) { - String pathInfo = request.getRequestPathInfo(); - String name = null; - if (pathInfo != null && pathInfo.length() > 0) { - Matcher matcher = WINDOW_NAME_PATTERN.matcher(pathInfo); - if (matcher.matches()) { - // Skip the initial slash - name = matcher.group(1); - } - } - UI.LegacyWindow window = getWindow(name); - if (window != null) { - return window; - } - return mainWindow; - } - - /** - * This implementation simulates the way of finding a window for a request - * by extracting a window name from the requested path and passes that name - * to {@link #getWindow(String)}. - * <p> - * {@inheritDoc} - */ - @Override - public UI getExistingUI(WrappedRequest request) { - UI uiInstance = getUIInstance(request); - if (uiInstance.getUIId() == -1) { - // Not initialized -> Let go through createUIInstance to make it - // initialized - return null; - } else { - UI.setCurrent(uiInstance); - return uiInstance; - } - } - /** * Sets the application's theme. * <p> @@ -289,15 +218,64 @@ public abstract class LegacyApplication extends AbstractUIProvider implements return VaadinSession.getCurrent(); } - protected void close() { - VaadinSession.getCurrent().close(); + public void close() { + isRunning = false; + Collection<LegacyWindow> windows = getWindows(); + for (LegacyWindow legacyWindow : windows) { + String logoutUrl = getLogoutURL(); + if (logoutUrl == null) { + URL url = getURL(); + if (url != null) { + logoutUrl = url.toString(); + } + } + if (logoutUrl != null) { + legacyWindow.getPage().setLocation(logoutUrl); + } + legacyWindow.getSession().cleanupUI(legacyWindow); + } } public boolean isRunning() { - return VaadinSession.getCurrent().isRunning(); + return isRunning; } public URL getURL() { return VaadinSession.getCurrent().getURL(); } + + /** + * Returns the URL user is redirected to on application close. If the URL is + * <code>null</code>, the application is closed normally as defined by the + * application running environment. + * <p> + * Desktop application just closes the application window and + * web-application redirects the browser to application main URL. + * </p> + * + * @return the URL. + * + * @deprecated might be refactored or removed before 7.0.0 + */ + @Deprecated + public String getLogoutURL() { + return logoutURL; + } + + /** + * Sets the URL user is redirected to on application close. If the URL is + * <code>null</code>, the application is closed normally as defined by the + * application running environment: Desktop application just closes the + * application window and web-application redirects the browser to + * application main URL. + * + * @param logoutURL + * the logoutURL to set. + * + * @deprecated might be refactored or removed before 7.0.0 + */ + @Deprecated + public void setLogoutURL(String logoutURL) { + this.logoutURL = logoutURL; + } }
\ No newline at end of file diff --git a/server/src/com/vaadin/navigator/Navigator.java b/server/src/com/vaadin/navigator/Navigator.java index 257dfa208f..c9f6946ce5 100644 --- a/server/src/com/vaadin/navigator/Navigator.java +++ b/server/src/com/vaadin/navigator/Navigator.java @@ -112,7 +112,7 @@ public class Navigator implements Serializable { public String getState() { String fragment = page.getFragment(); if (fragment.startsWith("!")) { - return page.getFragment().substring(1); + return fragment.substring(1); } else { return ""; } @@ -338,6 +338,7 @@ public class Navigator implements Serializable { private View currentView = null; private List<ViewChangeListener> listeners = new LinkedList<ViewChangeListener>(); private List<ViewProvider> providers = new LinkedList<ViewProvider>(); + private ViewProvider errorProvider; /** * Creates a navigator that is tracking the active view using URI fragments @@ -443,6 +444,8 @@ public class Navigator implements Serializable { * * @param navigationState * view name and parameters + * + * @throws IllegalArgumentException */ public void navigateTo(String navigationState) { String longestViewName = null; @@ -459,6 +462,10 @@ public class Navigator implements Serializable { } } } + if (viewWithLongestName == null && errorProvider != null) { + longestViewName = errorProvider.getViewName(navigationState); + viewWithLongestName = errorProvider.getView(longestViewName); + } if (viewWithLongestName != null) { String parameters = ""; if (navigationState.length() > longestViewName.length() + 1) { @@ -466,8 +473,12 @@ public class Navigator implements Serializable { .substring(longestViewName.length() + 1); } navigateTo(viewWithLongestName, longestViewName, parameters); + } else { + throw new IllegalArgumentException( + "Trying to navigate to an unknown state '" + + navigationState + + "' and an error view provider not present"); } - // TODO if no view is found, what to do? } /** @@ -600,11 +611,11 @@ public class Navigator implements Serializable { } /** - * Register a view class for a view name. + * Registers a view class for a view name. * <p> * Registering another view with a name that is already registered * overwrites the old registration of the same type. - * + * <p> * A new view instance is created every time a view is requested. * * @param viewName @@ -676,9 +687,76 @@ public class Navigator implements Serializable { } /** - * Adds a listener for listening to changes of the active view. + * Registers a view class that is instantiated when no other view matches + * the navigation state. This implicitly sets an appropriate error view + * provider and overrides any previous + * {@link #setErrorProvider(ViewProvider)} call. + * + * @param viewClass + * The View class whose instance should be used as the error + * view. + */ + public void setErrorView(final Class<? extends View> viewClass) { + setErrorProvider(new ViewProvider() { + @Override + public View getView(String viewName) { + try { + return viewClass.newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public String getViewName(String navigationState) { + return navigationState; + } + }); + } + + /** + * Registers a view that is displayed when no other view matches the + * navigation state. This implicitly sets an appropriate error view provider + * and overrides any previous {@link #setErrorProvider(ViewProvider)} call. + * + * @param view + * The View that should be used as the error view. + */ + public void setErrorView(final View view) { + setErrorProvider(new ViewProvider() { + @Override + public View getView(String viewName) { + return view; + }; + + @Override + public String getViewName(String navigationState) { + return navigationState; + } + }); + } + + /** + * Registers a view provider that is queried for a view when no other view + * matches the navigation state. An error view provider should match any + * navigation state, but could return different views for different states. + * Its <code>getViewName(String navigationState)</code> should return + * <code>navigationState</code>. + * + * @param provider + */ + public void setErrorProvider(ViewProvider provider) { + errorProvider = provider; + } + + /** + * Listen to changes of the active view. * <p> - * The listener will get notified after the view has changed. + * Registered listeners are invoked in registration order before ( + * {@link ViewChangeListener#beforeViewChange(ViewChangeEvent) + * beforeViewChange()}) and after ( + * {@link ViewChangeListener#afterViewChange(ViewChangeEvent) + * afterViewChange()}) a view change occurs. * * @param listener * Listener to invoke during a view change. diff --git a/server/src/com/vaadin/server/AbstractCommunicationManager.java b/server/src/com/vaadin/server/AbstractCommunicationManager.java index 9ac33df7d2..b7d0b98ab8 100644 --- a/server/src/com/vaadin/server/AbstractCommunicationManager.java +++ b/server/src/com/vaadin/server/AbstractCommunicationManager.java @@ -83,6 +83,7 @@ import com.vaadin.ui.Component; import com.vaadin.ui.ConnectorTracker; import com.vaadin.ui.HasComponents; import com.vaadin.ui.UI; +import com.vaadin.ui.UI.LegacyWindow; import com.vaadin.ui.Window; import com.vaadin.util.CurrentInstance; @@ -576,18 +577,11 @@ public abstract class AbstractCommunicationManager implements Serializable { session.getLock().lock(); try { - // Finds the UI within the session - if (session.isRunning()) { - // Returns if no window found - if (uI == null) { - // This should not happen, no windows exists but - // session is still open. - getLogger().warning("Could not get UI for session"); - return; - } - } else { - // session has been closed - endApplication(request, response, session); + // Verify that there's an UI + if (uI == null) { + // This should not happen, no windows exists but + // session is still open. + getLogger().warning("Could not get UI for session"); return; } @@ -653,6 +647,16 @@ public abstract class AbstractCommunicationManager implements Serializable { * */ protected void postPaint(UI uI) { + if (uI instanceof LegacyWindow) { + LegacyWindow legacyWindow = (LegacyWindow) uI; + if (!legacyWindow.getApplication().isRunning()) { + // Detach LegacyWindow if it belongs to a closed + // LegacyApplication + legacyWindow.setApplication(null); + legacyWindow.setSession(null); + } + } + // Remove connectors that have been detached from the session during // handling of the request uI.getConnectorTracker().cleanConnectorMap(); @@ -752,13 +756,6 @@ public abstract class AbstractCommunicationManager implements Serializable { WrappedResponse response, Callback callback, boolean repaintAll, final PrintWriter outWriter, UI uI, boolean analyzeLayouts) throws PaintException, IOException, JSONException { - - // Removes session if it has stopped during variable changes - if (!session.isRunning()) { - endApplication(request, response, session); - return; - } - openJsonMessage(outWriter, response); // security key @@ -2177,45 +2174,6 @@ public abstract class AbstractCommunicationManager implements Serializable { outWriter.print("]"); // Close locales } - /** - * Ends the Application. - * - * The browser is redirected to the Application logout URL set with - * {@link VaadinSession#setLogoutURL(String)}, or to the application URL if - * no logout URL is given. - * - * @param request - * the request instance. - * @param response - * the response to write to. - * @param application - * the Application to end. - * @throws IOException - * if the writing failed due to input/output error. - */ - private void endApplication(WrappedRequest request, - WrappedResponse response, VaadinSession application) - throws IOException { - - String logoutUrl = application.getLogoutURL(); - if (logoutUrl == null) { - logoutUrl = application.getURL().toString(); - } - // clients JS app is still running, send a special json file to tell - // client that application has quit and where to point browser now - // Set the response type - final OutputStream out = response.getOutputStream(); - final PrintWriter outWriter = new PrintWriter(new BufferedWriter( - new OutputStreamWriter(out, "UTF-8"))); - openJsonMessage(outWriter, response); - outWriter.print("\"redirect\":{"); - outWriter.write("\"url\":\"" + logoutUrl + "\"}"); - closeJsonMessage(outWriter); - outWriter.flush(); - outWriter.close(); - out.flush(); - } - protected void closeJsonMessage(PrintWriter outWriter) { outWriter.print("}]"); } diff --git a/server/src/com/vaadin/server/LegacyApplicationUIProvider.java b/server/src/com/vaadin/server/LegacyApplicationUIProvider.java new file mode 100644 index 0000000000..c0f64088b4 --- /dev/null +++ b/server/src/com/vaadin/server/LegacyApplicationUIProvider.java @@ -0,0 +1,134 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.vaadin.LegacyApplication; +import com.vaadin.ui.UI; + +public abstract class LegacyApplicationUIProvider extends AbstractUIProvider { + /** + * Ignore initial / and then get everything up to the next / + */ + private static final Pattern WINDOW_NAME_PATTERN = Pattern + .compile("^/?([^/]+).*"); + + @Override + public Class<? extends UI> getUIClass(WrappedRequest request) { + UI uiInstance = getUIInstance(request); + if (uiInstance != null) { + return uiInstance.getClass(); + } + return null; + } + + @Override + public UI createInstance(WrappedRequest request, Class<? extends UI> type) { + return getUIInstance(request); + } + + @Override + public String getTheme(WrappedRequest request, Class<? extends UI> uiClass) { + LegacyApplication application = getApplication(); + if (application != null) { + return application.getTheme(); + } else { + return null; + } + } + + @Override + public String getPageTitle(WrappedRequest request, + Class<? extends UI> uiClass) { + UI uiInstance = getUIInstance(request); + if (uiInstance != null) { + return uiInstance.getCaption(); + } else { + return super.getPageTitle(request, uiClass); + } + } + + private UI getUIInstance(WrappedRequest request) { + String pathInfo = request.getRequestPathInfo(); + String name = null; + if (pathInfo != null && pathInfo.length() > 0) { + Matcher matcher = WINDOW_NAME_PATTERN.matcher(pathInfo); + if (matcher.matches()) { + // Skip the initial slash + name = matcher.group(1); + } + } + + LegacyApplication application = getApplication(); + if (application == null) { + return null; + } + UI.LegacyWindow window = application.getWindow(name); + if (window != null) { + return window; + } + return application.getMainWindow(); + } + + /** + * 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 UI getExistingUI(WrappedRequest request) { + UI uiInstance = getUIInstance(request); + if (uiInstance == null || uiInstance.getUIId() == -1) { + // Not initialized -> Let go through createUIInstance to make it + // initialized + return null; + } else { + UI.setCurrent(uiInstance); + return uiInstance; + } + } + + private LegacyApplication getApplication() { + LegacyApplication application = VaadinSession.getCurrent() + .getAttribute(LegacyApplication.class); + if (application == null) { + application = createApplication(); + if (application == null) { + return null; + } + VaadinSession.getCurrent().setAttribute(LegacyApplication.class, + application); + application.doInit(); + } + + if (application != null && !application.isRunning()) { + VaadinSession.getCurrent().setAttribute(LegacyApplication.class, + null); + // Run again without a current application + return getApplication(); + } + + return application; + } + + protected abstract LegacyApplication createApplication(); + +} diff --git a/server/src/com/vaadin/server/LegacyVaadinPortlet.java b/server/src/com/vaadin/server/LegacyVaadinPortlet.java index bdc03ff643..6aa8fb7165 100644 --- a/server/src/com/vaadin/server/LegacyVaadinPortlet.java +++ b/server/src/com/vaadin/server/LegacyVaadinPortlet.java @@ -20,10 +20,29 @@ import javax.portlet.PortletException; import javax.portlet.PortletRequest; import com.vaadin.LegacyApplication; -import com.vaadin.server.ServletPortletHelper.ApplicationClassException; public class LegacyVaadinPortlet extends VaadinPortlet { + private static final LegacyApplicationUIProvider provider = new LegacyApplicationUIProvider() { + @Override + protected LegacyApplication createApplication() { + VaadinPortlet portlet = VaadinPortlet.getCurrent(); + if (portlet instanceof LegacyVaadinPortlet) { + LegacyVaadinPortlet legacyPortlet = (LegacyVaadinPortlet) portlet; + PortletRequest request = PortletService + .getCurrentPortletRequest(); + if (legacyPortlet.shouldCreateApplication(request)) { + try { + return legacyPortlet.getNewApplication(request); + } catch (PortletException e) { + throw new RuntimeException(e); + } + } + } + return null; + } + }; + @Override public void init() throws PortletException { super.init(); @@ -51,7 +70,7 @@ public class LegacyVaadinPortlet extends VaadinPortlet { try { return ServletPortletHelper .getLegacyApplicationClass(getVaadinService()); - } catch (ApplicationClassException e) { + } catch (ServiceException e) { throw new RuntimeException(e); } } @@ -68,21 +87,10 @@ public class LegacyVaadinPortlet extends VaadinPortlet { private void onVaadinSessionStarted(WrappedPortletRequest request, VaadinPortletSession session) throws PortletException { - if (shouldCreateApplication(request)) { - // Must set current before running init() - VaadinSession.setCurrent(session); - - // XXX Must update details here so they are available in init. - session.getBrowser().updateRequestDetails(request); - - LegacyApplication legacyApplication = getNewApplication(request - .getPortletRequest()); - legacyApplication.doInit(); - session.addUIProvider(legacyApplication); - } + session.addUIProvider(provider); } - protected boolean shouldCreateApplication(WrappedPortletRequest request) { + protected boolean shouldCreateApplication(PortletRequest request) { return true; } } diff --git a/server/src/com/vaadin/server/LegacyVaadinServlet.java b/server/src/com/vaadin/server/LegacyVaadinServlet.java index 8d55fddc39..ace29e9e1f 100644 --- a/server/src/com/vaadin/server/LegacyVaadinServlet.java +++ b/server/src/com/vaadin/server/LegacyVaadinServlet.java @@ -21,10 +21,30 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import com.vaadin.LegacyApplication; -import com.vaadin.server.ServletPortletHelper.ApplicationClassException; public class LegacyVaadinServlet extends VaadinServlet { + private static final UIProvider provider = new LegacyApplicationUIProvider() { + @Override + protected LegacyApplication createApplication() { + + VaadinServlet servlet = VaadinServlet.getCurrent(); + if (servlet instanceof LegacyVaadinServlet) { + LegacyVaadinServlet legacyServlet = (LegacyVaadinServlet) servlet; + HttpServletRequest request = ServletService + .getCurrentServletRequest(); + try { + if (legacyServlet.shouldCreateApplication(request)) { + return legacyServlet.getNewApplication(request); + } + } catch (ServletException e) { + throw new RuntimeException(e); + } + } + return null; + } + }; + @Override public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); @@ -50,7 +70,7 @@ public class LegacyVaadinServlet extends VaadinServlet { try { return ServletPortletHelper .getLegacyApplicationClass(getVaadinService()); - } catch (ApplicationClassException e) { + } catch (ServiceException e) { throw new RuntimeException(e); } } @@ -65,27 +85,14 @@ public class LegacyVaadinServlet extends VaadinServlet { } } - protected boolean shouldCreateApplication(WrappedHttpServletRequest request) + protected boolean shouldCreateApplication(HttpServletRequest request) throws ServletException { return true; } private void onVaadinSessionStarted(WrappedRequest wrappedRequest, VaadinSession session) throws ServletException { - WrappedHttpServletRequest request = WrappedHttpServletRequest - .cast(wrappedRequest); - - if (shouldCreateApplication(request)) { - // Must set current before running init() - VaadinSession.setCurrent(session); - - // XXX Must update details here so they are available in init. - session.getBrowser().updateRequestDetails(request); - - LegacyApplication legacyApplication = getNewApplication(request); - legacyApplication.doInit(); - session.addUIProvider(legacyApplication); - } + session.addUIProvider(provider); } } diff --git a/server/src/com/vaadin/server/ServiceException.java b/server/src/com/vaadin/server/ServiceException.java index 538e8b30ea..ac693e5747 100644 --- a/server/src/com/vaadin/server/ServiceException.java +++ b/server/src/com/vaadin/server/ServiceException.java @@ -18,12 +18,16 @@ package com.vaadin.server; public class ServiceException extends Exception { - public ServiceException(Exception e) { - super(e); + public ServiceException(Throwable t) { + super(t); } public ServiceException(String message) { super(message); } + public ServiceException(String message, Throwable t) { + super(message, t); + } + } diff --git a/server/src/com/vaadin/server/ServletPortletHelper.java b/server/src/com/vaadin/server/ServletPortletHelper.java index 7f4a05deef..3f2674392c 100644 --- a/server/src/com/vaadin/server/ServletPortletHelper.java +++ b/server/src/com/vaadin/server/ServletPortletHelper.java @@ -30,26 +30,15 @@ class ServletPortletHelper implements Serializable { */ static final SystemMessages DEFAULT_SYSTEM_MESSAGES = new SystemMessages(); - public static class ApplicationClassException extends Exception { - - public ApplicationClassException(String message, Throwable cause) { - super(message, cause); - } - - public ApplicationClassException(String message) { - super(message); - } - } - static Class<? extends LegacyApplication> getLegacyApplicationClass( - VaadinService vaadinService) throws ApplicationClassException { + VaadinService vaadinService) throws ServiceException { Properties initParameters = vaadinService.getDeploymentConfiguration() .getInitParameters(); String applicationParameter = initParameters.getProperty("application"); ClassLoader classLoader = vaadinService.getClassLoader(); if (applicationParameter == null) { - throw new ApplicationClassException( + throw new ServiceException( "No \"application\" init parameter found"); } @@ -57,16 +46,15 @@ class ServletPortletHelper implements Serializable { return classLoader.loadClass(applicationParameter).asSubclass( LegacyApplication.class); } catch (final ClassNotFoundException e) { - throw new ApplicationClassException( - "Failed to load application class: " + applicationParameter, - e); + throw new ServiceException("Failed to load application class: " + + applicationParameter, e); } } private static void verifyUIClass(String className, ClassLoader classLoader) - throws ApplicationClassException { + throws ServiceException { if (className == null) { - throw new ApplicationClassException(VaadinSession.UI_PARAMETER + throw new ServiceException(VaadinSession.UI_PARAMETER + " init parameter not defined"); } @@ -74,19 +62,17 @@ class ServletPortletHelper implements Serializable { try { Class<?> uiClass = classLoader.loadClass(className); if (!UI.class.isAssignableFrom(uiClass)) { - throw new ApplicationClassException(className - + " does not implement UI"); + throw new ServiceException(className + " does not implement UI"); } // Try finding a default constructor, else throw exception uiClass.getConstructor(); } catch (ClassNotFoundException e) { - throw new ApplicationClassException(className - + " could not be loaded", e); + throw new ServiceException(className + " could not be loaded", e); } catch (SecurityException e) { - throw new ApplicationClassException("Could not access " + className + throw new ServiceException("Could not access " + className + " class", e); } catch (NoSuchMethodException e) { - throw new ApplicationClassException(className + throw new ServiceException(className + " doesn't have a public no-args constructor"); } } @@ -132,7 +118,7 @@ class ServletPortletHelper implements Serializable { } public static void initDefaultUIProvider(VaadinSession application, - VaadinService vaadinService) throws ApplicationClassException { + VaadinService vaadinService) throws ServiceException { String uiProperty = vaadinService.getDeploymentConfiguration() .getInitParameters().getProperty(VaadinSession.UI_PARAMETER); if (uiProperty != null) { @@ -142,9 +128,9 @@ class ServletPortletHelper implements Serializable { } public static void checkUiProviders(VaadinSession newApplication) - throws ApplicationClassException { + throws ServiceException { if (newApplication.getUIProviders().isEmpty()) { - throw new ApplicationClassException( + throw new ServiceException( "No UIProvider has been added to the application 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 dc1f93b2a8..074c2aa9cc 100644 --- a/server/src/com/vaadin/server/VaadinPortlet.java +++ b/server/src/com/vaadin/server/VaadinPortlet.java @@ -222,10 +222,16 @@ public class VaadinPortlet extends GenericPortlet implements Constants { return new PortletCommunicationManager(session); } - public static WrappedPortletRequest getCurrentRequest() { + public static PortletRequest getCurrentPortletRequest() { WrappedRequest currentRequest = VaadinService.getCurrentRequest(); try { - return WrappedPortletRequest.cast(currentRequest); + WrappedPortletRequest request = WrappedPortletRequest + .cast(currentRequest); + if (request != null) { + return request.getPortletRequest(); + } else { + return null; + } } catch (ClassCastException e) { return null; } @@ -235,6 +241,11 @@ public class VaadinPortlet extends GenericPortlet implements Constants { return (WrappedPortletResponse) VaadinService.getCurrentResponse(); } + @Override + protected VaadinSession createVaadinSession(WrappedRequest request) + throws ServiceException { + return new VaadinPortletSession(); + } } public static class WrappedHttpAndPortletRequest extends @@ -520,84 +531,87 @@ public class VaadinPortlet extends GenericPortlet implements Constants { CurrentInstance.clearAll(); setCurrent(this); - AbstractApplicationPortletWrapper portletWrapper = new AbstractApplicationPortletWrapper( - this); - - WrappedPortletRequest wrappedRequest = createWrappedRequest(request); - - WrappedPortletResponse wrappedResponse = new WrappedPortletResponse( - response, getVaadinService()); - - getVaadinService().setCurrentInstances(wrappedRequest, wrappedResponse); - - RequestType requestType = getRequestType(wrappedRequest); - - if (requestType == RequestType.UNKNOWN) { - handleUnknownRequest(request, response); - } else if (requestType == RequestType.DUMMY) { - /* - * This dummy page is used by action responses to redirect to, in - * order to prevent the boot strap code from being rendered into - * strange places such as iframes. - */ - ((ResourceResponse) response).setContentType("text/html"); - final OutputStream out = ((ResourceResponse) response) - .getPortletOutputStream(); - final PrintWriter outWriter = new PrintWriter(new BufferedWriter( - new OutputStreamWriter(out, "UTF-8"))); - outWriter.print("<html><body>dummy page</body></html>"); - outWriter.close(); - } else if (requestType == RequestType.STATIC_FILE) { - serveStaticResources((ResourceRequest) request, - (ResourceResponse) response); - } else { - VaadinSession application = null; - boolean applicationRunning = false; + try { + AbstractApplicationPortletWrapper portletWrapper = new AbstractApplicationPortletWrapper( + this); - try { - // TODO What about PARAM_UNLOADBURST & redirectToApplication?? + WrappedPortletRequest wrappedRequest = createWrappedRequest(request); - /* Find out which application this request is related to */ - application = getVaadinService().findVaadinSession( - wrappedRequest); - if (application == null) { - return; - } - VaadinSession.setCurrent(application); - request.setAttribute(VaadinSession.class.getName(), application); + WrappedPortletResponse wrappedResponse = new WrappedPortletResponse( + response, getVaadinService()); + + getVaadinService().setCurrentInstances(wrappedRequest, + wrappedResponse); + RequestType requestType = getRequestType(wrappedRequest); + + if (requestType == RequestType.UNKNOWN) { + handleUnknownRequest(request, response); + } else if (requestType == RequestType.DUMMY) { /* - * Get or create an application context and an application - * manager for the session + * This dummy page is used by action responses to redirect to, + * in order to prevent the boot strap code from being rendered + * into strange places such as iframes. */ - VaadinPortletSession applicationContext = (VaadinPortletSession) application; - - PortletCommunicationManager applicationManager = (PortletCommunicationManager) applicationContext - .getApplicationManager(); - - if (requestType == RequestType.CONNECTOR_RESOURCE) { - applicationManager.serveConnectorResource(wrappedRequest, - wrappedResponse); - return; - } else if (requestType == RequestType.HEARTBEAT) { - applicationManager.handleHeartbeatRequest(wrappedRequest, - wrappedResponse, application); - return; - } + ((ResourceResponse) response).setContentType("text/html"); + final OutputStream out = ((ResourceResponse) response) + .getPortletOutputStream(); + final PrintWriter outWriter = new PrintWriter( + new BufferedWriter(new OutputStreamWriter(out, "UTF-8"))); + outWriter.print("<html><body>dummy page</body></html>"); + outWriter.close(); + } else if (requestType == RequestType.STATIC_FILE) { + serveStaticResources((ResourceRequest) request, + (ResourceResponse) response); + } else { + VaadinSession application = null; + boolean applicationRunning = false; + + try { + // TODO What about PARAM_UNLOADBURST & + // redirectToApplication?? - /* Update browser information from request */ - applicationContext.getBrowser().updateRequestDetails( - wrappedRequest); + /* Find out which application this request is related to */ + application = getVaadinService().findVaadinSession( + wrappedRequest); + if (application == null) { + return; + } + VaadinSession.setCurrent(application); + request.setAttribute(VaadinSession.class.getName(), + application); - applicationRunning = true; + /* + * Get or create an application context and an application + * manager for the session + */ + VaadinPortletSession applicationContext = (VaadinPortletSession) application; - /* Notify listeners */ + PortletCommunicationManager applicationManager = (PortletCommunicationManager) applicationContext + .getApplicationManager(); - // Finds the window within the application - UI uI = null; - application.getLock().lock(); - try { - if (application.isRunning()) { + if (requestType == RequestType.CONNECTOR_RESOURCE) { + applicationManager.serveConnectorResource( + wrappedRequest, wrappedResponse); + return; + } else if (requestType == RequestType.HEARTBEAT) { + applicationManager.handleHeartbeatRequest( + wrappedRequest, wrappedResponse, application); + return; + } + + /* Update browser information from request */ + applicationContext.getBrowser().updateRequestDetails( + wrappedRequest); + + applicationRunning = true; + + /* Notify listeners */ + + // Finds the window within the application + UI uI = null; + application.getLock().lock(); + try { switch (requestType) { case RENDER: case ACTION: @@ -621,81 +635,80 @@ public class VaadinPortlet extends GenericPortlet implements Constants { uI = application.getUIForRequest(wrappedRequest); } // if window not found, not a problem - use null + } finally { + application.getLock().unlock(); } - } finally { - application.getLock().unlock(); - } - // TODO Should this happen before or after the transaction - // starts? - if (request instanceof RenderRequest) { - applicationContext.firePortletRenderRequest(uI, - (RenderRequest) request, (RenderResponse) response); - } else if (request instanceof ActionRequest) { - applicationContext.firePortletActionRequest(uI, - (ActionRequest) request, (ActionResponse) response); - } else if (request instanceof EventRequest) { - applicationContext.firePortletEventRequest(uI, - (EventRequest) request, (EventResponse) response); - } else if (request instanceof ResourceRequest) { - applicationContext.firePortletResourceRequest(uI, - (ResourceRequest) request, - (ResourceResponse) response); - } + // TODO Should this happen before or after the transaction + // starts? + if (request instanceof RenderRequest) { + applicationContext.firePortletRenderRequest(uI, + (RenderRequest) request, + (RenderResponse) response); + } else if (request instanceof ActionRequest) { + applicationContext.firePortletActionRequest(uI, + (ActionRequest) request, + (ActionResponse) response); + } else if (request instanceof EventRequest) { + applicationContext.firePortletEventRequest(uI, + (EventRequest) request, + (EventResponse) response); + } else if (request instanceof ResourceRequest) { + applicationContext.firePortletResourceRequest(uI, + (ResourceRequest) request, + (ResourceResponse) response); + } - /* Handle the request */ - if (requestType == RequestType.FILE_UPLOAD) { - // UI is resolved in handleFileUpload by - // PortletCommunicationManager - applicationManager.handleFileUpload(application, - wrappedRequest, wrappedResponse); - return; - } else if (requestType == RequestType.BROWSER_DETAILS) { - applicationManager.handleBrowserDetailsRequest( - wrappedRequest, wrappedResponse, application); - return; - } else if (requestType == RequestType.UIDL) { - // Handles AJAX UIDL requests - applicationManager.handleUidlRequest(wrappedRequest, - wrappedResponse, portletWrapper, uI); - return; - } else { - /* - * Removes the application if it has stopped - */ - if (!application.isRunning()) { - endApplication(request, response, application); + /* Handle the request */ + if (requestType == RequestType.FILE_UPLOAD) { + // UI is resolved in handleFileUpload by + // PortletCommunicationManager + applicationManager.handleFileUpload(application, + wrappedRequest, wrappedResponse); + return; + } else if (requestType == RequestType.BROWSER_DETAILS) { + applicationManager.handleBrowserDetailsRequest( + wrappedRequest, wrappedResponse, application); + return; + } else if (requestType == RequestType.UIDL) { + // Handles AJAX UIDL requests + applicationManager.handleUidlRequest(wrappedRequest, + wrappedResponse, portletWrapper, uI); return; + } else { + handleOtherRequest(wrappedRequest, wrappedResponse, + requestType, application, applicationContext, + applicationManager); } + } catch (final SessionExpiredException e) { + // TODO Figure out a better way to deal with + // SessionExpiredExceptions + getLogger().finest("A user session has expired"); + } catch (final GeneralSecurityException e) { + // TODO Figure out a better way to deal with + // GeneralSecurityExceptions + getLogger() + .fine("General security exception, the security key was probably incorrect."); + } catch (final Throwable e) { + handleServiceException(wrappedRequest, wrappedResponse, + application, e); + } finally { - handleOtherRequest(wrappedRequest, wrappedResponse, - requestType, application, applicationContext, - applicationManager); - } - } catch (final SessionExpiredException e) { - // TODO Figure out a better way to deal with - // SessionExpiredExceptions - getLogger().finest("A user session has expired"); - } catch (final GeneralSecurityException e) { - // TODO Figure out a better way to deal with - // GeneralSecurityExceptions - getLogger() - .fine("General security exception, the security key was probably incorrect."); - } catch (final Throwable e) { - handleServiceException(wrappedRequest, wrappedResponse, - application, e); - } finally { - - if (applicationRunning) { - application.cleanupInactiveUIs(); - } + if (applicationRunning) { + application.cleanupInactiveUIs(); + } - CurrentInstance.clearAll(); + if (applicationRunning) { + application.cleanupInactiveUIs(); + } - if (application != null) { - requestTimer.stop(application); + if (application != null) { + requestTimer.stop(application); + } } } + } finally { + CurrentInstance.clearAll(); } } diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java index 80590df4a0..eb5d838b72 100644 --- a/server/src/com/vaadin/server/VaadinService.java +++ b/server/src/com/vaadin/server/VaadinService.java @@ -22,6 +22,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; import java.util.Iterator; import java.util.Locale; import java.util.ServiceLoader; @@ -32,8 +33,8 @@ import javax.servlet.ServletException; import com.vaadin.LegacyApplication; import com.vaadin.event.EventRouter; -import com.vaadin.server.ServletPortletHelper.ApplicationClassException; import com.vaadin.server.VaadinSession.SessionStartEvent; +import com.vaadin.ui.UI; import com.vaadin.util.CurrentInstance; import com.vaadin.util.ReflectTools; @@ -51,6 +52,10 @@ public abstract class VaadinService implements Serializable { VaadinSessionInitializationListener.class, "vaadinSessionInitialized", VaadinSessionInitializeEvent.class); + private static final Method SESSION_DESTROY_METHOD = ReflectTools + .findMethod(VaadinSessionDestroyListener.class, + "vaadinSessionDestroyed", VaadinSessionDestroyEvent.class); + /** * @deprecated Only supported for {@link LegacyApplication}. */ @@ -254,6 +259,44 @@ public abstract class VaadinService implements Serializable { } /** + * Adds a listener that gets notified when a Vaadin session that has been + * initialized for this service is destroyed. + * + * @see #addVaadinSessionInitializationListener(VaadinSessionInitializationListener) + * + * @param listener + * the vaadin session destroy listener + */ + public void addVaadinSessionDestroyListener( + VaadinSessionDestroyListener listener) { + eventRouter.addListener(VaadinSessionDestroyEvent.class, listener, + SESSION_DESTROY_METHOD); + } + + public void fireSessionDestroy(VaadinSession vaadinSession) { + for (UI ui : new ArrayList<UI>(vaadinSession.getUIs())) { + vaadinSession.cleanupUI(ui); + } + + eventRouter + .fireEvent(new VaadinSessionDestroyEvent(this, vaadinSession)); + } + + /** + * Removes a Vaadin session destroy listener from this service. + * + * @see #addVaadinSessionDestroyListener(VaadinSessionDestroyListener) + * + * @param listener + * the vaadin session destroy listener + */ + public void removeVaadinSessionDestroyListener( + VaadinSessionDestroyListener listener) { + eventRouter.removeListener(VaadinSessionDestroyEvent.class, listener, + SESSION_DESTROY_METHOD); + } + + /** * Attempts to find a Vaadin session associated with this request. * * @param request @@ -318,6 +361,9 @@ 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; @@ -376,18 +422,8 @@ public abstract class VaadinService implements Serializable { * @throws ServletException * @throws MalformedURLException */ - private VaadinServletSession createVaadinSession(WrappedRequest request) - throws ServiceException { - VaadinServletSession session = new VaadinServletSession(); - - try { - ServletPortletHelper.initDefaultUIProvider(session, this); - } catch (ApplicationClassException e) { - throw new ServiceException(e); - } - - return session; - } + protected abstract VaadinSession createVaadinSession(WrappedRequest request) + throws ServiceException; private void onVaadinSessionStarted(WrappedRequest request, VaadinSession session) throws ServiceException { @@ -395,11 +431,7 @@ public abstract class VaadinService implements Serializable { eventRouter.fireEvent(new VaadinSessionInitializeEvent(this, session, request)); - try { - ServletPortletHelper.checkUiProviders(session); - } catch (ApplicationClassException e) { - throw new ServiceException(e); - } + ServletPortletHelper.checkUiProviders(session); } private void closeApplication(VaadinSession application, @@ -408,7 +440,6 @@ public abstract class VaadinService implements Serializable { return; } - application.close(); if (session != null) { application.removeFromSession(); } @@ -430,11 +461,6 @@ public abstract class VaadinService implements Serializable { return null; } - if (!sessionApplication.isRunning()) { - sessionApplication.removeFromSession(); - return null; - } - return sessionApplication; } diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index c42d029dc4..3c129c49a8 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -41,7 +41,6 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import com.vaadin.DefaultDeploymentConfiguration; import com.vaadin.sass.ScssStylesheet; @@ -198,10 +197,16 @@ public class VaadinServlet extends HttpServlet implements Constants { return new CommunicationManager(session); } - public static WrappedHttpServletRequest getCurrentRequest() { + public static HttpServletRequest getCurrentServletRequest() { WrappedRequest currentRequest = VaadinService.getCurrentRequest(); try { - return WrappedHttpServletRequest.cast(currentRequest); + WrappedHttpServletRequest request = WrappedHttpServletRequest + .cast(currentRequest); + if (request != null) { + return request.getHttpServletRequest(); + } else { + return null; + } } catch (ClassCastException e) { return null; } @@ -211,6 +216,12 @@ public class VaadinServlet extends HttpServlet implements Constants { return (WrappedHttpServletResponse) VaadinService .getCurrentResponse(); } + + @Override + protected VaadinSession createVaadinSession(WrappedRequest request) + throws ServiceException { + return new VaadinServletSession(); + } } private static class AbstractApplicationServletWrapper implements Callback { @@ -469,13 +480,6 @@ public class VaadinServlet extends HttpServlet implements Constants { return; } - // Removes application if it has stopped (maybe by thread or - // transactionlistener) - if (!application.isRunning()) { - endApplication(request, response, application); - return; - } - if (applicationManager.handleApplicationRequest(request, response)) { return; } @@ -1336,35 +1340,6 @@ public class VaadinServlet extends HttpServlet implements Constants { } /** - * Ends the application. - * - * @param request - * the HTTP request. - * @param response - * the HTTP response to write to. - * @param application - * the application to end. - * @throws IOException - * if the writing failed due to input/output error. - */ - private void endApplication(HttpServletRequest request, - HttpServletResponse response, VaadinSession application) - throws IOException { - - String logoutUrl = application.getLogoutURL(); - if (logoutUrl == null) { - logoutUrl = application.getURL().toString(); - } - - final HttpSession session = request.getSession(); - if (session != null) { - application.removeFromSession(); - } - - response.sendRedirect(response.encodeRedirectURL(logoutUrl)); - } - - /** * Returns the path info; note that this _can_ be different than * request.getPathInfo(). Examples where this might be useful: * <ul> diff --git a/server/src/com/vaadin/server/VaadinServletSession.java b/server/src/com/vaadin/server/VaadinServletSession.java index b2dc84d857..9a937b401b 100644 --- a/server/src/com/vaadin/server/VaadinServletSession.java +++ b/server/src/com/vaadin/server/VaadinServletSession.java @@ -19,6 +19,7 @@ package com.vaadin.server; import java.util.Enumeration; import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; @@ -58,8 +59,8 @@ public class VaadinServletSession extends VaadinSession { * to avoid session fixation attacks. */ public void reinitializeSession() { - WrappedHttpServletRequest currentRequest = ServletService - .getCurrentRequest(); + HttpServletRequest currentRequest = ServletService + .getCurrentServletRequest(); if (currentRequest == null) { throw new IllegalStateException( "Can not reinitialize session outside normal request handling."); diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java index f1d4ab343a..5acc1e0ca3 100644 --- a/server/src/com/vaadin/server/VaadinSession.java +++ b/server/src/com/vaadin/server/VaadinSession.java @@ -19,11 +19,11 @@ package com.vaadin.server; import java.io.Serializable; import java.lang.reflect.Method; import java.net.URL; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EventObject; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -37,6 +37,7 @@ import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; +import com.vaadin.LegacyApplication; import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.ConverterFactory; import com.vaadin.data.util.converter.DefaultConverterFactory; @@ -154,22 +155,11 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { private URL applicationUrl; /** - * Application status. - */ - private volatile boolean applicationIsRunning = false; - - /** * Default locale of the session. */ private Locale locale; /** - * URL where the user is redirected to on application close, or null if - * application is just closed without redirection. - */ - private String logoutURL = null; - - /** * Session wide error handler which is used by default if an error is left * unhandled. */ @@ -206,6 +196,8 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { private final Map<String, Object> attributes = new HashMap<String, Object>(); + private VaadinService vaadinService; + /** * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent) */ @@ -221,7 +213,19 @@ 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 - close(); + if (vaadinService != null) { + vaadinService.fireSessionDestroy(this); + } + } + + /** + * Sets the Vaadin service to which this session belongs. + * + * @param vaadinService + * the Vaadin service. + */ + public void setVaadinService(VaadinService vaadinService) { + this.vaadinService = vaadinService; } /** @@ -302,25 +306,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { } /** - * Ends the session. - * <p> - * When the session is closed, close events are fired for its UIs, its state - * is removed from the underlying session, and the browser window is - * redirected to the application logout url set with - * {@link #setLogoutURL(String)}. If the logout url has not been set, the - * browser window is reloaded and the application is restarted. - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - public void close() { - applicationIsRunning = false; - for (UI ui : getUIs()) { - ui.fireCleanupEvent(); - } - } - - /** * @param underlyingSession * @return * @@ -389,25 +374,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { applicationUrl = event.getApplicationUrl(); configuration = event.getConfiguration(); communicationManager = event.getCommunicationManager(); - applicationIsRunning = true; - } - - /** - * Tests if the application is running or if it has been finished. - * - * <p> - * Application starts running when its {@link #start(SessionStartEvent)} - * method has been called and stops when the {@link #close()} is called. - * </p> - * - * @return <code>true</code> if the application is running, - * <code>false</code> if not. - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - public boolean isRunning() { - return applicationIsRunning; } /** @@ -567,41 +533,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { } /** - * Returns the URL user is redirected to on application close. If the URL is - * <code>null</code>, the application is closed normally as defined by the - * application running environment. - * <p> - * Desktop application just closes the application window and - * web-application redirects the browser to application main URL. - * </p> - * - * @return the URL. - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - public String getLogoutURL() { - return logoutURL; - } - - /** - * Sets the URL user is redirected to on application close. If the URL is - * <code>null</code>, the application is closed normally as defined by the - * application running environment: Desktop application just closes the - * application window and web-application redirects the browser to - * application main URL. - * - * @param logoutURL - * the logoutURL to set. - * - * @deprecated might be refactored or removed before 7.0.0 - */ - @Deprecated - public void setLogoutURL(String logoutURL) { - this.logoutURL = logoutURL; - } - - /** * Gets the session's error handler. * * @return the current error handler @@ -1180,12 +1111,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { */ @Deprecated public void cleanupInactiveUIs() { - for (Iterator<UI> i = uIs.values().iterator(); i.hasNext();) { - UI ui = i.next(); + for (UI ui : new ArrayList<UI>(uIs.values())) { if (!isUIAlive(ui)) { - i.remove(); - retainOnRefreshUIs.values().remove(ui.getUIId()); - ui.fireCleanupEvent(); + cleanupUI(ui); getLogger().fine( "Closed UI #" + ui.getUIId() + " due to inactivity"); } @@ -1193,6 +1121,25 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { } /** + * Called by the framework to remove an UI instance because it has been + * inactive. + * + * @param ui + * the UI to remove + * + * @deprecated Method is declared as public only to support + * {@link LegacyApplication#close()} and will be removed when + * LegacyApplciation support is removed. + */ + @Deprecated + public void cleanupUI(UI ui) { + Integer id = Integer.valueOf(ui.getUIId()); + uIs.remove(id); + retainOnRefreshUIs.values().remove(id); + ui.fireCleanupEvent(); + } + + /** * Returns the number of seconds that must pass without a valid heartbeat or * UIDL request being received from a UI before that UI is removed from the * application. This is a lower bound; it might take longer to close an diff --git a/server/src/com/vaadin/server/VaadinSessionDestroyEvent.java b/server/src/com/vaadin/server/VaadinSessionDestroyEvent.java new file mode 100644 index 0000000000..3916f756e9 --- /dev/null +++ b/server/src/com/vaadin/server/VaadinSessionDestroyEvent.java @@ -0,0 +1,70 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server; + +import java.util.EventObject; + +/** + * Event fired when a Vaadin session is no longer in use. + * + * @see VaadinSessionDestroyListener#vaadinSessionDestroyed(VaadinSessionDestroyEvent) + * + * @author Vaadin Ltd + * @since 7.0.0 + */ +public class VaadinSessionDestroyEvent extends EventObject { + + private final VaadinSession session; + + /** + * Creates a new event. + * + * @param service + * the Vaadin service from which the even originates + * @param session + * the Vaadin session that is no longer used + */ + public VaadinSessionDestroyEvent(VaadinService service, + VaadinSession session) { + super(service); + this.session = session; + } + + @Override + public VaadinService getSource() { + return (VaadinService) super.getSource(); + } + + /** + * Gets the Vaadin service from which the even originates. + * + * @return the Vaadin service + */ + public VaadinService getVaadinService() { + return getSource(); + } + + /** + * Gets the Vaadin session that is no longer used. + * + * @return the Vaadin session + */ + public VaadinSession getVaadinSession() { + return session; + } + +} diff --git a/server/src/com/vaadin/server/VaadinSessionDestroyListener.java b/server/src/com/vaadin/server/VaadinSessionDestroyListener.java new file mode 100644 index 0000000000..3f516c1eda --- /dev/null +++ b/server/src/com/vaadin/server/VaadinSessionDestroyListener.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server; + +/** + * A listener that gets notified when a Vaadin session is no longer used. + * + * @see VaadinService#addVaadinSessionDestroyListener(VaadinSessionDestroyListener) + * + * @author Vaadin Ltd + * @since 7.0.0 + */ +public interface VaadinSessionDestroyListener { + /** + * Called when a Vaadin session is no longer used. + * + * @param event + * the event with details about the destroyed session + */ + public void vaadinSessionDestroyed(VaadinSessionDestroyEvent event); +} diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index b15a83cdce..0d83f8258d 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -1123,11 +1123,11 @@ public abstract class UI extends AbstractComponentContainer implements } /** - * Adds a close listener to the UI. Close listeners are invoked when the UI - * is removed from the session due to UI or session expiration. + * Adds a cleanup listener to the UI. Cleanup listeners are invoked when the + * UI is removed from the session due to UI or session expiration. * * @param listener - * The CloseListener that should be added. + * The CleanupListener that should be added. */ public void addCleanupListener(CleanupListener listener) { addListener(CleanupEvent.CLEANUP_EVENT_IDENTIFIER, CleanupEvent.class, @@ -1156,11 +1156,11 @@ public abstract class UI extends AbstractComponentContainer implements } /** - * Removes a close listener from the UI, if previously added with + * Removes a cleanup listener from the UI, if previously added with * {@link #addCleanupListener(CleanupListener)}. * * @param listener - * The CloseListener that should be removed + * The CleanupListener that should be removed */ public void removeCleanupListener(CleanupListener listener) { removeListener(CleanupEvent.CLEANUP_EVENT_IDENTIFIER, diff --git a/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java index f31b17333a..c27e92626a 100644 --- a/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java +++ b/uitest/src/com/vaadin/launcher/ApplicationRunnerServlet.java @@ -129,7 +129,7 @@ public class ApplicationRunnerServlet extends LegacyVaadinServlet { } @Override - protected boolean shouldCreateApplication(WrappedHttpServletRequest request) + protected boolean shouldCreateApplication(HttpServletRequest request) throws ServletException { try { return LegacyApplication.class.isAssignableFrom(getClassToRun()); diff --git a/uitest/src/com/vaadin/tests/TestForUpload.java b/uitest/src/com/vaadin/tests/TestForUpload.java index 7146b1a4ff..31298aa392 100644 --- a/uitest/src/com/vaadin/tests/TestForUpload.java +++ b/uitest/src/com/vaadin/tests/TestForUpload.java @@ -43,6 +43,7 @@ import com.vaadin.ui.Panel; import com.vaadin.ui.ProgressIndicator; import com.vaadin.ui.Select; import com.vaadin.ui.TextField; +import com.vaadin.ui.UI.LegacyWindow; import com.vaadin.ui.Upload; import com.vaadin.ui.Upload.FinishedEvent; import com.vaadin.ui.Upload.StartedEvent; @@ -244,7 +245,8 @@ public class TestForUpload extends CustomComponent implements @Override public void buttonClick(ClickEvent event) { - getSession().close(); + LegacyWindow window = (LegacyWindow) event.getButton().getUI(); + window.getApplication().close(); } }); main.addComponent(restart); diff --git a/uitest/src/com/vaadin/tests/application/ApplicationCloseTest.java b/uitest/src/com/vaadin/tests/application/ApplicationCloseTest.java index e4f8552af1..151b58ed67 100644 --- a/uitest/src/com/vaadin/tests/application/ApplicationCloseTest.java +++ b/uitest/src/com/vaadin/tests/application/ApplicationCloseTest.java @@ -5,6 +5,7 @@ import com.vaadin.tests.components.TestBase; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Label; +import com.vaadin.ui.UI.LegacyWindow; public class ApplicationCloseTest extends TestBase { @@ -26,7 +27,8 @@ public class ApplicationCloseTest extends TestBase { @Override public void buttonClick(ClickEvent event) { - event.getButton().getUI().getSession().close(); + LegacyWindow ui = (LegacyWindow) event.getButton().getUI(); + ui.getApplication().close(); } }); diff --git a/uitest/src/com/vaadin/tests/layouts/ComplexGLColumnExpansionWithColSpan.java b/uitest/src/com/vaadin/tests/layouts/ComplexGLColumnExpansionWithColSpan.java index fecac38697..3a22a46e68 100644 --- a/uitest/src/com/vaadin/tests/layouts/ComplexGLColumnExpansionWithColSpan.java +++ b/uitest/src/com/vaadin/tests/layouts/ComplexGLColumnExpansionWithColSpan.java @@ -73,7 +73,7 @@ public class ComplexGLColumnExpansionWithColSpan extends AbstractTestCase { restart.addListener(new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { - mainLayout.getUI().getSession().close(); + close(); } }); diff --git a/uitest/src/com/vaadin/tests/navigator/NavigatorTest.html b/uitest/src/com/vaadin/tests/navigator/NavigatorTest.html index 8c4e8f657b..030b30f37a 100644 --- a/uitest/src/com/vaadin/tests/navigator/NavigatorTest.html +++ b/uitest/src/com/vaadin/tests/navigator/NavigatorTest.html @@ -17,6 +17,11 @@ <td></td> </tr> <tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestsnavigatorNavigatorTest::/VVerticalLayout[0]/VOrderedLayout$Slot[4]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLabel[0]</td> + <td>1. Navigated to DefaultView with params</td> +</tr> +<tr> <td>click</td> <td>vaadin=runcomvaadintestsnavigatorNavigatorTest::/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> <td></td> @@ -24,7 +29,7 @@ <tr> <td>assertText</td> <td>vaadin=runcomvaadintestsnavigatorNavigatorTest::/VVerticalLayout[0]/VOrderedLayout$Slot[4]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLabel[0]</td> - <td>1. Navigated to ListView with params</td> + <td>2. Navigated to ListView with params</td> </tr> <tr> <td>assertLocation</td> @@ -39,7 +44,7 @@ <tr> <td>assertText</td> <td>vaadin=runcomvaadintestsnavigatorNavigatorTest::/VVerticalLayout[0]/VOrderedLayout$Slot[4]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLabel[0]</td> - <td>2. Navigated to EditView with params</td> + <td>3. Navigated to EditView with params</td> </tr> <tr> <td>assertLocation</td> @@ -64,7 +69,7 @@ <tr> <td>assertText</td> <td>vaadin=runcomvaadintestsnavigatorNavigatorTest::/VVerticalLayout[0]/VOrderedLayout$Slot[4]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLabel[0]</td> - <td>3. Navigated to ListView with params param=value</td> + <td>4. Navigated to ListView with params param=value</td> </tr> <tr> <td>assertLocation</td> @@ -79,7 +84,7 @@ <tr> <td>assertText</td> <td>vaadin=runcomvaadintestsnavigatorNavigatorTest::/VVerticalLayout[0]/VOrderedLayout$Slot[4]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLabel[0]</td> - <td>4. Navigated to EditView with params param=value</td> + <td>5. Navigated to EditView with params param=value</td> </tr> <tr> <td>assertLocation</td> @@ -94,13 +99,53 @@ <tr> <td>assertText</td> <td>vaadin=runcomvaadintestsnavigatorNavigatorTest::/VVerticalLayout[0]/VOrderedLayout$Slot[4]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLabel[0]</td> - <td>5. Prevent navigation to ForbiddenView</td> + <td>6. Prevent navigation to ForbiddenView</td> </tr> <tr> <td>assertLocation</td> <td>*#!edit/param=value</td> <td></td> </tr> +<tr> + <td>runScript</td> + <td>window.location.hash='!foo'</td> + <td></td> +</tr> +<tr> + <td>pause</td> + <td>1000</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestsnavigatorNavigatorTest::/VVerticalLayout[0]/VOrderedLayout$Slot[4]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLabel[0]</td> + <td>7. View 'foo' not found!</td> +</tr> +<tr> + <td>assertLocation</td> + <td>*#!foo</td> + <td></td> +</tr> +<tr> + <td>runScript</td> + <td>window.location.hash='foo'</td> + <td></td> +</tr> +<tr> + <td>pause</td> + <td>1000</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestsnavigatorNavigatorTest::/VVerticalLayout[0]/VOrderedLayout$Slot[4]/VVerticalLayout[0]/VOrderedLayout$Slot[0]/VLabel[0]</td> + <td>8. Navigated to DefaultView with params</td> +</tr> +<tr> + <td>assertLocation</td> + <td>*#foo</td> + <td></td> +</tr> </tbody></table> </body> diff --git a/uitest/src/com/vaadin/tests/navigator/NavigatorTest.java b/uitest/src/com/vaadin/tests/navigator/NavigatorTest.java index 4986bd21e6..a3f7ce9f5b 100644 --- a/uitest/src/com/vaadin/tests/navigator/NavigatorTest.java +++ b/uitest/src/com/vaadin/tests/navigator/NavigatorTest.java @@ -74,6 +74,15 @@ public class NavigatorTest extends UI { } } + class ErrorView extends Label implements View { + @Override + public void enter(ViewChangeEvent event) { + log.log("View '" + event.getViewName() + "' not found!"); + setValue("Tried to navigate to " + event.getViewName() + + " but such a view could not be found :("); + } + } + class NaviListener implements ViewChangeListener { @Override @@ -114,6 +123,8 @@ public class NavigatorTest extends UI { navi.addViewChangeListener(new NaviListener()); + navi.setErrorView(new ErrorView()); + navi.navigate(); addComponent(new NaviButton("list")); |