diff options
author | Leif Åstrand <leif@vaadin.com> | 2012-09-05 19:35:55 +0300 |
---|---|---|
committer | Leif Åstrand <leif@vaadin.com> | 2012-09-05 19:35:55 +0300 |
commit | d344148d038498aeb8ef04511d0a44d95ebff32b (patch) | |
tree | 1ab0e8fef98bdfe48fa4b7055d8434a16d424fa4 /server | |
parent | 4a69dcbca783a0fd0ed353a7a218c31d8d814c34 (diff) | |
parent | 4019f7d03a1d8437a24ccadc562c30f99da5efe0 (diff) | |
download | vaadin-framework-d344148d038498aeb8ef04511d0a44d95ebff32b.tar.gz vaadin-framework-d344148d038498aeb8ef04511d0a44d95ebff32b.zip |
Merge branch 'removeApplication'
Diffstat (limited to 'server')
78 files changed, 3802 insertions, 4179 deletions
diff --git a/server/src/com/vaadin/Application.java b/server/src/com/vaadin/Application.java index 9498534b4a..13ce23d1e4 100644 --- a/server/src/com/vaadin/Application.java +++ b/server/src/com/vaadin/Application.java @@ -16,2267 +16,287 @@ package com.vaadin; -import java.io.IOException; -import java.io.Serializable; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.net.SocketException; import java.net.URL; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Enumeration; -import java.util.EventObject; import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Map.Entry; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.vaadin.annotations.PreserveOnRefresh; -import com.vaadin.annotations.Theme; -import com.vaadin.annotations.Title; -import com.vaadin.annotations.Widgetset; -import com.vaadin.data.util.converter.Converter; -import com.vaadin.data.util.converter.ConverterFactory; -import com.vaadin.data.util.converter.DefaultConverterFactory; -import com.vaadin.event.EventRouter; -import com.vaadin.server.AbstractErrorMessage; -import com.vaadin.server.ApplicationContext; -import com.vaadin.server.BootstrapFragmentResponse; -import com.vaadin.server.BootstrapListener; -import com.vaadin.server.BootstrapPageResponse; -import com.vaadin.server.BootstrapResponse; -import com.vaadin.server.ChangeVariablesErrorEvent; -import com.vaadin.server.ClientConnector; -import com.vaadin.server.CombinedRequest; -import com.vaadin.server.DeploymentConfiguration; -import com.vaadin.server.GlobalResourceHandler; -import com.vaadin.server.RequestHandler; -import com.vaadin.server.ServletApplicationContext; -import com.vaadin.server.Terminal; -import com.vaadin.server.UIProvider; -import com.vaadin.server.VaadinServlet; -import com.vaadin.server.VariableOwner; +import com.vaadin.server.AbstractUIProvider; +import com.vaadin.server.VaadinSession; +import com.vaadin.server.Terminal.ErrorEvent; +import com.vaadin.server.Terminal.ErrorListener; import com.vaadin.server.WrappedRequest; -import com.vaadin.server.WrappedRequest.BrowserDetails; -import com.vaadin.server.WrappedResponse; -import com.vaadin.shared.ui.ui.UIConstants; -import com.vaadin.ui.AbstractComponent; -import com.vaadin.ui.AbstractField; -import com.vaadin.ui.Table; import com.vaadin.ui.UI; -import com.vaadin.ui.Window; -import com.vaadin.util.ReflectTools; /** - * <p> - * Base class required for all Vaadin applications. This class provides all the - * basic services required by Vaadin. These services allow external discovery - * and manipulation of the user, {@link com.vaadin.ui.Window windows} and - * themes, and starting and stopping the application. - * </p> + * A special application designed to help migrating applications from Vaadin 6 + * to Vaadin 7. The legacy application supports setting a main window, adding + * additional browser level windows and defining the theme for the entire + * application. * - * <p> - * As mentioned, all Vaadin applications must inherit this class. However, this - * is almost all of what one needs to do to create a fully functional - * application. The only thing a class inheriting the <code>Application</code> - * needs to do is implement the <code>init</code> method where it creates the - * windows it needs to perform its function. Note that all applications must - * have at least one window: the main window. The first unnamed window - * constructed by an application automatically becomes the main window which - * behaves just like other windows with one exception: when accessing windows - * using URLs the main window corresponds to the application URL whereas other - * windows correspond to a URL gotten by catenating the window's name to the - * application URL. - * </p> + * @deprecated This class is only intended to ease migration and should not be + * used for new projects. * - * <p> - * See the class <code>com.vaadin.demo.HelloWorld</code> for a simple example of - * a fully working application. - * </p> - * - * <p> - * <strong>Window access.</strong> <code>Application</code> provides methods to - * list, add and remove the windows it contains. - * </p> - * - * <p> - * <strong>Execution control.</strong> This class includes method to start and - * finish the execution of the application. Being finished means basically that - * no windows will be available from the application anymore. - * </p> - * - * <p> - * <strong>Theme selection.</strong> The theme selection process allows a theme - * to be specified at three different levels. When a window's theme needs to be - * found out, the window itself is queried for a preferred theme. If the window - * does not prefer a specific theme, the application containing the window is - * queried. If neither the application prefers a theme, the default theme for - * the {@link com.vaadin.server.Terminal terminal} is used. The terminal always - * defines a default theme. - * </p> - * - * @author Vaadin Ltd. - * @since 3.0 + * @since 7.0 */ -@SuppressWarnings("serial") -public class Application implements Terminal.ErrorListener, Serializable { - +@Deprecated +public abstract class Application extends AbstractUIProvider implements + ErrorListener { /** - * The name of the parameter that is by default used in e.g. web.xml to - * define the name of the default {@link UI} class. + * Ignore initial / and then get everything up to the next / */ - public static final String UI_PARAMETER = "UI"; - - private static final Method BOOTSTRAP_FRAGMENT_METHOD = ReflectTools - .findMethod(BootstrapListener.class, "modifyBootstrapFragment", - BootstrapFragmentResponse.class); - private static final Method BOOTSTRAP_PAGE_METHOD = ReflectTools - .findMethod(BootstrapListener.class, "modifyBootstrapPage", - BootstrapPageResponse.class); - - /** - * A special application designed to help migrating applications from Vaadin - * 6 to Vaadin 7. The legacy application supports setting a main window, - * adding additional browser level windows and defining the theme for the - * entire application. - * - * @deprecated This class is only intended to ease migration and should not - * be used for new projects. - * - * @since 7.0 - */ - @Deprecated - public static class LegacyApplication extends Application { - /** - * Ignore initial / and then get everything up to the next / - */ - private static final Pattern WINDOW_NAME_PATTERN = Pattern - .compile("^/?([^/]+).*"); - - private UI.LegacyWindow mainWindow; - private String theme; - - private Map<String, UI.LegacyWindow> legacyUINames = new HashMap<String, UI.LegacyWindow>(); - - /** - * Sets the main window of this application. Setting window as a main - * window of this application also adds the window to this application. - * - * @param mainWindow - * the UI to set as the default window - */ - public void setMainWindow(UI.LegacyWindow mainWindow) { - if (this.mainWindow != null) { - throw new IllegalStateException( - "mainWindow has already been set"); - } - if (mainWindow.getApplication() == null) { - mainWindow.setApplication(this); - } else if (mainWindow.getApplication() != this) { - throw new IllegalStateException( - "mainWindow is attached to another application"); - } - if (UI.getCurrent() == null) { - // Assume setting a main window from Application.init if there's - // no current UI -> set the main window as the current UI - UI.setCurrent(mainWindow); - } - this.mainWindow = mainWindow; - } - - /** - * Gets the mainWindow of the application. - * - * <p> - * The main window is the window attached to the application URL ( - * {@link #getURL()}) and thus which is show by default to the user. - * </p> - * <p> - * Note that each application must have at least one main window. - * </p> - * - * @return the UI used as the default window - */ - public UI.LegacyWindow getMainWindow() { - return mainWindow; - } - - /** - * This implementation simulates the way of finding a window for a - * request by extracting a window name from the requested path and - * passes that name to {@link #getWindow(String)}. - * <p> - * {@inheritDoc} - */ - @Override - protected <T extends UI> T createUIInstance(WrappedRequest request, - Class<T> uiClass) { - return uiClass.cast(getUIInstance(request)); - } - - private UI getUIInstance(WrappedRequest request) { - String pathInfo = request.getRequestPathInfo(); - String name = null; - 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 getUIForRequest(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; - } - } - - /** - * This implementation simulates the way of finding a window for a - * request by extracting a window name from the requested path and - * passes that name to {@link #getWindow(String)}. - * - * <p> - * {@inheritDoc} - */ - @Override - public Class<? extends UI> getUIClass(WrappedRequest request) { - return getUIInstance(request).getClass(); - } - - /** - * Sets the application's theme. - * <p> - * Note that this theme can be overridden for a specific UI with - * {@link Application#getThemeForUI(UI)}. Setting theme to be - * <code>null</code> selects the default theme. For the available theme - * names, see the contents of the VAADIN/themes directory. - * </p> - * - * @param theme - * the new theme for this application. - */ - public void setTheme(String theme) { - this.theme = theme; - } - - /** - * Gets the application's theme. The application's theme is the default - * theme used by all the uIs for which a theme is not explicitly - * defined. If the application theme is not explicitly set, - * <code>null</code> is returned. - * - * @return the name of the application's theme. - */ - public String getTheme() { - return theme; - } - - /** - * This implementation returns the theme that has been set using - * {@link #setTheme(String)} - * <p> - * {@inheritDoc} - */ - @Override - public String getThemeForUI(WrappedRequest request, - Class<? extends UI> uiClass) { - return theme; - } + private static final Pattern WINDOW_NAME_PATTERN = Pattern + .compile("^/?([^/]+).*"); - /** - * <p> - * Gets a UI by name. Returns <code>null</code> if the application is - * not running or it does not contain a window corresponding to the - * name. - * </p> - * - * @param name - * the name of the requested window - * @return a UI corresponding to the name, or <code>null</code> to use - * the default window - */ - public UI.LegacyWindow getWindow(String name) { - return legacyUINames.get(name); - } - - /** - * Counter to get unique names for windows with no explicit name - */ - private int namelessUIIndex = 0; - - /** - * Adds a new browser level window to this application. Please note that - * UI doesn't have a name that is used in the URL - to add a named - * window you should instead use {@link #addWindow(UI, String)} - * - * @param uI - * the UI window to add to the application - * @return returns the name that has been assigned to the window - * - * @see #addWindow(UI, String) - */ - public void addWindow(UI.LegacyWindow uI) { - if (uI.getName() == null) { - String name = Integer.toString(namelessUIIndex++); - uI.setName(name); - } - - legacyUINames.put(uI.getName(), uI); - uI.setApplication(this); - } + private UI.LegacyWindow mainWindow; + private String theme; - /** - * Removes the specified window from the application. This also removes - * all name mappings for the window (see {@link #addWindow(UI, String) - * and #getWindowName(UI)}. - * - * <p> - * Note that removing window from the application does not close the - * browser window - the window is only removed from the server-side. - * </p> - * - * @param uI - * the UI to remove - */ - public void removeWindow(UI.LegacyWindow uI) { - for (Entry<String, UI.LegacyWindow> entry : legacyUINames - .entrySet()) { - if (entry.getValue() == uI) { - legacyUINames.remove(entry.getKey()); - } - } - } - - /** - * Gets the set of windows contained by the application. - * - * <p> - * Note that the returned set of windows can not be modified. - * </p> - * - * @return the unmodifiable collection of windows. - */ - public Collection<UI.LegacyWindow> getWindows() { - return Collections.unmodifiableCollection(legacyUINames.values()); - } - } + private Map<String, UI.LegacyWindow> legacyUINames = new HashMap<String, UI.LegacyWindow>(); /** - * An event sent to {@link #start(ApplicationStartEvent)} when a new - * Application is being started. + * Sets the main window of this application. Setting window as a main window + * of this application also adds the window to this application. * - * @since 7.0 + * @param mainWindow + * the UI to set as the default window */ - public static class ApplicationStartEvent implements Serializable { - private final URL applicationUrl; - - private final DeploymentConfiguration configuration; - - private final ApplicationContext context; - - /** - * @param applicationUrl - * the URL the application should respond to. - * @param configuration - * the deployment configuration for the application. - * @param context - * the context application will be running in. - */ - public ApplicationStartEvent(URL applicationUrl, - DeploymentConfiguration configuration, - ApplicationContext context) { - this.applicationUrl = applicationUrl; - this.configuration = configuration; - this.context = context; + public void setMainWindow(UI.LegacyWindow mainWindow) { + if (this.mainWindow != null) { + throw new IllegalStateException("mainWindow has already been set"); } - - /** - * Gets the URL the application should respond to. - * - * @return the URL the application should respond to or - * <code>null</code> if the URL is not defined. - * - * @see Application#getURL() - */ - public URL getApplicationUrl() { - return applicationUrl; - } - - /** - * Returns the deployment configuration used by this application. - * - * @return the deployment configuration. - */ - public DeploymentConfiguration getConfiguration() { - return configuration; - } - - /** - * Gets the context application will be running in. - * - * @return the context application will be running in. - * - * @see Application#getContext() - */ - public ApplicationContext getContext() { - return context; + if (mainWindow.getSession() == null) { + mainWindow.setSession(VaadinSession.getCurrent()); + } else if (mainWindow.getSession() != VaadinSession.getCurrent()) { + throw new IllegalStateException( + "mainWindow is attached to another application"); } - } - - private final static Logger logger = Logger.getLogger(Application.class - .getName()); - - /** - * Application context the application is running in. - */ - private ApplicationContext context; - - /** - * Deployment configuration for the application. - */ - private DeploymentConfiguration configuration; - - /** - * The application's URL. - */ - private URL applicationUrl; - - /** - * Application status. - */ - private volatile boolean applicationIsRunning = false; - - /** - * Default locale of the application. - */ - 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; - - /** - * Application wide error handler which is used by default if an error is - * left unhandled. - */ - private Terminal.ErrorListener errorHandler = this; - - /** - * The converter factory that is used to provide default converters for the - * application. - */ - private ConverterFactory converterFactory = new DefaultConverterFactory(); - - private LinkedList<RequestHandler> requestHandlers = new LinkedList<RequestHandler>(); - - private int nextUIId = 0; - private Map<Integer, UI> uIs = new HashMap<Integer, UI>(); - - private final Map<String, Integer> retainOnRefreshUIs = new HashMap<String, Integer>(); - - private final EventRouter eventRouter = new EventRouter(); - - private List<UIProvider> uiProviders = new LinkedList<UIProvider>(); - - private GlobalResourceHandler globalResourceHandler; - - /** - * Gets the URL of the application. - * - * <p> - * This is the URL what can be entered to a browser window to start the - * application. Navigating to the application URL shows the main window ( - * {@link #getMainWindow()}) of the application. Note that the main window - * can also be shown by navigating to the window url ( - * {@link com.vaadin.ui.Window#getURL()}). - * </p> - * - * @return the application's URL. - */ - public URL getURL() { - return applicationUrl; - } - - /** - * Ends the Application. - * <p> - * In effect this will cause the application stop returning any windows when - * asked. When the application is closed, close events are fired for its - * UIs, its state is removed from the 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. - */ - public void close() { - applicationIsRunning = false; - for (UI ui : getUIs()) { - ui.fireCloseEvent(); + if (UI.getCurrent() == null) { + // Assume setting a main window from Application.init if there's + // no current UI -> set the main window as the current UI + UI.setCurrent(mainWindow); } + this.mainWindow = mainWindow; } - /** - * Starts the application on the given URL. - * - * <p> - * This method is called by Vaadin framework when a user navigates to the - * application. After this call the application corresponds to the given URL - * and it will return windows when asked for them. There is no need to call - * this method directly. - * </p> - * - * <p> - * Application properties are defined by servlet configuration object - * {@link javax.servlet.ServletConfig} and they are overridden by - * context-wide initialization parameters - * {@link javax.servlet.ServletContext}. - * </p> - * - * @param event - * the application start event containing details required for - * starting the application. - * - */ - public void start(ApplicationStartEvent event) { - applicationUrl = event.getApplicationUrl(); - configuration = event.getConfiguration(); - context = event.getContext(); + public void doInit() { + VaadinSession.getCurrent().setErrorHandler(this); init(); - applicationIsRunning = true; - } - - /** - * Tests if the application is running or if it has been finished. - * - * <p> - * Application starts running when its - * {@link #start(URL, Properties, ApplicationContext)} 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. - */ - public boolean isRunning() { - return applicationIsRunning; - } - - /** - * <p> - * Main initializer of the application. The <code>init</code> method is - * called by the framework when the application is started, and it should - * perform whatever initialization operations the application needs. - * </p> - */ - public void init() { - // Default implementation does nothing - } - - /** - * Returns the properties of this application as specified in the deployment - * configuration. - * - * @return Application properties - */ - protected Properties getProperties() { - return configuration.getInitParameters(); - } - - /** - * Returns an enumeration of all the names in this application. - * - * <p> - * See {@link #start(URL, Properties, ApplicationContext)} how properties - * are defined. - * </p> - * - * @return an enumeration of all the keys in this property list, including - * the keys in the default property list. - * - */ - public Enumeration<?> getPropertyNames() { - return getProperties().propertyNames(); - } - - /** - * Searches for the property with the specified name in this application. - * This method returns <code>null</code> if the property is not found. - * - * See {@link #start(URL, Properties, ApplicationContext)} how properties - * are defined. - * - * @param name - * the name of the property. - * @return the value in this property list with the specified key value. - */ - public String getProperty(String name) { - return getProperties().getProperty(name); - } - - /** - * Gets the default locale for this application. - * - * By default this is the preferred locale of the user using the - * application. In most cases it is read from the browser defaults. - * - * @return the locale of this application. - */ - public Locale getLocale() { - if (locale != null) { - return locale; - } - return Locale.getDefault(); } - /** - * Sets the default locale for this application. - * - * By default this is the preferred locale of the user using the - * application. In most cases it is read from the browser defaults. - * - * @param locale - * the Locale object. - * - */ - public void setLocale(Locale locale) { - this.locale = locale; - } - - /** - * Window detach event. - * - * This event is sent each time a window is removed from the application - * with {@link com.vaadin.Application#removeWindow(Window)}. - */ - public static class WindowDetachEvent extends EventObject { - - private final Window window; - - /** - * Creates a event. - * - * @param application - * the application to which the detached window belonged. - * @param window - * the Detached window. - */ - public WindowDetachEvent(Application application, Window window) { - super(application); - this.window = window; - } + protected abstract void init(); - /** - * Gets the detached window. - * - * @return the detached window. - */ - public Window getWindow() { - return window; - } - - /** - * Gets the application from which the window was detached. - * - * @return the Application. - */ - public Application getApplication() { - return (Application) getSource(); - } - } - - /** - * Window attach event. - * - * This event is sent each time a window is attached tothe application with - * {@link com.vaadin.Application#addWindow(Window)}. - */ - public static class WindowAttachEvent extends EventObject { - - private final Window window; - - /** - * Creates a event. - * - * @param application - * the application to which the detached window belonged. - * @param window - * the Attached window. - */ - public WindowAttachEvent(Application application, Window window) { - super(application); - this.window = window; - } - - /** - * Gets the attached window. - * - * @return the attached window. - */ - public Window getWindow() { - return window; - } - - /** - * Gets the application to which the window was attached. - * - * @return the Application. - */ - public Application getApplication() { - return (Application) getSource(); + @Override + public Class<? extends UI> getUIClass(VaadinSession application, + WrappedRequest request) { + UI uiInstance = getUIInstance(request); + if (uiInstance != null) { + return uiInstance.getClass(); } + return null; } - /** - * Window attach listener interface. - */ - public interface WindowAttachListener extends Serializable { - - /** - * Window attached - * - * @param event - * the window attach event. - */ - public void windowAttached(WindowAttachEvent event); - } - - /** - * Window detach listener interface. - */ - public interface WindowDetachListener extends Serializable { - - /** - * Window detached. - * - * @param event - * the window detach event. - */ - public void windowDetached(WindowDetachEvent event); - } - - /** - * 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. - */ - public String getLogoutURL() { - return logoutURL; + @Override + public UI createInstance(VaadinSession application, Class<? extends UI> type, + WrappedRequest request) { + return getUIInstance(request); } - /** - * 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. - */ - public void setLogoutURL(String logoutURL) { - this.logoutURL = logoutURL; + @Override + public String getThemeForUI(WrappedRequest request, + Class<? extends UI> uiClass) { + return theme; } - /** - * <p> - * Invoked by the terminal on any exception that occurs in application and - * is thrown by the <code>setVariable</code> to the terminal. The default - * implementation sets the exceptions as <code>ComponentErrors</code> to the - * component that initiated the exception and prints stack trace to standard - * error stream. - * </p> - * <p> - * You can safely override this method in your application in order to - * direct the errors to some other destination (for example log). - * </p> - * - * @param event - * the change event. - * @see com.vaadin.server.Terminal.ErrorListener#terminalError(com.vaadin.server.Terminal.ErrorEvent) - */ - @Override - public void terminalError(Terminal.ErrorEvent event) { - final Throwable t = event.getThrowable(); - if (t instanceof SocketException) { - // Most likely client browser closed socket - getLogger().info( - "SocketException in CommunicationManager." - + " Most likely client (browser) closed socket."); - return; - } - - // Finds the original source of the error/exception - Object owner = null; - if (event instanceof VariableOwner.ErrorEvent) { - owner = ((VariableOwner.ErrorEvent) event).getVariableOwner(); - } else if (event instanceof ChangeVariablesErrorEvent) { - owner = ((ChangeVariablesErrorEvent) event).getComponent(); - } - - // Shows the error in AbstractComponent - if (owner instanceof AbstractComponent) { - ((AbstractComponent) owner).setComponentError(AbstractErrorMessage - .getErrorMessageForException(t)); + public String getPageTitleForUI(WrappedRequest request, + Class<? extends UI> uiClass) { + UI uiInstance = getUIInstance(request); + if (uiInstance != null) { + return uiInstance.getCaption(); + } else { + return super.getPageTitleForUI(request, uiClass); } - - // also print the error on console - getLogger().log(Level.SEVERE, "Terminal error:", t); - } - - /** - * Gets the application context. - * <p> - * The application context is the environment where the application is - * running in. The actual implementation class of may contains quite a lot - * more functionality than defined in the {@link ApplicationContext} - * interface. - * </p> - * <p> - * By default, when you are deploying your application to a servlet - * container, the implementation class is {@link ServletApplicationContext} - * - you can safely cast to this class and use the methods from there. When - * you are deploying your application as a portlet, context implementation - * is {@link PortletApplicationContext}. - * </p> - * - * @return the application context. - */ - public ApplicationContext getContext() { - return context; } /** - * Gets the application error handler. + * Gets the mainWindow of the application. * - * The default error handler is the application itself. - * - * @return Application error handler - */ - public Terminal.ErrorListener getErrorHandler() { - return errorHandler; - } - - /** - * Sets the application error handler. - * - * The default error handler is the application itself. By overriding this, - * you can redirect the error messages to your selected target (log for - * example). - * - * @param errorHandler - */ - public void setErrorHandler(Terminal.ErrorListener errorHandler) { - this.errorHandler = errorHandler; - } - - /** - * Gets the {@link ConverterFactory} used to locate a suitable - * {@link Converter} for fields in the application. - * - * See {@link #setConverterFactory(ConverterFactory)} for more details - * - * @return The converter factory used in the application - */ - public ConverterFactory getConverterFactory() { - return converterFactory; - } - - /** - * Sets the {@link ConverterFactory} used to locate a suitable - * {@link Converter} for fields in the application. * <p> - * The {@link ConverterFactory} is used to find a suitable converter when - * binding data to a UI component and the data type does not match the UI - * component type, e.g. binding a Double to a TextField (which is based on a - * String). + * The main window is the window attached to the application URL ( + * {@link #getURL()}) and thus which is show by default to the user. * </p> * <p> - * The {@link Converter} for an individual field can be overridden using - * {@link AbstractField#setConverter(Converter)} and for individual property - * ids in a {@link Table} using - * {@link Table#setConverter(Object, Converter)}. + * Note that each application must have at least one main window. * </p> - * <p> - * The converter factory must never be set to null. * - * @param converterFactory - * The converter factory used in the application + * @return the UI used as the default window */ - public void setConverterFactory(ConverterFactory converterFactory) { - this.converterFactory = converterFactory; + public UI.LegacyWindow getMainWindow() { + return mainWindow; } - /** - * Contains the system messages used to notify the user about various - * critical situations that can occur. - * <p> - * Customize by overriding the static - * {@link Application#getSystemMessages()} and returning - * {@link CustomizedSystemMessages}. - * </p> - * <p> - * The defaults defined in this class are: - * <ul> - * <li><b>sessionExpiredURL</b> = null</li> - * <li><b>sessionExpiredNotificationEnabled</b> = true</li> - * <li><b>sessionExpiredCaption</b> = ""</li> - * <li><b>sessionExpiredMessage</b> = - * "Take note of any unsaved data, and <u>click here</u> to continue."</li> - * <li><b>communicationErrorURL</b> = null</li> - * <li><b>communicationErrorNotificationEnabled</b> = true</li> - * <li><b>communicationErrorCaption</b> = "Communication problem"</li> - * <li><b>communicationErrorMessage</b> = - * "Take note of any unsaved data, and <u>click here</u> to continue."</li> - * <li><b>internalErrorURL</b> = null</li> - * <li><b>internalErrorNotificationEnabled</b> = true</li> - * <li><b>internalErrorCaption</b> = "Internal error"</li> - * <li><b>internalErrorMessage</b> = "Please notify the administrator.<br/> - * Take note of any unsaved data, and <u>click here</u> to continue."</li> - * <li><b>outOfSyncURL</b> = null</li> - * <li><b>outOfSyncNotificationEnabled</b> = true</li> - * <li><b>outOfSyncCaption</b> = "Out of sync"</li> - * <li><b>outOfSyncMessage</b> = "Something has caused us to be out of sync - * with the server.<br/> - * Take note of any unsaved data, and <u>click here</u> to re-sync."</li> - * <li><b>cookiesDisabledURL</b> = null</li> - * <li><b>cookiesDisabledNotificationEnabled</b> = true</li> - * <li><b>cookiesDisabledCaption</b> = "Cookies disabled"</li> - * <li><b>cookiesDisabledMessage</b> = "This application requires cookies to - * function.<br/> - * Please enable cookies in your browser and <u>click here</u> to try again. - * </li> - * </ul> - * </p> - * - */ - public static class SystemMessages implements Serializable { - protected String sessionExpiredURL = null; - protected boolean sessionExpiredNotificationEnabled = true; - protected String sessionExpiredCaption = "Session Expired"; - protected String sessionExpiredMessage = "Take note of any unsaved data, and <u>click here</u> to continue."; - - protected String communicationErrorURL = null; - protected boolean communicationErrorNotificationEnabled = true; - protected String communicationErrorCaption = "Communication problem"; - protected String communicationErrorMessage = "Take note of any unsaved data, and <u>click here</u> to continue."; - - protected String authenticationErrorURL = null; - protected boolean authenticationErrorNotificationEnabled = true; - protected String authenticationErrorCaption = "Authentication problem"; - protected String authenticationErrorMessage = "Take note of any unsaved data, and <u>click here</u> to continue."; - - protected String internalErrorURL = null; - protected boolean internalErrorNotificationEnabled = true; - protected String internalErrorCaption = "Internal error"; - protected String internalErrorMessage = "Please notify the administrator.<br/>Take note of any unsaved data, and <u>click here</u> to continue."; - - protected String outOfSyncURL = null; - protected boolean outOfSyncNotificationEnabled = true; - protected String outOfSyncCaption = "Out of sync"; - protected String outOfSyncMessage = "Something has caused us to be out of sync with the server.<br/>Take note of any unsaved data, and <u>click here</u> to re-sync."; - - protected String cookiesDisabledURL = null; - protected boolean cookiesDisabledNotificationEnabled = true; - protected String cookiesDisabledCaption = "Cookies disabled"; - protected String cookiesDisabledMessage = "This application requires cookies to function.<br/>Please enable cookies in your browser and <u>click here</u> to try again."; - - /** - * Use {@link CustomizedSystemMessages} to customize - */ - private SystemMessages() { - - } - - /** - * @return null to indicate that the application will be restarted after - * session expired message has been shown. - */ - public String getSessionExpiredURL() { - return sessionExpiredURL; - } - - /** - * @return true to show session expiration message. - */ - public boolean isSessionExpiredNotificationEnabled() { - return sessionExpiredNotificationEnabled; - } - - /** - * @return "" to show no caption. - */ - public String getSessionExpiredCaption() { - return (sessionExpiredNotificationEnabled ? sessionExpiredCaption - : null); - } - - /** - * @return - * "Take note of any unsaved data, and <u>click here</u> to continue." - */ - public String getSessionExpiredMessage() { - return (sessionExpiredNotificationEnabled ? sessionExpiredMessage - : null); - } - - /** - * @return null to reload the application after communication error - * message. - */ - public String getCommunicationErrorURL() { - return communicationErrorURL; - } - - /** - * @return true to show the communication error message. - */ - public boolean isCommunicationErrorNotificationEnabled() { - return communicationErrorNotificationEnabled; - } - - /** - * @return "Communication problem" - */ - public String getCommunicationErrorCaption() { - return (communicationErrorNotificationEnabled ? communicationErrorCaption - : null); - } - - /** - * @return - * "Take note of any unsaved data, and <u>click here</u> to continue." - */ - public String getCommunicationErrorMessage() { - return (communicationErrorNotificationEnabled ? communicationErrorMessage - : null); - } - - /** - * @return null to reload the application after authentication error - * message. - */ - public String getAuthenticationErrorURL() { - return authenticationErrorURL; - } - - /** - * @return true to show the authentication error message. - */ - public boolean isAuthenticationErrorNotificationEnabled() { - return authenticationErrorNotificationEnabled; - } - - /** - * @return "Authentication problem" - */ - public String getAuthenticationErrorCaption() { - return (authenticationErrorNotificationEnabled ? authenticationErrorCaption - : null); - } - - /** - * @return - * "Take note of any unsaved data, and <u>click here</u> to continue." - */ - public String getAuthenticationErrorMessage() { - return (authenticationErrorNotificationEnabled ? authenticationErrorMessage - : null); - } - - /** - * @return null to reload the current URL after internal error message - * has been shown. - */ - public String getInternalErrorURL() { - return internalErrorURL; - } - - /** - * @return true to enable showing of internal error message. - */ - public boolean isInternalErrorNotificationEnabled() { - return internalErrorNotificationEnabled; - } - - /** - * @return "Internal error" - */ - public String getInternalErrorCaption() { - return (internalErrorNotificationEnabled ? internalErrorCaption - : null); - } - - /** - * @return "Please notify the administrator.<br/> - * Take note of any unsaved data, and <u>click here</u> to - * continue." - */ - public String getInternalErrorMessage() { - return (internalErrorNotificationEnabled ? internalErrorMessage - : null); - } - - /** - * @return null to reload the application after out of sync message. - */ - public String getOutOfSyncURL() { - return outOfSyncURL; - } - - /** - * @return true to enable showing out of sync message - */ - public boolean isOutOfSyncNotificationEnabled() { - return outOfSyncNotificationEnabled; - } - - /** - * @return "Out of sync" - */ - public String getOutOfSyncCaption() { - return (outOfSyncNotificationEnabled ? outOfSyncCaption : null); - } - - /** - * @return "Something has caused us to be out of sync with the server.<br/> - * Take note of any unsaved data, and <u>click here</u> to - * re-sync." - */ - public String getOutOfSyncMessage() { - return (outOfSyncNotificationEnabled ? outOfSyncMessage : null); - } - - /** - * Returns the URL the user should be redirected to after dismissing the - * "you have to enable your cookies" message. Typically null. - * - * @return A URL the user should be redirected to after dismissing the - * message or null to reload the current URL. - */ - public String getCookiesDisabledURL() { - return cookiesDisabledURL; - } - - /** - * Determines if "cookies disabled" messages should be shown to the end - * user or not. If the notification is disabled the user will be - * immediately redirected to the URL returned by - * {@link #getCookiesDisabledURL()}. - * - * @return true to show "cookies disabled" messages to the end user, - * false to redirect to the given URL directly - */ - public boolean isCookiesDisabledNotificationEnabled() { - return cookiesDisabledNotificationEnabled; - } - - /** - * Returns the caption of the message shown to the user when cookies are - * disabled in the browser. - * - * @return The caption of the "cookies disabled" message - */ - public String getCookiesDisabledCaption() { - return (cookiesDisabledNotificationEnabled ? cookiesDisabledCaption - : null); - } - - /** - * Returns the message shown to the user when cookies are disabled in - * the browser. - * - * @return The "cookies disabled" message - */ - public String getCookiesDisabledMessage() { - return (cookiesDisabledNotificationEnabled ? cookiesDisabledMessage - : null); - } - - } - - /** - * Contains the system messages used to notify the user about various - * critical situations that can occur. - * <p> - * Vaadin gets the SystemMessages from your application by calling a static - * getSystemMessages() method. By default the - * Application.getSystemMessages() is used. You can customize this by - * defining a static MyApplication.getSystemMessages() and returning - * CustomizedSystemMessages. Note that getSystemMessages() is static - - * changing the system messages will by default change the message for all - * users of the application. - * </p> - * <p> - * The default behavior is to show a notification, and restart the - * application the the user clicks the message. <br/> - * Instead of restarting the application, you can set a specific URL that - * the user is taken to.<br/> - * Setting both caption and message to null will restart the application (or - * go to the specified URL) without displaying a notification. - * set*NotificationEnabled(false) will achieve the same thing. - * </p> - * <p> - * The situations are: - * <li>Session expired: the user session has expired, usually due to - * inactivity.</li> - * <li>Communication error: the client failed to contact the server, or the - * server returned and invalid response.</li> - * <li>Internal error: unhandled critical server error (e.g out of memory, - * database crash) - * <li>Out of sync: the client is not in sync with the server. E.g the user - * opens two windows showing the same application, but the application does - * not support this and uses the same Window instance. When the user makes - * changes in one of the windows - the other window is no longer in sync, - * and (for instance) pressing a button that is no longer present in the UI - * will cause a out-of-sync -situation. - * </p> - */ - - public static class CustomizedSystemMessages extends SystemMessages - implements Serializable { - - /** - * Sets the URL to go to when the session has expired. - * - * @param sessionExpiredURL - * the URL to go to, or null to reload current - */ - public void setSessionExpiredURL(String sessionExpiredURL) { - this.sessionExpiredURL = sessionExpiredURL; - } - - /** - * Enables or disables the notification. If disabled, the set URL (or - * current) is loaded directly when next transaction between server and - * client happens. - * - * @param sessionExpiredNotificationEnabled - * true = enabled, false = disabled - */ - public void setSessionExpiredNotificationEnabled( - boolean sessionExpiredNotificationEnabled) { - this.sessionExpiredNotificationEnabled = sessionExpiredNotificationEnabled; - } - - /** - * Sets the caption of the notification. Set to null for no caption. If - * both caption and message are null, client automatically forwards to - * sessionExpiredUrl after timeout timer expires. Timer uses value read - * from HTTPSession.getMaxInactiveInterval() - * - * @param sessionExpiredCaption - * the caption - */ - public void setSessionExpiredCaption(String sessionExpiredCaption) { - this.sessionExpiredCaption = sessionExpiredCaption; - } - - /** - * Sets the message of the notification. Set to null for no message. If - * both caption and message are null, client automatically forwards to - * sessionExpiredUrl after timeout timer expires. Timer uses value read - * from HTTPSession.getMaxInactiveInterval() - * - * @param sessionExpiredMessage - * the message - */ - public void setSessionExpiredMessage(String sessionExpiredMessage) { - this.sessionExpiredMessage = sessionExpiredMessage; - } - - /** - * Sets the URL to go to when there is a authentication error. - * - * @param authenticationErrorURL - * the URL to go to, or null to reload current - */ - public void setAuthenticationErrorURL(String authenticationErrorURL) { - this.authenticationErrorURL = authenticationErrorURL; - } - - /** - * Enables or disables the notification. If disabled, the set URL (or - * current) is loaded directly. - * - * @param authenticationErrorNotificationEnabled - * true = enabled, false = disabled - */ - public void setAuthenticationErrorNotificationEnabled( - boolean authenticationErrorNotificationEnabled) { - this.authenticationErrorNotificationEnabled = authenticationErrorNotificationEnabled; - } - - /** - * Sets the caption of the notification. Set to null for no caption. If - * both caption and message is null, the notification is disabled; - * - * @param authenticationErrorCaption - * the caption - */ - public void setAuthenticationErrorCaption( - String authenticationErrorCaption) { - this.authenticationErrorCaption = authenticationErrorCaption; - } - - /** - * Sets the message of the notification. Set to null for no message. If - * both caption and message is null, the notification is disabled; - * - * @param authenticationErrorMessage - * the message - */ - public void setAuthenticationErrorMessage( - String authenticationErrorMessage) { - this.authenticationErrorMessage = authenticationErrorMessage; - } - - /** - * Sets the URL to go to when there is a communication error. - * - * @param communicationErrorURL - * the URL to go to, or null to reload current - */ - public void setCommunicationErrorURL(String communicationErrorURL) { - this.communicationErrorURL = communicationErrorURL; - } - - /** - * Enables or disables the notification. If disabled, the set URL (or - * current) is loaded directly. - * - * @param communicationErrorNotificationEnabled - * true = enabled, false = disabled - */ - public void setCommunicationErrorNotificationEnabled( - boolean communicationErrorNotificationEnabled) { - this.communicationErrorNotificationEnabled = communicationErrorNotificationEnabled; - } - - /** - * Sets the caption of the notification. Set to null for no caption. If - * both caption and message is null, the notification is disabled; - * - * @param communicationErrorCaption - * the caption - */ - public void setCommunicationErrorCaption( - String communicationErrorCaption) { - this.communicationErrorCaption = communicationErrorCaption; - } - - /** - * Sets the message of the notification. Set to null for no message. If - * both caption and message is null, the notification is disabled; - * - * @param communicationErrorMessage - * the message - */ - public void setCommunicationErrorMessage( - String communicationErrorMessage) { - this.communicationErrorMessage = communicationErrorMessage; - } - - /** - * Sets the URL to go to when an internal error occurs. - * - * @param internalErrorURL - * the URL to go to, or null to reload current - */ - public void setInternalErrorURL(String internalErrorURL) { - this.internalErrorURL = internalErrorURL; - } - - /** - * Enables or disables the notification. If disabled, the set URL (or - * current) is loaded directly. - * - * @param internalErrorNotificationEnabled - * true = enabled, false = disabled - */ - public void setInternalErrorNotificationEnabled( - boolean internalErrorNotificationEnabled) { - this.internalErrorNotificationEnabled = internalErrorNotificationEnabled; - } - - /** - * Sets the caption of the notification. Set to null for no caption. If - * both caption and message is null, the notification is disabled; - * - * @param internalErrorCaption - * the caption - */ - public void setInternalErrorCaption(String internalErrorCaption) { - this.internalErrorCaption = internalErrorCaption; - } - - /** - * Sets the message of the notification. Set to null for no message. If - * both caption and message is null, the notification is disabled; - * - * @param internalErrorMessage - * the message - */ - public void setInternalErrorMessage(String internalErrorMessage) { - this.internalErrorMessage = internalErrorMessage; - } - - /** - * Sets the URL to go to when the client is out-of-sync. - * - * @param outOfSyncURL - * the URL to go to, or null to reload current - */ - public void setOutOfSyncURL(String outOfSyncURL) { - this.outOfSyncURL = outOfSyncURL; - } - - /** - * Enables or disables the notification. If disabled, the set URL (or - * current) is loaded directly. - * - * @param outOfSyncNotificationEnabled - * true = enabled, false = disabled - */ - public void setOutOfSyncNotificationEnabled( - boolean outOfSyncNotificationEnabled) { - this.outOfSyncNotificationEnabled = outOfSyncNotificationEnabled; - } - - /** - * Sets the caption of the notification. Set to null for no caption. If - * both caption and message is null, the notification is disabled; - * - * @param outOfSyncCaption - * the caption - */ - public void setOutOfSyncCaption(String outOfSyncCaption) { - this.outOfSyncCaption = outOfSyncCaption; - } - - /** - * Sets the message of the notification. Set to null for no message. If - * both caption and message is null, the notification is disabled; - * - * @param outOfSyncMessage - * the message - */ - public void setOutOfSyncMessage(String outOfSyncMessage) { - this.outOfSyncMessage = outOfSyncMessage; - } - - /** - * Sets the URL to redirect to when the browser has cookies disabled. - * - * @param cookiesDisabledURL - * the URL to redirect to, or null to reload the current URL - */ - public void setCookiesDisabledURL(String cookiesDisabledURL) { - this.cookiesDisabledURL = cookiesDisabledURL; - } - - /** - * Enables or disables the notification for "cookies disabled" messages. - * If disabled, the URL returned by {@link #getCookiesDisabledURL()} is - * loaded directly. - * - * @param cookiesDisabledNotificationEnabled - * true to enable "cookies disabled" messages, false - * otherwise - */ - public void setCookiesDisabledNotificationEnabled( - boolean cookiesDisabledNotificationEnabled) { - this.cookiesDisabledNotificationEnabled = cookiesDisabledNotificationEnabled; - } - - /** - * Sets the caption of the "cookies disabled" notification. Set to null - * for no caption. If both caption and message is null, the notification - * is disabled. - * - * @param cookiesDisabledCaption - * the caption for the "cookies disabled" notification - */ - public void setCookiesDisabledCaption(String cookiesDisabledCaption) { - this.cookiesDisabledCaption = cookiesDisabledCaption; - } - - /** - * Sets the message of the "cookies disabled" notification. Set to null - * for no message. If both caption and message is null, the notification - * is disabled. - * - * @param cookiesDisabledMessage - * the message for the "cookies disabled" notification - */ - public void setCookiesDisabledMessage(String cookiesDisabledMessage) { - this.cookiesDisabledMessage = cookiesDisabledMessage; - } - - } - - /** - * Application error is an error message defined on the application level. - * - * When an error occurs on the application level, this error message type - * should be used. This indicates that the problem is caused by the - * application - not by the user. - */ - public class ApplicationError implements Terminal.ErrorEvent { - private final Throwable throwable; - - public ApplicationError(Throwable throwable) { - this.throwable = throwable; - } - - @Override - public Throwable getThrowable() { - return throwable; - } - - } - - /** - * Gets the UI class for a request for which no UI is already known. This - * method is called when the framework processes a request that does not - * originate from an existing UI instance. This typically happens when a - * host page is requested. - * <p> - * Subclasses of Application may override this method to provide custom - * logic for choosing what kind of UI to use. - * <p> - * The default implementation in {@link Application} uses the - * {@value #UI_PARAMETER} parameter from web.xml for finding the name of the - * UI class. If {@link DeploymentConfiguration#getClassLoader()} does not - * return <code>null</code>, the returned {@link ClassLoader} is used for - * loading the UI class. Otherwise the {@link ClassLoader} used to load this - * class is used. - * - * </p> - * - * @param request - * the wrapped request for which a UI is needed - * @return a UI instance to use for the request - * - * @see UI - * @see WrappedRequest#getBrowserDetails() - * - * @since 7.0 - */ - public Class<? extends UI> getUIClass(WrappedRequest request) { - // Iterate in reverse order - check newest provider first - int providersSize = uiProviders.size(); - if (providersSize == 0) { - throw new IllegalStateException("There are no UI providers"); - } - for (int i = providersSize - 1; i >= 0; i--) { - UIProvider provider = uiProviders.get(i); - - Class<? extends UI> uiClass = provider.getUIClass(this, request); - - if (uiClass != null) { - return uiClass; - } - } - - throw new RuntimeException( - "No UI provider returned an UI class for request"); - } - - /** - * Creates an UI instance for a request for which no UI is already known. - * This method is called when the framework processes a request that does - * not originate from an existing UI instance. This typically happens when a - * host page is requested. - * <p> - * Subclasses of Application may override this method to provide custom - * logic for choosing how to create a suitable UI or for picking an already - * created UI. If an existing UI is picked, care should be taken to avoid - * keeping the same UI open in multiple browser windows, as that will cause - * the states to go out of sync. - * </p> - * - * @param request - * @param uiClass - * @return - */ - protected <T extends UI> T createUIInstance(WrappedRequest request, - Class<T> uiClass) { - int providersSize = uiProviders.size(); - if (providersSize == 0) { - throw new IllegalStateException("There are no UI providers"); - } - - for (int i = providersSize - 1; i >= 0; i--) { - UIProvider provider = uiProviders.get(i); - - Class<? extends UI> providerClass = provider.getUIClass(this, - request); - if (providerClass != null) { - if (providerClass != uiClass) { - getLogger().warning( - "Mismatching UI classes. Expected " + uiClass - + " but got " + providerClass + " from " - + provider); - // Try with next provider if we didn't get the expected - // class - continue; - } - return uiClass.cast(provider.createInstance(this, uiClass, - request)); + 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); } } - - throw new RuntimeException( - "No UI provider created an UI instance for request"); - } - - /** - * Finds the theme to use for a specific UI. If no specific theme is - * required, <code>null</code> is returned. - * - * TODO Tell what the default implementation does once it does something. - * - * @param uI - * the UI to get a theme for - * @return the name of the theme, or <code>null</code> if the default theme - * should be used - * - * @since 7.0 - */ - public String getThemeForUI(WrappedRequest request, - Class<? extends UI> uiClass) { - Theme uiTheme = getAnnotationFor(uiClass, Theme.class); - if (uiTheme != null) { - return uiTheme.value(); - } else { - return null; + UI.LegacyWindow window = getWindow(name); + if (window != null) { + return window; } + return mainWindow; } /** - * Finds the widgetset to use for a specific UI. If no specific widgetset is - * required, <code>null</code> is returned. + * 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> - * The default implementation uses the @{@link Widgetset} annotation if it's - * defined for the UI class. - * - * @param request - * the wrapped request for which to get a widgetset - * @param uiClass - * the UI class to get a widgetset for - * @return the name of the widgetset, or <code>null</code> if the default - * widgetset should be used - * - * @since 7.0 + * {@inheritDoc} */ - public String getWidgetsetForUI(WrappedRequest request, - Class<? extends UI> uiClass) { - Widgetset uiWidgetset = getAnnotationFor(uiClass, Widgetset.class); - if (uiWidgetset != null) { - return uiWidgetset.value(); - } else { + @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; } } /** - * Helper to get an annotation for a class. If the annotation is not present - * on the target class, it's superclasses and implemented interfaces are - * also searched for the annotation. - * - * @param type - * the target class from which the annotation should be found - * @param annotationType - * the annotation type to look for - * @return an annotation of the given type, or <code>null</code> if the - * annotation is not present on the class - */ - private static <T extends Annotation> T getAnnotationFor(Class<?> type, - Class<T> annotationType) { - // Find from the class hierarchy - Class<?> currentType = type; - while (currentType != Object.class) { - T annotation = currentType.getAnnotation(annotationType); - if (annotation != null) { - return annotation; - } else { - currentType = currentType.getSuperclass(); - } - } - - // Find from an implemented interface - for (Class<?> iface : type.getInterfaces()) { - T annotation = iface.getAnnotation(annotationType); - if (annotation != null) { - return annotation; - } - } - - return null; - } - - /** - * Handles a request by passing it to each registered {@link RequestHandler} - * in turn until one produces a response. This method is used for requests - * that have not been handled by any specific functionality in the terminal - * implementation (e.g. {@link VaadinServlet}). - * <p> - * The request handlers are invoked in the revere order in which they were - * added to the application until a response has been produced. This means - * that the most recently added handler is used first and the first request - * handler that was added to the application is invoked towards the end - * unless any previous handler has already produced a response. - * </p> - * - * @param request - * the wrapped request to get information from - * @param response - * the response to which data can be written - * @return returns <code>true</code> if a {@link RequestHandler} has - * produced a response and <code>false</code> if no response has - * been written. - * @throws IOException - * - * @see #addRequestHandler(RequestHandler) - * @see RequestHandler - * - * @since 7.0 - */ - public boolean handleRequest(WrappedRequest request, - WrappedResponse response) throws IOException { - // Use a copy to avoid ConcurrentModificationException - for (RequestHandler handler : new ArrayList<RequestHandler>( - requestHandlers)) { - if (handler.handleRequest(this, request, response)) { - return true; - } - } - // If not handled - return false; - } - - /** - * Adds a request handler to this application. Request handlers can be added - * to provide responses to requests that are not handled by the default - * functionality of the framework. + * Sets the application's theme. * <p> - * Handlers are called in reverse order of addition, so the most recently - * added handler will be called first. + * Note that this theme can be overridden for a specific UI with + * {@link VaadinSession#getThemeForUI(UI)}. Setting theme to be + * <code>null</code> selects the default theme. For the available theme + * names, see the contents of the VAADIN/themes directory. * </p> * - * @param handler - * the request handler to add - * - * @see #handleRequest(WrappedRequest, WrappedResponse) - * @see #removeRequestHandler(RequestHandler) - * - * @since 7.0 + * @param theme + * the new theme for this application. */ - public void addRequestHandler(RequestHandler handler) { - requestHandlers.addFirst(handler); + public void setTheme(String theme) { + this.theme = theme; } /** - * Removes a request handler from the application. - * - * @param handler - * the request handler to remove + * Gets the application's theme. The application's theme is the default + * theme used by all the uIs for which a theme is not explicitly defined. If + * the application theme is not explicitly set, <code>null</code> is + * returned. * - * @since 7.0 + * @return the name of the application's theme. */ - public void removeRequestHandler(RequestHandler handler) { - requestHandlers.remove(handler); + public String getTheme() { + return theme; } /** - * Gets the request handlers that are registered to the application. The - * iteration order of the returned collection is the same as the order in - * which the request handlers will be invoked when a request is handled. - * - * @return a collection of request handlers, with the iteration order - * according to the order they would be invoked - * - * @see #handleRequest(WrappedRequest, WrappedResponse) - * @see #addRequestHandler(RequestHandler) - * @see #removeRequestHandler(RequestHandler) - * - * @since 7.0 - */ - public Collection<RequestHandler> getRequestHandlers() { - return Collections.unmodifiableCollection(requestHandlers); - } - - /** - * Thread local for keeping track of currently used application instance - * - * @since 7.0 - */ - private static final ThreadLocal<Application> currentApplication = new ThreadLocal<Application>(); - - /** - * Gets the currently used application. The current application is - * automatically defined when processing requests to the server. In other - * cases, (e.g. from background threads), the current application is not - * automatically defined. - * - * @return the current application instance if available, otherwise - * <code>null</code> - * - * @see #setCurrent(Application) - * - * @since 7.0 - */ - public static Application getCurrent() { - return currentApplication.get(); - } - - /** - * Sets the thread local for the current application. This method is used by - * the framework to set the current application whenever a new request is - * processed and it is cleared when the request has been processed. * <p> - * The application developer can also use this method to define the current - * application outside the normal request handling, e.g. when initiating - * custom background threads. + * Gets a UI by name. Returns <code>null</code> if the application is not + * running or it does not contain a window corresponding to the name. * </p> * - * @param application - * - * @see #getCurrent() - * @see ThreadLocal - * - * @since 7.0 + * @param name + * the name of the requested window + * @return a UI corresponding to the name, or <code>null</code> to use the + * default window */ - public static void setCurrent(Application application) { - currentApplication.set(application); + public UI.LegacyWindow getWindow(String name) { + return legacyUINames.get(name); } /** - * Check whether this application is in production mode. If an application - * is in production mode, certain debugging facilities are not available. - * - * @return the status of the production mode flag - * - * @since 7.0 + * Counter to get unique names for windows with no explicit name */ - public boolean isProductionMode() { - return configuration.isProductionMode(); - } - - public void addUIProvider(UIProvider uIProvider) { - uiProviders.add(uIProvider); - } - - public void removeUIProvider(UIProvider uIProvider) { - uiProviders.remove(uIProvider); - } + private int namelessUIIndex = 0; /** - * 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 + * Adds a new browser level window to this application. Please note that UI + * doesn't have a name that is used in the URL - to add a named window you + * should instead use {@link #addWindow(UI, String)} * - * @see #createUI(WrappedRequest) + * @param uI + * the UI window to add to the application + * @return returns the name that has been assigned to the window * - * @since 7.0 + * @see #addWindow(UI, String) */ - public UI getUIForRequest(WrappedRequest request) { - UI uI = UI.getCurrent(); - if (uI != null) { - return uI; + public void addWindow(UI.LegacyWindow uI) { + if (uI.getName() == null) { + String name = Integer.toString(namelessUIIndex++); + uI.setName(name); } - Integer uiId = getUIId(request); - - synchronized (this) { - BrowserDetails browserDetails = request.getBrowserDetails(); - boolean hasBrowserDetails = browserDetails != null - && browserDetails.getUriFragment() != null; - - uI = uIs.get(uiId); - Class<? extends UI> uiClass = null; - if (uI == null && hasBrowserDetails - && !retainOnRefreshUIs.isEmpty()) { - uiClass = getUIClass(request); - - // Check for a known UI - - @SuppressWarnings("null") - String windowName = browserDetails.getWindowName(); - Integer retainedUIId = retainOnRefreshUIs.get(windowName); - - if (retainedUIId != null) { - UI retainedUI = uIs.get(retainedUIId); - // We've had the same UI instance in a window with this - // name, but should we still use it? - if (retainedUI.getClass() == uiClass) { - uiId = retainedUIId; - uI = 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."); - } - } - } - - } // end synchronized block - - UI.setCurrent(uI); - - return uI; - } - - public UI createUI(WrappedRequest request) { - Class<? extends UI> uiClass = getUIClass(request); - - UI ui = createUIInstance(request, uiClass); - - // Initialize some fields for a newly created UI - if (ui.getApplication() == null) { - ui.setApplication(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 (isUiPreserved(request, uiClass)) { - // Remember this UI - String windowName = request.getBrowserDetails().getWindowName(); - if (windowName == null) { - getLogger().warning( - "There is no window.name available for UI " + uiClass - + " that should be preserved."); - } else { - retainOnRefreshUIs.put(windowName, uiId); - } - } - - return ui; + legacyUINames.put(uI.getName(), uI); + uI.setSession(VaadinSession.getCurrent()); } /** - * 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 - */ - private static Integer getUIId(WrappedRequest 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; - } - - /** - * Checks whether the same UI state should be reused if the framework can - * detect that the application is opened in a browser window where it has - * previously been open. The framework attempts to discover this by checking - * the value of window.name in the browser. - * - * @param request - * @param uiClass + * Removes the specified window from the application. This also removes all + * name mappings for the window (see {@link #addWindow(UI, String) and + * #getWindowName(UI)}. * - * @return <code>true</code>if the same UI instance should be reused e.g. - * when the browser window is refreshed. - */ - public boolean isUiPreserved(WrappedRequest request, - Class<? extends UI> uiClass) { - PreserveOnRefresh preserveOnRefresh = getAnnotationFor(uiClass, - PreserveOnRefresh.class); - return preserveOnRefresh != null; - } - - /** - * Gets all the uIs of this application. This includes uIs that have been - * requested but not yet initialized. Please note, that uIs are not - * automatically removed e.g. if the browser window is closed and that there - * is no way to manually remove a UI. Inactive uIs will thus not be released - * for GC until the entire application is released when the session has - * timed out (unless there are dangling references). Improved support for - * releasing unused uIs is planned for an upcoming alpha release of Vaadin - * 7. - * - * @return a collection of uIs belonging to this application - * - * @since 7.0 - */ - public Collection<UI> getUIs() { - return Collections.unmodifiableCollection(uIs.values()); - } - - private int connectorIdSequence = 0; - - /** - * Generate an id for the given Connector. Connectors must not call this - * method more than once, the first time they need an id. - * - * @param connector - * A connector that has not yet been assigned an id. - * @return A new id for the connector - */ - public String createConnectorId(ClientConnector connector) { - return String.valueOf(connectorIdSequence++); - } - - private static final Logger getLogger() { - return Logger.getLogger(Application.class.getName()); - } - - /** - * Returns a UI with the given id. * <p> - * This is meant for framework internal use. + * Note that removing window from the application does not close the browser + * window - the window is only removed from the server-side. * </p> * - * @param uiId - * The UI id - * @return The UI with the given id or null if not found - */ - public UI getUIById(int uiId) { - return uIs.get(uiId); - } - - /** - * Adds a listener that will be invoked when the bootstrap HTML is about to - * be generated. This can be used to modify the contents of the HTML that - * loads the Vaadin application in the browser and the HTTP headers that are - * included in the response serving the HTML. - * - * @see BootstrapListener#modifyBootstrapFragment(BootstrapFragmentResponse) - * @see BootstrapListener#modifyBootstrapPage(BootstrapPageResponse) - * - * @param listener - * the bootstrap listener to add - */ - public void addBootstrapListener(BootstrapListener listener) { - eventRouter.addListener(BootstrapFragmentResponse.class, listener, - BOOTSTRAP_FRAGMENT_METHOD); - eventRouter.addListener(BootstrapPageResponse.class, listener, - BOOTSTRAP_PAGE_METHOD); - } - - /** - * Remove a bootstrap listener that was previously added. - * - * @see #addBootstrapListener(BootstrapListener) - * - * @param listener - * the bootstrap listener to remove - */ - public void removeBootstrapListener(BootstrapListener listener) { - eventRouter.removeListener(BootstrapFragmentResponse.class, listener, - BOOTSTRAP_FRAGMENT_METHOD); - eventRouter.removeListener(BootstrapPageResponse.class, listener, - BOOTSTRAP_PAGE_METHOD); - } - - /** - * Fires a bootstrap event to all registered listeners. There are currently - * two supported events, both inheriting from {@link BootstrapResponse}: - * {@link BootstrapFragmentResponse} and {@link BootstrapPageResponse}. - * - * @param response - * the bootstrap response event for which listeners should be - * fired - */ - public void modifyBootstrapResponse(BootstrapResponse response) { - eventRouter.fireEvent(response); - } - - /** - * Removes all those UIs from the application for which {@link #isUIAlive} - * returns false. Close events are fired for the removed UIs. - * <p> - * Called by the framework at the end of every request. - * - * @see UI.CloseEvent - * @see UI.CloseListener - * @see #isUIAlive(UI) - * - * @since 7.0.0 + * @param uI + * the UI to remove */ - public void closeInactiveUIs() { - for (Iterator<UI> i = uIs.values().iterator(); i.hasNext();) { - UI ui = i.next(); - if (!isUIAlive(ui)) { - i.remove(); - retainOnRefreshUIs.values().remove(ui.getUIId()); - ui.fireCloseEvent(); - getLogger().info( - "Closed UI #" + ui.getUIId() + " due to inactivity"); + public void removeWindow(UI.LegacyWindow uI) { + for (Entry<String, UI.LegacyWindow> entry : legacyUINames.entrySet()) { + if (entry.getValue() == uI) { + legacyUINames.remove(entry.getKey()); } } } /** - * 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 - * inactive UI. Returns a negative number if heartbeat is disabled and - * timeout never occurs. + * Gets the set of windows contained by the application. * - * @see #getUidlRequestTimeout() - * @see #closeInactiveUIs() - * @see DeploymentConfiguration#getHeartbeatInterval() - * - * @since 7.0.0 + * <p> + * Note that the returned set of windows can not be modified. + * </p> * - * @return The heartbeat timeout in seconds or a negative number if timeout - * never occurs. + * @return the unmodifiable collection of windows. */ - protected int getHeartbeatTimeout() { - // Permit three missed heartbeats before closing the UI - return (int) (configuration.getHeartbeatInterval() * (3.1)); + public Collection<UI.LegacyWindow> getWindows() { + return Collections.unmodifiableCollection(legacyUINames.values()); } - /** - * Returns the number of seconds that must pass without a valid UIDL request - * being received from a UI before the UI is removed from the application, - * even though heartbeat requests are received. This is a lower bound; it - * might take longer to close an inactive UI. Returns a negative number if - * <p> - * This timeout only has effect if cleanup of inactive UIs is enabled; - * otherwise heartbeat requests are enough to extend UI lifetime - * indefinitely. - * - * @see DeploymentConfiguration#isIdleUICleanupEnabled() - * @see #getHeartbeatTimeout() - * @see #closeInactiveUIs() - * - * @since 7.0.0 - * - * @return The UIDL request timeout in seconds, or a negative number if - * timeout never occurs. - */ - protected int getUidlRequestTimeout() { - return configuration.isIdleUICleanupEnabled() ? getContext() - .getMaxInactiveInterval() : -1; + @Override + public void terminalError(ErrorEvent event) { + VaadinSession.getCurrent().terminalError(event); } - /** - * Returns whether the given UI is alive (the client-side actively - * communicates with the server) or whether it can be removed from the - * application and eventually collected. - * - * @since 7.0.0 - * - * @param ui - * The UI whose status to check - * @return true if the UI is alive, false if it could be removed. - */ - protected boolean isUIAlive(UI ui) { - long now = System.currentTimeMillis(); - if (getHeartbeatTimeout() >= 0 - && now - ui.getLastHeartbeatTime() > 1000 * getHeartbeatTimeout()) { - return false; - } - if (getUidlRequestTimeout() >= 0 - && now - ui.getLastUidlRequestTime() > 1000 * getUidlRequestTimeout()) { - return false; - } - return true; + public VaadinSession getContext() { + return VaadinSession.getCurrent(); } - /** - * Gets this application's global resource handler that takes care of - * serving connector resources that are not served by any single connector - * because e.g. because they are served with strong caching or because of - * legacy reasons. - * - * @param createOnDemand - * <code>true</code> if a resource handler should be initialized - * if there is no handler associated with this application. - * </code>false</code> if </code>null</code> should be returned - * if there is no registered handler. - * @return this application's global resource handler, or <code>null</code> - * if there is no handler and the createOnDemand parameter is - * <code>false</code>. - * - * @since 7.0.0 - */ - public GlobalResourceHandler getGlobalResourceHandler(boolean createOnDemand) { - if (globalResourceHandler == null && createOnDemand) { - globalResourceHandler = new GlobalResourceHandler(); - addRequestHandler(globalResourceHandler); - } + protected void close() { + VaadinSession.getCurrent().close(); + } - return globalResourceHandler; + public boolean isRunning() { + return VaadinSession.getCurrent().isRunning(); } - public String getPageTitleForUI(WrappedRequest request, - Class<? extends UI> uiClass) { - Title titleAnnotation = getAnnotationFor(uiClass, Title.class); - if (titleAnnotation == null) { - return null; - } else { - return titleAnnotation.value(); - } + public URL getURL() { + return VaadinSession.getCurrent().getURL(); } -} +}
\ No newline at end of file diff --git a/server/src/com/vaadin/DefaultApplicationConfiguration.java b/server/src/com/vaadin/DefaultApplicationConfiguration.java new file mode 100644 index 0000000000..2e2267193e --- /dev/null +++ b/server/src/com/vaadin/DefaultApplicationConfiguration.java @@ -0,0 +1,229 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin; + +import java.util.Properties; +import java.util.logging.Logger; + +import com.vaadin.server.ApplicationConfiguration; +import com.vaadin.server.Constants; + +public class DefaultApplicationConfiguration implements + ApplicationConfiguration { + private final Properties applicationProperties; + private boolean productionMode; + private boolean xsrfProtectionEnabled; + private int resourceCacheTime; + private int heartbeatInterval; + private boolean idleRootCleanupEnabled; + private final Class<?> systemPropertyBaseClass; + + public DefaultApplicationConfiguration(Class<?> systemPropertyBaseClass, + Properties applicationProperties) { + this.applicationProperties = applicationProperties; + this.systemPropertyBaseClass = systemPropertyBaseClass; + + checkProductionMode(); + checkXsrfProtection(); + checkResourceCacheTime(); + checkHeartbeatInterval(); + checkIdleUICleanup(); + } + + @Override + public String getApplicationOrSystemProperty(String propertyName, + String defaultValue) { + String val = null; + + // Try application properties + val = getApplicationProperty(propertyName); + if (val != null) { + return val; + } + + // Try system properties + val = getSystemProperty(propertyName); + if (val != null) { + return val; + } + + return defaultValue; + } + + /** + * Gets an system property value. + * + * @param parameterName + * the Name or the parameter. + * @return String value or null if not found + */ + protected String getSystemProperty(String parameterName) { + String val = null; + + String pkgName; + final Package pkg = systemPropertyBaseClass.getPackage(); + if (pkg != null) { + pkgName = pkg.getName(); + } else { + final String className = systemPropertyBaseClass.getName(); + pkgName = new String(className.toCharArray(), 0, + className.lastIndexOf('.')); + } + val = System.getProperty(pkgName + "." + parameterName); + if (val != null) { + return val; + } + + // Try lowercased system properties + val = System.getProperty(pkgName + "." + parameterName.toLowerCase()); + return val; + } + + /** + * Gets an application property value. + * + * @param parameterName + * the Name or the parameter. + * @return String value or null if not found + */ + public String getApplicationProperty(String parameterName) { + + String val = applicationProperties.getProperty(parameterName); + if (val != null) { + return val; + } + + // Try lower case application properties for backward compatibility with + // 3.0.2 and earlier + val = applicationProperties.getProperty(parameterName.toLowerCase()); + + return val; + } + + /** + * {@inheritDoc} + * + * The default is false. + */ + @Override + public boolean isProductionMode() { + return productionMode; + } + + /** + * {@inheritDoc} + * <p> + * The default is true. + */ + @Override + public boolean isXsrfProtectionEnabled() { + return xsrfProtectionEnabled; + } + + /** + * {@inheritDoc} + * <p> + * The default interval is 3600 seconds (1 hour). + */ + @Override + public int getResourceCacheTime() { + return resourceCacheTime; + } + + /** + * {@inheritDoc} + * <p> + * The default interval is 300 seconds (5 minutes). + */ + @Override + public int getHeartbeatInterval() { + return heartbeatInterval; + } + + @Override + public boolean isIdleUICleanupEnabled() { + return idleRootCleanupEnabled; + } + + /** + * Log a warning if Vaadin is not running in production mode. + */ + private void checkProductionMode() { + productionMode = getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_PRODUCTION_MODE, "false").equals( + "true"); + if (!productionMode) { + getLogger().warning(Constants.NOT_PRODUCTION_MODE_INFO); + } + } + + /** + * Log a warning if cross-site request forgery protection is disabled. + */ + private void checkXsrfProtection() { + xsrfProtectionEnabled = !getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION, "false") + .equals("true"); + if (!xsrfProtectionEnabled) { + getLogger().warning(Constants.WARNING_XSRF_PROTECTION_DISABLED); + } + } + + /** + * Log a warning if resource cache time is set but is not an integer. + */ + private void checkResourceCacheTime() { + try { + resourceCacheTime = Integer + .parseInt(getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_RESOURCE_CACHE_TIME, + "3600")); + } catch (NumberFormatException e) { + getLogger().warning( + Constants.WARNING_RESOURCE_CACHING_TIME_NOT_NUMERIC); + resourceCacheTime = 3600; + } + } + + private void checkHeartbeatInterval() { + try { + heartbeatInterval = Integer + .parseInt(getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_HEARTBEAT_RATE, "300")); + } catch (NumberFormatException e) { + getLogger().warning( + Constants.WARNING_HEARTBEAT_INTERVAL_NOT_NUMERIC); + heartbeatInterval = 300; + } + } + + private void checkIdleUICleanup() { + idleRootCleanupEnabled = getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_CLOSE_IDLE_UIS, "false").equals( + "true"); + } + + private Logger getLogger() { + return Logger.getLogger(getClass().getName()); + } + + @Override + public Properties getInitParameters() { + return applicationProperties; + } + +} diff --git a/server/src/com/vaadin/data/util/converter/ConverterUtil.java b/server/src/com/vaadin/data/util/converter/ConverterUtil.java index 36cd6d0859..a6014bb3eb 100644 --- a/server/src/com/vaadin/data/util/converter/ConverterUtil.java +++ b/server/src/com/vaadin/data/util/converter/ConverterUtil.java @@ -18,7 +18,7 @@ package com.vaadin.data.util.converter; import java.io.Serializable; import java.util.Locale; -import com.vaadin.Application; +import com.vaadin.server.VaadinSession; public class ConverterUtil implements Serializable { @@ -26,7 +26,7 @@ public class ConverterUtil implements Serializable { * Finds a converter that can convert from the given presentation type to * the given model type and back. Uses the given application to find a * {@link ConverterFactory} or, if application is null, uses the - * {@link Application#getCurrent()}. + * {@link VaadinSession#getCurrent()}. * * @param <PRESENTATIONTYPE> * The presentation type @@ -44,10 +44,10 @@ public class ConverterUtil implements Serializable { */ public static <PRESENTATIONTYPE, MODELTYPE> Converter<PRESENTATIONTYPE, MODELTYPE> getConverter( Class<PRESENTATIONTYPE> presentationType, - Class<MODELTYPE> modelType, Application application) { + Class<MODELTYPE> modelType, VaadinSession application) { Converter<PRESENTATIONTYPE, MODELTYPE> converter = null; if (application == null) { - application = Application.getCurrent(); + application = VaadinSession.getCurrent(); } if (application != null) { diff --git a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java index ffd432076f..17b89ccb20 100644 --- a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java +++ b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java @@ -19,14 +19,14 @@ package com.vaadin.data.util.converter; import java.util.Date; import java.util.logging.Logger; -import com.vaadin.Application; +import com.vaadin.server.VaadinSession; /** * Default implementation of {@link ConverterFactory}. Provides converters for * standard types like {@link String}, {@link Double} and {@link Date}. </p> * <p> * Custom converters can be provided by extending this class and using - * {@link Application#setConverterFactory(ConverterFactory)}. + * {@link VaadinSession#setConverterFactory(ConverterFactory)}. * </p> * * @author Vaadin Ltd diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java b/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java index 6258aed423..dee807c610 100644 --- a/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java +++ b/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java @@ -48,7 +48,7 @@ public class SourceIs extends ClientSideCriterion { int paintedComponents = 0; for (int i = 0; i < components.length; i++) { Component c = components[i]; - if (c.getApplication() != null) { + if (c.getUI() != null && c.getUI().getSession() != null) { target.addAttribute("component" + paintedComponents++, c); } else { Logger.getLogger(SourceIs.class.getName()) diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java index 09e366260b..f8fbbeeb4c 100644 --- a/server/src/com/vaadin/server/AbstractClientConnector.java +++ b/server/src/com/vaadin/server/AbstractClientConnector.java @@ -31,7 +31,6 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.logging.Logger; -import com.vaadin.Application; import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONObject; import com.vaadin.shared.communication.ClientRpc; @@ -343,11 +342,11 @@ public abstract class AbstractClientConnector implements ClientConnector { @Override public String getConnectorId() { if (connectorId == null) { - if (getApplication() == null) { + if (getSession() == null) { throw new RuntimeException( "Component must be attached to an application when getConnectorId() is called for the first time"); } - connectorId = getApplication().createConnectorId(this); + connectorId = getSession().createConnectorId(this); } return connectorId; } @@ -358,12 +357,12 @@ public abstract class AbstractClientConnector implements ClientConnector { * * @return The connector's application, or <code>null</code> if not attached */ - protected Application getApplication() { + protected VaadinSession getSession() { UI uI = getUI(); if (uI == null) { return null; } else { - return uI.getApplication(); + return uI.getSession(); } } @@ -502,7 +501,7 @@ public abstract class AbstractClientConnector implements ClientConnector { } // Send detach event if the component have been connected to a window - if (getApplication() != null) { + if (getSession() != null) { detach(); } @@ -510,7 +509,7 @@ public abstract class AbstractClientConnector implements ClientConnector { this.parent = parent; // Send attach event if connected to an application - if (getApplication() != null) { + if (getSession() != null) { attach(); } } @@ -536,7 +535,7 @@ public abstract class AbstractClientConnector implements ClientConnector { * {@inheritDoc} * * <p> - * The {@link #getApplication()} and {@link #getUI()} methods might return + * The {@link #getSession()} and {@link #getUI()} methods might return * <code>null</code> after this method is called. * </p> */ diff --git a/server/src/com/vaadin/server/AbstractCommunicationManager.java b/server/src/com/vaadin/server/AbstractCommunicationManager.java index 02d0b0d791..4f408d6fe9 100644 --- a/server/src/com/vaadin/server/AbstractCommunicationManager.java +++ b/server/src/com/vaadin/server/AbstractCommunicationManager.java @@ -56,7 +56,6 @@ import java.util.logging.Logger; import javax.servlet.http.HttpServletResponse; -import com.vaadin.Application; import com.vaadin.annotations.JavaScript; import com.vaadin.annotations.StyleSheet; import com.vaadin.external.json.JSONArray; @@ -143,7 +142,7 @@ public abstract class AbstractCommunicationManager implements Serializable { /** * The application this communication manager is used for */ - private final Application application; + private final VaadinSession application; private List<String> locales; @@ -170,7 +169,7 @@ public abstract class AbstractCommunicationManager implements Serializable { * * @param application */ - public AbstractCommunicationManager(Application application) { + public AbstractCommunicationManager(VaadinSession application) { this.application = application; application.addRequestHandler(getBootstrapHandler()); application.addRequestHandler(UNSUPPORTED_BROWSER_HANDLER); @@ -178,7 +177,7 @@ public abstract class AbstractCommunicationManager implements Serializable { requireLocale(application.getLocale().toString()); } - protected Application getApplication() { + protected VaadinSession getApplication() { return application; } @@ -366,7 +365,7 @@ public abstract class AbstractCommunicationManager implements Serializable { "StreamVariable for the post not found"); } - final Application application = getApplication(); + final VaadinSession application = getApplication(); OutputStream out = null; int totalBytes = 0; @@ -491,7 +490,7 @@ public abstract class AbstractCommunicationManager implements Serializable { * Internally process a UIDL request from the client. * * This method calls - * {@link #handleVariables(WrappedRequest, WrappedResponse, Callback, Application, UI)} + * {@link #handleVariables(WrappedRequest, WrappedResponse, Callback, VaadinSession, UI)} * to process any changes to variables by the client and then repaints * affected components using {@link #paintAfterVariableChanges()}. * @@ -518,7 +517,8 @@ public abstract class AbstractCommunicationManager implements Serializable { checkWidgetsetVersion(request); requestThemeName = request.getParameter("theme"); - maxInactiveInterval = request.getSessionMaxInactiveInterval(); + maxInactiveInterval = request.getWrappedSession() + .getMaxInactiveInterval(); // repaint requested or session has timed out and new one is created boolean repaintAll; final OutputStream out; @@ -676,7 +676,7 @@ public abstract class AbstractCommunicationManager implements Serializable { } sb.append("\nComponent hierarchy:\n"); - Application application2 = component.getApplication(); + VaadinSession application2 = component.getUI().getSession(); sb.append(application2.getClass().getName()); sb.append("."); sb.append(application2.getClass().getSimpleName()); @@ -772,12 +772,13 @@ public abstract class AbstractCommunicationManager implements Serializable { */ protected String getSecurityKey(WrappedRequest request) { String seckey = null; - seckey = (String) request - .getSessionAttribute(ApplicationConstants.UIDL_SECURITY_TOKEN_ID); + WrappedSession session = request.getWrappedSession(); + seckey = (String) session + .getAttribute(ApplicationConstants.UIDL_SECURITY_TOKEN_ID); if (seckey == null) { seckey = UUID.randomUUID().toString(); - request.setSessionAttribute( - ApplicationConstants.UIDL_SECURITY_TOKEN_ID, seckey); + session.setAttribute(ApplicationConstants.UIDL_SECURITY_TOKEN_ID, + seckey); } return seckey; @@ -788,7 +789,7 @@ public abstract class AbstractCommunicationManager implements Serializable { final PrintWriter outWriter, UI ui, boolean analyzeLayouts) throws PaintException, JSONException { ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>(); - Application application = ui.getApplication(); + VaadinSession application = ui.getSession(); // Paints components ConnectorTracker uiConnectorTracker = ui.getConnectorTracker(); getLogger().log(Level.FINE, "* Creating response to client"); @@ -1311,9 +1312,9 @@ public abstract class AbstractCommunicationManager implements Serializable { * response. */ private void writePerformanceData(final PrintWriter outWriter) { - ApplicationContext ctx = application.getContext(); outWriter.write(String.format(", \"timings\":[%d, %d]", - ctx.getTotalSessionTime(), ctx.getLastRequestTime())); + application.getTotalSessionTime(), + application.getLastRequestTime())); } private void legacyPaint(PaintTarget paintTarget, @@ -1519,10 +1520,8 @@ public abstract class AbstractCommunicationManager implements Serializable { * @param application * @return false if the XSRF is turned off, true otherwise */ - public boolean isXSRFEnabled(Application application) { - return !"true" - .equals(application - .getProperty(VaadinServlet.SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION)); + public boolean isXSRFEnabled(VaadinSession application) { + return application.getConfiguration().isXsrfProtectionEnabled(); } /** @@ -1536,7 +1535,7 @@ public abstract class AbstractCommunicationManager implements Serializable { */ private boolean handleVariables(WrappedRequest request, WrappedResponse response, Callback callback, - Application application2, UI uI) throws IOException, + VaadinSession application2, UI uI) throws IOException, InvalidUIDLSecurityKeyException, JSONException { boolean success = true; @@ -1559,7 +1558,9 @@ public abstract class AbstractCommunicationManager implements Serializable { // ApplicationServlet has stored the security token in the // session; check that it matched the one sent in the UIDL String sessId = (String) request - .getSessionAttribute(ApplicationConstants.UIDL_SECURITY_TOKEN_ID); + .getWrappedSession() + .getAttribute( + ApplicationConstants.UIDL_SECURITY_TOKEN_ID); if (sessId == null || !sessId.equals(bursts[0])) { throw new InvalidUIDLSecurityKeyException( @@ -1696,7 +1697,7 @@ public abstract class AbstractCommunicationManager implements Serializable { if (connector instanceof Component) { errorComponent = (Component) connector; } - handleChangeVariablesError(uI.getApplication(), + handleChangeVariablesError(uI.getSession(), errorComponent, realException, null); } } else { @@ -1728,7 +1729,7 @@ public abstract class AbstractCommunicationManager implements Serializable { errorComponent = (Component) dropHandlerOwner; } } - handleChangeVariablesError(uI.getApplication(), + handleChangeVariablesError(uI.getSession(), errorComponent, e, changes); } } @@ -1941,7 +1942,7 @@ public abstract class AbstractCommunicationManager implements Serializable { * @param m * map from variable names to values */ - private void handleChangeVariablesError(Application application, + private void handleChangeVariablesError(VaadinSession application, Component owner, Throwable t, Map<String, Object> m) { boolean handled = false; ChangeVariablesErrorEvent errorEvent = new ChangeVariablesErrorEvent( @@ -2153,8 +2154,8 @@ public abstract class AbstractCommunicationManager implements Serializable { * Ends the Application. * * The browser is redirected to the Application logout URL set with - * {@link Application#setLogoutURL(String)}, or to the application URL if no - * logout URL is given. + * {@link VaadinSession#setLogoutURL(String)}, or to the application URL if + * no logout URL is given. * * @param request * the request instance. @@ -2166,7 +2167,7 @@ public abstract class AbstractCommunicationManager implements Serializable { * if the writing failed due to input/output error. */ private void endApplication(WrappedRequest request, - WrappedResponse response, Application application) + WrappedResponse response, VaadinSession application) throws IOException { String logoutUrl = application.getLogoutURL(); @@ -2392,7 +2393,7 @@ public abstract class AbstractCommunicationManager implements Serializable { } public void handleBrowserDetailsRequest(WrappedRequest request, - WrappedResponse response, Application application) + WrappedResponse response, VaadinSession application) throws IOException { assert UI.getCurrent() == null; @@ -2449,7 +2450,7 @@ public abstract class AbstractCommunicationManager implements Serializable { StringWriter sWriter = new StringWriter(); PrintWriter pWriter = new PrintWriter(sWriter); pWriter.print("{"); - if (isXSRFEnabled(uI.getApplication())) { + if (isXSRFEnabled(uI.getSession())) { pWriter.print(getSecurityKeyUIDL(request)); } writeUidlResponse(request, true, pWriter, uI, false); @@ -2570,7 +2571,7 @@ public abstract class AbstractCommunicationManager implements Serializable { * @throws IOException * @throws InvalidUIDLSecurityKeyException */ - public void handleFileUpload(Application application, + public void handleFileUpload(VaadinSession application, WrappedRequest request, WrappedResponse response) throws IOException, InvalidUIDLSecurityKeyException { @@ -2632,7 +2633,7 @@ public abstract class AbstractCommunicationManager implements Serializable { * @throws IOException */ public void handleHeartbeatRequest(WrappedRequest request, - WrappedResponse response, Application application) + WrappedResponse response, VaadinSession application) throws IOException { UI ui = null; try { diff --git a/server/src/com/vaadin/server/AbstractDeploymentConfiguration.java b/server/src/com/vaadin/server/AbstractDeploymentConfiguration.java index d1280d29ce..3d7efa6711 100644 --- a/server/src/com/vaadin/server/AbstractDeploymentConfiguration.java +++ b/server/src/com/vaadin/server/AbstractDeploymentConfiguration.java @@ -18,87 +18,28 @@ package com.vaadin.server; import java.lang.reflect.Constructor; import java.util.Iterator; -import java.util.Properties; import java.util.ServiceLoader; -import java.util.logging.Logger; public abstract class AbstractDeploymentConfiguration implements DeploymentConfiguration { - private final Class<?> systemPropertyBaseClass; - private final Properties applicationProperties; private AddonContext addonContext; - private boolean productionMode; - private boolean xsrfProtectionEnabled; - private int resourceCacheTime; - private int heartbeatInterval; - private boolean idleRootCleanupEnabled; + private final ApplicationConfiguration applicationConfiguration; - public AbstractDeploymentConfiguration(Class<?> systemPropertyBaseClass, - Properties applicationProperties) { - this.systemPropertyBaseClass = systemPropertyBaseClass; - this.applicationProperties = applicationProperties; - - checkProductionMode(); - checkXsrfProtection(); - checkResourceCacheTime(); - checkHeartbeatInterval(); - checkIdleUICleanup(); + public AbstractDeploymentConfiguration( + ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; } @Override - public String getApplicationOrSystemProperty(String propertyName, - String defaultValue) { - String val = null; - - // Try application properties - val = getApplicationProperty(propertyName); - if (val != null) { - return val; - } - - // Try system properties - val = getSystemProperty(propertyName); - if (val != null) { - return val; - } - - return defaultValue; - } - - /** - * Gets an system property value. - * - * @param parameterName - * the Name or the parameter. - * @return String value or null if not found - */ - protected String getSystemProperty(String parameterName) { - String val = null; - - String pkgName; - final Package pkg = systemPropertyBaseClass.getPackage(); - if (pkg != null) { - pkgName = pkg.getName(); - } else { - final String className = systemPropertyBaseClass.getName(); - pkgName = new String(className.toCharArray(), 0, - className.lastIndexOf('.')); - } - val = System.getProperty(pkgName + "." + parameterName); - if (val != null) { - return val; - } - - // Try lowercased system properties - val = System.getProperty(pkgName + "." + parameterName.toLowerCase()); - return val; + public ApplicationConfiguration getApplicationConfiguration() { + return applicationConfiguration; } @Override public ClassLoader getClassLoader() { - final String classLoaderName = getApplicationOrSystemProperty( - "ClassLoader", null); + final String classLoaderName = getApplicationConfiguration() + .getApplicationOrSystemProperty("ClassLoader", null); ClassLoader classLoader; if (classLoaderName == null) { classLoader = getClass().getClassLoader(); @@ -119,32 +60,6 @@ public abstract class AbstractDeploymentConfiguration implements return classLoader; } - /** - * Gets an application property value. - * - * @param parameterName - * the Name or the parameter. - * @return String value or null if not found - */ - protected String getApplicationProperty(String parameterName) { - - String val = applicationProperties.getProperty(parameterName); - if (val != null) { - return val; - } - - // Try lower case application properties for backward compatibility with - // 3.0.2 and earlier - val = applicationProperties.getProperty(parameterName.toLowerCase()); - - return val; - } - - @Override - public Properties getInitParameters() { - return applicationProperties; - } - @Override public Iterator<AddonContextListener> getAddonContextListeners() { // Called once for init and then no more, so there's no point in caching @@ -163,111 +78,4 @@ public abstract class AbstractDeploymentConfiguration implements public AddonContext getAddonContext() { return addonContext; } - - /** - * {@inheritDoc} - * - * The default is false. - */ - @Override - public boolean isProductionMode() { - return productionMode; - } - - /** - * {@inheritDoc} - * - * The default is true. - */ - @Override - public boolean isXsrfProtectionEnabled() { - return xsrfProtectionEnabled; - } - - /** - * {@inheritDoc} - * - * The default interval is 3600 seconds (1 hour). - */ - @Override - public int getResourceCacheTime() { - return resourceCacheTime; - } - - /** - * {@inheritDoc} - * - * The default interval is 300 seconds (5 minutes). - */ - @Override - public int getHeartbeatInterval() { - return heartbeatInterval; - } - - @Override - public boolean isIdleUICleanupEnabled() { - return idleRootCleanupEnabled; - } - - /** - * Log a warning if Vaadin is not running in production mode. - */ - private void checkProductionMode() { - productionMode = getApplicationOrSystemProperty( - Constants.SERVLET_PARAMETER_PRODUCTION_MODE, "false").equals( - "true"); - if (!productionMode) { - getLogger().warning(Constants.NOT_PRODUCTION_MODE_INFO); - } - } - - /** - * Log a warning if cross-site request forgery protection is disabled. - */ - private void checkXsrfProtection() { - xsrfProtectionEnabled = !getApplicationOrSystemProperty( - Constants.SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION, "false") - .equals("true"); - if (!xsrfProtectionEnabled) { - getLogger().warning(Constants.WARNING_XSRF_PROTECTION_DISABLED); - } - } - - /** - * Log a warning if resource cache time is set but is not an integer. - */ - private void checkResourceCacheTime() { - try { - resourceCacheTime = Integer - .parseInt(getApplicationOrSystemProperty( - Constants.SERVLET_PARAMETER_RESOURCE_CACHE_TIME, - "3600")); - } catch (NumberFormatException e) { - getLogger().warning( - Constants.WARNING_RESOURCE_CACHING_TIME_NOT_NUMERIC); - resourceCacheTime = 3600; - } - } - - private void checkHeartbeatInterval() { - try { - heartbeatInterval = Integer - .parseInt(getApplicationOrSystemProperty( - Constants.SERVLET_PARAMETER_HEARTBEAT_RATE, "300")); - } catch (NumberFormatException e) { - getLogger().warning( - Constants.WARNING_HEARTBEAT_INTERVAL_NOT_NUMERIC); - heartbeatInterval = 300; - } - } - - private void checkIdleUICleanup() { - idleRootCleanupEnabled = getApplicationOrSystemProperty( - Constants.SERVLET_PARAMETER_CLOSE_IDLE_UIS, "false").equals( - "true"); - } - - private Logger getLogger() { - return Logger.getLogger(getClass().getName()); - } } diff --git a/server/src/com/vaadin/server/AbstractUIProvider.java b/server/src/com/vaadin/server/AbstractUIProvider.java index 59ce31891d..ee8ebd2745 100644 --- a/server/src/com/vaadin/server/AbstractUIProvider.java +++ b/server/src/com/vaadin/server/AbstractUIProvider.java @@ -16,13 +16,18 @@ package com.vaadin.server; -import com.vaadin.Application; +import java.lang.annotation.Annotation; + +import com.vaadin.annotations.PreserveOnRefresh; +import com.vaadin.annotations.Theme; +import com.vaadin.annotations.Title; +import com.vaadin.annotations.Widgetset; import com.vaadin.ui.UI; public abstract class AbstractUIProvider implements UIProvider { @Override - public UI createInstance(Application application, Class<? extends UI> type, + public UI createInstance(VaadinSession application, Class<? extends UI> type, WrappedRequest request) { try { return type.newInstance(); @@ -32,4 +37,86 @@ public abstract class AbstractUIProvider implements UIProvider { throw new RuntimeException("Could not access root class", e); } } + + /** + * Helper to get an annotation for a class. If the annotation is not present + * on the target class, it's superclasses and implemented interfaces are + * also searched for the annotation. + * + * @param type + * the target class from which the annotation should be found + * @param annotationType + * the annotation type to look for + * @return an annotation of the given type, or <code>null</code> if the + * annotation is not present on the class + */ + protected static <T extends Annotation> T getAnnotationFor(Class<?> type, + Class<T> annotationType) { + // Find from the class hierarchy + Class<?> currentType = type; + while (currentType != Object.class) { + T annotation = currentType.getAnnotation(annotationType); + if (annotation != null) { + return annotation; + } else { + currentType = currentType.getSuperclass(); + } + } + + // Find from an implemented interface + for (Class<?> iface : type.getInterfaces()) { + T annotation = iface.getAnnotation(annotationType); + if (annotation != null) { + return annotation; + } + } + + return null; + } + + @Override + public String getThemeForUI(WrappedRequest request, + Class<? extends UI> uiClass) { + Theme uiTheme = getAnnotationFor(uiClass, Theme.class); + if (uiTheme != null) { + return uiTheme.value(); + } else { + return null; + } + } + + @Override + public String getWidgetsetForUI(WrappedRequest request, + Class<? extends UI> uiClass) { + Widgetset uiWidgetset = getAnnotationFor(uiClass, Widgetset.class); + if (uiWidgetset != null) { + return uiWidgetset.value(); + } else { + return null; + } + } + + @Override + public boolean isUiPreserved(WrappedRequest request, + Class<? extends UI> uiClass) { + PreserveOnRefresh preserveOnRefresh = getAnnotationFor(uiClass, + PreserveOnRefresh.class); + return preserveOnRefresh != null; + } + + @Override + public String getPageTitleForUI(WrappedRequest request, + Class<? extends UI> uiClass) { + Title titleAnnotation = getAnnotationFor(uiClass, Title.class); + if (titleAnnotation == null) { + return null; + } else { + return titleAnnotation.value(); + } + } + + @Override + public UI getExistingUI(WrappedRequest request) { + return null; + } } diff --git a/server/src/com/vaadin/server/AddonContext.java b/server/src/com/vaadin/server/AddonContext.java index c9f2ac07eb..ca8c837a2e 100644 --- a/server/src/com/vaadin/server/AddonContext.java +++ b/server/src/com/vaadin/server/AddonContext.java @@ -22,7 +22,6 @@ import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; -import com.vaadin.Application; import com.vaadin.event.EventRouter; import com.vaadin.util.ReflectTools; @@ -116,7 +115,7 @@ public class AddonContext { * application. * * @see #addApplicationStartedListener(ApplicationStartedListener) - * @see Application#addBootstrapListener(BootstrapListener) + * @see VaadinSession#addBootstrapListener(BootstrapListener) * * @param listener * the bootstrap listener that should be added to all new @@ -136,7 +135,7 @@ public class AddonContext { * @param application * the newly started application */ - public void fireApplicationStarted(Application application) { + public void fireApplicationStarted(VaadinSession application) { eventRouter.fireEvent(new ApplicationStartedEvent(this, application)); for (BootstrapListener l : bootstrapListeners) { application.addBootstrapListener(l); @@ -144,9 +143,9 @@ public class AddonContext { } /** - * Adds a listener that will be notified any time a new {@link Application} + * Adds a listener that will be notified any time a new {@link VaadinSession} * instance is started or more precisely directly after - * {@link Application#init()} has been invoked. + * {@link VaadinSession#init()} has been invoked. * * @param applicationStartListener * the application start listener that should be added diff --git a/server/src/com/vaadin/server/ApplicationConfiguration.java b/server/src/com/vaadin/server/ApplicationConfiguration.java new file mode 100644 index 0000000000..edb33e6c39 --- /dev/null +++ b/server/src/com/vaadin/server/ApplicationConfiguration.java @@ -0,0 +1,99 @@ +/* + * 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.Properties; + + +/** + * A collection of properties configured for all applications as well as a way + * of accessing third party properties not explicitely supported by this class. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public interface ApplicationConfiguration { + /** + * Returns whether Vaadin is in production mode. + * + * @return true if in production mode, false otherwise. + */ + public boolean isProductionMode(); + + /** + * Returns whether cross-site request forgery protection is enabled. + * + * @return true if XSRF protection is enabled, false otherwise. + */ + public boolean isXsrfProtectionEnabled(); + + /** + * Returns the time resources can be cached in the browsers, in seconds. + * + * @return The resource cache time. + */ + public int getResourceCacheTime(); + + /** + * Returns the number of seconds between heartbeat requests of a UI, or a + * non-positive number if heartbeat is disabled. + * + * @return The time between heartbeats. + */ + public int getHeartbeatInterval(); + + /** + * Returns whether UIs that have no other activity than heartbeat requests + * should be closed after they have been idle the maximum inactivity time + * enforced by the session. + * + * @see ApplicationContext#getMaxInactiveInterval() + * + * @since 7.0.0 + * + * @return True if UIs receiving only heartbeat requests are eventually + * closed; false if heartbeat requests extend UI lifetime + * indefinitely. + */ + public boolean isIdleUICleanupEnabled(); + + /** + * Gets the properties configured for the deployment, e.g. as init + * parameters to the servlet or portlet. + * + * @return properties for the application. + */ + public Properties getInitParameters(); + + /** + * Gets a configured property. The properties are typically read from e.g. + * web.xml or from system properties of the JVM. + * + * @param propertyName + * The simple of the property, in some contexts, lookup might be + * performed using variations of the provided name. + * @param defaultValue + * the default value that should be used if no value has been + * defined + * @return the property value, or the passed default value if no property + * value is found + */ + public String getApplicationOrSystemProperty(String propertyName, + String defaultValue); + +} diff --git a/server/src/com/vaadin/server/ApplicationContext.java b/server/src/com/vaadin/server/ApplicationContext.java deleted file mode 100644 index b698ea51c8..0000000000 --- a/server/src/com/vaadin/server/ApplicationContext.java +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright 2011 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server; - -import java.io.File; -import java.io.PrintWriter; -import java.io.Serializable; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.servlet.http.HttpSessionBindingEvent; -import javax.servlet.http.HttpSessionBindingListener; - -import com.vaadin.Application; - -/** - * <code>ApplicationContext</code> provides information about the running - * context of the application. Each context is shared by all applications that - * are open for one user. In a web-environment this corresponds to a - * HttpSession. - * <p> - * Base class for web application contexts (including portlet contexts) that - * handles the common tasks. - * - * @author Vaadin Ltd. - * @since 3.1 - */ -public abstract class ApplicationContext implements HttpSessionBindingListener, - Serializable { - - /** - * Interface for listening to transaction events. Implement this interface - * to listen to all transactions between the client and the application. - * - */ - public static interface TransactionListener extends Serializable { - - /** - * Invoked at the beginning of every transaction. - * - * The transaction is linked to the context, not the application so if - * you have multiple applications running in the same context you need - * to check that the request is associated with the application you are - * interested in. This can be done looking at the application parameter. - * - * @param application - * the Application object. - * @param transactionData - * the Data identifying the transaction. - */ - public void transactionStart(Application application, - Object transactionData); - - /** - * Invoked at the end of every transaction. - * - * The transaction is linked to the context, not the application so if - * you have multiple applications running in the same context you need - * to check that the request is associated with the application you are - * interested in. This can be done looking at the application parameter. - * - * @param applcation - * the Application object. - * @param transactionData - * the Data identifying the transaction. - */ - public void transactionEnd(Application application, - Object transactionData); - - } - - protected Collection<ApplicationContext.TransactionListener> listeners = Collections - .synchronizedList(new LinkedList<ApplicationContext.TransactionListener>()); - - protected final HashSet<Application> applications = new HashSet<Application>(); - - protected WebBrowser browser = new WebBrowser(); - - protected HashMap<Application, AbstractCommunicationManager> applicationToAjaxAppMgrMap = new HashMap<Application, AbstractCommunicationManager>(); - - private long totalSessionTime = 0; - - private long lastRequestTime = -1; - - /** - * Adds a transaction listener to this context. The transaction listener is - * called before and after each each request related to this session except - * when serving static resources. - * - * The transaction listener must not be null. - * - * @see com.vaadin.service.ApplicationContext#addTransactionListener(com.vaadin.service.ApplicationContext.TransactionListener) - */ - public void addTransactionListener( - ApplicationContext.TransactionListener listener) { - if (listener != null) { - listeners.add(listener); - } - } - - /** - * Removes a transaction listener from this context. - * - * @param listener - * the listener to be removed. - * @see ApplicationContext.TransactionListener - */ - public void removeTransactionListener( - ApplicationContext.TransactionListener listener) { - listeners.remove(listener); - } - - /** - * Sends a notification that a transaction is starting. - * - * @param application - * The application associated with the transaction. - * @param request - * the HTTP or portlet request that triggered the transaction. - */ - protected void startTransaction(Application application, Object request) { - ArrayList<ApplicationContext.TransactionListener> currentListeners; - synchronized (listeners) { - currentListeners = new ArrayList<ApplicationContext.TransactionListener>( - listeners); - } - for (ApplicationContext.TransactionListener listener : currentListeners) { - listener.transactionStart(application, request); - } - } - - /** - * Sends a notification that a transaction has ended. - * - * @param application - * The application associated with the transaction. - * @param request - * the HTTP or portlet request that triggered the transaction. - */ - protected void endTransaction(Application application, Object request) { - LinkedList<Exception> exceptions = null; - - ArrayList<ApplicationContext.TransactionListener> currentListeners; - synchronized (listeners) { - currentListeners = new ArrayList<ApplicationContext.TransactionListener>( - listeners); - } - - for (ApplicationContext.TransactionListener listener : currentListeners) { - try { - listener.transactionEnd(application, request); - } catch (final RuntimeException t) { - if (exceptions == null) { - exceptions = new LinkedList<Exception>(); - } - exceptions.add(t); - } - } - - // If any runtime exceptions occurred, throw a combined exception - if (exceptions != null) { - final StringBuffer msg = new StringBuffer(); - for (Exception e : exceptions) { - if (msg.length() == 0) { - msg.append("\n\n--------------------------\n\n"); - } - msg.append(e.getMessage() + "\n"); - final StringWriter trace = new StringWriter(); - e.printStackTrace(new PrintWriter(trace, true)); - msg.append(trace.toString()); - } - throw new RuntimeException(msg.toString()); - } - } - - /** - * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent) - */ - @Override - public void valueBound(HttpSessionBindingEvent arg0) { - // We are not interested in bindings - } - - /** - * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent) - */ - @Override - public void valueUnbound(HttpSessionBindingEvent event) { - // If we are going to be unbound from the session, the session must be - // closing - try { - while (!applications.isEmpty()) { - final Application app = applications.iterator().next(); - app.close(); - removeApplication(app); - } - } catch (Exception e) { - // This should never happen but is possible with rare - // configurations (e.g. robustness tests). If you have one - // thread doing HTTP socket write and another thread trying to - // remove same application here. Possible if you got e.g. session - // lifetime 1 min but socket write may take longer than 1 min. - // FIXME: Handle exception - getLogger().log(Level.SEVERE, - "Could not remove application, leaking memory.", e); - } - } - - /** - * Get the web browser associated with this application context. - * - * Because application context is related to the http session and server - * maintains one session per browser-instance, each context has exactly one - * web browser associated with it. - * - * @return - */ - public WebBrowser getBrowser() { - return browser; - } - - /** - * Returns a collection of all the applications in this context. - * - * Each application context contains all active applications for one user. - * - * @return A collection containing all the applications in this context. - */ - public Collection<Application> getApplications() { - return Collections.unmodifiableCollection(applications); - } - - protected void removeApplication(Application application) { - applications.remove(application); - applicationToAjaxAppMgrMap.remove(application); - } - - /** - * @return The total time spent servicing requests in this session. - */ - public long getTotalSessionTime() { - return totalSessionTime; - } - - /** - * Sets the time spent servicing the last request in the session and updates - * the total time spent servicing requests in this session. - * - * @param time - * the time spent in the last request. - */ - public void setLastRequestTime(long time) { - lastRequestTime = time; - totalSessionTime += time; - } - - /** - * @return the time spent servicing the last request in this session. - */ - public long getLastRequestTime() { - return lastRequestTime; - } - - private Logger getLogger() { - return Logger.getLogger(ApplicationContext.class.getName()); - } - - /** - * Returns application context base directory. - * - * Typically an application is deployed in a such way that is has an - * application directory. For web applications this directory is the root - * directory of the web applications. In some cases applications might not - * have an application directory (for example web applications running - * inside a war). - * - * @return The application base directory or null if the application has no - * base directory. - */ - public abstract File getBaseDirectory(); - - /** - * Returns the time between requests, in seconds, before this context is - * invalidated. A negative time indicates the context should never timeout. - */ - public abstract int getMaxInactiveInterval(); - -}
\ No newline at end of file diff --git a/server/src/com/vaadin/server/ApplicationStartedEvent.java b/server/src/com/vaadin/server/ApplicationStartedEvent.java index d06744ae40..9ecab4d477 100644 --- a/server/src/com/vaadin/server/ApplicationStartedEvent.java +++ b/server/src/com/vaadin/server/ApplicationStartedEvent.java @@ -18,7 +18,6 @@ package com.vaadin.server; import java.util.EventObject; -import com.vaadin.Application; /** * Event used by @@ -29,7 +28,7 @@ import com.vaadin.Application; * @since 7.0.0 */ public class ApplicationStartedEvent extends EventObject { - private final Application application; + private final VaadinSession application; /** * Creates a new event. @@ -39,7 +38,7 @@ public class ApplicationStartedEvent extends EventObject { * @param application * the application that has been started */ - public ApplicationStartedEvent(AddonContext context, Application application) { + public ApplicationStartedEvent(AddonContext context, VaadinSession application) { super(context); this.application = application; } @@ -58,7 +57,7 @@ public class ApplicationStartedEvent extends EventObject { * * @return the newly created application */ - public Application getApplication() { + public VaadinSession getApplication() { return application; } diff --git a/server/src/com/vaadin/server/ApplicationStartedListener.java b/server/src/com/vaadin/server/ApplicationStartedListener.java index c54132d875..b7858447df 100644 --- a/server/src/com/vaadin/server/ApplicationStartedListener.java +++ b/server/src/com/vaadin/server/ApplicationStartedListener.java @@ -18,10 +18,9 @@ package com.vaadin.server; import java.util.EventListener; -import com.vaadin.Application; /** - * Listener that gets notified when a new {@link Application} has been started. + * Listener that gets notified when a new {@link VaadinSession} has been started. * Add-ons can use this listener to automatically integrate with API tied to the * Application API. * @@ -33,7 +32,7 @@ import com.vaadin.Application; public interface ApplicationStartedListener extends EventListener { /** * Tells the listener that an application has been started (meaning that - * {@link Application#init()} has been invoked. + * {@link VaadinSession#init()} has been invoked. * * @param event * details about the event diff --git a/server/src/com/vaadin/server/BootstrapFragmentResponse.java b/server/src/com/vaadin/server/BootstrapFragmentResponse.java index 149f59e7a5..4b960263e7 100644 --- a/server/src/com/vaadin/server/BootstrapFragmentResponse.java +++ b/server/src/com/vaadin/server/BootstrapFragmentResponse.java @@ -20,7 +20,6 @@ import java.util.List; import org.jsoup.nodes.Node; -import com.vaadin.Application; import com.vaadin.ui.UI; /** @@ -38,7 +37,7 @@ public class BootstrapFragmentResponse extends BootstrapResponse { * Crate a new bootstrap fragment response. * * @see BootstrapResponse#BootstrapResponse(BootstrapHandler, - * WrappedRequest, Application, Class) + * WrappedRequest, VaadinSession, Class) * * @param handler * the bootstrap handler that is firing the event @@ -55,7 +54,7 @@ public class BootstrapFragmentResponse extends BootstrapResponse { * application HTML */ public BootstrapFragmentResponse(BootstrapHandler handler, - WrappedRequest request, Application application, + WrappedRequest request, VaadinSession application, Class<? extends UI> uiClass, List<Node> fragmentNodes) { super(handler, request, application, uiClass); this.fragmentNodes = fragmentNodes; diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index a1438312b6..6b8fb1952a 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -36,7 +36,6 @@ import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import org.jsoup.parser.Tag; -import com.vaadin.Application; import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONObject; import com.vaadin.shared.ApplicationConstants; @@ -68,7 +67,7 @@ public abstract class BootstrapHandler implements RequestHandler { return bootstrapResponse.getRequest(); } - public Application getApplication() { + public VaadinSession getApplication() { return bootstrapResponse.getApplication(); } @@ -104,7 +103,7 @@ public abstract class BootstrapHandler implements RequestHandler { } @Override - public boolean handleRequest(Application application, + public boolean handleRequest(VaadinSession application, WrappedRequest request, WrappedResponse response) throws IOException { @@ -218,8 +217,9 @@ public abstract class BootstrapHandler implements RequestHandler { head.appendElement("meta").attr("http-equiv", "X-UA-Compatible") .attr("content", "chrome=1"); - String title = context.getApplication().getPageTitleForUI( - context.getRequest(), context.getUIClass()); + String title = context.getApplication() + .getUiProvider(context.getRequest(), context.getUIClass()) + .getPageTitleForUI(context.getRequest(), context.getUIClass()); if (title != null) { head.appendElement("title").appendText(title); } @@ -245,7 +245,7 @@ public abstract class BootstrapHandler implements RequestHandler { } private BootstrapContext createContext(WrappedRequest request, - WrappedResponse response, Application application, + WrappedResponse response, VaadinSession application, Class<? extends UI> uiClass) { BootstrapContext context = new BootstrapContext(response, new BootstrapFragmentResponse(this, request, application, @@ -270,8 +270,9 @@ public abstract class BootstrapHandler implements RequestHandler { public String getWidgetsetForUI(BootstrapContext context) { WrappedRequest request = context.getRequest(); - String widgetset = context.getApplication().getWidgetsetForUI( - context.getRequest(), context.getUIClass()); + String widgetset = context.getApplication() + .getUiProvider(context.getRequest(), context.getUIClass()) + .getWidgetsetForUI(context.getRequest(), context.getUIClass()); if (widgetset == null) { widgetset = request.getDeploymentConfiguration() .getConfiguredWidgetset(request); @@ -390,7 +391,7 @@ public abstract class BootstrapHandler implements RequestHandler { protected JSONObject getApplicationParameters(BootstrapContext context) throws JSONException, PaintException { - Application application = context.getApplication(); + VaadinSession application = context.getApplication(); JSONObject appConfig = new JSONObject(); @@ -419,7 +420,7 @@ public abstract class BootstrapHandler implements RequestHandler { JSONObject defaults = new JSONObject(); WrappedRequest request = context.getRequest(); - Application application = context.getApplication(); + VaadinSession application = context.getApplication(); DeploymentConfiguration deploymentConfiguration = request .getDeploymentConfiguration(); @@ -461,8 +462,8 @@ public abstract class BootstrapHandler implements RequestHandler { defaults.put("standalone", true); } - defaults.put("heartbeatInterval", - deploymentConfiguration.getHeartbeatInterval()); + defaults.put("heartbeatInterval", deploymentConfiguration + .getApplicationConfiguration().getHeartbeatInterval()); defaults.put("appUri", getAppUri(context)); @@ -497,8 +498,9 @@ public abstract class BootstrapHandler implements RequestHandler { * @return */ public String getThemeName(BootstrapContext context) { - return context.getApplication().getThemeForUI(context.getRequest(), - context.getUIClass()); + return context.getApplication() + .getUiProvider(context.getRequest(), context.getUIClass()) + .getThemeForUI(context.getRequest(), context.getUIClass()); } /** diff --git a/server/src/com/vaadin/server/BootstrapPageResponse.java b/server/src/com/vaadin/server/BootstrapPageResponse.java index a5fdfe4707..8f56042f8f 100644 --- a/server/src/com/vaadin/server/BootstrapPageResponse.java +++ b/server/src/com/vaadin/server/BootstrapPageResponse.java @@ -20,7 +20,6 @@ import java.util.Map; import org.jsoup.nodes.Document; -import com.vaadin.Application; import com.vaadin.ui.UI; /** @@ -40,7 +39,7 @@ public class BootstrapPageResponse extends BootstrapResponse { * Crate a new bootstrap page response. * * @see BootstrapResponse#BootstrapResponse(BootstrapHandler, - * WrappedRequest, Application, Class) + * WrappedRequest, VaadinSession, Class) * * @param handler * the bootstrap handler that is firing the event @@ -58,7 +57,7 @@ public class BootstrapPageResponse extends BootstrapResponse { * a map into which header data can be added */ public BootstrapPageResponse(BootstrapHandler handler, - WrappedRequest request, Application application, + WrappedRequest request, VaadinSession application, Class<? extends UI> uiClass, Document document, Map<String, Object> headers) { super(handler, request, application, uiClass); diff --git a/server/src/com/vaadin/server/BootstrapResponse.java b/server/src/com/vaadin/server/BootstrapResponse.java index 3173569059..b75544a87c 100644 --- a/server/src/com/vaadin/server/BootstrapResponse.java +++ b/server/src/com/vaadin/server/BootstrapResponse.java @@ -18,7 +18,6 @@ package com.vaadin.server; import java.util.EventObject; -import com.vaadin.Application; import com.vaadin.ui.UI; /** @@ -30,7 +29,7 @@ import com.vaadin.ui.UI; */ public abstract class BootstrapResponse extends EventObject { private final WrappedRequest request; - private final Application application; + private final VaadinSession application; private final Class<? extends UI> uiClass; /** @@ -48,7 +47,7 @@ public abstract class BootstrapResponse extends EventObject { * the class of the UI that will be displayed on the page */ public BootstrapResponse(BootstrapHandler handler, WrappedRequest request, - Application application, Class<? extends UI> uiClass) { + VaadinSession application, Class<? extends UI> uiClass) { super(handler); this.request = request; this.application = application; @@ -83,7 +82,7 @@ public abstract class BootstrapResponse extends EventObject { * * @return the application */ - public Application getApplication() { + public VaadinSession getApplication() { return application; } diff --git a/server/src/com/vaadin/server/ClassResource.java b/server/src/com/vaadin/server/ClassResource.java index 2f05115fd4..27643eda13 100644 --- a/server/src/com/vaadin/server/ClassResource.java +++ b/server/src/com/vaadin/server/ClassResource.java @@ -18,7 +18,6 @@ package com.vaadin.server; import java.io.Serializable; -import com.vaadin.Application; import com.vaadin.service.FileTypeResolver; import com.vaadin.ui.UI; import com.vaadin.ui.UI.LegacyWindow; @@ -115,7 +114,7 @@ public class ClassResource implements ConnectorResource, Serializable { if (associatedClass == null) { Class<? extends UI> associatedClass = UI.getCurrent().getClass(); if (associatedClass == LegacyWindow.class) { - return Application.getCurrent().getClass(); + return VaadinSession.getCurrent().getClass(); } return associatedClass; } diff --git a/server/src/com/vaadin/server/CombinedRequest.java b/server/src/com/vaadin/server/CombinedRequest.java index 0577c0098a..cc336ffa73 100644 --- a/server/src/com/vaadin/server/CombinedRequest.java +++ b/server/src/com/vaadin/server/CombinedRequest.java @@ -24,7 +24,6 @@ import java.util.Iterator; import java.util.Locale; import java.util.Map; -import com.vaadin.Application; import com.vaadin.external.json.JSONArray; import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONObject; @@ -114,18 +113,8 @@ public class CombinedRequest implements WrappedRequest { } @Override - public int getSessionMaxInactiveInterval() { - return secondRequest.getSessionMaxInactiveInterval(); - } - - @Override - public Object getSessionAttribute(String name) { - return secondRequest.getSessionAttribute(name); - } - - @Override - public void setSessionAttribute(String name, Object attribute) { - secondRequest.setSessionAttribute(name, attribute); + public WrappedSession getWrappedSession() { + return secondRequest.getWrappedSession(); } @Override @@ -153,9 +142,7 @@ public class CombinedRequest implements WrappedRequest { @Override public WebBrowser getWebBrowser() { - ApplicationContext context = Application.getCurrent() - .getContext(); - return context.getBrowser(); + return VaadinSession.getCurrent().getBrowser(); } }; } diff --git a/server/src/com/vaadin/server/CommunicationManager.java b/server/src/com/vaadin/server/CommunicationManager.java index cc92023919..32c4d2c217 100644 --- a/server/src/com/vaadin/server/CommunicationManager.java +++ b/server/src/com/vaadin/server/CommunicationManager.java @@ -21,7 +21,6 @@ import java.net.URL; import javax.servlet.ServletContext; -import com.vaadin.Application; import com.vaadin.ui.UI; /** @@ -39,12 +38,12 @@ import com.vaadin.ui.UI; public class CommunicationManager extends AbstractCommunicationManager { /** - * @deprecated use {@link #CommunicationManager(Application)} instead + * @deprecated use {@link #CommunicationManager(VaadinSession)} instead * @param application * @param applicationServlet */ @Deprecated - public CommunicationManager(Application application, + public CommunicationManager(VaadinSession application, VaadinServlet applicationServlet) { super(application); } @@ -54,7 +53,7 @@ public class CommunicationManager extends AbstractCommunicationManager { * * @param application */ - public CommunicationManager(Application application) { + public CommunicationManager(VaadinSession application) { super(application); } @@ -88,7 +87,7 @@ 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 - Application application = context.getApplication(); + VaadinSession application = context.getApplication(); URL url = application.getURL(); String appUrl = url.getPath(); if (appUrl.endsWith("/")) { @@ -112,8 +111,8 @@ public class CommunicationManager extends AbstractCommunicationManager { @Override protected InputStream getThemeResourceAsStream(UI uI, String themeName, String resource) { - ServletApplicationContext context = (ServletApplicationContext) uI - .getApplication().getContext(); + VaadinServletSession context = (VaadinServletSession) uI + .getSession(); ServletContext servletContext = context.getHttpSession() .getServletContext(); return servletContext.getResourceAsStream("/" diff --git a/server/src/com/vaadin/server/ConnectorResourceHandler.java b/server/src/com/vaadin/server/ConnectorResourceHandler.java index b988510b8e..6702fb140f 100644 --- a/server/src/com/vaadin/server/ConnectorResourceHandler.java +++ b/server/src/com/vaadin/server/ConnectorResourceHandler.java @@ -8,7 +8,6 @@ import java.util.regex.Pattern; import javax.servlet.http.HttpServletResponse; -import com.vaadin.Application; import com.vaadin.shared.ApplicationConstants; import com.vaadin.ui.UI; @@ -25,7 +24,7 @@ public class ConnectorResourceHandler implements RequestHandler { } @Override - public boolean handleRequest(Application application, + public boolean handleRequest(VaadinSession application, WrappedRequest request, WrappedResponse response) throws IOException { String requestPath = request.getRequestPathInfo(); @@ -45,7 +44,7 @@ public class ConnectorResourceHandler implements RequestHandler { } UI.setCurrent(ui); - Application.setCurrent(ui.getApplication()); + VaadinSession.setCurrent(ui.getSession()); ClientConnector connector = ui.getConnectorTracker().getConnector( cid); diff --git a/server/src/com/vaadin/server/DefaultUIProvider.java b/server/src/com/vaadin/server/DefaultUIProvider.java index 93128aad28..9babf4704e 100644 --- a/server/src/com/vaadin/server/DefaultUIProvider.java +++ b/server/src/com/vaadin/server/DefaultUIProvider.java @@ -16,16 +16,15 @@ package com.vaadin.server; -import com.vaadin.Application; import com.vaadin.ui.UI; public class DefaultUIProvider extends AbstractUIProvider { @Override - public Class<? extends UI> getUIClass(Application application, + public Class<? extends UI> getUIClass(VaadinSession application, WrappedRequest request) { - Object uiClassNameObj = application - .getProperty(Application.UI_PARAMETER); + Object uiClassNameObj = application.getConfiguration() + .getInitParameters().getProperty(VaadinSession.UI_PARAMETER); if (uiClassNameObj instanceof String) { String uiClassName = uiClassNameObj.toString(); diff --git a/server/src/com/vaadin/server/DeploymentConfiguration.java b/server/src/com/vaadin/server/DeploymentConfiguration.java index 6150edbafb..0a70ef09b3 100644 --- a/server/src/com/vaadin/server/DeploymentConfiguration.java +++ b/server/src/com/vaadin/server/DeploymentConfiguration.java @@ -16,9 +16,9 @@ package com.vaadin.server; +import java.io.File; import java.io.Serializable; import java.util.Iterator; -import java.util.Properties; import javax.portlet.PortletContext; import javax.servlet.ServletContext; @@ -82,22 +82,6 @@ public interface DeploymentConfiguration extends Serializable { public boolean isStandalone(WrappedRequest request); /** - * Gets a configured property. The properties are typically read from e.g. - * web.xml or from system properties of the JVM. - * - * @param propertyName - * The simple of the property, in some contexts, lookup might be - * performed using variations of the provided name. - * @param defaultValue - * the default value that should be used if no value has been - * defined - * @return the property value, or the passed default value if no property - * value is found - */ - public String getApplicationOrSystemProperty(String propertyName, - String defaultValue); - - /** * Get the class loader to use for loading classes loaded by name, e.g. * custom UI classes. <code>null</code> indicates that the default class * loader should be used. @@ -122,12 +106,11 @@ public interface DeploymentConfiguration extends Serializable { public String getMimeType(String resourceName); /** - * Gets the properties configured for the deployment, e.g. as init - * parameters to the servlet or portlet. + * Gets the application configuration. * - * @return properties for the application. + * @return the application configuration */ - public Properties getInitParameters(); + public ApplicationConfiguration getApplicationConfiguration(); public Iterator<AddonContextListener> getAddonContextListeners(); @@ -136,61 +119,23 @@ public interface DeploymentConfiguration extends Serializable { public void setAddonContext(AddonContext vaadinContext); /** - * Returns whether Vaadin is in production mode. - * - * @since 7.0.0 - * - * @return true if in production mode, false otherwise. - */ - public boolean isProductionMode(); - - /** - * Returns whether cross-site request forgery protection is enabled. - * - * @since 7.0.0 - * - * @return true if XSRF protection is enabled, false otherwise. - */ - public boolean isXsrfProtectionEnabled(); - - /** - * Returns the time resources can be cached in the browsers, in seconds. - * - * @since 7.0.0 - * - * @return The resource cache time. - */ - public int getResourceCacheTime(); - - /** - * Returns the number of seconds between heartbeat requests of a UI, or a - * non-positive number if heartbeat is disabled. - * - * @since 7.0.0 + * Gets the system messages object * - * @return The time between heartbeats. + * @return the system messages object */ - public int getHeartbeatInterval(); + public SystemMessages getSystemMessages(); /** - * Returns whether UIs that have no other activity than heartbeat requests - * should be closed after they have been idle the maximum inactivity time - * enforced by the session. - * - * @see ApplicationContext#getMaxInactiveInterval() + * Returns application context base directory. * - * @since 7.0.0 + * Typically an application is deployed in a such way that is has an + * application directory. For web applications this directory is the root + * directory of the web applications. In some cases applications might not + * have an application directory (for example web applications running + * inside a war). * - * @return True if UIs receiving only heartbeat requests are eventually - * closed; false if heartbeat requests extend UI lifetime - * indefinitely. + * @return The application base directory or null if the application has no + * base directory. */ - public boolean isIdleUICleanupEnabled(); - - /** - * Gets the system messages object - * - * @return the system messages object - */ - public SystemMessages getSystemMessages(); + public File getBaseDirectory(); } diff --git a/server/src/com/vaadin/server/FileResource.java b/server/src/com/vaadin/server/FileResource.java index fbf353362e..2dd5b7f589 100644 --- a/server/src/com/vaadin/server/FileResource.java +++ b/server/src/com/vaadin/server/FileResource.java @@ -20,7 +20,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import com.vaadin.Application; import com.vaadin.server.Terminal.ErrorEvent; import com.vaadin.service.FileTypeResolver; @@ -74,7 +73,7 @@ public class FileResource implements ConnectorResource { return ds; } catch (final FileNotFoundException e) { // Log the exception using the application error handler - Application.getCurrent().getErrorHandler() + VaadinSession.getCurrent().getErrorHandler() .terminalError(new ErrorEvent() { @Override diff --git a/server/src/com/vaadin/server/GAEVaadinServlet.java b/server/src/com/vaadin/server/GAEVaadinServlet.java index 642737f73b..4ea1dff7f4 100644 --- a/server/src/com/vaadin/server/GAEVaadinServlet.java +++ b/server/src/com/vaadin/server/GAEVaadinServlet.java @@ -241,7 +241,7 @@ public class GAEVaadinServlet extends VaadinServlet { } // de-serialize or create application context, store in session - ApplicationContext ctx = getApplicationContext(request, memcache); + VaadinSession ctx = getApplicationContext(request, memcache); super.service(request, response); @@ -291,8 +291,8 @@ public class GAEVaadinServlet extends VaadinServlet { } } - protected ApplicationContext getApplicationContext( - HttpServletRequest request, MemcacheService memcache) { + protected VaadinSession getApplicationContext(HttpServletRequest request, + MemcacheService memcache) { HttpSession session = request.getSession(); String id = AC_BASE + session.getId(); byte[] serializedAC = (byte[]) memcache.get(id); @@ -320,10 +320,9 @@ public class GAEVaadinServlet extends VaadinServlet { ObjectInputStream ois; try { ois = new ObjectInputStream(bais); - ApplicationContext applicationContext = (ApplicationContext) ois - .readObject(); - session.setAttribute(ServletApplicationContext.class.getName(), - applicationContext); + VaadinSession applicationContext = (VaadinSession) ois.readObject(); + applicationContext.storeInSession(new WrappedHttpSession( + session)); } catch (IOException e) { getLogger().log( Level.WARNING, @@ -360,7 +359,7 @@ public class GAEVaadinServlet extends VaadinServlet { private void cleanSession(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session != null) { - session.removeAttribute(ServletApplicationContext.class.getName()); + session.removeAttribute(VaadinServletSession.class.getName()); } } diff --git a/server/src/com/vaadin/server/GlobalResourceHandler.java b/server/src/com/vaadin/server/GlobalResourceHandler.java index 441d884f58..03dab9817d 100644 --- a/server/src/com/vaadin/server/GlobalResourceHandler.java +++ b/server/src/com/vaadin/server/GlobalResourceHandler.java @@ -28,7 +28,6 @@ import java.util.regex.Pattern; import javax.servlet.http.HttpServletResponse; -import com.vaadin.Application; import com.vaadin.shared.ApplicationConstants; import com.vaadin.ui.UI; @@ -66,7 +65,7 @@ public class GlobalResourceHandler implements RequestHandler { ""); @Override - public boolean handleRequest(Application application, + public boolean handleRequest(VaadinSession application, WrappedRequest request, WrappedResponse response) throws IOException { String pathInfo = request.getRequestPathInfo(); diff --git a/server/src/com/vaadin/server/HttpServletRequestListener.java b/server/src/com/vaadin/server/HttpServletRequestListener.java deleted file mode 100644 index e871dca05d..0000000000 --- a/server/src/com/vaadin/server/HttpServletRequestListener.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2011 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server; - -import java.io.Serializable; - -import javax.servlet.Filter; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.vaadin.Application; - -/** - * {@link Application} that implements this interface gets notified of request - * start and end by terminal. - * <p> - * Interface can be used for several helper tasks including: - * <ul> - * <li>Opening and closing database connections - * <li>Implementing {@link ThreadLocal} - * <li>Setting/Getting {@link Cookie} - * </ul> - * <p> - * Alternatives for implementing similar features are are Servlet {@link Filter} - * s and {@link ApplicationContext.TransactionListener}s in Vaadin. - * - * @since 6.2 - * @see PortletRequestListener - */ -public interface HttpServletRequestListener extends Serializable { - - /** - * This method is called before {@link Terminal} applies the request to - * Application. - * - * @param request - * @param response - */ - public void onRequestStart(HttpServletRequest request, - HttpServletResponse response); - - /** - * This method is called at the end of each request. - * - * @param request - * @param response - */ - public void onRequestEnd(HttpServletRequest request, - HttpServletResponse response); -}
\ No newline at end of file diff --git a/server/src/com/vaadin/server/JsonPaintTarget.java b/server/src/com/vaadin/server/JsonPaintTarget.java index b193c47528..d2f90c33b6 100644 --- a/server/src/com/vaadin/server/JsonPaintTarget.java +++ b/server/src/com/vaadin/server/JsonPaintTarget.java @@ -345,7 +345,7 @@ public class JsonPaintTarget implements PaintTarget { throw new NullPointerException(); } ClientConnector ownerConnector = openPaintables.peek(); - ownerConnector.getUI().getApplication().getGlobalResourceHandler(true) + ownerConnector.getUI().getSession().getGlobalResourceHandler(true) .register(value, ownerConnector); ResourceReference reference = ResourceReference.create(value, diff --git a/server/src/com/vaadin/server/LegacyVaadinPortlet.java b/server/src/com/vaadin/server/LegacyVaadinPortlet.java new file mode 100644 index 0000000000..f436cbb624 --- /dev/null +++ b/server/src/com/vaadin/server/LegacyVaadinPortlet.java @@ -0,0 +1,62 @@ +/* + * 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 javax.portlet.PortletException; +import javax.portlet.PortletRequest; + +import com.vaadin.Application; +import com.vaadin.server.ServletPortletHelper.ApplicationClassException; + +public class LegacyVaadinPortlet extends VaadinPortlet { + + protected Class<? extends Application> getApplicationClass() + throws ClassNotFoundException { + try { + return ServletPortletHelper + .getLegacyApplicationClass(getDeploymentConfiguration()); + } catch (ApplicationClassException e) { + throw new RuntimeException(e); + } + } + + protected Application getNewApplication(PortletRequest request) + throws PortletException { + try { + Class<? extends Application> applicationClass = getApplicationClass(); + return applicationClass.newInstance(); + } catch (Exception e) { + throw new PortletException(e); + } + } + + @Override + protected VaadinPortletSession createApplication( + PortletRequest request) throws PortletException { + VaadinPortletSession application = super + .createApplication(request); + + // Must set current before running init() + VaadinSession.setCurrent(application); + + Application legacyApplication = getNewApplication(request); + legacyApplication.doInit(); + application.addUIProvider(legacyApplication); + + return application; + } +} diff --git a/server/src/com/vaadin/server/LegacyVaadinServlet.java b/server/src/com/vaadin/server/LegacyVaadinServlet.java new file mode 100644 index 0000000000..93a9410509 --- /dev/null +++ b/server/src/com/vaadin/server/LegacyVaadinServlet.java @@ -0,0 +1,63 @@ +/* + * 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 javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import com.vaadin.Application; +import com.vaadin.server.ServletPortletHelper.ApplicationClassException; + +public class LegacyVaadinServlet extends VaadinServlet { + + protected Class<? extends Application> getApplicationClass() + throws ClassNotFoundException { + try { + return ServletPortletHelper + .getLegacyApplicationClass(getDeploymentConfiguration()); + } catch (ApplicationClassException e) { + throw new RuntimeException(e); + } + } + + protected Application getNewApplication(HttpServletRequest request) + throws ServletException { + try { + Class<? extends Application> applicationClass = getApplicationClass(); + return applicationClass.newInstance(); + } catch (Exception e) { + throw new ServletException(e); + } + } + + @Override + protected VaadinServletSession createApplication( + HttpServletRequest request) throws ServletException { + VaadinServletSession application = super + .createApplication(request); + + // Must set current before running init() + VaadinSession.setCurrent(application); + + Application legacyApplication = getNewApplication(request); + legacyApplication.doInit(); + application.addUIProvider(legacyApplication); + + return application; + } + +} diff --git a/server/src/com/vaadin/server/Page.java b/server/src/com/vaadin/server/Page.java index 015c6c907f..9a0948edc8 100644 --- a/server/src/com/vaadin/server/Page.java +++ b/server/src/com/vaadin/server/Page.java @@ -391,7 +391,7 @@ public class Page implements Serializable { } public WebBrowser getWebBrowser() { - return uI.getApplication().getContext().getBrowser(); + return uI.getSession().getBrowser(); } public void setBrowserWindowSize(int width, int height) { diff --git a/server/src/com/vaadin/server/PortletCommunicationManager.java b/server/src/com/vaadin/server/PortletCommunicationManager.java index cb3a8eab80..1a2b892a32 100644 --- a/server/src/com/vaadin/server/PortletCommunicationManager.java +++ b/server/src/com/vaadin/server/PortletCommunicationManager.java @@ -26,7 +26,6 @@ import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.ResourceURL; -import com.vaadin.Application; import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONObject; import com.vaadin.shared.ApplicationConstants; @@ -41,7 +40,7 @@ import com.vaadin.ui.UI; @SuppressWarnings("serial") public class PortletCommunicationManager extends AbstractCommunicationManager { - public PortletCommunicationManager(Application application) { + public PortletCommunicationManager(VaadinSession application) { super(application); } @@ -49,7 +48,7 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { protected BootstrapHandler createBootstrapHandler() { return new BootstrapHandler() { @Override - public boolean handleRequest(Application application, + public boolean handleRequest(VaadinSession application, WrappedRequest request, WrappedResponse response) throws IOException { PortletRequest portletRequest = WrappedPortletRequest.cast( @@ -130,8 +129,9 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { protected String getMainDivStyle(BootstrapContext context) { DeploymentConfiguration deploymentConfiguration = context .getRequest().getDeploymentConfiguration(); - return deploymentConfiguration.getApplicationOrSystemProperty( - VaadinPortlet.PORTLET_PARAMETER_STYLE, null); + return deploymentConfiguration.getApplicationConfiguration() + .getApplicationOrSystemProperty( + VaadinPortlet.PORTLET_PARAMETER_STYLE, null); } @Override @@ -156,8 +156,8 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { @Override protected InputStream getThemeResourceAsStream(UI uI, String themeName, String resource) { - PortletApplicationContext2 context = (PortletApplicationContext2) uI - .getApplication().getContext(); + VaadinPortletSession context = (VaadinPortletSession) uI + .getSession(); PortletContext portletContext = context.getPortletSession() .getPortletContext(); return portletContext.getResourceAsStream("/" diff --git a/server/src/com/vaadin/server/PortletRequestListener.java b/server/src/com/vaadin/server/PortletRequestListener.java deleted file mode 100644 index 4562ddf7b9..0000000000 --- a/server/src/com/vaadin/server/PortletRequestListener.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2011 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server; - -import java.io.Serializable; - -import javax.portlet.PortletRequest; -import javax.portlet.PortletResponse; -import javax.servlet.Filter; - -import com.vaadin.Application; - -/** - * An {@link Application} that implements this interface gets notified of - * request start and end by the terminal. It is quite similar to the - * {@link HttpServletRequestListener}, but the parameters are Portlet specific. - * If an Application is deployed as both a Servlet and a Portlet, one most - * likely needs to implement both. - * <p> - * Only JSR 286 style Portlets are supported. - * <p> - * The interface can be used for several helper tasks including: - * <ul> - * <li>Opening and closing database connections - * <li>Implementing {@link ThreadLocal} - * <li>Inter-portlet communication - * </ul> - * <p> - * Alternatives for implementing similar features are are Servlet {@link Filter} - * s and {@link ApplicationContext.TransactionListener}s in Vaadin. - * - * @since 6.2 - * @see HttpServletRequestListener - */ -public interface PortletRequestListener extends Serializable { - - /** - * This method is called before {@link Terminal} applies the request to - * Application. - * - * @param requestData - * the {@link PortletRequest} about to change Application state - */ - public void onRequestStart(PortletRequest request, PortletResponse response); - - /** - * This method is called at the end of each request. - * - * @param requestData - * the {@link PortletRequest} - */ - public void onRequestEnd(PortletRequest request, PortletResponse response); -}
\ No newline at end of file diff --git a/server/src/com/vaadin/server/RequestHandler.java b/server/src/com/vaadin/server/RequestHandler.java index fcc506cc54..0557c06378 100644 --- a/server/src/com/vaadin/server/RequestHandler.java +++ b/server/src/com/vaadin/server/RequestHandler.java @@ -19,11 +19,10 @@ package com.vaadin.server; import java.io.IOException; import java.io.Serializable; -import com.vaadin.Application; /** * Handler for producing a response to non-UIDL requests. Handlers can be added - * to applications using {@link Application#addRequestHandler(RequestHandler)} + * to applications using {@link VaadinSession#addRequestHandler(RequestHandler)} */ public interface RequestHandler extends Serializable { @@ -42,7 +41,7 @@ public interface RequestHandler extends Serializable { * handlers should be called, otherwise false * @throws IOException */ - boolean handleRequest(Application application, WrappedRequest request, + boolean handleRequest(VaadinSession application, WrappedRequest request, WrappedResponse response) throws IOException; } diff --git a/server/src/com/vaadin/server/RequestTimer.java b/server/src/com/vaadin/server/RequestTimer.java index 470677e331..149a1499e7 100644 --- a/server/src/com/vaadin/server/RequestTimer.java +++ b/server/src/com/vaadin/server/RequestTimer.java @@ -44,7 +44,7 @@ public class RequestTimer implements Serializable { * * @param context */ - public void stop(ApplicationContext context) { + public void stop(VaadinSession context) { // Measure and store the total handling time. This data can be // used in TestBench 3 tests. long time = (System.nanoTime() - requestStartTime) / 1000000; diff --git a/server/src/com/vaadin/server/ResourceReference.java b/server/src/com/vaadin/server/ResourceReference.java index 098fb6c3e4..815cbee275 100644 --- a/server/src/com/vaadin/server/ResourceReference.java +++ b/server/src/com/vaadin/server/ResourceReference.java @@ -71,7 +71,7 @@ public class ResourceReference extends URLReference { ConnectorResource connectorResource = (ConnectorResource) resource; GlobalResourceHandler globalResourceHandler = connector.getUI() - .getApplication().getGlobalResourceHandler(false); + .getSession().getGlobalResourceHandler(false); if (globalResourceHandler != null) { String uri = globalResourceHandler.getUri(connector, connectorResource); diff --git a/server/src/com/vaadin/server/RestrictedRenderResponse.java b/server/src/com/vaadin/server/RestrictedRenderResponse.java index 6923a042d6..206c5f349c 100644 --- a/server/src/com/vaadin/server/RestrictedRenderResponse.java +++ b/server/src/com/vaadin/server/RestrictedRenderResponse.java @@ -36,7 +36,7 @@ import org.w3c.dom.Element; * Read-only wrapper for a {@link RenderResponse}. * * Only for use by {@link PortletApplicationContext} and - * {@link PortletApplicationContext2}. + * {@link VaadinPortletSession}. */ class RestrictedRenderResponse implements RenderResponse, Serializable { diff --git a/server/src/com/vaadin/server/ServletApplicationContext.java b/server/src/com/vaadin/server/ServletApplicationContext.java deleted file mode 100644 index a38c523254..0000000000 --- a/server/src/com/vaadin/server/ServletApplicationContext.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2011 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server; - -import java.io.File; -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; - -import com.vaadin.Application; - -/** - * Web application context for Vaadin applications. - * - * This is automatically added as a {@link HttpSessionBindingListener} when - * added to a {@link HttpSession}. - * - * @author Vaadin Ltd. - * @since 3.1 - */ -@SuppressWarnings("serial") -public class ServletApplicationContext extends ApplicationContext { - - protected transient HttpSession session; - private transient boolean reinitializingSession = false; - - /** - * Stores a reference to the currentRequest. Null it not inside a request. - */ - private transient Object currentRequest = null; - - /** - * Creates a new Web Application Context. - * - */ - protected ServletApplicationContext() { - - } - - @Override - protected void startTransaction(Application application, Object request) { - currentRequest = request; - super.startTransaction(application, request); - } - - @Override - protected void endTransaction(Application application, Object request) { - super.endTransaction(application, request); - currentRequest = null; - } - - @Override - public void valueUnbound(HttpSessionBindingEvent event) { - if (!reinitializingSession) { - // Avoid closing the application if we are only reinitializing the - // session. Closing the application would cause the state to be lost - // and a new application to be created, which is not what we want. - super.valueUnbound(event); - } - } - - /** - * Discards the current session and creates a new session with the same - * contents. The purpose of this is to introduce a new session key in order - * to avoid session fixation attacks. - */ - @SuppressWarnings("unchecked") - public void reinitializeSession() { - - HttpSession oldSession = getHttpSession(); - - // Stores all attributes (security key, reference to this context - // instance) so they can be added to the new session - HashMap<String, Object> attrs = new HashMap<String, Object>(); - for (Enumeration<String> e = oldSession.getAttributeNames(); e - .hasMoreElements();) { - String name = e.nextElement(); - attrs.put(name, oldSession.getAttribute(name)); - } - - // Invalidate the current session, set flag to avoid call to - // valueUnbound - reinitializingSession = true; - oldSession.invalidate(); - reinitializingSession = false; - - // Create a new session - HttpSession newSession = ((HttpServletRequest) currentRequest) - .getSession(); - - // Restores all attributes (security key, reference to this context - // instance) - for (String name : attrs.keySet()) { - newSession.setAttribute(name, attrs.get(name)); - } - - // Update the "current session" variable - session = newSession; - } - - /** - * Gets the application context base directory. - * - * @see com.vaadin.server.ApplicationContext#getBaseDirectory() - */ - @Override - public File getBaseDirectory() { - final String realPath = VaadinServlet.getResourcePath( - session.getServletContext(), "/"); - if (realPath == null) { - return null; - } - return new File(realPath); - } - - /** - * Gets the http-session application is running in. - * - * @return HttpSession this application context resides in. - */ - public HttpSession getHttpSession() { - return session; - } - - /** - * Gets the application context for an HttpSession. - * - * @param session - * the HTTP session. - * @return the application context for HttpSession. - */ - static public ServletApplicationContext getApplicationContext( - HttpSession session) { - ServletApplicationContext cx = (ServletApplicationContext) session - .getAttribute(ServletApplicationContext.class.getName()); - if (cx == null) { - cx = new ServletApplicationContext(); - session.setAttribute(ServletApplicationContext.class.getName(), cx); - } - if (cx.session == null) { - cx.session = session; - } - return cx; - } - - protected void addApplication(Application application) { - applications.add(application); - } - - /** - * Gets communication manager for an application. - * - * If this application has not been running before, a new manager is - * created. - * - * @param application - * @return CommunicationManager - */ - public CommunicationManager getApplicationManager(Application application, - VaadinServlet servlet) { - CommunicationManager mgr = (CommunicationManager) applicationToAjaxAppMgrMap - .get(application); - - if (mgr == null) { - // Creates new manager - mgr = servlet.createCommunicationManager(application); - applicationToAjaxAppMgrMap.put(application, mgr); - } - return mgr; - } - - @Override - public int getMaxInactiveInterval() { - return getHttpSession().getMaxInactiveInterval(); - } -} diff --git a/server/src/com/vaadin/server/ServletPortletHelper.java b/server/src/com/vaadin/server/ServletPortletHelper.java index f9ca55b50e..609168ee96 100644 --- a/server/src/com/vaadin/server/ServletPortletHelper.java +++ b/server/src/com/vaadin/server/ServletPortletHelper.java @@ -1,6 +1,7 @@ package com.vaadin.server; import java.io.Serializable; +import java.util.Properties; import com.vaadin.Application; import com.vaadin.shared.ApplicationConstants; @@ -40,27 +41,22 @@ class ServletPortletHelper implements Serializable { } } - static Class<? extends Application> getApplicationClass( + static Class<? extends Application> getLegacyApplicationClass( DeploymentConfiguration deploymentConfiguration) throws ApplicationClassException { - String applicationParameter = deploymentConfiguration - .getInitParameters().getProperty("application"); - String uiParameter = deploymentConfiguration.getInitParameters() - .getProperty(Application.UI_PARAMETER); + Properties initParameters = deploymentConfiguration + .getApplicationConfiguration().getInitParameters(); + String applicationParameter = initParameters.getProperty("application"); ClassLoader classLoader = deploymentConfiguration.getClassLoader(); if (applicationParameter == null) { - - // Validate the parameter value - verifyUIClass(uiParameter, classLoader); - - // Application can be used if a valid rootLayout is defined - return Application.class; + throw new ApplicationClassException( + "No \"application\" init parameter found"); } try { - return (Class<? extends Application>) classLoader - .loadClass(applicationParameter); + return classLoader.loadClass(applicationParameter).asSubclass( + Application.class); } catch (final ClassNotFoundException e) { throw new ApplicationClassException( "Failed to load application class: " + applicationParameter, @@ -71,7 +67,7 @@ class ServletPortletHelper implements Serializable { private static void verifyUIClass(String className, ClassLoader classLoader) throws ApplicationClassException { if (className == null) { - throw new ApplicationClassException(Application.UI_PARAMETER + throw new ApplicationClassException(VaadinSession.UI_PARAMETER + " init parameter not defined"); } @@ -136,4 +132,25 @@ class ServletPortletHelper implements Serializable { ApplicationConstants.HEARTBEAT_REQUEST_PATH); } + public static void initDefaultUIProvider(VaadinSession application, + DeploymentConfiguration deploymentConfiguration) + throws ApplicationClassException { + String uiProperty = deploymentConfiguration + .getApplicationConfiguration().getInitParameters() + .getProperty(VaadinSession.UI_PARAMETER); + if (uiProperty != null) { + verifyUIClass(uiProperty, deploymentConfiguration.getClassLoader()); + application.addUIProvider(new DefaultUIProvider()); + } + } + + public static void checkUiProviders(VaadinSession newApplication) + throws ApplicationClassException { + if (newApplication.getUIProviders().isEmpty()) { + throw new ApplicationClassException( + "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/StreamVariable.java b/server/src/com/vaadin/server/StreamVariable.java index f289e7612a..a75cc2f0d7 100644 --- a/server/src/com/vaadin/server/StreamVariable.java +++ b/server/src/com/vaadin/server/StreamVariable.java @@ -18,7 +18,6 @@ package com.vaadin.server; import java.io.OutputStream; import java.io.Serializable; -import com.vaadin.Application; import com.vaadin.server.StreamVariable.StreamingEndEvent; import com.vaadin.server.StreamVariable.StreamingErrorEvent; import com.vaadin.server.StreamVariable.StreamingStartEvent; @@ -152,7 +151,7 @@ public interface StreamVariable extends Serializable { * the streaming ended before the end of the input. The streaming may fail * due an interruption by {@link } or due an other unknown exception in * communication. In the latter case the exception is also passed to - * {@link Application#terminalError(com.vaadin.server.Terminal.ErrorEvent)} + * {@link VaadinSession#terminalError(com.vaadin.server.Terminal.ErrorEvent)} * . */ public interface StreamingErrorEvent extends StreamingEvent { diff --git a/server/src/com/vaadin/server/SystemMessages.java b/server/src/com/vaadin/server/SystemMessages.java index 17aed0003e..3f6a0f9a68 100644 --- a/server/src/com/vaadin/server/SystemMessages.java +++ b/server/src/com/vaadin/server/SystemMessages.java @@ -18,7 +18,6 @@ package com.vaadin.server; import java.io.Serializable; -import com.vaadin.Application; /** @@ -26,7 +25,7 @@ import com.vaadin.Application; * critical situations that can occur. * <p> * Customize by overriding the static - * {@link Application#getSystemMessages()} and returning + * {@link VaadinSession#getSystemMessages()} and returning * {@link CustomizedSystemMessages}. * </p> * <p> diff --git a/server/src/com/vaadin/server/UIProvider.java b/server/src/com/vaadin/server/UIProvider.java index ea73a705ea..b2908a04d6 100644 --- a/server/src/com/vaadin/server/UIProvider.java +++ b/server/src/com/vaadin/server/UIProvider.java @@ -16,13 +16,84 @@ package com.vaadin.server; -import com.vaadin.Application; +import com.vaadin.annotations.Widgetset; import com.vaadin.ui.UI; public interface UIProvider { - public Class<? extends UI> getUIClass(Application application, + public Class<? extends UI> getUIClass(VaadinSession application, WrappedRequest request); - public UI createInstance(Application application, Class<? extends UI> type, + public UI createInstance(VaadinSession application, Class<? extends UI> type, WrappedRequest request); + + public String getPageTitleForUI(WrappedRequest request, + Class<? extends UI> uiClass); + + /** + * Checks whether the same UI state should be reused if the framework can + * detect that the application is opened in a browser window where it has + * previously been open. The framework attempts to discover this by checking + * the value of window.name in the browser. + * + * @param request + * @param uiClass + * + * @return <code>true</code>if the same UI instance should be reused e.g. + * when the browser window is refreshed. + */ + public boolean isUiPreserved(WrappedRequest request, + Class<? extends UI> uiClass); + + /** + * Finds the widgetset to use for a specific UI. If no specific widgetset is + * required, <code>null</code> is returned. + * <p> + * The default implementation uses the @{@link Widgetset} annotation if it's + * defined for the UI class. + * + * @param request + * the wrapped request for which to get a widgetset + * @param uiClass + * the UI class to get a widgetset for + * @return the name of the widgetset, or <code>null</code> if the default + * widgetset should be used + * + */ + public String getWidgetsetForUI(WrappedRequest request, + Class<? extends UI> uiClass); + + /** + * Finds the theme to use for a specific UI. If no specific theme is + * required, <code>null</code> is returned. + * + * TODO Tell what the default implementation does once it does something. + * + * @param uI + * the UI to get a theme for + * @return the name of the theme, or <code>null</code> if the default theme + * should be used + * + */ + public String getThemeForUI(WrappedRequest request, + Class<? extends UI> uiClass); + + /** + * Finds an existing {@link UI} for a request. + * <p> + * Implementations should take care to not return an UI instance that might + * be used in some other browser as that might cause synchronization issues + * when changes from one browser window are not present in the other. + * <p> + * If no UI provider returns an existing UI, the framework does also check + * the window.name for an existing instance with + * {@link #isUiPreserved(WrappedRequest, Class)} before falling back to + * bootstrapping and creating a new UI instance. + * + * @param request + * the request for which a UI is desired + * @return a UI belonging to the request, or <code>null</code> if this UI + * provider doesn't have an existing UI for the request. + */ + public UI getExistingUI(WrappedRequest request); + } diff --git a/server/src/com/vaadin/server/UnsupportedBrowserHandler.java b/server/src/com/vaadin/server/UnsupportedBrowserHandler.java index 325edb5d61..dccbf7ba7b 100644 --- a/server/src/com/vaadin/server/UnsupportedBrowserHandler.java +++ b/server/src/com/vaadin/server/UnsupportedBrowserHandler.java @@ -18,7 +18,6 @@ package com.vaadin.server; import java.io.IOException; import java.io.Writer; -import com.vaadin.Application; /** * A {@link RequestHandler} that presents an informative page if the browser in @@ -36,7 +35,7 @@ public class UnsupportedBrowserHandler implements RequestHandler { public static final String FORCE_LOAD_COOKIE = "vaadinforceload=1"; @Override - public boolean handleRequest(Application application, + public boolean handleRequest(VaadinSession application, WrappedRequest request, WrappedResponse response) throws IOException { diff --git a/server/src/com/vaadin/server/VaadinPortlet.java b/server/src/com/vaadin/server/VaadinPortlet.java index 4d6d7b84f0..941d96ea25 100644 --- a/server/src/com/vaadin/server/VaadinPortlet.java +++ b/server/src/com/vaadin/server/VaadinPortlet.java @@ -16,6 +16,7 @@ package com.vaadin.server; import java.io.BufferedWriter; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -24,11 +25,13 @@ import java.io.PrintWriter; import java.io.Serializable; import java.lang.reflect.Method; import java.net.MalformedURLException; +import java.net.URL; import java.security.GeneralSecurityException; import java.util.Enumeration; import java.util.Locale; import java.util.Map; import java.util.Properties; +import java.util.logging.Level; import java.util.logging.Logger; import javax.portlet.ActionRequest; @@ -52,11 +55,12 @@ import javax.servlet.http.HttpServletResponse; import com.liferay.portal.kernel.util.PortalClassInvoker; import com.liferay.portal.kernel.util.PropsUtil; -import com.vaadin.Application; -import com.vaadin.Application.ApplicationStartEvent; +import com.vaadin.DefaultApplicationConfiguration; import com.vaadin.server.AbstractCommunicationManager.Callback; import com.vaadin.server.ServletPortletHelper.ApplicationClassException; +import com.vaadin.server.VaadinSession.ApplicationStartEvent; import com.vaadin.ui.UI; +import com.vaadin.util.CurrentInstance; /** * Portlet 2.0 base class. This replaces the servlet in servlet/portlet 1.0 @@ -75,8 +79,8 @@ public class VaadinPortlet extends GenericPortlet implements Constants { private final VaadinPortlet portlet; public PortletDeploymentConfiguration(VaadinPortlet portlet, - Properties applicationProperties) { - super(portlet.getClass(), applicationProperties); + ApplicationConfiguration applicationConfiguration) { + super(applicationConfiguration); this.portlet = portlet; } @@ -87,8 +91,8 @@ public class VaadinPortlet extends GenericPortlet implements Constants { @Override public String getConfiguredWidgetset(WrappedRequest request) { - String widgetset = getApplicationOrSystemProperty( - PARAMETER_WIDGETSET, null); + String widgetset = getApplicationConfiguration() + .getApplicationOrSystemProperty(PARAMETER_WIDGETSET, null); if (widgetset == null) { // If no widgetset defined for the application, check the @@ -152,6 +156,29 @@ public class VaadinPortlet extends GenericPortlet implements Constants { public SystemMessages getSystemMessages() { return ServletPortletHelper.DEFAULT_SYSTEM_MESSAGES; } + + @Override + public File getBaseDirectory() { + PortletContext context = getPortlet().getPortletContext(); + String resultPath = context.getRealPath("/"); + if (resultPath != null) { + return new File(resultPath); + } else { + try { + final URL url = context.getResource("/"); + return new File(url.getFile()); + } catch (final Exception e) { + // FIXME: Handle exception + getLogger() + .log(Level.INFO, + "Cannot access base directory, possible security issue " + + "with Application Server or Servlet Container", + e); + } + } + return null; + } + } public static class WrappedHttpAndPortletRequest extends @@ -293,7 +320,7 @@ public class VaadinPortlet extends GenericPortlet implements Constants { // TODO Can we close the application when the portlet is removed? Do we know // when the portlet is removed? - private DeploymentConfiguration deploymentConfiguration; + private PortletDeploymentConfiguration deploymentConfiguration; private AddonContext addonContext; @Override @@ -318,15 +345,23 @@ public class VaadinPortlet extends GenericPortlet implements Constants { config.getInitParameter(name)); } - deploymentConfiguration = createDeploymentConfiguration(applicationProperties); + ApplicationConfiguration applicationConfiguration = createApplicationConfiguration(applicationProperties); + deploymentConfiguration = createDeploymentConfiguration(applicationConfiguration); addonContext = new AddonContext(deploymentConfiguration); addonContext.init(); } - protected DeploymentConfiguration createDeploymentConfiguration( + protected ApplicationConfiguration createApplicationConfiguration( Properties applicationProperties) { - return new PortletDeploymentConfiguration(this, applicationProperties); + return new DefaultApplicationConfiguration(getClass(), + applicationProperties); + } + + protected PortletDeploymentConfiguration createDeploymentConfiguration( + ApplicationConfiguration applicationConfiguration) { + return new PortletDeploymentConfiguration(this, + applicationConfiguration); } @Override @@ -383,16 +418,6 @@ public class VaadinPortlet extends GenericPortlet implements Constants { && request.getResourceID().equals("DUMMY"); } - /** - * Returns true if the portlet is running in production mode. Production - * mode disables all debug facilities. - * - * @return true if in production mode, false if in debug mode - */ - public boolean isProductionMode() { - return deploymentConfiguration.isProductionMode(); - } - protected void handleRequest(PortletRequest request, PortletResponse response) throws PortletException, IOException { RequestTimer requestTimer = new RequestTimer(); @@ -406,6 +431,9 @@ public class VaadinPortlet extends GenericPortlet implements Constants { WrappedPortletResponse wrappedResponse = new WrappedPortletResponse( response, getDeploymentConfiguration()); + CurrentInstance.set(WrappedRequest.class, wrappedRequest); + CurrentInstance.set(WrappedResponse.class, wrappedResponse); + RequestType requestType = getRequestType(wrappedRequest); if (requestType == RequestType.UNKNOWN) { @@ -427,9 +455,7 @@ public class VaadinPortlet extends GenericPortlet implements Constants { serveStaticResources((ResourceRequest) request, (ResourceResponse) response); } else { - Application application = null; - boolean transactionStarted = false; - boolean requestStarted = false; + VaadinSession application = null; boolean applicationRunning = false; try { @@ -441,19 +467,16 @@ public class VaadinPortlet extends GenericPortlet implements Constants { if (application == null) { return; } - Application.setCurrent(application); + VaadinSession.setCurrent(application); /* * Get or create an application context and an application * manager for the session */ - PortletApplicationContext2 applicationContext = getApplicationContext(request - .getPortletSession()); - applicationContext.setResponse(response); - applicationContext.setPortletConfig(getPortletConfig()); + VaadinPortletSession applicationContext = (VaadinPortletSession) application; - PortletCommunicationManager applicationManager = applicationContext - .getApplicationManager(application); + PortletCommunicationManager applicationManager = (PortletCommunicationManager) applicationContext + .getApplicationManager(); if (requestType == RequestType.CONNECTOR_RESOURCE) { applicationManager.serveConnectorResource(wrappedRequest, @@ -469,27 +492,8 @@ public class VaadinPortlet extends GenericPortlet implements Constants { applicationContext.getBrowser().updateRequestDetails( wrappedRequest); - /* - * Call application requestStart before Application.init() is - * called (bypasses the limitation in TransactionListener) - */ - if (application instanceof PortletRequestListener) { - ((PortletRequestListener) application).onRequestStart( - request, response); - requestStarted = true; - } - - /* Start the newly created application */ - startApplication(request, application, applicationContext); applicationRunning = true; - /* - * Transaction starts. Call transaction listeners. Transaction - * end is called in the finally block below. - */ - applicationContext.startTransaction(application, request); - transactionStarted = true; - /* Notify listeners */ // Finds the window within the application @@ -525,19 +529,17 @@ public class VaadinPortlet extends GenericPortlet implements Constants { // TODO Should this happen before or after the transaction // starts? if (request instanceof RenderRequest) { - applicationContext.firePortletRenderRequest(application, - uI, (RenderRequest) request, - (RenderResponse) response); + applicationContext.firePortletRenderRequest(uI, + (RenderRequest) request, (RenderResponse) response); } else if (request instanceof ActionRequest) { - applicationContext.firePortletActionRequest(application, - uI, (ActionRequest) request, - (ActionResponse) response); + applicationContext.firePortletActionRequest(uI, + (ActionRequest) request, (ActionResponse) response); } else if (request instanceof EventRequest) { - applicationContext.firePortletEventRequest(application, uI, + applicationContext.firePortletEventRequest(uI, (EventRequest) request, (EventResponse) response); } else if (request instanceof ResourceRequest) { - applicationContext.firePortletResourceRequest(application, - uI, (ResourceRequest) request, + applicationContext.firePortletResourceRequest(uI, + (ResourceRequest) request, (ResourceResponse) response); } @@ -588,29 +590,10 @@ public class VaadinPortlet extends GenericPortlet implements Constants { application.closeInactiveUIs(); } - // Notifies transaction end - try { - if (transactionStarted) { - ((PortletApplicationContext2) application.getContext()) - .endTransaction(application, request); - } - } finally { - try { - if (requestStarted) { - ((PortletRequestListener) application) - .onRequestEnd(request, response); + CurrentInstance.clearAll(); - } - } finally { - UI.setCurrent(null); - Application.setCurrent(null); - - PortletSession session = request - .getPortletSession(false); - if (session != null) { - requestTimer.stop(getApplicationContext(session)); - } - } + if (application != null) { + requestTimer.stop(application); } } } @@ -640,7 +623,7 @@ public class VaadinPortlet extends GenericPortlet implements Constants { } - protected DeploymentConfiguration getDeploymentConfiguration() { + protected PortletDeploymentConfiguration getDeploymentConfiguration() { return deploymentConfiguration; } @@ -668,8 +651,8 @@ public class VaadinPortlet extends GenericPortlet implements Constants { */ private void handleOtherRequest(WrappedPortletRequest request, WrappedResponse response, RequestType requestType, - Application application, - PortletApplicationContext2 applicationContext, + VaadinSession application, + VaadinPortletSession applicationContext, PortletCommunicationManager applicationManager) throws PortletException, IOException, MalformedURLException { if (requestType == RequestType.APPLICATION_RESOURCE @@ -776,30 +759,14 @@ public class VaadinPortlet extends GenericPortlet implements Constants { && (request.getParameter(URL_PARAMETER_REPAINT_ALL).equals("1")); } - private void startApplication(PortletRequest request, - Application application, PortletApplicationContext2 context) - throws PortletException, MalformedURLException { - if (!application.isRunning()) { - Locale locale = request.getLocale(); - application.setLocale(locale); - // No application URL when running inside a portlet - application.start(new ApplicationStartEvent(null, - getDeploymentConfiguration(), context)); - addonContext.fireApplicationStarted(application); - } - } - private void endApplication(PortletRequest request, - PortletResponse response, Application application) + PortletResponse response, VaadinSession application) throws IOException { - final PortletSession session = request.getPortletSession(); - if (session != null) { - getApplicationContext(session).removeApplication(application); - } + application.removeFromSession(); // Do not send any redirects when running inside a portlet. } - private Application findApplicationInstance( + private VaadinSession findApplicationInstance( WrappedPortletRequest wrappedRequest, RequestType requestType) throws PortletException, SessionExpiredException, MalformedURLException { @@ -809,7 +776,7 @@ public class VaadinPortlet extends GenericPortlet implements Constants { request, requestType); /* Find an existing application for this request. */ - Application application = getExistingApplication(request, + VaadinSession application = getExistingApplication(request, requestCanCreateApplication); if (application != null) { @@ -825,7 +792,7 @@ public class VaadinPortlet extends GenericPortlet implements Constants { if (restartApplication) { closeApplication(application, request.getPortletSession(false)); - return createApplication(request); + return createAndRegisterApplication(request); } else if (closeApplication) { closeApplication(application, request.getPortletSession(false)); return null; @@ -837,35 +804,61 @@ public class VaadinPortlet extends GenericPortlet implements Constants { // No existing application was found if (requestCanCreateApplication) { - return createApplication(request); + return createAndRegisterApplication(request); } else { throw new SessionExpiredException(); } } - private void closeApplication(Application application, + private void closeApplication(VaadinSession application, PortletSession session) { if (application == null) { return; } application.close(); - if (session != null) { - PortletApplicationContext2 context = getApplicationContext(session); - context.removeApplication(application); - } + application.removeFromSession(); } - private Application createApplication(PortletRequest request) - throws PortletException, MalformedURLException { - Application newApplication = getNewApplication(request); - final PortletApplicationContext2 context = getApplicationContext(request - .getPortletSession()); - context.addApplication(newApplication, request.getWindowID()); + private VaadinSession createAndRegisterApplication(PortletRequest request) + throws PortletException { + VaadinSession newApplication = createApplication(request); + + try { + ServletPortletHelper.checkUiProviders(newApplication); + } catch (ApplicationClassException e) { + throw new PortletException(e); + } + + newApplication.storeInSession(new WrappedPortletSession(request + .getPortletSession())); + + Locale locale = request.getLocale(); + newApplication.setLocale(locale); + // No application URL when running inside a portlet + newApplication.start(new ApplicationStartEvent(null, + getDeploymentConfiguration().getApplicationConfiguration(), + new PortletCommunicationManager(newApplication))); + addonContext.fireApplicationStarted(newApplication); + return newApplication; } - private Application getExistingApplication(PortletRequest request, + protected VaadinPortletSession createApplication( + PortletRequest request) throws PortletException { + VaadinPortletSession application = new VaadinPortletSession(); + + try { + ServletPortletHelper.initDefaultUIProvider(application, + getDeploymentConfiguration()); + } catch (ApplicationClassException e) { + throw new PortletException(e); + } + + return application; + } + + private VaadinSession getExistingApplication(PortletRequest request, boolean allowSessionCreation) throws MalformedURLException, SessionExpiredException { @@ -876,43 +869,21 @@ public class VaadinPortlet extends GenericPortlet implements Constants { throw new SessionExpiredException(); } - PortletApplicationContext2 context = getApplicationContext(session); - Application application = context.getApplicationForWindowId(request - .getWindowID()); + VaadinSession application = VaadinSession + .getForSession(new WrappedPortletSession(session)); if (application == null) { return null; } - if (application.isRunning()) { - return application; + if (!application.isRunning()) { + application.removeFromSession(); + return null; } - // application found but not running - context.removeApplication(application); - - return null; - } - - protected Class<? extends Application> getApplicationClass() - throws ApplicationClassException { - return ServletPortletHelper - .getApplicationClass(getDeploymentConfiguration()); - } - protected Application getNewApplication(PortletRequest request) - throws PortletException { - try { - final Application application = getApplicationClass().newInstance(); - return application; - } catch (final IllegalAccessException e) { - throw new PortletException("getNewApplication failed", e); - } catch (final InstantiationException e) { - throw new PortletException("getNewApplication failed", e); - } catch (final ApplicationClassException e) { - throw new PortletException("getNewApplication failed", e); - } + return application; } private void handleServiceException(WrappedPortletRequest request, - WrappedPortletResponse response, Application application, + WrappedPortletResponse response, VaadinSession application, Throwable e) throws IOException, PortletException { // TODO Check that this error handler is working when running inside a // portlet @@ -1013,21 +984,6 @@ public class VaadinPortlet extends GenericPortlet implements Constants { outWriter.close(); } - /** - * - * Gets the application context for a PortletSession. If no context is - * currently stored in a session a new context is created and stored in the - * session. - * - * @param portletSession - * the portlet session. - * @return the application context for the session. - */ - protected PortletApplicationContext2 getApplicationContext( - PortletSession portletSession) { - return PortletApplicationContext2.getApplicationContext(portletSession); - } - private static final Logger getLogger() { return Logger.getLogger(VaadinPortlet.class.getName()); } diff --git a/server/src/com/vaadin/server/PortletApplicationContext2.java b/server/src/com/vaadin/server/VaadinPortletSession.java index cea97bc939..8d1ae84fca 100644 --- a/server/src/com/vaadin/server/PortletApplicationContext2.java +++ b/server/src/com/vaadin/server/VaadinPortletSession.java @@ -15,15 +15,12 @@ */ package com.vaadin.server; -import java.io.File; import java.io.Serializable; -import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; @@ -44,8 +41,8 @@ import javax.portlet.StateAwareResponse; import javax.servlet.http.HttpSessionBindingListener; import javax.xml.namespace.QName; -import com.vaadin.Application; import com.vaadin.ui.UI; +import com.vaadin.util.CurrentInstance; /** * TODO Write documentation, fix JavaDoc tags. @@ -56,16 +53,9 @@ import com.vaadin.ui.UI; * @author peholmst */ @SuppressWarnings("serial") -public class PortletApplicationContext2 extends ApplicationContext { +public class VaadinPortletSession extends VaadinSession { - protected Map<Application, Set<PortletListener>> portletListeners = new HashMap<Application, Set<PortletListener>>(); - - protected transient PortletSession session; - protected transient PortletConfig portletConfig; - - protected HashMap<String, Application> portletWindowIdToApplicationMap = new HashMap<String, Application>(); - - private transient PortletResponse response; + private final Set<PortletListener> portletListeners = new LinkedHashSet<PortletListener>(); private final Map<String, QName> eventActionDestinationMap = new HashMap<String, QName>(); private final Map<String, Serializable> eventActionValueMap = new HashMap<String, Serializable>(); @@ -73,125 +63,50 @@ public class PortletApplicationContext2 extends ApplicationContext { private final Map<String, String> sharedParameterActionNameMap = new HashMap<String, String>(); private final Map<String, String> sharedParameterActionValueMap = new HashMap<String, String>(); - @Override - public File getBaseDirectory() { - String resultPath = session.getPortletContext().getRealPath("/"); - if (resultPath != null) { - return new File(resultPath); - } else { - try { - final URL url = session.getPortletContext().getResource("/"); - return new File(url.getFile()); - } catch (final Exception e) { - // FIXME: Handle exception - getLogger() - .log(Level.INFO, - "Cannot access base directory, possible security issue " - + "with Application Server or Servlet Container", - e); - } - } - return null; - } - - protected PortletCommunicationManager getApplicationManager( - Application application) { - PortletCommunicationManager mgr = (PortletCommunicationManager) applicationToAjaxAppMgrMap - .get(application); - - if (mgr == null) { - // Creates a new manager - mgr = createPortletCommunicationManager(application); - applicationToAjaxAppMgrMap.put(application, mgr); - } - return mgr; + public PortletSession getPortletSession() { + WrappedSession wrappedSession = getSession(); + PortletSession session = ((WrappedPortletSession) wrappedSession) + .getPortletSession(); + return session; } - protected PortletCommunicationManager createPortletCommunicationManager( - Application application) { - return new PortletCommunicationManager(application); - } + private PortletResponse getCurrentResponse() { + WrappedPortletResponse currentResponse = (WrappedPortletResponse) CurrentInstance + .get(WrappedResponse.class); - public static PortletApplicationContext2 getApplicationContext( - PortletSession session) { - Object cxattr = session.getAttribute(PortletApplicationContext2.class - .getName()); - PortletApplicationContext2 cx = null; - // can be false also e.g. if old context comes from another - // classloader when using - // <private-session-attributes>false</private-session-attributes> - // and redeploying the portlet - see #7461 - if (cxattr instanceof PortletApplicationContext2) { - cx = (PortletApplicationContext2) cxattr; - } - if (cx == null) { - cx = new PortletApplicationContext2(); - session.setAttribute(PortletApplicationContext2.class.getName(), cx); - } - if (cx.session == null) { - cx.session = session; + if (currentResponse != null) { + return currentResponse.getPortletResponse(); + } else { + return null; } - return cx; - } - - @Override - protected void removeApplication(Application application) { - super.removeApplication(application); - // values() is backed by map, removes the key-value pair from the map - portletWindowIdToApplicationMap.values().remove(application); - } - - protected void addApplication(Application application, - String portletWindowId) { - applications.add(application); - portletWindowIdToApplicationMap.put(portletWindowId, application); - } - - public Application getApplicationForWindowId(String portletWindowId) { - return portletWindowIdToApplicationMap.get(portletWindowId); - } - - public PortletSession getPortletSession() { - return session; } public PortletConfig getPortletConfig() { - return portletConfig; + WrappedPortletResponse response = (WrappedPortletResponse) CurrentInstance + .get(WrappedResponse.class); + return response.getDeploymentConfiguration().getPortlet() + .getPortletConfig(); } - public void setPortletConfig(PortletConfig config) { - portletConfig = config; + public void addPortletListener(PortletListener listener) { + portletListeners.add(listener); } - public void addPortletListener(Application app, PortletListener listener) { - Set<PortletListener> l = portletListeners.get(app); - if (l == null) { - l = new LinkedHashSet<PortletListener>(); - portletListeners.put(app, l); - } - l.add(listener); + public void removePortletListener(PortletListener listener) { + portletListeners.remove(listener); } - public void removePortletListener(Application app, PortletListener listener) { - Set<PortletListener> l = portletListeners.get(app); - if (l != null) { - l.remove(listener); + public void firePortletRenderRequest(UI uI, RenderRequest request, + RenderResponse response) { + for (PortletListener l : new ArrayList<PortletListener>( + portletListeners)) { + l.handleRenderRequest(request, new RestrictedRenderResponse( + response), uI); } } - public void firePortletRenderRequest(Application app, UI uI, - RenderRequest request, RenderResponse response) { - Set<PortletListener> listeners = portletListeners.get(app); - if (listeners != null) { - for (PortletListener l : listeners) { - l.handleRenderRequest(request, new RestrictedRenderResponse( - response), uI); - } - } - } - - public void firePortletActionRequest(Application app, UI uI, - ActionRequest request, ActionResponse response) { + public void firePortletActionRequest(UI uI, ActionRequest request, + ActionResponse response) { String key = request.getParameter(ActionRequest.ACTION_NAME); if (eventActionDestinationMap.containsKey(key)) { // this action request is only to send queued portlet events @@ -209,32 +124,26 @@ public class PortletApplicationContext2 extends ApplicationContext { sharedParameterActionValueMap.remove(key); } else { // normal action request, notify listeners - Set<PortletListener> listeners = portletListeners.get(app); - if (listeners != null) { - for (PortletListener l : listeners) { - l.handleActionRequest(request, response, uI); - } + for (PortletListener l : new ArrayList<PortletListener>( + portletListeners)) { + l.handleActionRequest(request, response, uI); } } } - public void firePortletEventRequest(Application app, UI uI, - EventRequest request, EventResponse response) { - Set<PortletListener> listeners = portletListeners.get(app); - if (listeners != null) { - for (PortletListener l : listeners) { - l.handleEventRequest(request, response, uI); - } + public void firePortletEventRequest(UI uI, EventRequest request, + EventResponse response) { + for (PortletListener l : new ArrayList<PortletListener>( + portletListeners)) { + l.handleEventRequest(request, response, uI); } } - public void firePortletResourceRequest(Application app, UI uI, - ResourceRequest request, ResourceResponse response) { - Set<PortletListener> listeners = portletListeners.get(app); - if (listeners != null) { - for (PortletListener l : listeners) { - l.handleResourceRequest(request, response, uI); - } + public void firePortletResourceRequest(UI uI, ResourceRequest request, + ResourceResponse response) { + for (PortletListener l : new ArrayList<PortletListener>( + portletListeners)) { + l.handleResourceRequest(request, response, uI); } } @@ -254,17 +163,6 @@ public class PortletApplicationContext2 extends ApplicationContext { } /** - * This is for use by {@link VaadinPortlet} only. - * - * TODO cleaner implementation, now "semi-static"! - * - * @param mimeResponse - */ - void setResponse(PortletResponse response) { - this.response = response; - } - - /** * Creates a new action URL. * * @param action @@ -273,6 +171,7 @@ public class PortletApplicationContext2 extends ApplicationContext { */ public PortletURL generateActionURL(String action) { PortletURL url = null; + PortletResponse response = getCurrentResponse(); if (response instanceof MimeResponse) { url = ((MimeResponse) response).createActionURL(); url.setParameter("javax.portlet.action", action); @@ -305,6 +204,7 @@ public class PortletApplicationContext2 extends ApplicationContext { */ public void sendPortletEvent(UI uI, QName name, Serializable value) throws IllegalStateException { + PortletResponse response = getCurrentResponse(); if (response instanceof MimeResponse) { String actionKey = "" + System.currentTimeMillis(); while (eventActionDestinationMap.containsKey(actionKey)) { @@ -351,6 +251,7 @@ public class PortletApplicationContext2 extends ApplicationContext { */ public void setSharedRenderParameter(UI uI, String name, String value) throws IllegalStateException { + PortletResponse response = getCurrentResponse(); if (response instanceof MimeResponse) { String actionKey = "" + System.currentTimeMillis(); while (sharedParameterActionNameMap.containsKey(actionKey)) { @@ -390,6 +291,7 @@ public class PortletApplicationContext2 extends ApplicationContext { */ public void setPortletMode(UI uI, PortletMode portletMode) throws IllegalStateException, PortletModeException { + PortletResponse response = getCurrentResponse(); if (response instanceof MimeResponse) { PortletURL url = ((MimeResponse) response).createRenderURL(); url.setPortletMode(portletMode); @@ -402,13 +304,4 @@ public class PortletApplicationContext2 extends ApplicationContext { "Portlet mode can only be changed from a portlet request"); } } - - @Override - public int getMaxInactiveInterval() { - return getPortletSession().getMaxInactiveInterval(); - } - - private Logger getLogger() { - return Logger.getLogger(PortletApplicationContext2.class.getName()); - } } diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index a10ad965c5..d2e04da2e9 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -16,6 +16,7 @@ package com.vaadin.server; import java.io.BufferedWriter; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -30,7 +31,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; -import java.util.Iterator; import java.util.Locale; import java.util.Properties; import java.util.logging.Level; @@ -44,12 +44,13 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import com.vaadin.Application; -import com.vaadin.Application.ApplicationStartEvent; +import com.vaadin.DefaultApplicationConfiguration; import com.vaadin.server.AbstractCommunicationManager.Callback; import com.vaadin.server.ServletPortletHelper.ApplicationClassException; +import com.vaadin.server.VaadinSession.ApplicationStartEvent; import com.vaadin.shared.ApplicationConstants; import com.vaadin.ui.UI; +import com.vaadin.util.CurrentInstance; @SuppressWarnings("serial") public class VaadinServlet extends HttpServlet implements Constants { @@ -59,8 +60,8 @@ public class VaadinServlet extends HttpServlet implements Constants { private final VaadinServlet servlet; public ServletDeploymentConfiguration(VaadinServlet servlet, - Properties applicationProperties) { - super(servlet.getClass(), applicationProperties); + ApplicationConfiguration applicationProperties) { + super(applicationProperties); this.servlet = servlet; } @@ -74,8 +75,9 @@ public class VaadinServlet extends HttpServlet implements Constants { .cast(request); String staticFileLocation; // if property is defined in configurations, use that - staticFileLocation = getApplicationOrSystemProperty( - PARAMETER_VAADIN_RESOURCES, null); + staticFileLocation = getApplicationConfiguration() + .getApplicationOrSystemProperty(PARAMETER_VAADIN_RESOURCES, + null); if (staticFileLocation != null) { return staticFileLocation; } @@ -111,9 +113,10 @@ public class VaadinServlet extends HttpServlet implements Constants { @Override public String getConfiguredWidgetset(WrappedRequest request) { - return getApplicationOrSystemProperty( - VaadinServlet.PARAMETER_WIDGETSET, - VaadinServlet.DEFAULT_WIDGETSET); + return getApplicationConfiguration() + .getApplicationOrSystemProperty( + VaadinServlet.PARAMETER_WIDGETSET, + VaadinServlet.DEFAULT_WIDGETSET); } @Override @@ -136,6 +139,16 @@ public class VaadinServlet extends HttpServlet implements Constants { public SystemMessages getSystemMessages() { return ServletPortletHelper.DEFAULT_SYSTEM_MESSAGES; } + + @Override + public File getBaseDirectory() { + final String realPath = VaadinServlet.getResourcePath( + servlet.getServletContext(), "/"); + if (realPath == null) { + return null; + } + return new File(realPath); + } } private static class AbstractApplicationServletWrapper implements Callback { @@ -200,15 +213,23 @@ public class VaadinServlet extends HttpServlet implements Constants { servletConfig.getInitParameter(name)); } - deploymentConfiguration = createDeploymentConfiguration(applicationProperties); + ApplicationConfiguration applicationConfiguration = createApplicationConfiguration(applicationProperties); + deploymentConfiguration = createDeploymentConfiguration(applicationConfiguration); addonContext = new AddonContext(deploymentConfiguration); addonContext.init(); } - protected ServletDeploymentConfiguration createDeploymentConfiguration( + protected ApplicationConfiguration createApplicationConfiguration( Properties applicationProperties) { - return new ServletDeploymentConfiguration(this, applicationProperties); + return new DefaultApplicationConfiguration(getClass(), + applicationProperties); + } + + protected ServletDeploymentConfiguration createDeploymentConfiguration( + ApplicationConfiguration applicationConfiguration) { + return new ServletDeploymentConfiguration(this, + applicationConfiguration); } @Override @@ -219,26 +240,6 @@ public class VaadinServlet extends HttpServlet implements Constants { } /** - * Returns true if the servlet is running in production mode. Production - * mode disables all debug facilities. - * - * @return true if in production mode, false if in debug mode - */ - public boolean isProductionMode() { - return getDeploymentConfiguration().isProductionMode(); - } - - /** - * Returns the number of seconds the browser should cache a file. Default is - * 1 hour (3600 s). - * - * @return The number of seconds files are cached in the browser - */ - public int getResourceCacheTime() { - return getDeploymentConfiguration().getResourceCacheTime(); - } - - /** * Receives standard HTTP requests from the public service method and * dispatches them. * @@ -267,6 +268,9 @@ public class VaadinServlet extends HttpServlet implements Constants { RequestTimer requestTimer = new RequestTimer(); requestTimer.start(); + CurrentInstance.set(WrappedResponse.class, response); + CurrentInstance.set(WrappedRequest.class, request); + AbstractApplicationServletWrapper servletWrapper = new AbstractApplicationServletWrapper( this); @@ -280,9 +284,7 @@ public class VaadinServlet extends HttpServlet implements Constants { return; } - Application application = null; - boolean transactionStarted = false; - boolean requestStarted = false; + VaadinSession application = null; boolean applicationRunning = false; try { @@ -309,16 +311,15 @@ public class VaadinServlet extends HttpServlet implements Constants { if (application == null) { return; } - Application.setCurrent(application); + VaadinSession.setCurrent(application); /* * Get or create a WebApplicationContext and an ApplicationManager * for the session */ - ServletApplicationContext webApplicationContext = getApplicationContext(request - .getSession()); - CommunicationManager applicationManager = webApplicationContext - .getApplicationManager(application, this); + VaadinServletSession webApplicationContext = (VaadinServletSession) application; + CommunicationManager applicationManager = (CommunicationManager) webApplicationContext + .getApplicationManager(); if (requestType == RequestType.CONNECTOR_RESOURCE) { applicationManager.serveConnectorResource(request, response); @@ -332,27 +333,8 @@ public class VaadinServlet extends HttpServlet implements Constants { /* Update browser information from the request */ webApplicationContext.getBrowser().updateRequestDetails(request); - /* - * Call application requestStart before Application.init() is called - * (bypasses the limitation in TransactionListener) - */ - if (application instanceof HttpServletRequestListener) { - ((HttpServletRequestListener) application).onRequestStart( - request, response); - requestStarted = true; - } - - // Start the application if it's newly created - startApplication(request, application, webApplicationContext); applicationRunning = true; - /* - * Transaction starts. Call transaction listeners. Transaction end - * is called in the finally block below. - */ - webApplicationContext.startTransaction(application, request); - transactionStarted = true; - /* Handle the request */ if (requestType == RequestType.FILE_UPLOAD) { // UI is resolved in communication manager @@ -400,31 +382,11 @@ public class VaadinServlet extends HttpServlet implements Constants { application.closeInactiveUIs(); } - // Notifies transaction end - try { - if (transactionStarted) { - ((ServletApplicationContext) application.getContext()) - .endTransaction(application, request); - - } - - } finally { - try { - if (requestStarted) { - ((HttpServletRequestListener) application) - .onRequestEnd(request, response); - } - } finally { - UI.setCurrent(null); - Application.setCurrent(null); + CurrentInstance.clearAll(); - HttpSession session = request.getSession(false); - if (session != null) { - requestTimer.stop(getApplicationContext(session)); - } - } + if (application != null) { + requestTimer.stop(application); } - } } @@ -612,7 +574,7 @@ public class VaadinServlet extends HttpServlet implements Constants { * @throws ServletException * @throws SessionExpiredException */ - private Application findApplicationInstance(HttpServletRequest request, + private VaadinSession findApplicationInstance(HttpServletRequest request, RequestType requestType) throws MalformedURLException, ServletException, SessionExpiredException { @@ -620,7 +582,7 @@ public class VaadinServlet extends HttpServlet implements Constants { request, requestType); /* Find an existing application for this request. */ - Application application = getExistingApplication(request, + VaadinSession application = getExistingApplication(request, requestCanCreateApplication); if (application != null) { @@ -636,7 +598,7 @@ public class VaadinServlet extends HttpServlet implements Constants { if (restartApplication) { closeApplication(application, request.getSession(false)); - return createApplication(request); + return createAndRegisterApplication(request); } else if (closeApplication) { closeApplication(application, request.getSession(false)); return null; @@ -652,7 +614,7 @@ public class VaadinServlet extends HttpServlet implements Constants { * If the request is such that it should create a new application if * one as not found, we do that. */ - return createApplication(request); + return createAndRegisterApplication(request); } else { /* * The application was not found and a new one should not be @@ -663,6 +625,33 @@ public class VaadinServlet extends HttpServlet implements Constants { } + private VaadinSession createAndRegisterApplication(HttpServletRequest request) + throws ServletException, MalformedURLException { + VaadinSession newApplication = createApplication(request); + + try { + ServletPortletHelper.checkUiProviders(newApplication); + } catch (ApplicationClassException e) { + throw new ServletException(e); + } + + newApplication.storeInSession(new WrappedHttpSession(request + .getSession())); + + final URL applicationUrl = getApplicationUrl(request); + + // Initial locale comes from the request + Locale locale = request.getLocale(); + newApplication.setLocale(locale); + newApplication.start(new ApplicationStartEvent(applicationUrl, + getDeploymentConfiguration().getApplicationConfiguration(), + createCommunicationManager(newApplication))); + + addonContext.fireApplicationStarted(newApplication); + + return newApplication; + } + /** * Check if the request should create an application if an existing * application is not found. @@ -732,19 +721,22 @@ public class VaadinServlet extends HttpServlet implements Constants { * @throws ServletException * @throws MalformedURLException */ - private Application createApplication(HttpServletRequest request) - throws ServletException, MalformedURLException { - Application newApplication = getNewApplication(request); + protected VaadinServletSession createApplication( + HttpServletRequest request) throws ServletException { + VaadinServletSession newApplication = new VaadinServletSession(); - final ServletApplicationContext context = getApplicationContext(request - .getSession()); - context.addApplication(newApplication); + try { + ServletPortletHelper.initDefaultUIProvider(newApplication, + getDeploymentConfiguration()); + } catch (ApplicationClassException e) { + throw new ServletException(e); + } return newApplication; } private void handleServiceException(WrappedHttpServletRequest request, - WrappedHttpServletResponse response, Application application, + WrappedHttpServletResponse response, VaadinSession application, Throwable e) throws IOException, ServletException { // if this was an UIDL request, response UIDL back to client if (getRequestType(request) == RequestType.UIDL) { @@ -881,62 +873,6 @@ public class VaadinServlet extends HttpServlet implements Constants { } /** - * Creates a new application for the given request. - * - * @param request - * the HTTP request. - * @return A new Application instance. - * @throws ServletException - */ - protected Application getNewApplication(HttpServletRequest request) - throws ServletException { - - // Creates a new application instance - try { - Class<? extends Application> applicationClass = ServletPortletHelper - .getApplicationClass(getDeploymentConfiguration()); - - final Application application = applicationClass.newInstance(); - application.addUIProvider(new DefaultUIProvider()); - - return application; - } catch (final IllegalAccessException e) { - throw new ServletException("getNewApplication failed", e); - } catch (final InstantiationException e) { - throw new ServletException("getNewApplication failed", e); - } catch (ApplicationClassException e) { - throw new ServletException("getNewApplication failed", e); - } - } - - /** - * Starts the application if it is not already running. - * - * @param request - * @param application - * @param webApplicationContext - * @throws ServletException - * @throws MalformedURLException - */ - private void startApplication(HttpServletRequest request, - Application application, - ServletApplicationContext webApplicationContext) - throws ServletException, MalformedURLException { - - if (!application.isRunning()) { - // Create application - final URL applicationUrl = getApplicationUrl(request); - - // Initial locale comes from the request - Locale locale = request.getLocale(); - application.setLocale(locale); - application.start(new ApplicationStartEvent(applicationUrl, - getDeploymentConfiguration(), webApplicationContext)); - addonContext.fireApplicationStarted(application); - } - } - - /** * Check if this is a request for a static resource and, if it is, serve the * resource to the client. * @@ -1076,8 +1012,10 @@ public class VaadinServlet extends HttpServlet implements Constants { * cache timeout can be configured by setting the resourceCacheTime * parameter in web.xml */ + int resourceCacheTime = getDeploymentConfiguration() + .getApplicationConfiguration().getResourceCacheTime(); response.setHeader("Cache-Control", - "max-age= " + String.valueOf(getResourceCacheTime())); + "max-age= " + String.valueOf(resourceCacheTime)); } // Write the resource to the client. @@ -1331,7 +1269,7 @@ public class VaadinServlet extends HttpServlet implements Constants { * @throws InstantiationException * @throws SessionExpiredException */ - protected Application getExistingApplication(HttpServletRequest request, + protected VaadinSession getExistingApplication(HttpServletRequest request, boolean allowSessionCreation) throws MalformedURLException, SessionExpiredException { @@ -1341,35 +1279,18 @@ public class VaadinServlet extends HttpServlet implements Constants { throw new SessionExpiredException(); } - ServletApplicationContext context = getApplicationContext(session); - - // Gets application list for the session. - final Collection<Application> applications = context.getApplications(); + VaadinSession sessionApplication = getApplicationContext(session); - // Search for the application (using the application URI) from the list - for (final Iterator<Application> i = applications.iterator(); i - .hasNext();) { - final Application sessionApplication = i.next(); - final String sessionApplicationPath = sessionApplication.getURL() - .getPath(); - String requestApplicationPath = getApplicationUrl(request) - .getPath(); + if (sessionApplication == null) { + return null; + } - if (requestApplicationPath.equals(sessionApplicationPath)) { - // Found a running application - if (sessionApplication.isRunning()) { - return sessionApplication; - } - // Application has stopped, so remove it before creating a new - // application - getApplicationContext(session).removeApplication( - sessionApplication); - break; - } + if (!sessionApplication.isRunning()) { + sessionApplication.removeFromSession(); + return null; } - // Existing application not found - return null; + return sessionApplication; } /** @@ -1385,7 +1306,7 @@ public class VaadinServlet extends HttpServlet implements Constants { * if the writing failed due to input/output error. */ private void endApplication(HttpServletRequest request, - HttpServletResponse response, Application application) + HttpServletResponse response, VaadinSession application) throws IOException { String logoutUrl = application.getLogoutURL(); @@ -1395,7 +1316,7 @@ public class VaadinServlet extends HttpServlet implements Constants { final HttpSession session = request.getSession(); if (session != null) { - getApplicationContext(session).removeApplication(application); + application.removeFromSession(); } response.sendRedirect(response.encodeRedirectURL(logoutUrl)); @@ -1440,36 +1361,21 @@ public class VaadinServlet extends HttpServlet implements Constants { && (request.getParameter(URL_PARAMETER_REPAINT_ALL).equals("1")); } - private void closeApplication(Application application, HttpSession session) { + private void closeApplication(VaadinSession application, HttpSession session) { if (application == null) { return; } application.close(); if (session != null) { - ServletApplicationContext context = getApplicationContext(session); - context.removeApplication(application); + application.removeFromSession(); } } - /** - * - * Gets the application context from an HttpSession. If no context is - * currently stored in a session a new context is created and stored in the - * session. - * - * @param session - * the HTTP session. - * @return the application context for HttpSession. - */ - protected ServletApplicationContext getApplicationContext( - HttpSession session) { - /* - * TODO the ApplicationContext.getApplicationContext() should be removed - * and logic moved here. Now overriding context type is possible, but - * the whole creation logic should be here. MT 1101 - */ - return ServletApplicationContext.getApplicationContext(session); + protected VaadinSession getApplicationContext(final HttpSession session) { + VaadinSession sessionApplication = VaadinSession + .getForSession(new WrappedHttpSession(session)); + return sessionApplication; } public class RequestError implements Terminal.ErrorEvent, Serializable { @@ -1492,11 +1398,11 @@ public class VaadinServlet extends HttpServlet implements Constants { * mananger implementation. * * @deprecated Instead of overriding this method, override - * {@link ServletApplicationContext} implementation via + * {@link VaadinServletSession} implementation via * {@link VaadinServlet#getApplicationContext(HttpSession)} * method and in that customized implementation return your * CommunicationManager in - * {@link ServletApplicationContext#getApplicationManager(Application, VaadinServlet)} + * {@link VaadinServletSession#getApplicationManager(VaadinSession, VaadinServlet)} * method. * * @param application @@ -1504,7 +1410,7 @@ public class VaadinServlet extends HttpServlet implements Constants { */ @Deprecated public CommunicationManager createCommunicationManager( - Application application) { + VaadinSession application) { return new CommunicationManager(application); } diff --git a/server/src/com/vaadin/server/VaadinServletSession.java b/server/src/com/vaadin/server/VaadinServletSession.java new file mode 100644 index 0000000000..72b744da72 --- /dev/null +++ b/server/src/com/vaadin/server/VaadinServletSession.java @@ -0,0 +1,100 @@ +/* + * 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.Enumeration; +import java.util.HashMap; + +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionBindingListener; + +import com.vaadin.util.CurrentInstance; + +/** + * Web application context for Vaadin applications. + * + * This is automatically added as a {@link HttpSessionBindingListener} when + * added to a {@link HttpSession}. + * + * @author Vaadin Ltd. + * @since 3.1 + */ +@SuppressWarnings("serial") +public class VaadinServletSession extends VaadinSession { + + private transient boolean reinitializingSession = false; + + @Override + public void valueUnbound(HttpSessionBindingEvent event) { + if (!reinitializingSession) { + // Avoid closing the application if we are only reinitializing the + // session. Closing the application would cause the state to be lost + // and a new application to be created, which is not what we want. + super.valueUnbound(event); + } + } + + /** + * Discards the current session and creates a new session with the same + * contents. The purpose of this is to introduce a new session key in order + * to avoid session fixation attacks. + */ + public void reinitializeSession() { + + HttpSession oldSession = getHttpSession(); + + // Stores all attributes (security key, reference to this context + // instance) so they can be added to the new session + HashMap<String, Object> attrs = new HashMap<String, Object>(); + for (Enumeration<String> e = oldSession.getAttributeNames(); e + .hasMoreElements();) { + String name = e.nextElement(); + attrs.put(name, oldSession.getAttribute(name)); + } + + // Invalidate the current session, set flag to avoid call to + // valueUnbound + reinitializingSession = true; + oldSession.invalidate(); + reinitializingSession = false; + + // Create a new session + HttpSession newSession = WrappedHttpServletRequest.cast( + CurrentInstance.get(WrappedRequest.class)).getSession(); + + // Restores all attributes (security key, reference to this context + // instance) + for (String name : attrs.keySet()) { + newSession.setAttribute(name, attrs.get(name)); + } + + // Update the "current session" variable + storeInSession(new WrappedHttpSession(newSession)); + } + + /** + * Gets the http-session application is running in. + * + * @return HttpSession this application context resides in. + */ + public HttpSession getHttpSession() { + WrappedSession session = getSession(); + return ((WrappedHttpSession) session).getHttpSession(); + } + +} diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java new file mode 100644 index 0000000000..440fc02ee6 --- /dev/null +++ b/server/src/com/vaadin/server/VaadinSession.java @@ -0,0 +1,1978 @@ +/* + * 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.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.net.SocketException; +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; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionBindingListener; + +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.WrappedRequest.BrowserDetails; +import com.vaadin.shared.ui.ui.UIConstants; +import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.AbstractField; +import com.vaadin.ui.Table; +import com.vaadin.ui.UI; +import com.vaadin.ui.Window; +import com.vaadin.util.CurrentInstance; +import com.vaadin.util.ReflectTools; + +/** + * <p> + * Base class required for all Vaadin applications. This class provides all the + * basic services required by Vaadin. These services allow external discovery + * and manipulation of the user, {@link com.vaadin.ui.Window windows} and + * themes, and starting and stopping the application. + * </p> + * + * <p> + * As mentioned, all Vaadin applications must inherit this class. However, this + * is almost all of what one needs to do to create a fully functional + * application. The only thing a class inheriting the <code>Application</code> + * needs to do is implement the <code>init</code> method where it creates the + * windows it needs to perform its function. Note that all applications must + * have at least one window: the main window. The first unnamed window + * constructed by an application automatically becomes the main window which + * behaves just like other windows with one exception: when accessing windows + * using URLs the main window corresponds to the application URL whereas other + * windows correspond to a URL gotten by catenating the window's name to the + * application URL. + * </p> + * + * <p> + * See the class <code>com.vaadin.demo.HelloWorld</code> for a simple example of + * a fully working application. + * </p> + * + * <p> + * <strong>Window access.</strong> <code>Application</code> provides methods to + * list, add and remove the windows it contains. + * </p> + * + * <p> + * <strong>Execution control.</strong> This class includes method to start and + * finish the execution of the application. Being finished means basically that + * no windows will be available from the application anymore. + * </p> + * + * <p> + * <strong>Theme selection.</strong> The theme selection process allows a theme + * to be specified at three different levels. When a window's theme needs to be + * found out, the window itself is queried for a preferred theme. If the window + * does not prefer a specific theme, the application containing the window is + * queried. If neither the application prefers a theme, the default theme for + * the {@link com.vaadin.server.Terminal terminal} is used. The terminal always + * defines a default theme. + * </p> + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class VaadinSession implements Terminal.ErrorListener, + HttpSessionBindingListener, Serializable { + + /** + * The name of the parameter that is by default used in e.g. web.xml to + * define the name of the default {@link UI} class. + */ + public static final String UI_PARAMETER = "UI"; + + private static final Method BOOTSTRAP_FRAGMENT_METHOD = ReflectTools + .findMethod(BootstrapListener.class, "modifyBootstrapFragment", + BootstrapFragmentResponse.class); + private static final Method BOOTSTRAP_PAGE_METHOD = ReflectTools + .findMethod(BootstrapListener.class, "modifyBootstrapPage", + BootstrapPageResponse.class); + + /** + * An event sent to {@link #start(ApplicationStartEvent)} when a new + * Application is being started. + * + * @since 7.0 + */ + public static class ApplicationStartEvent implements Serializable { + private final URL applicationUrl; + + private final ApplicationConfiguration configuration; + + private final AbstractCommunicationManager communicationManager; + + /** + * @param applicationUrl + * the URL the application should respond to. + * @param configuration + * the application configuration for the application. + * @param communicationManager + * the communication manager for the application. + */ + public ApplicationStartEvent(URL applicationUrl, + ApplicationConfiguration configuration, + AbstractCommunicationManager communicationManager) { + this.applicationUrl = applicationUrl; + this.configuration = configuration; + this.communicationManager = communicationManager; + } + + /** + * Gets the URL the application should respond to. + * + * @return the URL the application should respond to or + * <code>null</code> if the URL is not defined. + * + * @see VaadinSession#getURL() + */ + public URL getApplicationUrl() { + return applicationUrl; + } + + /** + * Returns the application configuration used by this application. + * + * @return the deployment configuration. + */ + public ApplicationConfiguration getConfiguration() { + return configuration; + } + + /** + * Gets the communication manager for this application. + * + * @return the communication manager for this application. + * + * @see VaadinSession#getCommunicationManager + */ + public AbstractCommunicationManager getCommunicationManager() { + return communicationManager; + } + } + + private final static Logger logger = Logger.getLogger(VaadinSession.class + .getName()); + + /** + * Configuration for the application. + */ + private ApplicationConfiguration configuration; + + /** + * The application's URL. + */ + private URL applicationUrl; + + /** + * Application status. + */ + private volatile boolean applicationIsRunning = false; + + /** + * Default locale of the application. + */ + 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; + + /** + * Application wide error handler which is used by default if an error is + * left unhandled. + */ + private Terminal.ErrorListener errorHandler = this; + + /** + * The converter factory that is used to provide default converters for the + * application. + */ + private ConverterFactory converterFactory = new DefaultConverterFactory(); + + private LinkedList<RequestHandler> requestHandlers = new LinkedList<RequestHandler>(); + + private int nextUIId = 0; + private Map<Integer, UI> uIs = new HashMap<Integer, UI>(); + + private final Map<String, Integer> retainOnRefreshUIs = new HashMap<String, Integer>(); + + private final EventRouter eventRouter = new EventRouter(); + + private List<UIProvider> uiProviders = new LinkedList<UIProvider>(); + + private GlobalResourceHandler globalResourceHandler; + + protected WebBrowser browser = new WebBrowser(); + + private AbstractCommunicationManager communicationManager; + + private long totalSessionTime = 0; + + private long lastRequestTime = -1; + + private transient WrappedSession session; + + /** + * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent) + */ + @Override + public void valueBound(HttpSessionBindingEvent arg0) { + // We are not interested in bindings + } + + /** + * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent) + */ + @Override + public void valueUnbound(HttpSessionBindingEvent event) { + // If we are going to be unbound from the session, the session must be + // closing + close(); + } + + /** + * Get the web browser associated with this application context. + * + * Because application context is related to the http session and server + * maintains one session per browser-instance, each context has exactly one + * web browser associated with it. + * + * @return + */ + public WebBrowser getBrowser() { + return browser; + } + + /** + * @return The total time spent servicing requests in this session. + */ + public long getTotalSessionTime() { + return totalSessionTime; + } + + /** + * Sets the time spent servicing the last request in the session and updates + * the total time spent servicing requests in this session. + * + * @param time + * the time spent in the last request. + */ + public void setLastRequestTime(long time) { + lastRequestTime = time; + totalSessionTime += time; + } + + /** + * @return the time spent servicing the last request in this session. + */ + public long getLastRequestTime() { + return lastRequestTime; + } + + /** + * Gets the session to which this application context is currently + * associated. + * + * @return the wrapped session for this context + */ + public WrappedSession getSession() { + return session; + } + + public AbstractCommunicationManager getApplicationManager() { + return communicationManager; + } + + /** + * Gets the URL of the application. + * + * <p> + * This is the URL what can be entered to a browser window to start the + * application. Navigating to the application URL shows the main window ( + * {@link #getMainWindow()}) of the application. Note that the main window + * can also be shown by navigating to the window url ( + * {@link com.vaadin.ui.Window#getURL()}). + * </p> + * + * @return the application's URL. + */ + public URL getURL() { + return applicationUrl; + } + + /** + * Ends the Application. + * <p> + * In effect this will cause the application stop returning any windows when + * asked. When the application is closed, close events are fired for its + * UIs, its state is removed from the 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. + */ + public void close() { + applicationIsRunning = false; + for (UI ui : getUIs()) { + ui.fireCloseEvent(); + } + } + + public static VaadinSession getForSession(WrappedSession session) { + Object attribute = session.getAttribute(VaadinSession.class.getName()); + if (attribute instanceof VaadinSession) { + VaadinSession application = (VaadinSession) attribute; + application.session = session; + return application; + } + + return null; + } + + public void removeFromSession() { + assert (getForSession(session) == this); + + session.setAttribute(VaadinSession.class.getName(), null); + } + + public void storeInSession(WrappedSession session) { + session.setAttribute(VaadinSession.class.getName(), this); + this.session = session; + } + + /** + * Starts the application on the given URL. + * + * <p> + * This method is called by Vaadin framework when a user navigates to the + * application. After this call the application corresponds to the given URL + * and it will return windows when asked for them. There is no need to call + * this method directly. + * </p> + * + * <p> + * Application properties are defined by servlet configuration object + * {@link javax.servlet.ServletConfig} and they are overridden by + * context-wide initialization parameters + * {@link javax.servlet.ServletContext}. + * </p> + * + * @param event + * the application start event containing details required for + * starting the application. + * + */ + public void start(ApplicationStartEvent event) { + 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(ApplicationStartEvent)} + * 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. + */ + public boolean isRunning() { + return applicationIsRunning; + } + + /** + * Gets the configuration for this application + * + * @return the application configuration + */ + public ApplicationConfiguration getConfiguration() { + return configuration; + } + + /** + * Gets the default locale for this application. + * + * By default this is the preferred locale of the user using the + * application. In most cases it is read from the browser defaults. + * + * @return the locale of this application. + */ + public Locale getLocale() { + if (locale != null) { + return locale; + } + return Locale.getDefault(); + } + + /** + * Sets the default locale for this application. + * + * By default this is the preferred locale of the user using the + * application. In most cases it is read from the browser defaults. + * + * @param locale + * the Locale object. + * + */ + public void setLocale(Locale locale) { + this.locale = locale; + } + + /** + * Window detach event. + * + * This event is sent each time a window is removed from the application + * with {@link com.vaadin.server.VaadinSession#removeWindow(Window)}. + */ + public static class WindowDetachEvent extends EventObject { + + private final Window window; + + /** + * Creates a event. + * + * @param application + * the application to which the detached window belonged. + * @param window + * the Detached window. + */ + public WindowDetachEvent(VaadinSession application, Window window) { + super(application); + this.window = window; + } + + /** + * Gets the detached window. + * + * @return the detached window. + */ + public Window getWindow() { + return window; + } + + /** + * Gets the application from which the window was detached. + * + * @return the Application. + */ + public VaadinSession getApplication() { + return (VaadinSession) getSource(); + } + } + + /** + * Window attach event. + * + * This event is sent each time a window is attached tothe application with + * {@link com.vaadin.server.VaadinSession#addWindow(Window)}. + */ + public static class WindowAttachEvent extends EventObject { + + private final Window window; + + /** + * Creates a event. + * + * @param application + * the application to which the detached window belonged. + * @param window + * the Attached window. + */ + public WindowAttachEvent(VaadinSession application, Window window) { + super(application); + this.window = window; + } + + /** + * Gets the attached window. + * + * @return the attached window. + */ + public Window getWindow() { + return window; + } + + /** + * Gets the application to which the window was attached. + * + * @return the Application. + */ + public VaadinSession getApplication() { + return (VaadinSession) getSource(); + } + } + + /** + * Window attach listener interface. + */ + public interface WindowAttachListener extends Serializable { + + /** + * Window attached + * + * @param event + * the window attach event. + */ + public void windowAttached(WindowAttachEvent event); + } + + /** + * Window detach listener interface. + */ + public interface WindowDetachListener extends Serializable { + + /** + * Window detached. + * + * @param event + * the window detach event. + */ + public void windowDetached(WindowDetachEvent event); + } + + /** + * 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. + */ + 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. + */ + public void setLogoutURL(String logoutURL) { + this.logoutURL = logoutURL; + } + + /** + * <p> + * Invoked by the terminal on any exception that occurs in application and + * is thrown by the <code>setVariable</code> to the terminal. The default + * implementation sets the exceptions as <code>ComponentErrors</code> to the + * component that initiated the exception and prints stack trace to standard + * error stream. + * </p> + * <p> + * You can safely override this method in your application in order to + * direct the errors to some other destination (for example log). + * </p> + * + * @param event + * the change event. + * @see com.vaadin.server.Terminal.ErrorListener#terminalError(com.vaadin.server.Terminal.ErrorEvent) + */ + + @Override + public void terminalError(Terminal.ErrorEvent event) { + final Throwable t = event.getThrowable(); + if (t instanceof SocketException) { + // Most likely client browser closed socket + getLogger().info( + "SocketException in CommunicationManager." + + " Most likely client (browser) closed socket."); + return; + } + + // Finds the original source of the error/exception + Object owner = null; + if (event instanceof VariableOwner.ErrorEvent) { + owner = ((VariableOwner.ErrorEvent) event).getVariableOwner(); + } else if (event instanceof ChangeVariablesErrorEvent) { + owner = ((ChangeVariablesErrorEvent) event).getComponent(); + } + + // Shows the error in AbstractComponent + if (owner instanceof AbstractComponent) { + ((AbstractComponent) owner).setComponentError(AbstractErrorMessage + .getErrorMessageForException(t)); + } + + // also print the error on console + getLogger().log(Level.SEVERE, "Terminal error:", t); + } + + /** + * Gets the application error handler. + * + * The default error handler is the application itself. + * + * @return Application error handler + */ + public Terminal.ErrorListener getErrorHandler() { + return errorHandler; + } + + /** + * Sets the application error handler. + * + * The default error handler is the application itself. By overriding this, + * you can redirect the error messages to your selected target (log for + * example). + * + * @param errorHandler + */ + public void setErrorHandler(Terminal.ErrorListener errorHandler) { + this.errorHandler = errorHandler; + } + + /** + * Gets the {@link ConverterFactory} used to locate a suitable + * {@link Converter} for fields in the application. + * + * See {@link #setConverterFactory(ConverterFactory)} for more details + * + * @return The converter factory used in the application + */ + public ConverterFactory getConverterFactory() { + return converterFactory; + } + + /** + * Sets the {@link ConverterFactory} used to locate a suitable + * {@link Converter} for fields in the application. + * <p> + * The {@link ConverterFactory} is used to find a suitable converter when + * binding data to a UI component and the data type does not match the UI + * component type, e.g. binding a Double to a TextField (which is based on a + * String). + * </p> + * <p> + * The {@link Converter} for an individual field can be overridden using + * {@link AbstractField#setConverter(Converter)} and for individual property + * ids in a {@link Table} using + * {@link Table#setConverter(Object, Converter)}. + * </p> + * <p> + * The converter factory must never be set to null. + * + * @param converterFactory + * The converter factory used in the application + */ + public void setConverterFactory(ConverterFactory converterFactory) { + this.converterFactory = converterFactory; + } + + /** + * Contains the system messages used to notify the user about various + * critical situations that can occur. + * <p> + * Customize by overriding the static + * {@link VaadinSession#getSystemMessages()} and returning + * {@link CustomizedSystemMessages}. + * </p> + * <p> + * The defaults defined in this class are: + * <ul> + * <li><b>sessionExpiredURL</b> = null</li> + * <li><b>sessionExpiredNotificationEnabled</b> = true</li> + * <li><b>sessionExpiredCaption</b> = ""</li> + * <li><b>sessionExpiredMessage</b> = + * "Take note of any unsaved data, and <u>click here</u> to continue."</li> + * <li><b>communicationErrorURL</b> = null</li> + * <li><b>communicationErrorNotificationEnabled</b> = true</li> + * <li><b>communicationErrorCaption</b> = "Communication problem"</li> + * <li><b>communicationErrorMessage</b> = + * "Take note of any unsaved data, and <u>click here</u> to continue."</li> + * <li><b>internalErrorURL</b> = null</li> + * <li><b>internalErrorNotificationEnabled</b> = true</li> + * <li><b>internalErrorCaption</b> = "Internal error"</li> + * <li><b>internalErrorMessage</b> = "Please notify the administrator.<br/> + * Take note of any unsaved data, and <u>click here</u> to continue."</li> + * <li><b>outOfSyncURL</b> = null</li> + * <li><b>outOfSyncNotificationEnabled</b> = true</li> + * <li><b>outOfSyncCaption</b> = "Out of sync"</li> + * <li><b>outOfSyncMessage</b> = "Something has caused us to be out of sync + * with the server.<br/> + * Take note of any unsaved data, and <u>click here</u> to re-sync."</li> + * <li><b>cookiesDisabledURL</b> = null</li> + * <li><b>cookiesDisabledNotificationEnabled</b> = true</li> + * <li><b>cookiesDisabledCaption</b> = "Cookies disabled"</li> + * <li><b>cookiesDisabledMessage</b> = "This application requires cookies to + * function.<br/> + * Please enable cookies in your browser and <u>click here</u> to try again. + * </li> + * </ul> + * </p> + * + */ + public static class SystemMessages implements Serializable { + protected String sessionExpiredURL = null; + protected boolean sessionExpiredNotificationEnabled = true; + protected String sessionExpiredCaption = "Session Expired"; + protected String sessionExpiredMessage = "Take note of any unsaved data, and <u>click here</u> to continue."; + + protected String communicationErrorURL = null; + protected boolean communicationErrorNotificationEnabled = true; + protected String communicationErrorCaption = "Communication problem"; + protected String communicationErrorMessage = "Take note of any unsaved data, and <u>click here</u> to continue."; + + protected String authenticationErrorURL = null; + protected boolean authenticationErrorNotificationEnabled = true; + protected String authenticationErrorCaption = "Authentication problem"; + protected String authenticationErrorMessage = "Take note of any unsaved data, and <u>click here</u> to continue."; + + protected String internalErrorURL = null; + protected boolean internalErrorNotificationEnabled = true; + protected String internalErrorCaption = "Internal error"; + protected String internalErrorMessage = "Please notify the administrator.<br/>Take note of any unsaved data, and <u>click here</u> to continue."; + + protected String outOfSyncURL = null; + protected boolean outOfSyncNotificationEnabled = true; + protected String outOfSyncCaption = "Out of sync"; + protected String outOfSyncMessage = "Something has caused us to be out of sync with the server.<br/>Take note of any unsaved data, and <u>click here</u> to re-sync."; + + protected String cookiesDisabledURL = null; + protected boolean cookiesDisabledNotificationEnabled = true; + protected String cookiesDisabledCaption = "Cookies disabled"; + protected String cookiesDisabledMessage = "This application requires cookies to function.<br/>Please enable cookies in your browser and <u>click here</u> to try again."; + + /** + * Use {@link CustomizedSystemMessages} to customize + */ + private SystemMessages() { + + } + + /** + * @return null to indicate that the application will be restarted after + * session expired message has been shown. + */ + public String getSessionExpiredURL() { + return sessionExpiredURL; + } + + /** + * @return true to show session expiration message. + */ + public boolean isSessionExpiredNotificationEnabled() { + return sessionExpiredNotificationEnabled; + } + + /** + * @return "" to show no caption. + */ + public String getSessionExpiredCaption() { + return (sessionExpiredNotificationEnabled ? sessionExpiredCaption + : null); + } + + /** + * @return + * "Take note of any unsaved data, and <u>click here</u> to continue." + */ + public String getSessionExpiredMessage() { + return (sessionExpiredNotificationEnabled ? sessionExpiredMessage + : null); + } + + /** + * @return null to reload the application after communication error + * message. + */ + public String getCommunicationErrorURL() { + return communicationErrorURL; + } + + /** + * @return true to show the communication error message. + */ + public boolean isCommunicationErrorNotificationEnabled() { + return communicationErrorNotificationEnabled; + } + + /** + * @return "Communication problem" + */ + public String getCommunicationErrorCaption() { + return (communicationErrorNotificationEnabled ? communicationErrorCaption + : null); + } + + /** + * @return + * "Take note of any unsaved data, and <u>click here</u> to continue." + */ + public String getCommunicationErrorMessage() { + return (communicationErrorNotificationEnabled ? communicationErrorMessage + : null); + } + + /** + * @return null to reload the application after authentication error + * message. + */ + public String getAuthenticationErrorURL() { + return authenticationErrorURL; + } + + /** + * @return true to show the authentication error message. + */ + public boolean isAuthenticationErrorNotificationEnabled() { + return authenticationErrorNotificationEnabled; + } + + /** + * @return "Authentication problem" + */ + public String getAuthenticationErrorCaption() { + return (authenticationErrorNotificationEnabled ? authenticationErrorCaption + : null); + } + + /** + * @return + * "Take note of any unsaved data, and <u>click here</u> to continue." + */ + public String getAuthenticationErrorMessage() { + return (authenticationErrorNotificationEnabled ? authenticationErrorMessage + : null); + } + + /** + * @return null to reload the current URL after internal error message + * has been shown. + */ + public String getInternalErrorURL() { + return internalErrorURL; + } + + /** + * @return true to enable showing of internal error message. + */ + public boolean isInternalErrorNotificationEnabled() { + return internalErrorNotificationEnabled; + } + + /** + * @return "Internal error" + */ + public String getInternalErrorCaption() { + return (internalErrorNotificationEnabled ? internalErrorCaption + : null); + } + + /** + * @return "Please notify the administrator.<br/> + * Take note of any unsaved data, and <u>click here</u> to + * continue." + */ + public String getInternalErrorMessage() { + return (internalErrorNotificationEnabled ? internalErrorMessage + : null); + } + + /** + * @return null to reload the application after out of sync message. + */ + public String getOutOfSyncURL() { + return outOfSyncURL; + } + + /** + * @return true to enable showing out of sync message + */ + public boolean isOutOfSyncNotificationEnabled() { + return outOfSyncNotificationEnabled; + } + + /** + * @return "Out of sync" + */ + public String getOutOfSyncCaption() { + return (outOfSyncNotificationEnabled ? outOfSyncCaption : null); + } + + /** + * @return "Something has caused us to be out of sync with the server.<br/> + * Take note of any unsaved data, and <u>click here</u> to + * re-sync." + */ + public String getOutOfSyncMessage() { + return (outOfSyncNotificationEnabled ? outOfSyncMessage : null); + } + + /** + * Returns the URL the user should be redirected to after dismissing the + * "you have to enable your cookies" message. Typically null. + * + * @return A URL the user should be redirected to after dismissing the + * message or null to reload the current URL. + */ + public String getCookiesDisabledURL() { + return cookiesDisabledURL; + } + + /** + * Determines if "cookies disabled" messages should be shown to the end + * user or not. If the notification is disabled the user will be + * immediately redirected to the URL returned by + * {@link #getCookiesDisabledURL()}. + * + * @return true to show "cookies disabled" messages to the end user, + * false to redirect to the given URL directly + */ + public boolean isCookiesDisabledNotificationEnabled() { + return cookiesDisabledNotificationEnabled; + } + + /** + * Returns the caption of the message shown to the user when cookies are + * disabled in the browser. + * + * @return The caption of the "cookies disabled" message + */ + public String getCookiesDisabledCaption() { + return (cookiesDisabledNotificationEnabled ? cookiesDisabledCaption + : null); + } + + /** + * Returns the message shown to the user when cookies are disabled in + * the browser. + * + * @return The "cookies disabled" message + */ + public String getCookiesDisabledMessage() { + return (cookiesDisabledNotificationEnabled ? cookiesDisabledMessage + : null); + } + + } + + /** + * Contains the system messages used to notify the user about various + * critical situations that can occur. + * <p> + * Vaadin gets the SystemMessages from your application by calling a static + * getSystemMessages() method. By default the + * Application.getSystemMessages() is used. You can customize this by + * defining a static MyApplication.getSystemMessages() and returning + * CustomizedSystemMessages. Note that getSystemMessages() is static - + * changing the system messages will by default change the message for all + * users of the application. + * </p> + * <p> + * The default behavior is to show a notification, and restart the + * application the the user clicks the message. <br/> + * Instead of restarting the application, you can set a specific URL that + * the user is taken to.<br/> + * Setting both caption and message to null will restart the application (or + * go to the specified URL) without displaying a notification. + * set*NotificationEnabled(false) will achieve the same thing. + * </p> + * <p> + * The situations are: + * <li>Session expired: the user session has expired, usually due to + * inactivity.</li> + * <li>Communication error: the client failed to contact the server, or the + * server returned and invalid response.</li> + * <li>Internal error: unhandled critical server error (e.g out of memory, + * database crash) + * <li>Out of sync: the client is not in sync with the server. E.g the user + * opens two windows showing the same application, but the application does + * not support this and uses the same Window instance. When the user makes + * changes in one of the windows - the other window is no longer in sync, + * and (for instance) pressing a button that is no longer present in the UI + * will cause a out-of-sync -situation. + * </p> + */ + + public static class CustomizedSystemMessages extends SystemMessages + implements Serializable { + + /** + * Sets the URL to go to when the session has expired. + * + * @param sessionExpiredURL + * the URL to go to, or null to reload current + */ + public void setSessionExpiredURL(String sessionExpiredURL) { + this.sessionExpiredURL = sessionExpiredURL; + } + + /** + * Enables or disables the notification. If disabled, the set URL (or + * current) is loaded directly when next transaction between server and + * client happens. + * + * @param sessionExpiredNotificationEnabled + * true = enabled, false = disabled + */ + public void setSessionExpiredNotificationEnabled( + boolean sessionExpiredNotificationEnabled) { + this.sessionExpiredNotificationEnabled = sessionExpiredNotificationEnabled; + } + + /** + * Sets the caption of the notification. Set to null for no caption. If + * both caption and message are null, client automatically forwards to + * sessionExpiredUrl after timeout timer expires. Timer uses value read + * from HTTPSession.getMaxInactiveInterval() + * + * @param sessionExpiredCaption + * the caption + */ + public void setSessionExpiredCaption(String sessionExpiredCaption) { + this.sessionExpiredCaption = sessionExpiredCaption; + } + + /** + * Sets the message of the notification. Set to null for no message. If + * both caption and message are null, client automatically forwards to + * sessionExpiredUrl after timeout timer expires. Timer uses value read + * from HTTPSession.getMaxInactiveInterval() + * + * @param sessionExpiredMessage + * the message + */ + public void setSessionExpiredMessage(String sessionExpiredMessage) { + this.sessionExpiredMessage = sessionExpiredMessage; + } + + /** + * Sets the URL to go to when there is a authentication error. + * + * @param authenticationErrorURL + * the URL to go to, or null to reload current + */ + public void setAuthenticationErrorURL(String authenticationErrorURL) { + this.authenticationErrorURL = authenticationErrorURL; + } + + /** + * Enables or disables the notification. If disabled, the set URL (or + * current) is loaded directly. + * + * @param authenticationErrorNotificationEnabled + * true = enabled, false = disabled + */ + public void setAuthenticationErrorNotificationEnabled( + boolean authenticationErrorNotificationEnabled) { + this.authenticationErrorNotificationEnabled = authenticationErrorNotificationEnabled; + } + + /** + * Sets the caption of the notification. Set to null for no caption. If + * both caption and message is null, the notification is disabled; + * + * @param authenticationErrorCaption + * the caption + */ + public void setAuthenticationErrorCaption( + String authenticationErrorCaption) { + this.authenticationErrorCaption = authenticationErrorCaption; + } + + /** + * Sets the message of the notification. Set to null for no message. If + * both caption and message is null, the notification is disabled; + * + * @param authenticationErrorMessage + * the message + */ + public void setAuthenticationErrorMessage( + String authenticationErrorMessage) { + this.authenticationErrorMessage = authenticationErrorMessage; + } + + /** + * Sets the URL to go to when there is a communication error. + * + * @param communicationErrorURL + * the URL to go to, or null to reload current + */ + public void setCommunicationErrorURL(String communicationErrorURL) { + this.communicationErrorURL = communicationErrorURL; + } + + /** + * Enables or disables the notification. If disabled, the set URL (or + * current) is loaded directly. + * + * @param communicationErrorNotificationEnabled + * true = enabled, false = disabled + */ + public void setCommunicationErrorNotificationEnabled( + boolean communicationErrorNotificationEnabled) { + this.communicationErrorNotificationEnabled = communicationErrorNotificationEnabled; + } + + /** + * Sets the caption of the notification. Set to null for no caption. If + * both caption and message is null, the notification is disabled; + * + * @param communicationErrorCaption + * the caption + */ + public void setCommunicationErrorCaption( + String communicationErrorCaption) { + this.communicationErrorCaption = communicationErrorCaption; + } + + /** + * Sets the message of the notification. Set to null for no message. If + * both caption and message is null, the notification is disabled; + * + * @param communicationErrorMessage + * the message + */ + public void setCommunicationErrorMessage( + String communicationErrorMessage) { + this.communicationErrorMessage = communicationErrorMessage; + } + + /** + * Sets the URL to go to when an internal error occurs. + * + * @param internalErrorURL + * the URL to go to, or null to reload current + */ + public void setInternalErrorURL(String internalErrorURL) { + this.internalErrorURL = internalErrorURL; + } + + /** + * Enables or disables the notification. If disabled, the set URL (or + * current) is loaded directly. + * + * @param internalErrorNotificationEnabled + * true = enabled, false = disabled + */ + public void setInternalErrorNotificationEnabled( + boolean internalErrorNotificationEnabled) { + this.internalErrorNotificationEnabled = internalErrorNotificationEnabled; + } + + /** + * Sets the caption of the notification. Set to null for no caption. If + * both caption and message is null, the notification is disabled; + * + * @param internalErrorCaption + * the caption + */ + public void setInternalErrorCaption(String internalErrorCaption) { + this.internalErrorCaption = internalErrorCaption; + } + + /** + * Sets the message of the notification. Set to null for no message. If + * both caption and message is null, the notification is disabled; + * + * @param internalErrorMessage + * the message + */ + public void setInternalErrorMessage(String internalErrorMessage) { + this.internalErrorMessage = internalErrorMessage; + } + + /** + * Sets the URL to go to when the client is out-of-sync. + * + * @param outOfSyncURL + * the URL to go to, or null to reload current + */ + public void setOutOfSyncURL(String outOfSyncURL) { + this.outOfSyncURL = outOfSyncURL; + } + + /** + * Enables or disables the notification. If disabled, the set URL (or + * current) is loaded directly. + * + * @param outOfSyncNotificationEnabled + * true = enabled, false = disabled + */ + public void setOutOfSyncNotificationEnabled( + boolean outOfSyncNotificationEnabled) { + this.outOfSyncNotificationEnabled = outOfSyncNotificationEnabled; + } + + /** + * Sets the caption of the notification. Set to null for no caption. If + * both caption and message is null, the notification is disabled; + * + * @param outOfSyncCaption + * the caption + */ + public void setOutOfSyncCaption(String outOfSyncCaption) { + this.outOfSyncCaption = outOfSyncCaption; + } + + /** + * Sets the message of the notification. Set to null for no message. If + * both caption and message is null, the notification is disabled; + * + * @param outOfSyncMessage + * the message + */ + public void setOutOfSyncMessage(String outOfSyncMessage) { + this.outOfSyncMessage = outOfSyncMessage; + } + + /** + * Sets the URL to redirect to when the browser has cookies disabled. + * + * @param cookiesDisabledURL + * the URL to redirect to, or null to reload the current URL + */ + public void setCookiesDisabledURL(String cookiesDisabledURL) { + this.cookiesDisabledURL = cookiesDisabledURL; + } + + /** + * Enables or disables the notification for "cookies disabled" messages. + * If disabled, the URL returned by {@link #getCookiesDisabledURL()} is + * loaded directly. + * + * @param cookiesDisabledNotificationEnabled + * true to enable "cookies disabled" messages, false + * otherwise + */ + public void setCookiesDisabledNotificationEnabled( + boolean cookiesDisabledNotificationEnabled) { + this.cookiesDisabledNotificationEnabled = cookiesDisabledNotificationEnabled; + } + + /** + * Sets the caption of the "cookies disabled" notification. Set to null + * for no caption. If both caption and message is null, the notification + * is disabled. + * + * @param cookiesDisabledCaption + * the caption for the "cookies disabled" notification + */ + public void setCookiesDisabledCaption(String cookiesDisabledCaption) { + this.cookiesDisabledCaption = cookiesDisabledCaption; + } + + /** + * Sets the message of the "cookies disabled" notification. Set to null + * for no message. If both caption and message is null, the notification + * is disabled. + * + * @param cookiesDisabledMessage + * the message for the "cookies disabled" notification + */ + public void setCookiesDisabledMessage(String cookiesDisabledMessage) { + this.cookiesDisabledMessage = cookiesDisabledMessage; + } + + } + + /** + * Application error is an error message defined on the application level. + * + * When an error occurs on the application level, this error message type + * should be used. This indicates that the problem is caused by the + * application - not by the user. + */ + public class ApplicationError implements Terminal.ErrorEvent { + private final Throwable throwable; + + public ApplicationError(Throwable throwable) { + this.throwable = throwable; + } + + @Override + public Throwable getThrowable() { + return throwable; + } + + } + + /** + * Gets the UI class for a request for which no UI is already known. This + * method is called when the framework processes a request that does not + * originate from an existing UI instance. This typically happens when a + * host page is requested. + * <p> + * Subclasses of Application may override this method to provide custom + * logic for choosing what kind of UI to use. + * <p> + * The default implementation in {@link VaadinSession} uses the + * {@value #UI_PARAMETER} parameter from web.xml for finding the name of the + * UI class. If {@link DeploymentConfiguration#getClassLoader()} does not + * return <code>null</code>, the returned {@link ClassLoader} is used for + * loading the UI class. Otherwise the {@link ClassLoader} used to load this + * class is used. + * + * </p> + * + * @param request + * the wrapped request for which a UI is needed + * @return a UI instance to use for the request + * + * @see UI + * @see WrappedRequest#getBrowserDetails() + * + * @since 7.0 + */ + public Class<? extends UI> getUIClass(WrappedRequest request) { + UIProvider uiProvider = getUiProvider(request, null); + return uiProvider.getUIClass(this, request); + } + + /** + * Creates an UI instance for a request for which no UI is already known. + * This method is called when the framework processes a request that does + * not originate from an existing UI instance. This typically happens when a + * host page is requested. + * <p> + * Subclasses of Application may override this method to provide custom + * logic for choosing how to create a suitable UI or for picking an already + * created UI. If an existing UI is picked, care should be taken to avoid + * keeping the same UI open in multiple browser windows, as that will cause + * the states to go out of sync. + * </p> + * + * @param request + * @param uiClass + * @return + */ + protected <T extends UI> T createUIInstance(WrappedRequest request, + Class<T> uiClass) { + UIProvider uiProvider = getUiProvider(request, uiClass); + return uiClass.cast(uiProvider.createInstance(this, uiClass, request)); + } + + /** + * Gets the {@link UIProvider} that should be used for a request. The + * selection can further be restricted by also requiring the UI provider to + * support a specific UI class. + * + * @see UIProvider + * @see #addUIProvider(UIProvider) + * + * @param request + * the request for which to get an UI provider + * @param uiClass + * the UI class for which a provider is required, or + * <code>null</code> to use the first UI provider supporting the + * request. + * @return an UI provider supporting the request (and the UI class if + * provided). + * + * @since 7.0.0 + */ + public UIProvider getUiProvider(WrappedRequest request, Class<?> uiClass) { + UIProvider provider = (UIProvider) request + .getAttribute(UIProvider.class.getName()); + if (provider != null) { + // Cached provider found, verify that it's a sensible selection + Class<? extends UI> providerClass = provider.getUIClass(this, + request); + if (uiClass == null && providerClass != null) { + // Use it if it gives any answer if no specific class is + // required + return provider; + } else if (uiClass == providerClass) { + // Use it if it gives the expected UI class + return provider; + } else { + // Don't keep it cached if it doesn't match the expectations + request.setAttribute(UIProvider.class.getName(), null); + } + } + + // Iterate all current providers if no matching cached provider found + provider = doGetUiProvider(request, uiClass); + + // Cache the found provider + request.setAttribute(UIProvider.class.getName(), provider); + + return provider; + } + + private UIProvider doGetUiProvider(WrappedRequest request, Class<?> uiClass) { + int providersSize = uiProviders.size(); + if (providersSize == 0) { + throw new IllegalStateException("There are no UI providers"); + } + + for (int i = providersSize - 1; i >= 0; i--) { + UIProvider provider = uiProviders.get(i); + + Class<? extends UI> providerClass = provider.getUIClass(this, + 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"); + } + + /** + * Handles a request by passing it to each registered {@link RequestHandler} + * in turn until one produces a response. This method is used for requests + * that have not been handled by any specific functionality in the terminal + * implementation (e.g. {@link VaadinServlet}). + * <p> + * The request handlers are invoked in the revere order in which they were + * added to the application until a response has been produced. This means + * that the most recently added handler is used first and the first request + * handler that was added to the application is invoked towards the end + * unless any previous handler has already produced a response. + * </p> + * + * @param request + * the wrapped request to get information from + * @param response + * the response to which data can be written + * @return returns <code>true</code> if a {@link RequestHandler} has + * produced a response and <code>false</code> if no response has + * been written. + * @throws IOException + * + * @see #addRequestHandler(RequestHandler) + * @see RequestHandler + * + * @since 7.0 + */ + public boolean handleRequest(WrappedRequest request, + WrappedResponse response) throws IOException { + // Use a copy to avoid ConcurrentModificationException + for (RequestHandler handler : new ArrayList<RequestHandler>( + requestHandlers)) { + if (handler.handleRequest(this, request, response)) { + return true; + } + } + // If not handled + return false; + } + + /** + * Adds a request handler to this application. Request handlers can be added + * to provide responses to requests that are not handled by the default + * functionality of the framework. + * <p> + * Handlers are called in reverse order of addition, so the most recently + * added handler will be called first. + * </p> + * + * @param handler + * the request handler to add + * + * @see #handleRequest(WrappedRequest, WrappedResponse) + * @see #removeRequestHandler(RequestHandler) + * + * @since 7.0 + */ + public void addRequestHandler(RequestHandler handler) { + requestHandlers.addFirst(handler); + } + + /** + * Removes a request handler from the application. + * + * @param handler + * the request handler to remove + * + * @since 7.0 + */ + public void removeRequestHandler(RequestHandler handler) { + requestHandlers.remove(handler); + } + + /** + * Gets the request handlers that are registered to the application. The + * iteration order of the returned collection is the same as the order in + * which the request handlers will be invoked when a request is handled. + * + * @return a collection of request handlers, with the iteration order + * according to the order they would be invoked + * + * @see #handleRequest(WrappedRequest, WrappedResponse) + * @see #addRequestHandler(RequestHandler) + * @see #removeRequestHandler(RequestHandler) + * + * @since 7.0 + */ + public Collection<RequestHandler> getRequestHandlers() { + return Collections.unmodifiableCollection(requestHandlers); + } + + /** + * Gets the currently used application. The current application is + * automatically defined when processing requests to the server. In other + * cases, (e.g. from background threads), the current application is not + * automatically defined. + * + * @return the current application instance if available, otherwise + * <code>null</code> + * + * @see #setCurrent(VaadinSession) + * + * @since 7.0 + */ + public static VaadinSession getCurrent() { + return CurrentInstance.get(VaadinSession.class); + } + + /** + * Sets the thread local for the current application. This method is used by + * the framework to set the current application whenever a new request is + * processed and it is cleared when the request has been processed. + * <p> + * The application developer can also use this method to define the current + * application outside the normal request handling, e.g. when initiating + * custom background threads. + * </p> + * + * @param application + * + * @see #getCurrent() + * @see ThreadLocal + * + * @since 7.0 + */ + public static void setCurrent(VaadinSession application) { + CurrentInstance.setInheritable(VaadinSession.class, application); + } + + /** + * Check whether this application is in production mode. If an application + * is in production mode, certain debugging facilities are not available. + * + * @return the status of the production mode flag + * + * @since 7.0 + */ + public boolean isProductionMode() { + return configuration.isProductionMode(); + } + + 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(WrappedRequest) + * + * @since 7.0 + */ + public UI getUIForRequest(WrappedRequest request) { + UI uI = UI.getCurrent(); + if (uI != null) { + return uI; + } + Integer uiId = getUIId(request); + + synchronized (this) { + uI = uIs.get(uiId); + + if (uI == null) { + uI = findExistingUi(request); + } + + } // end synchronized block + + UI.setCurrent(uI); + + return uI; + } + + private UI findExistingUi(WrappedRequest 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; + } + + public UI createUI(WrappedRequest 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).isUiPreserved(request, uiClass)) { + // Remember this UI + String windowName = request.getBrowserDetails().getWindowName(); + if (windowName == null) { + getLogger().warning( + "There is no window.name available for UI " + uiClass + + " that should be preserved."); + } else { + retainOnRefreshUIs.put(windowName, uiId); + } + } + + 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 + */ + private static Integer getUIId(WrappedRequest 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 application. This includes uIs that have been + * requested but not yet initialized. Please note, that uIs are not + * automatically removed e.g. if the browser window is closed and that there + * is no way to manually remove a UI. Inactive uIs will thus not be released + * for GC until the entire application is released when the session has + * timed out (unless there are dangling references). Improved support for + * releasing unused uIs is planned for an upcoming alpha release of Vaadin + * 7. + * + * @return a collection of uIs belonging to this application + * + * @since 7.0 + */ + public Collection<UI> getUIs() { + return Collections.unmodifiableCollection(uIs.values()); + } + + private int connectorIdSequence = 0; + + /** + * Generate an id for the given Connector. Connectors must not call this + * method more than once, the first time they need an id. + * + * @param connector + * A connector that has not yet been assigned an id. + * @return A new id for the connector + */ + public String createConnectorId(ClientConnector connector) { + return String.valueOf(connectorIdSequence++); + } + + private static final Logger getLogger() { + return Logger.getLogger(VaadinSession.class.getName()); + } + + /** + * Returns a UI with the given id. + * <p> + * This is meant for framework internal use. + * </p> + * + * @param uiId + * The UI id + * @return The UI with the given id or null if not found + */ + public UI getUIById(int uiId) { + return uIs.get(uiId); + } + + /** + * Adds a listener that will be invoked when the bootstrap HTML is about to + * be generated. This can be used to modify the contents of the HTML that + * loads the Vaadin application in the browser and the HTTP headers that are + * included in the response serving the HTML. + * + * @see BootstrapListener#modifyBootstrapFragment(BootstrapFragmentResponse) + * @see BootstrapListener#modifyBootstrapPage(BootstrapPageResponse) + * + * @param listener + * the bootstrap listener to add + */ + public void addBootstrapListener(BootstrapListener listener) { + eventRouter.addListener(BootstrapFragmentResponse.class, listener, + BOOTSTRAP_FRAGMENT_METHOD); + eventRouter.addListener(BootstrapPageResponse.class, listener, + BOOTSTRAP_PAGE_METHOD); + } + + /** + * Remove a bootstrap listener that was previously added. + * + * @see #addBootstrapListener(BootstrapListener) + * + * @param listener + * the bootstrap listener to remove + */ + public void removeBootstrapListener(BootstrapListener listener) { + eventRouter.removeListener(BootstrapFragmentResponse.class, listener, + BOOTSTRAP_FRAGMENT_METHOD); + eventRouter.removeListener(BootstrapPageResponse.class, listener, + BOOTSTRAP_PAGE_METHOD); + } + + /** + * Fires a bootstrap event to all registered listeners. There are currently + * two supported events, both inheriting from {@link BootstrapResponse}: + * {@link BootstrapFragmentResponse} and {@link BootstrapPageResponse}. + * + * @param response + * the bootstrap response event for which listeners should be + * fired + */ + public void modifyBootstrapResponse(BootstrapResponse response) { + eventRouter.fireEvent(response); + } + + /** + * Removes all those UIs from the application for which {@link #isUIAlive} + * returns false. Close events are fired for the removed UIs. + * <p> + * Called by the framework at the end of every request. + * + * @see UI.CloseEvent + * @see UI.CloseListener + * @see #isUIAlive(UI) + * + * @since 7.0.0 + */ + public void closeInactiveUIs() { + for (Iterator<UI> i = uIs.values().iterator(); i.hasNext();) { + UI ui = i.next(); + if (!isUIAlive(ui)) { + i.remove(); + retainOnRefreshUIs.values().remove(ui.getUIId()); + ui.fireCloseEvent(); + getLogger().info( + "Closed UI #" + ui.getUIId() + " due to inactivity"); + } + } + } + + /** + * 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 + * inactive UI. Returns a negative number if heartbeat is disabled and + * timeout never occurs. + * + * @see #getUidlRequestTimeout() + * @see #closeInactiveUIs() + * @see DeploymentConfiguration#getHeartbeatInterval() + * + * @since 7.0.0 + * + * @return The heartbeat timeout in seconds or a negative number if timeout + * never occurs. + */ + protected int getHeartbeatTimeout() { + // Permit three missed heartbeats before closing the UI + return (int) (configuration.getHeartbeatInterval() * (3.1)); + } + + /** + * Returns the number of seconds that must pass without a valid UIDL request + * being received from a UI before the UI is removed from the application, + * even though heartbeat requests are received. This is a lower bound; it + * might take longer to close an inactive UI. Returns a negative number if + * <p> + * This timeout only has effect if cleanup of inactive UIs is enabled; + * otherwise heartbeat requests are enough to extend UI lifetime + * indefinitely. + * + * @see DeploymentConfiguration#isIdleUICleanupEnabled() + * @see #getHeartbeatTimeout() + * @see #closeInactiveUIs() + * + * @since 7.0.0 + * + * @return The UIDL request timeout in seconds, or a negative number if + * timeout never occurs. + */ + protected int getUidlRequestTimeout() { + return configuration.isIdleUICleanupEnabled() ? getSession() + .getMaxInactiveInterval() : -1; + } + + /** + * Returns whether the given UI is alive (the client-side actively + * communicates with the server) or whether it can be removed from the + * application and eventually collected. + * + * @since 7.0.0 + * + * @param ui + * The UI whose status to check + * @return true if the UI is alive, false if it could be removed. + */ + protected boolean isUIAlive(UI ui) { + long now = System.currentTimeMillis(); + if (getHeartbeatTimeout() >= 0 + && now - ui.getLastHeartbeatTime() > 1000 * getHeartbeatTimeout()) { + return false; + } + if (getUidlRequestTimeout() >= 0 + && now - ui.getLastUidlRequestTime() > 1000 * getUidlRequestTimeout()) { + return false; + } + return true; + } + + /** + * Gets this application's global resource handler that takes care of + * serving connector resources that are not served by any single connector + * because e.g. because they are served with strong caching or because of + * legacy reasons. + * + * @param createOnDemand + * <code>true</code> if a resource handler should be initialized + * if there is no handler associated with this application. + * </code>false</code> if </code>null</code> should be returned + * if there is no registered handler. + * @return this application's global resource handler, or <code>null</code> + * if there is no handler and the createOnDemand parameter is + * <code>false</code>. + * + * @since 7.0.0 + */ + public GlobalResourceHandler getGlobalResourceHandler(boolean createOnDemand) { + if (globalResourceHandler == null && createOnDemand) { + globalResourceHandler = new GlobalResourceHandler(); + addRequestHandler(globalResourceHandler); + } + + return globalResourceHandler; + } + + public Collection<UIProvider> getUIProviders() { + return Collections.unmodifiableCollection(uiProviders); + } + +} diff --git a/server/src/com/vaadin/server/WrappedHttpServletRequest.java b/server/src/com/vaadin/server/WrappedHttpServletRequest.java index b069235843..4221551601 100644 --- a/server/src/com/vaadin/server/WrappedHttpServletRequest.java +++ b/server/src/com/vaadin/server/WrappedHttpServletRequest.java @@ -19,7 +19,6 @@ package com.vaadin.server; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; -import com.vaadin.Application; /** * Wrapper for {@link HttpServletRequest}. @@ -56,18 +55,8 @@ public class WrappedHttpServletRequest extends HttpServletRequestWrapper } @Override - public int getSessionMaxInactiveInterval() { - return getSession().getMaxInactiveInterval(); - } - - @Override - public Object getSessionAttribute(String name) { - return getSession().getAttribute(name); - } - - @Override - public void setSessionAttribute(String name, Object attribute) { - getSession().setAttribute(name, attribute); + public WrappedSession getWrappedSession() { + return new WrappedHttpSession(getSession()); } /** @@ -99,9 +88,7 @@ public class WrappedHttpServletRequest extends HttpServletRequestWrapper @Override public WebBrowser getWebBrowser() { - ApplicationContext context = Application.getCurrent() - .getContext(); - return context.getBrowser(); + return VaadinSession.getCurrent().getBrowser(); } }; } diff --git a/server/src/com/vaadin/server/WrappedHttpSession.java b/server/src/com/vaadin/server/WrappedHttpSession.java new file mode 100644 index 0000000000..1465588e08 --- /dev/null +++ b/server/src/com/vaadin/server/WrappedHttpSession.java @@ -0,0 +1,66 @@ +/* + * 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 javax.servlet.http.HttpSession; + +/** + * Wrapper for {@link HttpSession}. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class WrappedHttpSession implements WrappedSession { + + private final HttpSession session; + + /** + * Creates a new wrapped http session. + * + * @param session + * the http session to wrap. + */ + public WrappedHttpSession(HttpSession session) { + this.session = session; + } + + @Override + public int getMaxInactiveInterval() { + return session.getMaxInactiveInterval(); + } + + @Override + public Object getAttribute(String name) { + return session.getAttribute(name); + } + + @Override + public void setAttribute(String name, Object value) { + session.setAttribute(name, value); + } + + /** + * Gets the wrapped {@link HttpSession}. + * + * @return the wrapped http session + */ + public HttpSession getHttpSession() { + return session; + } + +} diff --git a/server/src/com/vaadin/server/WrappedPortletRequest.java b/server/src/com/vaadin/server/WrappedPortletRequest.java index 47a8e2c358..65099add76 100644 --- a/server/src/com/vaadin/server/WrappedPortletRequest.java +++ b/server/src/com/vaadin/server/WrappedPortletRequest.java @@ -25,7 +25,6 @@ import javax.portlet.ClientDataRequest; import javax.portlet.PortletRequest; import javax.portlet.ResourceRequest; -import com.vaadin.Application; import com.vaadin.shared.ApplicationConstants; /** @@ -113,18 +112,8 @@ public class WrappedPortletRequest implements WrappedRequest { } @Override - public int getSessionMaxInactiveInterval() { - return request.getPortletSession().getMaxInactiveInterval(); - } - - @Override - public Object getSessionAttribute(String name) { - return request.getPortletSession().getAttribute(name); - } - - @Override - public void setSessionAttribute(String name, Object attribute) { - request.getPortletSession().setAttribute(name, attribute); + public WrappedSession getWrappedSession() { + return new WrappedPortletSession(request.getPortletSession()); } /** @@ -161,8 +150,8 @@ public class WrappedPortletRequest implements WrappedRequest { @Override public WebBrowser getWebBrowser() { - PortletApplicationContext2 context = (PortletApplicationContext2) Application - .getCurrent().getContext(); + VaadinPortletSession context = (VaadinPortletSession) VaadinSession + .getCurrent(); return context.getBrowser(); } }; diff --git a/server/src/com/vaadin/server/WrappedPortletResponse.java b/server/src/com/vaadin/server/WrappedPortletResponse.java index f84e3619d2..e3010501b6 100644 --- a/server/src/com/vaadin/server/WrappedPortletResponse.java +++ b/server/src/com/vaadin/server/WrappedPortletResponse.java @@ -29,6 +29,8 @@ import javax.portlet.MimeResponse; import javax.portlet.PortletResponse; import javax.portlet.ResourceResponse; +import com.vaadin.server.VaadinPortlet.PortletDeploymentConfiguration; + /** * Wrapper for {@link PortletResponse} and its subclasses. * @@ -46,7 +48,7 @@ public class WrappedPortletResponse implements WrappedResponse { } private final PortletResponse response; - private DeploymentConfiguration deploymentConfiguration; + private PortletDeploymentConfiguration deploymentConfiguration; /** * Wraps a portlet response and an associated deployment configuration @@ -57,7 +59,7 @@ public class WrappedPortletResponse implements WrappedResponse { * the associated deployment configuration */ public WrappedPortletResponse(PortletResponse response, - DeploymentConfiguration deploymentConfiguration) { + PortletDeploymentConfiguration deploymentConfiguration) { this.response = response; this.deploymentConfiguration = deploymentConfiguration; } @@ -114,7 +116,7 @@ public class WrappedPortletResponse implements WrappedResponse { } @Override - public DeploymentConfiguration getDeploymentConfiguration() { + public PortletDeploymentConfiguration getDeploymentConfiguration() { return deploymentConfiguration; } }
\ No newline at end of file diff --git a/server/src/com/vaadin/server/WrappedPortletSession.java b/server/src/com/vaadin/server/WrappedPortletSession.java new file mode 100644 index 0000000000..eb07eb38f6 --- /dev/null +++ b/server/src/com/vaadin/server/WrappedPortletSession.java @@ -0,0 +1,65 @@ +/* + * 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 javax.portlet.PortletSession; + +/** + * Wrapper for + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class WrappedPortletSession implements WrappedSession { + + private final PortletSession session; + + /** + * Creates a new wrapped portlet session. + * + * @param session + * the portlet session to wrap. + */ + public WrappedPortletSession(PortletSession session) { + this.session = session; + } + + @Override + public int getMaxInactiveInterval() { + return session.getMaxInactiveInterval(); + } + + @Override + public Object getAttribute(String name) { + return session.getAttribute(name); + } + + @Override + public void setAttribute(String name, Object value) { + session.setAttribute(name, value); + } + + /** + * Gets the wrapped {@link PortletSession}. + * + * @return the wrapped portlet session + */ + public PortletSession getPortletSession() { + return session; + } +} diff --git a/server/src/com/vaadin/server/WrappedRequest.java b/server/src/com/vaadin/server/WrappedRequest.java index 0714f73cad..5d7ece9ef1 100644 --- a/server/src/com/vaadin/server/WrappedRequest.java +++ b/server/src/com/vaadin/server/WrappedRequest.java @@ -32,7 +32,9 @@ import com.vaadin.ui.UI; * A generic request to the server, wrapping a more specific request type, e.g. * HttpServletReqest or PortletRequest. * - * @since 7.0 + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 */ public interface WrappedRequest extends Serializable { @@ -158,42 +160,15 @@ public interface WrappedRequest extends Serializable { public String getRequestPathInfo(); /** - * Returns the maximum time interval, in seconds, that the session - * associated with this request will be kept open between client accesses. + * Gets the session associated with this request. * - * @return an integer specifying the number of seconds the session - * associated with this request remains open between client requests + * @see WrappedSession + * @see HttpServletRequest#getSession() + * @see PortletRequest#getPortletSession() * - * @see javax.servlet.http.HttpSession#getMaxInactiveInterval() - * @see javax.portlet.PortletSession#getMaxInactiveInterval() + * @return the wrapped session for this request */ - public int getSessionMaxInactiveInterval(); - - /** - * Gets an attribute from the session associated with this request. - * - * @param name - * the name of the attribute - * @return the attribute value, or <code>null</code> if the attribute is not - * defined in the session - * - * @see javax.servlet.http.HttpSession#getAttribute(String) - * @see javax.portlet.PortletSession#getAttribute(String) - */ - public Object getSessionAttribute(String name); - - /** - * Saves an attribute value in the session associated with this request. - * - * @param name - * the name of the attribute - * @param attribute - * the attribute value - * - * @see javax.servlet.http.HttpSession#setAttribute(String, Object) - * @see javax.portlet.PortletSession#setAttribute(String, Object) - */ - public void setSessionAttribute(String name, Object attribute); + public WrappedSession getWrappedSession(); /** * Returns the MIME type of the body of the request, or null if the type is diff --git a/server/src/com/vaadin/server/WrappedSession.java b/server/src/com/vaadin/server/WrappedSession.java new file mode 100644 index 0000000000..3973c257c8 --- /dev/null +++ b/server/src/com/vaadin/server/WrappedSession.java @@ -0,0 +1,69 @@ +/* + * 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 javax.portlet.PortletSession; +import javax.servlet.http.HttpSession; + +/** + * A generic session, wrapping a more specific session implementation, e.g. + * {@link HttpSession} or {@link PortletSession}. + * + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public interface WrappedSession { + /** + * Returns the maximum time interval, in seconds, that this session will be + * kept open between client accesses. + * + * @return an integer specifying the number of seconds this session remains + * open between client requests + * + * @see javax.servlet.http.HttpSession#getMaxInactiveInterval() + * @see javax.portlet.PortletSession#getMaxInactiveInterval() + */ + public int getMaxInactiveInterval(); + + /** + * Gets an attribute from this session. + * + * @param name + * the name of the attribute + * @return the attribute value, or <code>null</code> if the attribute is not + * defined in the session + * + * @see javax.servlet.http.HttpSession#getAttribute(String) + * @see javax.portlet.PortletSession#getAttribute(String) + */ + public Object getAttribute(String name); + + /** + * Saves an attribute value in this session. + * + * @param name + * the name of the attribute + * @param value + * the attribute value + * + * @see javax.servlet.http.HttpSession#setAttribute(String, Object) + * @see javax.portlet.PortletSession#setAttribute(String, Object) + */ + public void setAttribute(String name, Object value); +} diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index 37ca9f1a03..85938c4fe3 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -27,7 +27,6 @@ import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.vaadin.Application; import com.vaadin.event.ActionManager; import com.vaadin.event.EventRouter; import com.vaadin.event.MethodEventSource; @@ -38,6 +37,7 @@ import com.vaadin.server.ComponentSizeValidator; import com.vaadin.server.ErrorMessage; import com.vaadin.server.Resource; import com.vaadin.server.Terminal; +import com.vaadin.server.VaadinSession; import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.ComponentState; import com.vaadin.shared.ui.ComponentStateUtil; @@ -259,7 +259,7 @@ public abstract class AbstractComponent extends AbstractClientConnector if (parent != null) { return parent.getLocale(); } - final Application app = getApplication(); + final VaadinSession app = getSession(); if (app != null) { return app.getLocale(); } @@ -616,7 +616,7 @@ public abstract class AbstractComponent extends AbstractClientConnector */ protected void focus() { if (this instanceof Focusable) { - final Application app = getApplication(); + final VaadinSession app = getSession(); if (app != null) { getUI().setFocusedComponent((Focusable) this); delayedFocus = false; @@ -627,31 +627,6 @@ public abstract class AbstractComponent extends AbstractClientConnector } /** - * Gets the application object to which the component is attached. - * - * <p> - * The method will return {@code null} if the component is not currently - * attached to an application. This is often a problem in constructors of - * regular components and in the initializers of custom composite - * components. A standard workaround is to move the problematic - * initialization to {@link #attach()}, as described in the documentation of - * the method. - * </p> - * <p> - * <b>This method is not meant to be overridden. Due to CDI requirements we - * cannot declare it as final even though it should be final.</b> - * </p> - * - * @return the parent application of the component or <code>null</code>. - * @see #attach() - */ - @Override - public Application getApplication() { - // Just make method inherited from Component interface public - return super.getApplication(); - } - - /** * Build CSS compatible string representation of height. * * @return CSS height diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java index 548cb06c8f..f673babc26 100644 --- a/server/src/com/vaadin/ui/AbstractField.java +++ b/server/src/com/vaadin/ui/AbstractField.java @@ -697,7 +697,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements */ public void setConverter(Class<?> datamodelType) { Converter<T, ?> c = (Converter<T, ?>) ConverterUtil.getConverter( - getType(), datamodelType, getApplication()); + getType(), datamodelType, getSession()); setConverter(c); } diff --git a/server/src/com/vaadin/ui/Component.java b/server/src/com/vaadin/ui/Component.java index 5bd1d53b86..492e0c25c6 100644 --- a/server/src/com/vaadin/ui/Component.java +++ b/server/src/com/vaadin/ui/Component.java @@ -21,7 +21,6 @@ import java.util.EventListener; import java.util.EventObject; import java.util.Locale; -import com.vaadin.Application; import com.vaadin.event.FieldEvents; import com.vaadin.server.ClientConnector; import com.vaadin.server.ErrorMessage; @@ -522,34 +521,12 @@ public interface Component extends ClientConnector, Sizeable, Serializable { public UI getUI(); /** - * Gets the application object to which the component is attached. - * - * <p> - * The method will return {@code null} if the component is not currently - * attached to an application. - * </p> - * - * <p> - * Getting a null value is often a problem in constructors of regular - * components and in the initializers of custom composite components. A - * standard workaround is to use {@link Application#getCurrent()} to - * retrieve the application instance that the current request relates to. - * Another way is to move the problematic initialization to - * {@link #attach()}, as described in the documentation of the method. - * </p> - * - * @return the parent application of the component or <code>null</code>. - * @see #attach() - */ - public Application getApplication(); - - /** * {@inheritDoc} * * <p> * Reimplementing the {@code attach()} method is useful for tasks that need * to get a reference to the parent, window, or application object with the - * {@link #getParent()}, {@link #getUI()}, and {@link #getApplication()} + * {@link #getParent()}, {@link #getUI()}, and {@link #getSession()} * methods. A component does not yet know these objects in the constructor, * so in such case, the methods will return {@code null}. For example, the * following is invalid: diff --git a/server/src/com/vaadin/ui/ConnectorTracker.java b/server/src/com/vaadin/ui/ConnectorTracker.java index c84b75ca51..d454df98ee 100644 --- a/server/src/com/vaadin/ui/ConnectorTracker.java +++ b/server/src/com/vaadin/ui/ConnectorTracker.java @@ -152,7 +152,7 @@ public class ConnectorTracker implements Serializable { } private void removeFromGlobalResourceHandler(ClientConnector connector) { - GlobalResourceHandler globalResourceHandler = uI.getApplication() + GlobalResourceHandler globalResourceHandler = uI.getSession() .getGlobalResourceHandler(false); // Nothing to do if there is no handler if (globalResourceHandler != null) { diff --git a/server/src/com/vaadin/ui/Label.java b/server/src/com/vaadin/ui/Label.java index ff4a5dcb07..53b618a87f 100644 --- a/server/src/com/vaadin/ui/Label.java +++ b/server/src/com/vaadin/ui/Label.java @@ -254,7 +254,7 @@ public class Label extends AbstractComponent implements Property<String>, newDataSource.getType())) { // Try to find a converter Converter<String, ?> c = ConverterUtil.getConverter(String.class, - newDataSource.getType(), getApplication()); + newDataSource.getType(), getSession()); setConverter(c); } dataSource = newDataSource; diff --git a/server/src/com/vaadin/ui/LoginForm.java b/server/src/com/vaadin/ui/LoginForm.java index 18927077d8..f597908b8c 100644 --- a/server/src/com/vaadin/ui/LoginForm.java +++ b/server/src/com/vaadin/ui/LoginForm.java @@ -24,10 +24,10 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import com.vaadin.Application; import com.vaadin.server.ConnectorResource; import com.vaadin.server.DownloadStream; import com.vaadin.server.RequestHandler; +import com.vaadin.server.VaadinSession; import com.vaadin.server.WrappedRequest; import com.vaadin.server.WrappedResponse; import com.vaadin.shared.ApplicationConstants; @@ -83,7 +83,7 @@ public class LoginForm extends CustomComponent { private final RequestHandler requestHandler = new RequestHandler() { @Override - public boolean handleRequest(Application application, + public boolean handleRequest(VaadinSession application, WrappedRequest request, WrappedResponse response) throws IOException { String requestPathInfo = request.getRequestPathInfo(); @@ -132,7 +132,7 @@ public class LoginForm extends CustomComponent { * @return byte array containing login page html */ protected byte[] getLoginHTML() { - String appUri = getApplication().getURL().toString(); + String appUri = getSession().getURL().toString(); try { return ("<!DOCTYPE html PUBLIC \"-//W3C//DTD " @@ -186,13 +186,13 @@ public class LoginForm extends CustomComponent { @Override public void attach() { super.attach(); - getApplication().addRequestHandler(requestHandler); + getSession().addRequestHandler(requestHandler); iframe.setSource(loginPage); } @Override public void detach() { - getApplication().removeRequestHandler(requestHandler); + getSession().removeRequestHandler(requestHandler); super.detach(); } diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java index 65189fed0c..e0469f6522 100644 --- a/server/src/com/vaadin/ui/Table.java +++ b/server/src/com/vaadin/ui/Table.java @@ -3717,7 +3717,7 @@ public class Table extends AbstractSelect implements Action.Container, converter = getConverter(colId); } else { ConverterUtil.getConverter(String.class, property.getType(), - getApplication()); + getSession()); } Object value = property.getValue(); if (converter != null) { diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index ee4cb9fd2c..1c1fcf5492 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -27,7 +27,6 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; -import com.vaadin.Application; import com.vaadin.event.Action; import com.vaadin.event.Action.Handler; import com.vaadin.event.ActionManager; @@ -41,6 +40,7 @@ import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.server.Resource; import com.vaadin.server.VaadinServlet; +import com.vaadin.server.VaadinSession; import com.vaadin.server.WrappedRequest; import com.vaadin.server.WrappedRequest.BrowserDetails; import com.vaadin.shared.EventId; @@ -49,6 +49,7 @@ import com.vaadin.shared.ui.BorderStyle; import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.shared.ui.ui.UIServerRpc; import com.vaadin.shared.ui.ui.UIState; +import com.vaadin.util.CurrentInstance; import com.vaadin.util.ReflectTools; /** @@ -64,9 +65,9 @@ import com.vaadin.util.ReflectTools; * <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 Application#getUIForRequest(WrappedRequest)} is invoked to get a UI. + * {@link VaadinSession#getUIForRequest(WrappedRequest)} is invoked to get a UI. * That method does by default create a UI according to the - * {@value Application#UI_PARAMETER} parameter from web.xml. + * {@value VaadinSession#UI_PARAMETER} parameter from web.xml. * </p> * <p> * After a UI has been created by the application, it is initialized using @@ -78,7 +79,7 @@ import com.vaadin.util.ReflectTools; * </p> * * @see #init(WrappedRequest) - * @see Application#createUI(WrappedRequest) + * @see VaadinSession#createUI(WrappedRequest) * * @since 7.0 */ @@ -88,7 +89,7 @@ public abstract class UI extends AbstractComponentContainer implements /** * Helper class to emulate the main window from Vaadin 6 using UIs. This * class should be used in the same way as Window used as a browser level - * window in Vaadin 6 with {@link com.vaadin.Application.LegacyApplication} + * window in Vaadin 6 with {@link com.vaadin.Application} */ @Deprecated public static class LegacyWindow extends UI { @@ -133,7 +134,7 @@ public abstract class UI extends AbstractComponentContainer implements * The name also determines the URL that can be used for direct access * to a window. All windows can be accessed through * {@code http://host:port/app/win} where {@code http://host:port/app} - * is the application URL (as returned by {@link Application#getURL()} + * is the application URL (as returned by {@link VaadinSession#getURL()} * and {@code win} is the window name. * </p> * <p> @@ -153,7 +154,7 @@ public abstract class UI extends AbstractComponentContainer implements * The name also determines the URL that can be used for direct access * to a window. All windows can be accessed through * {@code http://host:port/app/win} where {@code http://host:port/app} - * is the application URL (as returned by {@link Application#getURL()} + * is the application URL (as returned by {@link VaadinSession#getURL()} * and {@code win} is the window name. * </p> * <p> @@ -172,7 +173,7 @@ public abstract class UI extends AbstractComponentContainer implements public void setName(String name) { this.name = name; // The name can not be changed in application - if (getApplication() != null) { + if (getSession() != null) { throw new IllegalStateException( "Window name can not be changed while " + "the window is in application"); @@ -191,7 +192,7 @@ public abstract class UI extends AbstractComponentContainer implements * to an application */ public URL getURL() { - Application application = getApplication(); + VaadinSession application = getSession(); if (application == null) { return null; } @@ -422,7 +423,7 @@ public abstract class UI extends AbstractComponentContainer implements /** * The application to which this UI belongs */ - private Application application; + private VaadinSession session; /** * List of windows in this UI. @@ -440,7 +441,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 Application#nextUIId + * @see VaadinSession#nextUIId */ private int uiId = -1; @@ -450,11 +451,6 @@ public abstract class UI extends AbstractComponentContainer implements */ protected ActionManager actionManager; - /** - * Thread local for keeping track of the current UI. - */ - private static final ThreadLocal<UI> currentUI = new ThreadLocal<UI>(); - /** Identifies the click event */ private ConnectorTracker connectorTracker = new ConnectorTracker(this); @@ -566,9 +562,29 @@ public abstract class UI extends AbstractComponentContainer implements throw new UnsupportedOperationException(); } + /** + * Gets the application object to which the component is attached. + * + * <p> + * The method will return {@code null} if the component is not currently + * attached to an application. + * </p> + * + * <p> + * Getting a null value is often a problem in constructors of regular + * components and in the initializers of custom composite components. A + * standard workaround is to use {@link VaadinSession#getCurrent()} to + * retrieve the application instance that the current request relates to. + * Another way is to move the problematic initialization to + * {@link #attach()}, as described in the documentation of the method. + * </p> + * + * @return the parent application of the component or <code>null</code>. + * @see #attach() + */ @Override - public Application getApplication() { - return application; + public VaadinSession getSession() { + return session; } @Override @@ -679,25 +695,25 @@ public abstract class UI extends AbstractComponentContainer implements * This method is mainly intended for internal use by the framework. * </p> * - * @param application + * @param session * the application to set * * @throws IllegalStateException * if the application has already been set * - * @see #getApplication() + * @see #getSession() */ - public void setApplication(Application application) { - if ((application == null) == (this.application == null)) { + public void setSession(VaadinSession session) { + if ((session == null) == (this.session == null)) { throw new IllegalStateException("Application has already been set"); } else { - if (application == null) { + if (session == null) { detach(); } - this.application = application; + this.session = session; } - if (application != null) { + if (session != null) { attach(); } } @@ -706,8 +722,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 Application#getUIForRequest(WrappedRequest)} uses this id to find - * the route to which the request belongs. + * {@link VaadinSession#getUIForRequest(WrappedRequest)} uses this id to + * find the route to which the request belongs. * * @return */ @@ -719,7 +735,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 Application#createUI(WrappedRequest)} returns an appropriate UI + * {@link VaadinSession#createUI(WrappedRequest)} returns an appropriate UI * for the request. * * @param window @@ -735,7 +751,7 @@ public abstract class UI extends AbstractComponentContainer implements throw new NullPointerException("Argument must not be null"); } - if (window.getApplication() != null) { + if (window.getUI() != null && window.getUI().getSession() != null) { throw new IllegalArgumentException( "Window is already attached to an application."); } @@ -940,7 +956,8 @@ public abstract class UI extends AbstractComponentContainer implements throw new IllegalStateException("UI id has already been defined"); } this.uiId = uiId; - theme = getApplication().getThemeForUI(request, getClass()); + theme = getSession().getUiProvider(request, getClass()) + .getThemeForUI(request, getClass()); getPage().init(request); @@ -981,7 +998,7 @@ public abstract class UI extends AbstractComponentContainer implements * @see ThreadLocal */ public static void setCurrent(UI ui) { - currentUI.set(ui); + CurrentInstance.setInheritable(UI.class, ui); } /** @@ -994,7 +1011,7 @@ public abstract class UI extends AbstractComponentContainer implements * @see #setCurrent(UI) */ public static UI getCurrent() { - return currentUI.get(); + return CurrentInstance.get(UI.class); } public void setScrollTop(int scrollTop) { @@ -1296,7 +1313,7 @@ public abstract class UI extends AbstractComponentContainer implements * heartbeat for this UI. * * @see #heartbeat() - * @see Application#closeInactiveUIs() + * @see VaadinSession#closeInactiveUIs() * * @return The time the last heartbeat request occurred. */ diff --git a/server/src/com/vaadin/util/CurrentInstance.java b/server/src/com/vaadin/util/CurrentInstance.java new file mode 100644 index 0000000000..8478ba9486 --- /dev/null +++ b/server/src/com/vaadin/util/CurrentInstance.java @@ -0,0 +1,131 @@ +/* + * 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.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Keeps track of various thread local instances used by the framework. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class CurrentInstance { + private final Object instance; + private final boolean inheritable; + + private static InheritableThreadLocal<Map<Class<?>, CurrentInstance>> instances = new InheritableThreadLocal<Map<Class<?>, CurrentInstance>>() { + @Override + protected Map<Class<?>, CurrentInstance> childValue( + Map<Class<?>, CurrentInstance> parentValue) { + Map<Class<?>, CurrentInstance> value = new HashMap<Class<?>, CurrentInstance>(); + + // Copy all inheritable values to child map + for (Entry<Class<?>, CurrentInstance> e : parentValue.entrySet()) { + if (e.getValue().inheritable) { + value.put(e.getKey(), e.getValue()); + } + } + + return value; + } + + @Override + protected Map<java.lang.Class<?>, CurrentInstance> initialValue() { + return new HashMap<Class<?>, CurrentInstance>(); + } + }; + + private CurrentInstance(Object instance, boolean inheritable) { + this.instance = instance; + this.inheritable = inheritable; + } + + /** + * Gets the current instance of a specific type if available. + * + * @param type + * the class to get an instance of + * @return the current instance or the provided type, or <code>null</code> + * if there is no current instance. + */ + public static <T> T get(Class<T> type) { + CurrentInstance currentInstance = instances.get().get(type); + if (currentInstance != null) { + return type.cast(currentInstance.instance); + } else { + return null; + } + } + + /** + * Sets the current instance of the given type. + * + * @see #setInheritable(Class, Object) + * @see ThreadLocal + * + * @param type + * the class that should be used when getting the current + * instance back + * @param instance + * the actual instance + */ + public static <T> void set(Class<T> type, T instance) { + set(type, instance, false); + } + + /** + * Sets the current inheritable instance of the given type. A current + * instance that is inheritable will be available for child threads. + * + * @see #set(Class, Object) + * @see InheritableThreadLocal + * + * @param type + * the class that should be used when getting the current + * instance back + * @param instance + * the actual instance + */ + public static <T> void setInheritable(Class<T> type, T instance) { + set(type, instance, true); + } + + private static <T> void set(Class<T> type, T instance, boolean inheritable) { + if (instance == null) { + instances.get().remove(type); + } else { + assert type.isInstance(instance) : "Invald instance type"; + CurrentInstance previousInstance = instances.get().put(type, + new CurrentInstance(instance, inheritable)); + if (previousInstance != null) { + assert previousInstance.inheritable == inheritable : "Inheritable status mismatch for " + + type; + } + } + } + + /** + * Clears all current instances. + */ + public static void clearAll() { + instances.get().clear(); + } +} diff --git a/server/tests/src/com/vaadin/server/TestAbstractApplicationServletStaticFilesLocation.java b/server/tests/src/com/vaadin/server/TestAbstractApplicationServletStaticFilesLocation.java index b936cae1a5..2aa8d04364 100644 --- a/server/tests/src/com/vaadin/server/TestAbstractApplicationServletStaticFilesLocation.java +++ b/server/tests/src/com/vaadin/server/TestAbstractApplicationServletStaticFilesLocation.java @@ -13,6 +13,7 @@ import javax.servlet.http.HttpServletRequest; import junit.framework.TestCase; +import com.vaadin.DefaultApplicationConfiguration; import com.vaadin.server.VaadinServlet.ServletDeploymentConfiguration; public class TestAbstractApplicationServletStaticFilesLocation extends TestCase { @@ -32,7 +33,8 @@ public class TestAbstractApplicationServletStaticFilesLocation extends TestCase .getDeclaredField("deploymentConfiguration"); f.setAccessible(true); f.set(servlet, new ServletDeploymentConfiguration(servlet, - new Properties())); + new DefaultApplicationConfiguration(servlet.getClass(), + new Properties()))); } diff --git a/server/tests/src/com/vaadin/tests/VaadinClasses.java b/server/tests/src/com/vaadin/tests/VaadinClasses.java index 15e1283e47..b30ddcee7e 100644 --- a/server/tests/src/com/vaadin/tests/VaadinClasses.java +++ b/server/tests/src/com/vaadin/tests/VaadinClasses.java @@ -15,7 +15,7 @@ import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; -import com.vaadin.Application; +import com.vaadin.server.VaadinSession; import com.vaadin.ui.Component; import com.vaadin.ui.ComponentContainer; import com.vaadin.ui.CustomComponent; @@ -144,7 +144,7 @@ public class VaadinClasses { String basePackage, String[] ignoredPackages) throws IOException { List<Class<? extends T>> classes = new ArrayList<Class<? extends T>>(); String basePackageDirName = "/" + basePackage.replace('.', '/'); - URL location = Application.class.getResource(basePackageDirName); + URL location = VaadinSession.class.getResource(basePackageDirName); if (location.getProtocol().equals("file")) { try { File f = new File(location.toURI()); diff --git a/server/tests/src/com/vaadin/tests/data/converter/ConverterFactory.java b/server/tests/src/com/vaadin/tests/data/converter/ConverterFactory.java index 6393d61981..b64514ea14 100644 --- a/server/tests/src/com/vaadin/tests/data/converter/ConverterFactory.java +++ b/server/tests/src/com/vaadin/tests/data/converter/ConverterFactory.java @@ -19,9 +19,9 @@ import java.util.Locale; import junit.framework.TestCase; -import com.vaadin.Application; import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.DefaultConverterFactory; +import com.vaadin.server.VaadinSession; import com.vaadin.ui.TextField; public class ConverterFactory extends TestCase { @@ -65,14 +65,14 @@ public class ConverterFactory extends TestCase { } public void testApplicationConverterFactoryInBackgroundThread() { - Application.setCurrent(null); - final Application appWithCustomIntegerConverter = new Application(); + VaadinSession.setCurrent(null); + final VaadinSession appWithCustomIntegerConverter = new VaadinSession(); appWithCustomIntegerConverter .setConverterFactory(new ConverterFactory42()); TextField tf = new TextField("", "123") { @Override - public Application getApplication() { + public VaadinSession getSession() { return appWithCustomIntegerConverter; }; }; @@ -83,10 +83,10 @@ public class ConverterFactory extends TestCase { } public void testApplicationConverterFactoryForDetachedComponent() { - final Application appWithCustomIntegerConverter = new Application(); + final VaadinSession appWithCustomIntegerConverter = new VaadinSession(); appWithCustomIntegerConverter .setConverterFactory(new ConverterFactory42()); - Application.setCurrent(appWithCustomIntegerConverter); + VaadinSession.setCurrent(appWithCustomIntegerConverter); TextField tf = new TextField("", "123"); tf.setConverter(Integer.class); @@ -96,14 +96,14 @@ public class ConverterFactory extends TestCase { } public void testApplicationConverterFactoryForDifferentThanCurrentApplication() { - final Application fieldAppWithCustomIntegerConverter = new Application(); + final VaadinSession fieldAppWithCustomIntegerConverter = new VaadinSession(); fieldAppWithCustomIntegerConverter .setConverterFactory(new ConverterFactory42()); - Application.setCurrent(new Application()); + VaadinSession.setCurrent(new VaadinSession()); TextField tf = new TextField("", "123") { @Override - public Application getApplication() { + public VaadinSession getSession() { return fieldAppWithCustomIntegerConverter; } }; diff --git a/server/tests/src/com/vaadin/tests/server/TestMimeTypes.java b/server/tests/src/com/vaadin/tests/server/TestMimeTypes.java index dc730f6cc4..12e5b09632 100644 --- a/server/tests/src/com/vaadin/tests/server/TestMimeTypes.java +++ b/server/tests/src/com/vaadin/tests/server/TestMimeTypes.java @@ -2,21 +2,12 @@ package com.vaadin.tests.server; import junit.framework.TestCase; -import com.vaadin.Application; import com.vaadin.server.ClassResource; import com.vaadin.ui.Embedded; public class TestMimeTypes extends TestCase { public void testEmbeddedPDF() { - Application app = new Application() { - - @Override - public void init() { - // TODO Auto-generated method stub - - } - }; Embedded e = new Embedded("A pdf", new ClassResource("file.pddf")); assertEquals("Invalid mimetype", "application/octet-stream", e.getMimeType()); diff --git a/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java b/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java index f3b367483a..0dd7efa507 100644 --- a/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java +++ b/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java @@ -4,9 +4,9 @@ import junit.framework.TestCase; import org.easymock.EasyMock; -import com.vaadin.Application; import com.vaadin.server.CommunicationManager; import com.vaadin.server.StreamVariable; +import com.vaadin.server.VaadinSession; import com.vaadin.server.WrappedRequest; import com.vaadin.ui.UI; import com.vaadin.ui.Upload; @@ -21,7 +21,7 @@ public class TestStreamVariableMapping extends TestCase { @Override protected void setUp() throws Exception { - final Application application = new Application(); + final VaadinSession application = new VaadinSession(); final UI uI = new UI() { @Override protected void init(WrappedRequest request) { @@ -30,7 +30,7 @@ public class TestStreamVariableMapping extends TestCase { } @Override - public Application getApplication() { + public VaadinSession getSession() { return application; } }; @@ -66,12 +66,7 @@ public class TestStreamVariableMapping extends TestCase { } private CommunicationManager createCommunicationManager() { - return new CommunicationManager(new Application() { - @Override - public void init() { - // TODO Auto-generated method stub - } - }); + return new CommunicationManager(new VaadinSession()); } } diff --git a/server/tests/src/com/vaadin/tests/server/TransactionListenersConcurrency.java b/server/tests/src/com/vaadin/tests/server/TransactionListenersConcurrency.java deleted file mode 100644 index 05ffcd1e36..0000000000 --- a/server/tests/src/com/vaadin/tests/server/TransactionListenersConcurrency.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.vaadin.tests.server; - -import static org.easymock.EasyMock.createMock; - -import java.lang.Thread.UncaughtExceptionHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Properties; -import java.util.Random; - -import javax.servlet.http.HttpSession; - -import junit.framework.TestCase; - -import org.easymock.EasyMock; - -import com.vaadin.Application; -import com.vaadin.Application.ApplicationStartEvent; -import com.vaadin.server.ApplicationContext; -import com.vaadin.server.DeploymentConfiguration; -import com.vaadin.server.ServletApplicationContext; - -public class TransactionListenersConcurrency extends TestCase { - - /** - * This test starts N threads concurrently. Each thread creates an - * application which adds a transaction listener to the context. A - * transaction is then started for each application. Some semi-random delays - * are included so that calls to addTransactionListener and - * WebApplicationContext.startTransaction are mixed. - * - */ - public void testTransactionListeners() throws Exception { - final List<Throwable> exceptions = new ArrayList<Throwable>(); - - HttpSession session = createSession(); - final ServletApplicationContext context = ServletApplicationContext - .getApplicationContext(session); - List<Thread> threads = new ArrayList<Thread>(); - - for (int i = 0; i < 5; i++) { - Thread t = new Thread(new Runnable() { - - @Override - public void run() { - Application app = new Application() { - - @Override - public void init() { - // Sleep 0-1000ms so another transaction has time to - // start before we add the transaction listener. - try { - Thread.sleep((long) (1000 * new Random() - .nextDouble())); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - getContext().addTransactionListener( - new DelayTransactionListener(2000)); - } - - }; - - // Start the application so the transaction listener is - // called later on. - try { - DeploymentConfiguration dc = EasyMock - .createMock(DeploymentConfiguration.class); - EasyMock.expect(dc.isProductionMode()).andReturn(true); - EasyMock.expect(dc.getInitParameters()).andReturn( - new Properties()); - EasyMock.replay(dc); - - app.start(new ApplicationStartEvent(new URL( - "http://localhost/"), dc, context)); - } catch (MalformedURLException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - try { - // Call the transaction listener using reflection as - // startTransaction is protected. - - Method m = ApplicationContext.class.getDeclaredMethod( - "startTransaction", Application.class, - Object.class); - m.setAccessible(true); - m.invoke(context, app, null); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - }); - - threads.add(t); - t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { - - @Override - public void uncaughtException(Thread t, Throwable e) { - if (e.getCause() != null) { - e = e.getCause(); - } - exceptions.add(e); - } - }); - } - - // Start the threads and wait for all of them to finish - for (Thread t : threads) { - t.start(); - } - int running = threads.size(); - - while (running > 0) { - for (Iterator<Thread> i = threads.iterator(); i.hasNext();) { - Thread t = i.next(); - if (!t.isAlive()) { - running--; - i.remove(); - } - } - } - - for (Throwable t : exceptions) { - if (t instanceof InvocationTargetException) { - t = t.getCause(); - } - if (t != null) { - t.printStackTrace(System.err); - fail(t.getClass().getName()); - } - } - - System.out.println("Done, all ok"); - - } - - /** - * Creates a HttpSession mock - * - */ - private static HttpSession createSession() { - HttpSession session = createMock(HttpSession.class); - EasyMock.expect( - session.getAttribute(ServletApplicationContext.class.getName())) - .andReturn(null).anyTimes(); - session.setAttribute( - EasyMock.eq(ServletApplicationContext.class.getName()), - EasyMock.anyObject()); - - EasyMock.replay(session); - return session; - } - - /** - * A transaction listener that just sleeps for the given amount of time in - * transactionStart and transactionEnd. - * - */ - public static class DelayTransactionListener implements - ApplicationContext.TransactionListener { - - private int delay; - - public DelayTransactionListener(int delay) { - this.delay = delay; - } - - @Override - public void transactionStart(Application application, - Object transactionData) { - try { - Thread.sleep(delay); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - } - - @Override - public void transactionEnd(Application application, - Object transactionData) { - try { - Thread.sleep(delay); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - } - } - -} diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java index 57af748247..b48ad62bcc 100644 --- a/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java +++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java @@ -4,10 +4,10 @@ import java.util.Locale; import junit.framework.TestCase; -import com.vaadin.Application; import com.vaadin.data.util.MethodProperty; import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.StringToIntegerConverter; +import com.vaadin.server.VaadinSession; import com.vaadin.tests.data.bean.Address; import com.vaadin.tests.data.bean.Country; import com.vaadin.tests.data.bean.Person; @@ -186,11 +186,11 @@ public class AbstractFieldValueConversions extends TestCase { } public void testNumberDoubleConverterChange() { - final Application a = new Application(); - Application.setCurrent(a); + final VaadinSession a = new VaadinSession(); + VaadinSession.setCurrent(a); TextField tf = new TextField() { @Override - public Application getApplication() { + public VaadinSession getSession() { return a; } }; diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java index 25430fc9a5..a1b6541ee0 100644 --- a/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java +++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java @@ -5,8 +5,8 @@ import java.util.Locale; import junit.framework.TestCase; -import com.vaadin.Application; import com.vaadin.data.util.MethodProperty; +import com.vaadin.server.VaadinSession; import com.vaadin.tests.data.bean.Address; import com.vaadin.tests.data.bean.Country; import com.vaadin.tests.data.bean.Person; @@ -26,8 +26,8 @@ public class DefaultConverterFactory extends TestCase { } public void testDefaultNumberConversion() { - Application app = new Application(); - Application.setCurrent(app); + VaadinSession app = new VaadinSession(); + VaadinSession.setCurrent(app); TextField tf = new TextField(); tf.setLocale(new Locale("en", "US")); tf.setPropertyDataSource(new MethodProperty<Integer>(paulaBean, diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java index 18567b62f0..c9f579a887 100644 --- a/server/tests/src/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java +++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java @@ -4,10 +4,10 @@ import static org.junit.Assert.assertEquals; import org.junit.Test; -import com.vaadin.Application; import com.vaadin.data.Property; import com.vaadin.data.util.AbstractProperty; import com.vaadin.data.util.converter.Converter.ConversionException; +import com.vaadin.server.VaadinSession; import com.vaadin.server.WrappedRequest; import com.vaadin.ui.AbstractField; import com.vaadin.ui.UI; @@ -18,7 +18,7 @@ public class RemoveListenersOnDetach { int numReadOnlyChanges = 0; AbstractField field = new AbstractField() { - final private Application application = new Application() { + final private VaadinSession application = new VaadinSession() { }; private UI uI = new UI() { @@ -29,7 +29,7 @@ public class RemoveListenersOnDetach { } @Override - public Application getApplication() { + public VaadinSession getSession() { return application; } @@ -59,7 +59,7 @@ public class RemoveListenersOnDetach { }; @Override - public Application getApplication() { + public VaadinSession getSession() { return application; }; }; diff --git a/server/tests/src/com/vaadin/tests/server/component/label/LabelConverters.java b/server/tests/src/com/vaadin/tests/server/component/label/LabelConverters.java index 48279c7c88..22cd450af7 100644 --- a/server/tests/src/com/vaadin/tests/server/component/label/LabelConverters.java +++ b/server/tests/src/com/vaadin/tests/server/component/label/LabelConverters.java @@ -17,9 +17,9 @@ package com.vaadin.tests.server.component.label; import junit.framework.TestCase; -import com.vaadin.Application; import com.vaadin.data.Property; import com.vaadin.data.util.MethodProperty; +import com.vaadin.server.VaadinSession; import com.vaadin.tests.data.bean.Person; import com.vaadin.ui.Label; @@ -37,7 +37,7 @@ public class LabelConverters extends TestCase { } public void testIntegerDataSource() { - Application.setCurrent(new Application()); + VaadinSession.setCurrent(new VaadinSession()); Label l = new Label("Foo"); Property ds = new MethodProperty<Integer>(Person.createTestPerson1(), "age"); diff --git a/server/tests/src/com/vaadin/tests/server/component/root/CustomUIClassLoader.java b/server/tests/src/com/vaadin/tests/server/component/root/CustomUIClassLoader.java index 89da55a31f..acc2ed3b14 100644 --- a/server/tests/src/com/vaadin/tests/server/component/root/CustomUIClassLoader.java +++ b/server/tests/src/com/vaadin/tests/server/component/root/CustomUIClassLoader.java @@ -8,11 +8,13 @@ import junit.framework.TestCase; import org.easymock.EasyMock; -import com.vaadin.Application; -import com.vaadin.Application.ApplicationStartEvent; +import com.vaadin.DefaultApplicationConfiguration; +import com.vaadin.server.ApplicationConfiguration; import com.vaadin.server.DefaultUIProvider; import com.vaadin.server.DeploymentConfiguration; +import com.vaadin.server.VaadinSession; import com.vaadin.server.WrappedRequest; +import com.vaadin.server.VaadinSession.ApplicationStartEvent; import com.vaadin.ui.UI; public class CustomUIClassLoader extends TestCase { @@ -51,7 +53,7 @@ public class CustomUIClassLoader extends TestCase { * if thrown */ public void testWithNullClassLoader() throws Exception { - Application application = createStubApplication(); + VaadinSession application = createStubApplication(); application.start(new ApplicationStartEvent(null, createConfigurationMock(), null)); @@ -62,15 +64,11 @@ public class CustomUIClassLoader extends TestCase { assertEquals(MyUI.class, uiClass); } - private static DeploymentConfiguration createConfigurationMock() { - DeploymentConfiguration configurationMock = EasyMock - .createMock(DeploymentConfiguration.class); - EasyMock.expect(configurationMock.isProductionMode()).andReturn(false); - EasyMock.expect(configurationMock.getInitParameters()).andReturn( - new Properties()); - - EasyMock.replay(configurationMock); - return configurationMock; + private static ApplicationConfiguration createConfigurationMock() { + Properties properties = new Properties(); + properties.put(VaadinSession.UI_PARAMETER, MyUI.class.getName()); + return new DefaultApplicationConfiguration(CustomUIClassLoader.class, + properties); } private static WrappedRequest createRequestMock(ClassLoader classloader) { @@ -99,7 +97,7 @@ public class CustomUIClassLoader extends TestCase { public void testWithClassLoader() throws Exception { LoggingClassLoader loggingClassLoader = new LoggingClassLoader(); - Application application = createStubApplication(); + VaadinSession application = createStubApplication(); application.start(new ApplicationStartEvent(null, createConfigurationMock(), null)); @@ -114,15 +112,11 @@ public class CustomUIClassLoader extends TestCase { } - private Application createStubApplication() { - return new Application() { + private VaadinSession createStubApplication() { + return new VaadinSession() { @Override - public String getProperty(String name) { - if (name.equals(UI_PARAMETER)) { - return MyUI.class.getName(); - } else { - return super.getProperty(name); - } + public ApplicationConfiguration getConfiguration() { + return createConfigurationMock(); } }; } diff --git a/server/tests/src/com/vaadin/tests/server/component/window/AddRemoveSubWindow.java b/server/tests/src/com/vaadin/tests/server/component/window/AddRemoveSubWindow.java index 88bc28bbc8..2e59f9ee41 100644 --- a/server/tests/src/com/vaadin/tests/server/component/window/AddRemoveSubWindow.java +++ b/server/tests/src/com/vaadin/tests/server/component/window/AddRemoveSubWindow.java @@ -13,7 +13,7 @@ import com.vaadin.ui.Window; public class AddRemoveSubWindow { - public class TestApp extends Application.LegacyApplication { + public class TestApp extends Application { @Override public void init() { diff --git a/server/tests/src/com/vaadin/tests/server/component/window/AttachDetachWindow.java b/server/tests/src/com/vaadin/tests/server/component/window/AttachDetachWindow.java index 0fb0765c9f..ec722fdffb 100644 --- a/server/tests/src/com/vaadin/tests/server/component/window/AttachDetachWindow.java +++ b/server/tests/src/com/vaadin/tests/server/component/window/AttachDetachWindow.java @@ -5,7 +5,8 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; -import com.vaadin.Application; +import com.vaadin.server.ClientConnector; +import com.vaadin.server.VaadinSession; import com.vaadin.server.WrappedRequest; import com.vaadin.ui.Label; import com.vaadin.ui.UI; @@ -14,7 +15,7 @@ import com.vaadin.ui.Window; public class AttachDetachWindow { - private Application testApp = new Application(); + private VaadinSession testApp = new VaadinSession(); private interface TestContainer { public boolean attachCalled(); @@ -23,7 +24,7 @@ public class AttachDetachWindow { public TestContent getTestContent(); - public Application getApplication(); + public VaadinSession getSession(); } private class TestWindow extends Window implements TestContainer { @@ -61,6 +62,11 @@ public class AttachDetachWindow { public TestContent getTestContent() { return testContent; } + + @Override + public VaadinSession getSession() { + return super.getSession(); + } } private class TestContent extends VerticalLayout { @@ -155,7 +161,7 @@ public class AttachDetachWindow { assertUnattached(sub); // attaching main should recurse to sub - main.setApplication(testApp); + main.setSession(testApp); assertAttached(main); assertAttached(sub); } @@ -165,7 +171,7 @@ public class AttachDetachWindow { assertUnattached(main); assertUnattached(sub); - main.setApplication(testApp); + main.setSession(testApp); assertAttached(main); assertUnattached(sub); @@ -177,7 +183,7 @@ public class AttachDetachWindow { @Test public void removeSubWindowBeforeDetachingMainWindow() { - main.setApplication(testApp); + main.setSession(testApp); main.addWindow(sub); // sub should be detached when removing from attached main @@ -186,18 +192,18 @@ public class AttachDetachWindow { assertDetached(sub); // main detach should recurse to sub - main.setApplication(null); + main.setSession(null); assertDetached(main); assertDetached(sub); } @Test public void removeSubWindowAfterDetachingMainWindow() { - main.setApplication(testApp); + main.setSession(testApp); main.addWindow(sub); // main detach should recurse to sub - main.setApplication(null); + main.setSession(null); assertDetached(main); assertDetached(sub); @@ -219,22 +225,31 @@ public class AttachDetachWindow { assertTrue("window child attach not called", testContent.childAttachCalled); - assertSame("window not attached", win.getApplication(), testApp); - assertSame("window content not attached", testContent.getApplication(), - testApp); - assertSame("window children not attached", - testContent.child.getApplication(), testApp); + assertSame("window not attached", win.getSession(), testApp); + assertSame("window content not attached", testContent.getUI() + .getSession(), testApp); + assertSame("window children not attached", testContent.child.getUI() + .getSession(), testApp); } /** * Asserts that win and its children are not attached. */ private void assertUnattached(TestContainer win) { - assertSame("window not detached", win.getApplication(), null); - assertSame("window content not detached", win.getTestContent() - .getApplication(), null); + assertSame("window not detached", win.getSession(), null); + assertSame("window content not detached", + getVaadinSession(win.getTestContent()), null); assertSame("window children not detached", - win.getTestContent().child.getApplication(), null); + getVaadinSession(win.getTestContent().child), null); + } + + private VaadinSession getVaadinSession(ClientConnector testContainer) { + UI ui = testContainer.getUI(); + if (ui != null) { + return ui.getSession(); + } else { + return null; + } } /** |