/* @VaadinApache2LicenseForJavaFiles@ */ package com.vaadin; import java.io.IOException; import java.io.Serializable; import java.lang.annotation.Annotation; 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.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.ChangeVariablesErrorEvent; import com.vaadin.terminal.gwt.server.ClientConnector; import com.vaadin.terminal.gwt.server.WebApplicationContext; 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; /** *
* 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. *
* *
* 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 Application
* needs to do is implement the init
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.
*
* See the class com.vaadin.demo.HelloWorld
for a simple example of
* a fully working application.
*
* Window access. Application
provides methods to
* list, add and remove the windows it contains.
*
* Execution control. 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. *
* ** Theme selection. 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. *
* * @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"; /** * 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* The main window is the window attached to the application URL ( * {@link #getURL()}) and thus which is show by default to the user. *
** Note that each application must have at least one main window. *
* * @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. *
* Note that this theme can be overridden for a specific root with
* {@link Application#getThemeForRoot(Root)}. Setting theme to be
* null
selects the default theme. For the available theme
* names, see the contents of the VAADIN/themes directory.
*
null
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)}
* * {@inheritDoc} */ @Override public String getThemeForRoot(Root root) { return theme; } /** *
* Gets a root by name. Returns null
if the application is
* not running or it does not contain a window corresponding to the
* name.
*
null
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)}.
*
* * Note that removing window from the application does not close the * browser window - the window is only removed from the server-side. *
* * @param root * the root to remove */ public void removeWindow(Root.LegacyWindow root) { for (Entry* Note that the returned set of windows can not be modified. *
* * @return the unmodifiable collection of windows. */ public Collectionnull
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 true
if in production mode, else
* false
*
* @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 null
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* TODO Investigate whether this might be derived from the different states * in getRootForRrequest. *
*/ private Set* 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)}. *
* * @return the User of the application. */ public Object getUser() { return user; } /** ** 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. *
** 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. *
** 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()}. *
* * @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. * ** 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()}). *
* * @return the application's URL. */ public URL getURL() { return applicationUrl; } /** * Ends the Application. * ** 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. *
* . */ public void close() { applicationIsRunning = false; } /** * Starts the application on the given URL. * ** 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. *
* ** 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}. *
* * @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. * ** Application starts running when its * {@link #start(URL, Properties, ApplicationContext)} method has been * called and stops when the {@link #close()} is called. *
* * @returntrue
if the application is running,
* false
if not.
*/
public boolean isRunning() {
return applicationIsRunning;
}
/**
*
* Main initializer of the application. The init
method is
* called by the framework when the application is started, and it should
* perform whatever initialization operations the application needs.
*
* See {@link #start(URL, Properties, ApplicationContext)} how properties * are defined. *
* * @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 returnsnull
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;
}
/**
* * An event that characterizes a change in the current selection. *
* 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 returnsnull
*/
public Object getPreviousUser() {
return prevUser;
}
/**
* Gets the application where the user change occurred.
*
* @return the Application.
*/
public Application getApplication() {
return (Application) getSource();
}
}
/**
* The UserChangeListener
interface for listening application
* user changes.
*
* @version
* @VERSION@
* @since 3.0
*/
public interface UserChangeListener extends EventListener, Serializable {
/**
* The applicationUserChanged
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 LinkedListnull
, 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. *
* * @return the URL. */ public String getLogoutURL() { return logoutURL; } /** * Sets the URL user is redirected to on application close. If the URL is *null
, 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;
}
/**
*
* Invoked by the terminal on any exception that occurs in application and
* is thrown by the setVariable
to the terminal. The default
* implementation sets the exceptions as ComponentErrors
to the
* component that initiated the exception and prints stack trace to standard
* error stream.
*
* You can safely override this method in your application in order to * direct the errors to some other destination (for example log). *
* * @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. ** 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. *
** 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}. *
* * @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. ** 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 {@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)}. *
** 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. *
* Customize by overriding the static * {@link Application#getSystemMessages()} and returning * {@link CustomizedSystemMessages}. *
** The defaults defined in this class are: *
* 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. *
*
* The default behavior is to show a notification, and restart the
* application the the user clicks the message.
* Instead of restarting the application, you can set a specific URL that
* the user is taken to.
* 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.
*
* The situations are: *
* 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. *
* ** 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. *
* ** 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. *
* * @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 theRoot
class that should be used for
* a request. The class must have an accessible no-args constructor.
* * The default implementation uses the {@value #ROOT_PARAMETER} parameter * from web.xml. *
** 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. *
* * @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,null
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 null
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, null
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 null
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 null
if the
* annotation is not present on the class
*/
private static * 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. *
* * @param request * the wrapped request to get information from * @param response * the response to which data can be written * @return returnstrue
if a {@link RequestHandler} has
* produced a response and false
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* Handlers are called in reverse order of addition, so the most recently * added handler will be called first. *
* * @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 Collectionnull
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 ThreadLocalnull
*
* @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.
* * 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. *
* * @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. *
* Please note that this method can also return a newly created
* Root
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.
*
null
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.
* * 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. *
* * @param rootPreserved *true
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 true
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 true
of the initialization is pending,
* false
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* This is meant for framework internal use. *
* * @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); } }