diff options
Diffstat (limited to 'src/com/vaadin/Application.java')
-rw-r--r-- | src/com/vaadin/Application.java | 2426 |
1 files changed, 0 insertions, 2426 deletions
diff --git a/src/com/vaadin/Application.java b/src/com/vaadin/Application.java deleted file mode 100644 index 1d31410185..0000000000 --- a/src/com/vaadin/Application.java +++ /dev/null @@ -1,2426 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ - -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.EventListener; -import java.util.EventObject; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.LinkedList; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.vaadin.annotations.EagerInit; -import com.vaadin.annotations.Theme; -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.service.ApplicationContext; -import com.vaadin.terminal.AbstractErrorMessage; -import com.vaadin.terminal.ApplicationResource; -import com.vaadin.terminal.CombinedRequest; -import com.vaadin.terminal.DeploymentConfiguration; -import com.vaadin.terminal.RequestHandler; -import com.vaadin.terminal.Terminal; -import com.vaadin.terminal.VariableOwner; -import com.vaadin.terminal.WrappedRequest; -import com.vaadin.terminal.WrappedRequest.BrowserDetails; -import com.vaadin.terminal.WrappedResponse; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.server.AbstractApplicationServlet; -import com.vaadin.terminal.gwt.server.BootstrapFragmentResponse; -import com.vaadin.terminal.gwt.server.BootstrapListener; -import com.vaadin.terminal.gwt.server.BootstrapPageResponse; -import com.vaadin.terminal.gwt.server.BootstrapResponse; -import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent; -import com.vaadin.terminal.gwt.server.ClientConnector; -import com.vaadin.terminal.gwt.server.WebApplicationContext; -import com.vaadin.tools.ReflectTools; -import com.vaadin.ui.AbstractComponent; -import com.vaadin.ui.AbstractField; -import com.vaadin.ui.Root; -import com.vaadin.ui.Table; -import com.vaadin.ui.Window; - -/** - * <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.terminal.Terminal terminal} is used. The terminal - * always defines a default theme. - * </p> - * - * @author Vaadin Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ -@SuppressWarnings("serial") -public class Application implements Terminal.ErrorListener, Serializable { - - /** - * The name of the parameter that is by default used in e.g. web.xml to - * define the name of the default {@link Root} class. - */ - public static final String ROOT_PARAMETER = "root"; - - 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 Root.LegacyWindow mainWindow; - private String theme; - - private Map<String, Root.LegacyWindow> legacyRootNames = new HashMap<String, Root.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 root to set as the default window - */ - public void setMainWindow(Root.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"); - } - 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 root used as the default window - */ - public Root.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)}. - * - * {@inheritDoc} - * - * @see #getWindow(String) - * @see Application#getRoot(WrappedRequest) - */ - - @Override - public Root.LegacyWindow getRoot(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); - } - } - Root.LegacyWindow window = getWindow(name); - if (window != null) { - return window; - } - return mainWindow; - } - - /** - * Sets the application's theme. - * <p> - * Note that this theme can be overridden for a specific root with - * {@link Application#getThemeForRoot(Root)}. 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 roots 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 getThemeForRoot(Root root) { - return theme; - } - - /** - * <p> - * Gets a root 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 root corresponding to the name, or <code>null</code> to use - * the default window - */ - public Root.LegacyWindow getWindow(String name) { - return legacyRootNames.get(name); - } - - /** - * Counter to get unique names for windows with no explicit name - */ - private int namelessRootIndex = 0; - - /** - * Adds a new browser level window to this application. Please note that - * Root doesn't have a name that is used in the URL - to add a named - * window you should instead use {@link #addWindow(Root, String)} - * - * @param root - * the root window to add to the application - * @return returns the name that has been assigned to the window - * - * @see #addWindow(Root, String) - */ - public void addWindow(Root.LegacyWindow root) { - if (root.getName() == null) { - String name = Integer.toString(namelessRootIndex++); - root.setName(name); - } - - legacyRootNames.put(root.getName(), root); - root.setApplication(this); - } - - /** - * Removes the specified window from the application. This also removes - * all name mappings for the window (see - * {@link #addWindow(Root, String) and #getWindowName(Root)}. - * - * <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 root - * the root to remove - */ - public void removeWindow(Root.LegacyWindow root) { - for (Entry<String, Root.LegacyWindow> entry : legacyRootNames - .entrySet()) { - if (entry.getValue() == root) { - legacyRootNames.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<Root.LegacyWindow> getWindows() { - return Collections.unmodifiableCollection(legacyRootNames.values()); - } - } - - /** - * 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 Properties applicationProperties; - - private final ApplicationContext context; - - private final boolean productionMode; - - /** - * @param applicationUrl - * the URL the application should respond to. - * @param applicationProperties - * the Application properties as specified by the deployment - * configuration. - * @param context - * the context application will be running in. - * @param productionMode - * flag indicating whether the application is running in - * production mode. - */ - public ApplicationStartEvent(URL applicationUrl, - Properties applicationProperties, ApplicationContext context, - boolean productionMode) { - this.applicationUrl = applicationUrl; - this.applicationProperties = applicationProperties; - this.context = context; - this.productionMode = productionMode; - } - - /** - * 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; - } - - /** - * Gets the Application properties as specified by the deployment - * configuration. - * - * @return the properties configured for the applciation. - * - * @see Application#getProperty(String) - */ - public Properties getApplicationProperties() { - return applicationProperties; - } - - /** - * Gets the context application will be running in. - * - * @return the context application will be running in. - * - * @see Application#getContext() - */ - public ApplicationContext getContext() { - return context; - } - - /** - * Checks whether the application is running in production mode. - * - * @return <code>true</code> if in production mode, else - * <code>false</code> - * - * @see Application#isProductionMode() - */ - public boolean isProductionMode() { - return productionMode; - } - } - - private final static Logger logger = Logger.getLogger(Application.class - .getName()); - - /** - * Application context the application is running in. - */ - private ApplicationContext context; - - /** - * The current user or <code>null</code> if no user has logged in. - */ - private Object user; - - /** - * The application's URL. - */ - private URL applicationUrl; - - /** - * Application status. - */ - private volatile boolean applicationIsRunning = false; - - /** - * Application properties. - */ - private Properties properties; - - /** - * Default locale of the application. - */ - private Locale locale; - - /** - * List of listeners listening user changes. - */ - private LinkedList<UserChangeListener> userChangeListeners = null; - - /** - * Application resource mapping: key <-> resource. - */ - private final Hashtable<ApplicationResource, String> resourceKeyMap = new Hashtable<ApplicationResource, String>(); - - private final Hashtable<String, ApplicationResource> keyResourceMap = new Hashtable<String, ApplicationResource>(); - - private long lastResourceKeyNumber = 0; - - /** - * URL where the user is redirected to on application close, or null if - * application is just closed without redirection. - */ - private String logoutURL = null; - - /** - * The default SystemMessages (read-only). Change by overriding - * getSystemMessages() and returning CustomizedSystemMessages - */ - private static final SystemMessages DEFAULT_SYSTEM_MESSAGES = new SystemMessages(); - - /** - * 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 nextRootId = 0; - private Map<Integer, Root> roots = new HashMap<Integer, Root>(); - - private boolean productionMode = true; - - private final Map<String, Integer> retainOnRefreshRoots = new HashMap<String, Integer>(); - - private final EventRouter eventRouter = new EventRouter(); - - /** - * Keeps track of which roots have been inited. - * <p> - * TODO Investigate whether this might be derived from the different states - * in getRootForRrequest. - * </p> - */ - private Set<Integer> initedRoots = new HashSet<Integer>(); - - /** - * Gets the user of the application. - * - * <p> - * Vaadin doesn't define of use user object in any way - it only provides - * this getter and setter methods for convenience. The user is any object - * that has been stored to the application with {@link #setUser(Object)}. - * </p> - * - * @return the User of the application. - */ - public Object getUser() { - return user; - } - - /** - * <p> - * Sets the user of the application instance. An application instance may - * have a user associated to it. This can be set in login procedure or - * application initialization. - * </p> - * <p> - * A component performing the user login procedure can assign the user - * property of the application and make the user object available to other - * components of the application. - * </p> - * <p> - * Vaadin doesn't define of use user object in any way - it only provides - * getter and setter methods for convenience. The user reference stored to - * the application can be read with {@link #getUser()}. - * </p> - * - * @param user - * the new user. - */ - public void setUser(Object user) { - final Object prevUser = this.user; - if (user == prevUser || (user != null && user.equals(prevUser))) { - return; - } - - this.user = user; - if (userChangeListeners != null) { - final Object[] listeners = userChangeListeners.toArray(); - final UserChangeEvent event = new UserChangeEvent(this, user, - prevUser); - for (int i = 0; i < listeners.length; i++) { - ((UserChangeListener) listeners[i]) - .applicationUserChanged(event); - } - } - } - - /** - * 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, 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. - * </p> - * . - */ - public void close() { - applicationIsRunning = false; - } - - /** - * 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(); - productionMode = event.isProductionMode(); - properties = event.getApplicationProperties(); - context = event.getContext(); - 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 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 properties.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 properties.getProperty(name); - } - - /** - * Adds new resource to the application. The resource can be accessed by the - * user of the application. - * - * @param resource - * the resource to add. - */ - public void addResource(ApplicationResource resource) { - - // Check if the resource is already mapped - if (resourceKeyMap.containsKey(resource)) { - return; - } - - // Generate key - final String key = String.valueOf(++lastResourceKeyNumber); - - // Add the resource to mappings - resourceKeyMap.put(resource, key); - keyResourceMap.put(key, resource); - } - - /** - * Removes the resource from the application. - * - * @param resource - * the resource to remove. - */ - public void removeResource(ApplicationResource resource) { - final Object key = resourceKeyMap.get(resource); - if (key != null) { - resourceKeyMap.remove(resource); - keyResourceMap.remove(key); - } - } - - /** - * Gets the relative uri of the resource. This method is intended to be - * called only be the terminal implementation. - * - * This method can only be called from within the processing of a UIDL - * request, not from a background thread. - * - * @param resource - * the resource to get relative location. - * @return the relative uri of the resource or null if called in a - * background thread - * - * @deprecated this method is intended to be used by the terminal only. It - * may be removed or moved in the future. - */ - @Deprecated - public String getRelativeLocation(ApplicationResource resource) { - - // Gets the key - final String key = resourceKeyMap.get(resource); - - // If the resource is not registered, return null - if (key == null) { - return null; - } - - return context.generateApplicationResourceURL(resource, key); - } - - /** - * 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; - } - - /** - * <p> - * An event that characterizes a change in the current selection. - * </p> - * Application user change event sent when the setUser is called to change - * the current user of the application. - * - * @version - * @VERSION@ - * @since 3.0 - */ - public class UserChangeEvent extends java.util.EventObject { - - /** - * New user of the application. - */ - private final Object newUser; - - /** - * Previous user of the application. - */ - private final Object prevUser; - - /** - * Constructor for user change event. - * - * @param source - * the application source. - * @param newUser - * the new User. - * @param prevUser - * the previous User. - */ - public UserChangeEvent(Application source, Object newUser, - Object prevUser) { - super(source); - this.newUser = newUser; - this.prevUser = prevUser; - } - - /** - * Gets the new user of the application. - * - * @return the new User. - */ - public Object getNewUser() { - return newUser; - } - - /** - * Gets the previous user of the application. - * - * @return the previous Vaadin user, if user has not changed ever on - * application it returns <code>null</code> - */ - public Object getPreviousUser() { - return prevUser; - } - - /** - * Gets the application where the user change occurred. - * - * @return the Application. - */ - public Application getApplication() { - return (Application) getSource(); - } - } - - /** - * The <code>UserChangeListener</code> interface for listening application - * user changes. - * - * @version - * @VERSION@ - * @since 3.0 - */ - public interface UserChangeListener extends EventListener, Serializable { - - /** - * The <code>applicationUserChanged</code> method Invoked when the - * application user has changed. - * - * @param event - * the change event. - */ - public void applicationUserChanged(Application.UserChangeEvent event); - } - - /** - * Adds the user change listener. - * - * This allows one to get notification each time {@link #setUser(Object)} is - * called. - * - * @param listener - * the user change listener to add. - */ - public void addListener(UserChangeListener listener) { - if (userChangeListeners == null) { - userChangeListeners = new LinkedList<UserChangeListener>(); - } - userChangeListeners.add(listener); - } - - /** - * Removes the user change listener. - * - * @param listener - * the user change listener to remove. - */ - public void removeListener(UserChangeListener listener) { - if (userChangeListeners == null) { - return; - } - userChangeListeners.remove(listener); - if (userChangeListeners.isEmpty()) { - userChangeListeners = null; - } - } - - /** - * Window detach event. - * - * This event is sent each time a window is removed from the application - * with {@link com.vaadin.Application#removeWindow(Window)}. - */ - public class WindowDetachEvent extends EventObject { - - private final Window window; - - /** - * Creates a event. - * - * @param window - * the Detached window. - */ - public WindowDetachEvent(Window window) { - super(Application.this); - 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 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 class WindowAttachEvent extends EventObject { - - private final Window window; - - /** - * Creates a event. - * - * @param window - * the Attached window. - */ - public WindowAttachEvent(Window window) { - super(Application.this); - 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(); - } - } - - /** - * 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; - } - - /** - * Gets the SystemMessages for this application. SystemMessages are used to - * notify the user of various critical situations that can occur, such as - * session expiration, client/server out of sync, and internal server error. - * - * You can customize the messages by "overriding" this method and returning - * {@link CustomizedSystemMessages}. To "override" this method, re-implement - * this method in your application (the class that extends - * {@link Application}). Even though overriding static methods is not - * possible in Java, Vaadin selects to call the static method from the - * subclass instead of the original {@link #getSystemMessages()} if such a - * method exists. - * - * @return the SystemMessages for this application - */ - public static SystemMessages getSystemMessages() { - return DEFAULT_SYSTEM_MESSAGES; - } - - /** - * <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.terminal.Terminal.ErrorListener#terminalError(com.vaadin.terminal.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 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 WebApplicationContext} - - * 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; - } - - /** - * Override this method to return correct version number of your - * Application. Version information is delivered for example to Testing - * Tools test results. By default this returns a string "NONVERSIONED". - * - * @return version string - */ - public String getVersion() { - return "NONVERSIONED"; - } - - /** - * 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 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 a root for a request for which no root is already known. This method - * is called when the framework processes a request that does not originate - * from an existing root 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 root or for picking an - * already created root. If an existing root is picked, care should be taken - * to avoid keeping the same root open in multiple browser windows, as that - * will cause the states to go out of sync. - * </p> - * - * <p> - * If {@link BrowserDetails} are required to create a Root, the - * implementation can throw a {@link RootRequiresMoreInformationException} - * exception. In this case, the framework will instruct the browser to send - * the additional details, whereupon this method is invoked again with the - * browser details present in the wrapped request. Throwing the exception if - * the browser details are already available is not supported. - * </p> - * - * <p> - * The default implementation in {@link Application} creates a new instance - * of the Root class returned by {@link #getRootClassName(WrappedRequest)}, - * which in turn uses the {@value #ROOT_PARAMETER} parameter from web.xml. - * If {@link DeploymentConfiguration#getClassLoader()} for the request - * returns a {@link ClassLoader}, it is used for loading the Root class. - * Otherwise the {@link ClassLoader} used to load this class is used. - * </p> - * - * @param request - * the wrapped request for which a root is needed - * @return a root instance to use for the request - * @throws RootRequiresMoreInformationException - * may be thrown by an implementation to indicate that - * {@link BrowserDetails} are required to create a root - * - * @see #getRootClassName(WrappedRequest) - * @see Root - * @see RootRequiresMoreInformationException - * @see WrappedRequest#getBrowserDetails() - * - * @since 7.0 - */ - protected Root getRoot(WrappedRequest request) - throws RootRequiresMoreInformationException { - String rootClassName = getRootClassName(request); - try { - ClassLoader classLoader = request.getDeploymentConfiguration() - .getClassLoader(); - if (classLoader == null) { - classLoader = getClass().getClassLoader(); - } - Class<? extends Root> rootClass = Class.forName(rootClassName, - true, classLoader).asSubclass(Root.class); - try { - Root root = rootClass.newInstance(); - return root; - } catch (Exception e) { - throw new RuntimeException("Could not instantiate root class " - + rootClassName, e); - } - } catch (ClassNotFoundException e) { - throw new RuntimeException("Could not load root class " - + rootClassName, e); - } - } - - /** - * Provides the name of the <code>Root</code> class that should be used for - * a request. The class must have an accessible no-args constructor. - * <p> - * The default implementation uses the {@value #ROOT_PARAMETER} parameter - * from web.xml. - * </p> - * <p> - * This method is mainly used by the default implementation of - * {@link #getRoot(WrappedRequest)}. If you override that method with your - * own functionality, the results of this method might not be used. - * </p> - * - * @param request - * the request for which a new root is required - * @return the name of the root class to use - * - * @since 7.0 - */ - protected String getRootClassName(WrappedRequest request) { - Object rootClassNameObj = properties.get(ROOT_PARAMETER); - if (rootClassNameObj instanceof String) { - return (String) rootClassNameObj; - } else { - throw new RuntimeException("No " + ROOT_PARAMETER - + " defined in web.xml"); - } - } - - /** - * Finds the theme to use for a specific root. If no specific theme is - * required, <code>null</code> is returned. - * - * TODO Tell what the default implementation does once it does something. - * - * @param root - * the root 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 getThemeForRoot(Root root) { - Theme rootTheme = getAnnotationFor(root.getClass(), Theme.class); - if (rootTheme != null) { - return rootTheme.value(); - } else { - return null; - } - } - - /** - * Finds the widgetset to use for a specific root. If no specific widgetset - * is required, <code>null</code> is returned. - * - * TODO Tell what the default implementation does once it does something. - * - * @param root - * the root to get a widgetset for - * @return the name of the widgetset, or <code>null</code> if the default - * widgetset should be used - * - * @since 7.0 - */ - public String getWidgetsetForRoot(Root root) { - Widgetset rootWidgetset = getAnnotationFor(root.getClass(), - Widgetset.class); - if (rootWidgetset != null) { - return rootWidgetset.value(); - } else { - return null; - } - } - - /** - * Helper to get an annotation for a class. If the annotation is not present - * on the target class, it's superclasses and implemented interfaces are - * also searched for the annotation. - * - * @param type - * the target class from which the annotation should be found - * @param annotationType - * the annotation type to look for - * @return an annotation of the given type, or <code>null</code> if the - * annotation is not present on the class - */ - private static <T extends Annotation> T getAnnotationFor(Class<?> type, - Class<T> annotationType) { - // Find from the class hierarchy - Class<?> currentType = type; - while (currentType != Object.class) { - T annotation = currentType.getAnnotation(annotationType); - if (annotation != null) { - return annotation; - } else { - currentType = currentType.getSuperclass(); - } - } - - // Find from an implemented interface - for (Class<?> iface : type.getInterfaces()) { - T annotation = iface.getAnnotation(annotationType); - if (annotation != null) { - return annotation; - } - } - - return null; - } - - /** - * 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 AbstractApplicationServlet}). - * <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); - } - - /** - * Find an application resource with a given key. - * - * @param key - * The key of the resource - * @return The application resource corresponding to the provided key, or - * <code>null</code> if no resource is registered for the key - * - * @since 7.0 - */ - public ApplicationResource getResource(String key) { - return keyResourceMap.get(key); - } - - /** - * Thread local for keeping track of currently used application instance - * - * @since 7.0 - */ - private static final ThreadLocal<Application> currentApplication = new ThreadLocal<Application>(); - - private boolean rootPreserved = false; - - /** - * 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. - * </p> - * - * @param application - * - * @see #getCurrent() - * @see ThreadLocal - * - * @since 7.0 - */ - public static void setCurrent(Application application) { - currentApplication.set(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 productionMode; - } - - /** - * Finds the {@link Root} to which a particular request belongs. If the - * request originates from an existing Root, that root is returned. In other - * cases, the method attempts to create and initialize a new root and might - * throw a {@link RootRequiresMoreInformationException} if all required - * information is not available. - * <p> - * Please note that this method can also return a newly created - * <code>Root</code> which has not yet been initialized. You can use - * {@link #isRootInitPending(int)} with the root's id ( - * {@link Root#getRootId()} to check whether the initialization is still - * pending. - * </p> - * - * @param request - * the request for which a root is desired - * @return a root belonging to the request - * @throws RootRequiresMoreInformationException - * if no existing root could be found and creating a new root - * requires additional information from the browser - * - * @see #getRoot(WrappedRequest) - * @see RootRequiresMoreInformationException - * - * @since 7.0 - */ - public Root getRootForRequest(WrappedRequest request) - throws RootRequiresMoreInformationException { - Root root = Root.getCurrent(); - if (root != null) { - return root; - } - Integer rootId = getRootId(request); - - synchronized (this) { - BrowserDetails browserDetails = request.getBrowserDetails(); - boolean hasBrowserDetails = browserDetails != null - && browserDetails.getUriFragment() != null; - - root = roots.get(rootId); - - if (root == null && isRootPreserved()) { - // Check for a known root - if (!retainOnRefreshRoots.isEmpty()) { - - Integer retainedRootId; - if (!hasBrowserDetails) { - throw new RootRequiresMoreInformationException(); - } else { - String windowName = browserDetails.getWindowName(); - retainedRootId = retainOnRefreshRoots.get(windowName); - } - - if (retainedRootId != null) { - rootId = retainedRootId; - root = roots.get(rootId); - } - } - } - - if (root == null) { - // Throws exception if root can not yet be created - root = getRoot(request); - - // Initialize some fields for a newly created root - if (root.getApplication() == null) { - root.setApplication(this); - } - if (root.getRootId() < 0) { - - if (rootId == null) { - // Get the next id if none defined - rootId = Integer.valueOf(nextRootId++); - } - root.setRootId(rootId.intValue()); - roots.put(rootId, root); - } - } - - // Set thread local here so it is available in init - Root.setCurrent(root); - - if (!initedRoots.contains(rootId)) { - boolean initRequiresBrowserDetails = isRootPreserved() - || !root.getClass() - .isAnnotationPresent(EagerInit.class); - if (!initRequiresBrowserDetails || hasBrowserDetails) { - root.doInit(request); - - // Remember that this root has been initialized - initedRoots.add(rootId); - - // init() might turn on preserve so do this afterwards - if (isRootPreserved()) { - // Remember this root - String windowName = request.getBrowserDetails() - .getWindowName(); - retainOnRefreshRoots.put(windowName, rootId); - } - } - } - } // end synchronized block - - return root; - } - - /** - * Internal helper to finds the root id for a request. - * - * @param request - * the request to get the root id for - * @return a root id, or <code>null</code> if no root id is defined - * - * @since 7.0 - */ - private static Integer getRootId(WrappedRequest request) { - if (request instanceof CombinedRequest) { - // Combined requests has the rootid parameter in the second request - CombinedRequest combinedRequest = (CombinedRequest) request; - request = combinedRequest.getSecondRequest(); - } - String rootIdString = request - .getParameter(ApplicationConnection.ROOT_ID_PARAMETER); - Integer rootId = rootIdString == null ? null - : new Integer(rootIdString); - return rootId; - } - - /** - * Sets whether the same Root state should be reused if the framework can - * detect that the application is opened in a browser window where it has - * previously been open. The framework attempts to discover this by checking - * the value of window.name in the browser. - * <p> - * NOTE that you should avoid turning this feature on/off on-the-fly when - * the UI is already shown, as it might not be retained as intended. - * </p> - * - * @param rootPreserved - * <code>true</code>if the same Root instance should be reused - * e.g. when the browser window is refreshed. - */ - public void setRootPreserved(boolean rootPreserved) { - this.rootPreserved = rootPreserved; - if (!rootPreserved) { - retainOnRefreshRoots.clear(); - } - } - - /** - * Checks whether the same Root 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. - * - * @return <code>true</code>if the same Root instance should be reused e.g. - * when the browser window is refreshed. - */ - public boolean isRootPreserved() { - return rootPreserved; - } - - /** - * Checks whether there's a pending initialization for the root with the - * given id. - * - * @param rootId - * root id to check for - * @return <code>true</code> of the initialization is pending, - * <code>false</code> if the root id is not registered or if the - * root has already been initialized - * - * @see #getRootForRequest(WrappedRequest) - */ - public boolean isRootInitPending(int rootId) { - return !initedRoots.contains(Integer.valueOf(rootId)); - } - - /** - * Gets all the roots of this application. This includes roots that have - * been requested but not yet initialized. Please note, that roots are not - * automatically removed e.g. if the browser window is closed and that there - * is no way to manually remove a root. Inactive roots 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 roots is planned for an upcoming alpha release of - * Vaadin 7. - * - * @return a collection of roots belonging to this application - * - * @since 7.0 - */ - public Collection<Root> getRoots() { - return Collections.unmodifiableCollection(roots.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 Root with the given id. - * <p> - * This is meant for framework internal use. - * </p> - * - * @param rootId - * The root id - * @return The root with the given id or null if not found - */ - public Root getRootById(int rootId) { - return roots.get(rootId); - } - - public void addBootstrapListener(BootstrapListener listener) { - eventRouter.addListener(BootstrapFragmentResponse.class, listener, - BOOTSTRAP_FRAGMENT_METHOD); - eventRouter.addListener(BootstrapPageResponse.class, listener, - BOOTSTRAP_PAGE_METHOD); - } - - public void removeBootstrapListener(BootstrapListener listener) { - eventRouter.removeListener(BootstrapFragmentResponse.class, listener, - BOOTSTRAP_FRAGMENT_METHOD); - eventRouter.removeListener(BootstrapPageResponse.class, listener, - BOOTSTRAP_PAGE_METHOD); - } - - public void modifyBootstrapResponse(BootstrapResponse response) { - eventRouter.fireEvent(response); - } -} |