You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Application.java 81KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin;
  5. import java.io.IOException;
  6. import java.io.Serializable;
  7. import java.lang.annotation.Annotation;
  8. import java.net.SocketException;
  9. import java.net.URL;
  10. import java.util.ArrayList;
  11. import java.util.Collection;
  12. import java.util.Collections;
  13. import java.util.Enumeration;
  14. import java.util.EventListener;
  15. import java.util.EventObject;
  16. import java.util.HashMap;
  17. import java.util.HashSet;
  18. import java.util.Hashtable;
  19. import java.util.Iterator;
  20. import java.util.LinkedList;
  21. import java.util.Locale;
  22. import java.util.Map;
  23. import java.util.Map.Entry;
  24. import java.util.Properties;
  25. import java.util.Set;
  26. import java.util.logging.Level;
  27. import java.util.logging.Logger;
  28. import java.util.regex.Matcher;
  29. import java.util.regex.Pattern;
  30. import com.vaadin.annotations.EagerInit;
  31. import com.vaadin.annotations.Theme;
  32. import com.vaadin.annotations.Widgetset;
  33. import com.vaadin.data.util.converter.Converter;
  34. import com.vaadin.data.util.converter.ConverterFactory;
  35. import com.vaadin.data.util.converter.DefaultConverterFactory;
  36. import com.vaadin.service.ApplicationContext;
  37. import com.vaadin.terminal.AbstractErrorMessage;
  38. import com.vaadin.terminal.ApplicationResource;
  39. import com.vaadin.terminal.CombinedRequest;
  40. import com.vaadin.terminal.RequestHandler;
  41. import com.vaadin.terminal.Terminal;
  42. import com.vaadin.terminal.VariableOwner;
  43. import com.vaadin.terminal.WrappedRequest;
  44. import com.vaadin.terminal.WrappedRequest.BrowserDetails;
  45. import com.vaadin.terminal.WrappedResponse;
  46. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  47. import com.vaadin.terminal.gwt.client.Connector;
  48. import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
  49. import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent;
  50. import com.vaadin.terminal.gwt.server.WebApplicationContext;
  51. import com.vaadin.ui.AbstractComponent;
  52. import com.vaadin.ui.AbstractField;
  53. import com.vaadin.ui.Component;
  54. import com.vaadin.ui.Root;
  55. import com.vaadin.ui.Table;
  56. import com.vaadin.ui.Window;
  57. /**
  58. * <p>
  59. * Base class required for all Vaadin applications. This class provides all the
  60. * basic services required by Vaadin. These services allow external discovery
  61. * and manipulation of the user, {@link com.vaadin.ui.Window windows} and
  62. * themes, and starting and stopping the application.
  63. * </p>
  64. *
  65. * <p>
  66. * As mentioned, all Vaadin applications must inherit this class. However, this
  67. * is almost all of what one needs to do to create a fully functional
  68. * application. The only thing a class inheriting the <code>Application</code>
  69. * needs to do is implement the <code>init</code> method where it creates the
  70. * windows it needs to perform its function. Note that all applications must
  71. * have at least one window: the main window. The first unnamed window
  72. * constructed by an application automatically becomes the main window which
  73. * behaves just like other windows with one exception: when accessing windows
  74. * using URLs the main window corresponds to the application URL whereas other
  75. * windows correspond to a URL gotten by catenating the window's name to the
  76. * application URL.
  77. * </p>
  78. *
  79. * <p>
  80. * See the class <code>com.vaadin.demo.HelloWorld</code> for a simple example of
  81. * a fully working application.
  82. * </p>
  83. *
  84. * <p>
  85. * <strong>Window access.</strong> <code>Application</code> provides methods to
  86. * list, add and remove the windows it contains.
  87. * </p>
  88. *
  89. * <p>
  90. * <strong>Execution control.</strong> This class includes method to start and
  91. * finish the execution of the application. Being finished means basically that
  92. * no windows will be available from the application anymore.
  93. * </p>
  94. *
  95. * <p>
  96. * <strong>Theme selection.</strong> The theme selection process allows a theme
  97. * to be specified at three different levels. When a window's theme needs to be
  98. * found out, the window itself is queried for a preferred theme. If the window
  99. * does not prefer a specific theme, the application containing the window is
  100. * queried. If neither the application prefers a theme, the default theme for
  101. * the {@link com.vaadin.terminal.Terminal terminal} is used. The terminal
  102. * always defines a default theme.
  103. * </p>
  104. *
  105. * @author Vaadin Ltd.
  106. * @version
  107. * @VERSION@
  108. * @since 3.0
  109. */
  110. @SuppressWarnings("serial")
  111. public class Application implements Terminal.ErrorListener, Serializable {
  112. /**
  113. * The name of the parameter that is by default used in e.g. web.xml to
  114. * define the name of the default {@link Root} class.
  115. */
  116. public static final String ROOT_PARAMETER = "root";
  117. /**
  118. * A special application designed to help migrating applications from Vaadin
  119. * 6 to Vaadin 7. The legacy application supports setting a main window,
  120. * adding additional browser level windows and defining the theme for the
  121. * entire application.
  122. *
  123. * @deprecated This class is only intended to ease migration and should not
  124. * be used for new projects.
  125. *
  126. * @since 7.0
  127. */
  128. @Deprecated
  129. public static class LegacyApplication extends Application {
  130. /**
  131. * Ignore initial / and then get everything up to the next /
  132. */
  133. private static final Pattern WINDOW_NAME_PATTERN = Pattern
  134. .compile("^/?([^/]+).*");
  135. private Root.LegacyWindow mainWindow;
  136. private String theme;
  137. private Map<String, Root.LegacyWindow> legacyRootNames = new HashMap<String, Root.LegacyWindow>();
  138. /**
  139. * Sets the main window of this application. Setting window as a main
  140. * window of this application also adds the window to this application.
  141. *
  142. * @param mainWindow
  143. * the root to set as the default window
  144. */
  145. public void setMainWindow(Root.LegacyWindow mainWindow) {
  146. if (this.mainWindow != null) {
  147. throw new IllegalStateException(
  148. "mainWindow has already been set");
  149. }
  150. if (mainWindow.getApplication() == null) {
  151. mainWindow.setApplication(this);
  152. } else if (mainWindow.getApplication() != this) {
  153. throw new IllegalStateException(
  154. "mainWindow is attached to another application");
  155. }
  156. this.mainWindow = mainWindow;
  157. }
  158. /**
  159. * Gets the mainWindow of the application.
  160. *
  161. * <p>
  162. * The main window is the window attached to the application URL (
  163. * {@link #getURL()}) and thus which is show by default to the user.
  164. * </p>
  165. * <p>
  166. * Note that each application must have at least one main window.
  167. * </p>
  168. *
  169. * @return the root used as the default window
  170. */
  171. public Root.LegacyWindow getMainWindow() {
  172. return mainWindow;
  173. }
  174. /**
  175. * This implementation simulates the way of finding a window for a
  176. * request by extracting a window name from the requested path and
  177. * passes that name to {@link #getWindow(String)}.
  178. *
  179. * {@inheritDoc}
  180. *
  181. * @see #getWindow(String)
  182. * @see Application#getRoot(WrappedRequest)
  183. */
  184. @Override
  185. public Root.LegacyWindow getRoot(WrappedRequest request) {
  186. String pathInfo = request.getRequestPathInfo();
  187. String name = null;
  188. if (pathInfo != null && pathInfo.length() > 0) {
  189. Matcher matcher = WINDOW_NAME_PATTERN.matcher(pathInfo);
  190. if (matcher.matches()) {
  191. // Skip the initial slash
  192. name = matcher.group(1);
  193. }
  194. }
  195. Root.LegacyWindow window = getWindow(name);
  196. if (window != null) {
  197. return window;
  198. }
  199. return mainWindow;
  200. }
  201. /**
  202. * Sets the application's theme.
  203. * <p>
  204. * Note that this theme can be overridden for a specific root with
  205. * {@link Application#getThemeForRoot(Root)}. Setting theme to be
  206. * <code>null</code> selects the default theme. For the available theme
  207. * names, see the contents of the VAADIN/themes directory.
  208. * </p>
  209. *
  210. * @param theme
  211. * the new theme for this application.
  212. */
  213. public void setTheme(String theme) {
  214. this.theme = theme;
  215. }
  216. /**
  217. * Gets the application's theme. The application's theme is the default
  218. * theme used by all the roots for which a theme is not explicitly
  219. * defined. If the application theme is not explicitly set,
  220. * <code>null</code> is returned.
  221. *
  222. * @return the name of the application's theme.
  223. */
  224. public String getTheme() {
  225. return theme;
  226. }
  227. /**
  228. * This implementation returns the theme that has been set using
  229. * {@link #setTheme(String)}
  230. * <p>
  231. * {@inheritDoc}
  232. */
  233. @Override
  234. public String getThemeForRoot(Root root) {
  235. return theme;
  236. }
  237. /**
  238. * <p>
  239. * Gets a root by name. Returns <code>null</code> if the application is
  240. * not running or it does not contain a window corresponding to the
  241. * name.
  242. * </p>
  243. *
  244. * @param name
  245. * the name of the requested window
  246. * @return a root corresponding to the name, or <code>null</code> to use
  247. * the default window
  248. */
  249. public Root.LegacyWindow getWindow(String name) {
  250. return legacyRootNames.get(name);
  251. }
  252. /**
  253. * Counter to get unique names for windows with no explicit name
  254. */
  255. private int namelessRootIndex = 0;
  256. /**
  257. * Adds a new browser level window to this application. Please note that
  258. * Root doesn't have a name that is used in the URL - to add a named
  259. * window you should instead use {@link #addWindow(Root, String)}
  260. *
  261. * @param root
  262. * the root window to add to the application
  263. * @return returns the name that has been assigned to the window
  264. *
  265. * @see #addWindow(Root, String)
  266. */
  267. public void addWindow(Root.LegacyWindow root) {
  268. if (root.getName() == null) {
  269. String name = Integer.toString(namelessRootIndex++);
  270. root.setName(name);
  271. }
  272. legacyRootNames.put(root.getName(), root);
  273. root.setApplication(this);
  274. }
  275. /**
  276. * Removes the specified window from the application. This also removes
  277. * all name mappings for the window (see
  278. * {@link #addWindow(Root, String) and #getWindowName(Root)}.
  279. *
  280. * <p>
  281. * Note that removing window from the application does not close the
  282. * browser window - the window is only removed from the server-side.
  283. * </p>
  284. *
  285. * @param root
  286. * the root to remove
  287. */
  288. public void removeWindow(Root.LegacyWindow root) {
  289. for (Entry<String, Root.LegacyWindow> entry : legacyRootNames
  290. .entrySet()) {
  291. if (entry.getValue() == root) {
  292. legacyRootNames.remove(entry.getKey());
  293. }
  294. }
  295. }
  296. /**
  297. * Gets the set of windows contained by the application.
  298. *
  299. * <p>
  300. * Note that the returned set of windows can not be modified.
  301. * </p>
  302. *
  303. * @return the unmodifiable collection of windows.
  304. */
  305. public Collection<Root.LegacyWindow> getWindows() {
  306. return Collections.unmodifiableCollection(legacyRootNames.values());
  307. }
  308. }
  309. private final static Logger logger = Logger.getLogger(Application.class
  310. .getName());
  311. /**
  312. * Application context the application is running in.
  313. */
  314. private ApplicationContext context;
  315. /**
  316. * The current user or <code>null</code> if no user has logged in.
  317. */
  318. private Object user;
  319. /**
  320. * The application's URL.
  321. */
  322. private URL applicationUrl;
  323. /**
  324. * Application status.
  325. */
  326. private volatile boolean applicationIsRunning = false;
  327. /**
  328. * Application properties.
  329. */
  330. private Properties properties;
  331. /**
  332. * Default locale of the application.
  333. */
  334. private Locale locale;
  335. /**
  336. * List of listeners listening user changes.
  337. */
  338. private LinkedList<UserChangeListener> userChangeListeners = null;
  339. /**
  340. * Application resource mapping: key <-> resource.
  341. */
  342. private final Hashtable<ApplicationResource, String> resourceKeyMap = new Hashtable<ApplicationResource, String>();
  343. private final Hashtable<String, ApplicationResource> keyResourceMap = new Hashtable<String, ApplicationResource>();
  344. private long lastResourceKeyNumber = 0;
  345. /**
  346. * URL where the user is redirected to on application close, or null if
  347. * application is just closed without redirection.
  348. */
  349. private String logoutURL = null;
  350. /**
  351. * The default SystemMessages (read-only). Change by overriding
  352. * getSystemMessages() and returning CustomizedSystemMessages
  353. */
  354. private static final SystemMessages DEFAULT_SYSTEM_MESSAGES = new SystemMessages();
  355. /**
  356. * Application wide error handler which is used by default if an error is
  357. * left unhandled.
  358. */
  359. private Terminal.ErrorListener errorHandler = this;
  360. /**
  361. * The converter factory that is used to provide default converters for the
  362. * application.
  363. */
  364. private ConverterFactory converterFactory = new DefaultConverterFactory();
  365. private LinkedList<RequestHandler> requestHandlers = new LinkedList<RequestHandler>();
  366. private int nextRootId = 0;
  367. private Map<Integer, Root> roots = new HashMap<Integer, Root>();
  368. private boolean productionMode = true;
  369. private final Map<String, Integer> retainOnRefreshRoots = new HashMap<String, Integer>();
  370. /**
  371. * Keeps track of which roots have been inited.
  372. * <p>
  373. * TODO Investigate whether this might be derived from the different states
  374. * in getRootForRrequest.
  375. * </p>
  376. */
  377. private Set<Integer> initedRoots = new HashSet<Integer>();
  378. /**
  379. * Gets the user of the application.
  380. *
  381. * <p>
  382. * Vaadin doesn't define of use user object in any way - it only provides
  383. * this getter and setter methods for convenience. The user is any object
  384. * that has been stored to the application with {@link #setUser(Object)}.
  385. * </p>
  386. *
  387. * @return the User of the application.
  388. */
  389. public Object getUser() {
  390. return user;
  391. }
  392. /**
  393. * <p>
  394. * Sets the user of the application instance. An application instance may
  395. * have a user associated to it. This can be set in login procedure or
  396. * application initialization.
  397. * </p>
  398. * <p>
  399. * A component performing the user login procedure can assign the user
  400. * property of the application and make the user object available to other
  401. * components of the application.
  402. * </p>
  403. * <p>
  404. * Vaadin doesn't define of use user object in any way - it only provides
  405. * getter and setter methods for convenience. The user reference stored to
  406. * the application can be read with {@link #getUser()}.
  407. * </p>
  408. *
  409. * @param user
  410. * the new user.
  411. */
  412. public void setUser(Object user) {
  413. final Object prevUser = this.user;
  414. if (user == prevUser || (user != null && user.equals(prevUser))) {
  415. return;
  416. }
  417. this.user = user;
  418. if (userChangeListeners != null) {
  419. final Object[] listeners = userChangeListeners.toArray();
  420. final UserChangeEvent event = new UserChangeEvent(this, user,
  421. prevUser);
  422. for (int i = 0; i < listeners.length; i++) {
  423. ((UserChangeListener) listeners[i])
  424. .applicationUserChanged(event);
  425. }
  426. }
  427. }
  428. /**
  429. * Gets the URL of the application.
  430. *
  431. * <p>
  432. * This is the URL what can be entered to a browser window to start the
  433. * application. Navigating to the application URL shows the main window (
  434. * {@link #getMainWindow()}) of the application. Note that the main window
  435. * can also be shown by navigating to the window url (
  436. * {@link com.vaadin.ui.Window#getURL()}).
  437. * </p>
  438. *
  439. * @return the application's URL.
  440. */
  441. public URL getURL() {
  442. return applicationUrl;
  443. }
  444. /**
  445. * Ends the Application.
  446. *
  447. * <p>
  448. * In effect this will cause the application stop returning any windows when
  449. * asked. When the application is closed, its state is removed from the
  450. * session and the browser window is redirected to the application logout
  451. * url set with {@link #setLogoutURL(String)}. If the logout url has not
  452. * been set, the browser window is reloaded and the application is
  453. * restarted.
  454. * </p>
  455. * .
  456. */
  457. public void close() {
  458. applicationIsRunning = false;
  459. }
  460. /**
  461. * Starts the application on the given URL.
  462. *
  463. * <p>
  464. * This method is called by Vaadin framework when a user navigates to the
  465. * application. After this call the application corresponds to the given URL
  466. * and it will return windows when asked for them. There is no need to call
  467. * this method directly.
  468. * </p>
  469. *
  470. * <p>
  471. * Application properties are defined by servlet configuration object
  472. * {@link javax.servlet.ServletConfig} and they are overridden by
  473. * context-wide initialization parameters
  474. * {@link javax.servlet.ServletContext}.
  475. * </p>
  476. *
  477. * @param applicationUrl
  478. * the URL the application should respond to.
  479. * @param applicationProperties
  480. * the Application properties as specified by the servlet
  481. * configuration.
  482. * @param context
  483. * the context application will be running in.
  484. * @param productionMode
  485. *
  486. */
  487. public void start(URL applicationUrl, Properties applicationProperties,
  488. ApplicationContext context, boolean productionMode) {
  489. this.applicationUrl = applicationUrl;
  490. this.productionMode = productionMode;
  491. properties = applicationProperties;
  492. this.context = context;
  493. init();
  494. applicationIsRunning = true;
  495. }
  496. /**
  497. * Tests if the application is running or if it has been finished.
  498. *
  499. * <p>
  500. * Application starts running when its
  501. * {@link #start(URL, Properties, ApplicationContext)} method has been
  502. * called and stops when the {@link #close()} is called.
  503. * </p>
  504. *
  505. * @return <code>true</code> if the application is running,
  506. * <code>false</code> if not.
  507. */
  508. public boolean isRunning() {
  509. return applicationIsRunning;
  510. }
  511. /**
  512. * <p>
  513. * Main initializer of the application. The <code>init</code> method is
  514. * called by the framework when the application is started, and it should
  515. * perform whatever initialization operations the application needs.
  516. * </p>
  517. */
  518. public void init() {
  519. // Default implementation does nothing
  520. }
  521. /**
  522. * Returns an enumeration of all the names in this application.
  523. *
  524. * <p>
  525. * See {@link #start(URL, Properties, ApplicationContext)} how properties
  526. * are defined.
  527. * </p>
  528. *
  529. * @return an enumeration of all the keys in this property list, including
  530. * the keys in the default property list.
  531. *
  532. */
  533. public Enumeration<?> getPropertyNames() {
  534. return properties.propertyNames();
  535. }
  536. /**
  537. * Searches for the property with the specified name in this application.
  538. * This method returns <code>null</code> if the property is not found.
  539. *
  540. * See {@link #start(URL, Properties, ApplicationContext)} how properties
  541. * are defined.
  542. *
  543. * @param name
  544. * the name of the property.
  545. * @return the value in this property list with the specified key value.
  546. */
  547. public String getProperty(String name) {
  548. return properties.getProperty(name);
  549. }
  550. /**
  551. * Adds new resource to the application. The resource can be accessed by the
  552. * user of the application.
  553. *
  554. * @param resource
  555. * the resource to add.
  556. */
  557. public void addResource(ApplicationResource resource) {
  558. // Check if the resource is already mapped
  559. if (resourceKeyMap.containsKey(resource)) {
  560. return;
  561. }
  562. // Generate key
  563. final String key = String.valueOf(++lastResourceKeyNumber);
  564. // Add the resource to mappings
  565. resourceKeyMap.put(resource, key);
  566. keyResourceMap.put(key, resource);
  567. }
  568. /**
  569. * Removes the resource from the application.
  570. *
  571. * @param resource
  572. * the resource to remove.
  573. */
  574. public void removeResource(ApplicationResource resource) {
  575. final Object key = resourceKeyMap.get(resource);
  576. if (key != null) {
  577. resourceKeyMap.remove(resource);
  578. keyResourceMap.remove(key);
  579. }
  580. }
  581. /**
  582. * Gets the relative uri of the resource. This method is intended to be
  583. * called only be the terminal implementation.
  584. *
  585. * This method can only be called from within the processing of a UIDL
  586. * request, not from a background thread.
  587. *
  588. * @param resource
  589. * the resource to get relative location.
  590. * @return the relative uri of the resource or null if called in a
  591. * background thread
  592. *
  593. * @deprecated this method is intended to be used by the terminal only. It
  594. * may be removed or moved in the future.
  595. */
  596. @Deprecated
  597. public String getRelativeLocation(ApplicationResource resource) {
  598. // Gets the key
  599. final String key = resourceKeyMap.get(resource);
  600. // If the resource is not registered, return null
  601. if (key == null) {
  602. return null;
  603. }
  604. return context.generateApplicationResourceURL(resource, key);
  605. }
  606. /**
  607. * Gets the default locale for this application.
  608. *
  609. * By default this is the preferred locale of the user using the
  610. * application. In most cases it is read from the browser defaults.
  611. *
  612. * @return the locale of this application.
  613. */
  614. public Locale getLocale() {
  615. if (locale != null) {
  616. return locale;
  617. }
  618. return Locale.getDefault();
  619. }
  620. /**
  621. * Sets the default locale for this application.
  622. *
  623. * By default this is the preferred locale of the user using the
  624. * application. In most cases it is read from the browser defaults.
  625. *
  626. * @param locale
  627. * the Locale object.
  628. *
  629. */
  630. public void setLocale(Locale locale) {
  631. this.locale = locale;
  632. }
  633. /**
  634. * <p>
  635. * An event that characterizes a change in the current selection.
  636. * </p>
  637. * Application user change event sent when the setUser is called to change
  638. * the current user of the application.
  639. *
  640. * @version
  641. * @VERSION@
  642. * @since 3.0
  643. */
  644. public class UserChangeEvent extends java.util.EventObject {
  645. /**
  646. * New user of the application.
  647. */
  648. private final Object newUser;
  649. /**
  650. * Previous user of the application.
  651. */
  652. private final Object prevUser;
  653. /**
  654. * Constructor for user change event.
  655. *
  656. * @param source
  657. * the application source.
  658. * @param newUser
  659. * the new User.
  660. * @param prevUser
  661. * the previous User.
  662. */
  663. public UserChangeEvent(Application source, Object newUser,
  664. Object prevUser) {
  665. super(source);
  666. this.newUser = newUser;
  667. this.prevUser = prevUser;
  668. }
  669. /**
  670. * Gets the new user of the application.
  671. *
  672. * @return the new User.
  673. */
  674. public Object getNewUser() {
  675. return newUser;
  676. }
  677. /**
  678. * Gets the previous user of the application.
  679. *
  680. * @return the previous Vaadin user, if user has not changed ever on
  681. * application it returns <code>null</code>
  682. */
  683. public Object getPreviousUser() {
  684. return prevUser;
  685. }
  686. /**
  687. * Gets the application where the user change occurred.
  688. *
  689. * @return the Application.
  690. */
  691. public Application getApplication() {
  692. return (Application) getSource();
  693. }
  694. }
  695. /**
  696. * The <code>UserChangeListener</code> interface for listening application
  697. * user changes.
  698. *
  699. * @version
  700. * @VERSION@
  701. * @since 3.0
  702. */
  703. public interface UserChangeListener extends EventListener, Serializable {
  704. /**
  705. * The <code>applicationUserChanged</code> method Invoked when the
  706. * application user has changed.
  707. *
  708. * @param event
  709. * the change event.
  710. */
  711. public void applicationUserChanged(Application.UserChangeEvent event);
  712. }
  713. /**
  714. * Adds the user change listener.
  715. *
  716. * This allows one to get notification each time {@link #setUser(Object)} is
  717. * called.
  718. *
  719. * @param listener
  720. * the user change listener to add.
  721. */
  722. public void addListener(UserChangeListener listener) {
  723. if (userChangeListeners == null) {
  724. userChangeListeners = new LinkedList<UserChangeListener>();
  725. }
  726. userChangeListeners.add(listener);
  727. }
  728. /**
  729. * Removes the user change listener.
  730. *
  731. * @param listener
  732. * the user change listener to remove.
  733. */
  734. public void removeListener(UserChangeListener listener) {
  735. if (userChangeListeners == null) {
  736. return;
  737. }
  738. userChangeListeners.remove(listener);
  739. if (userChangeListeners.isEmpty()) {
  740. userChangeListeners = null;
  741. }
  742. }
  743. /**
  744. * Window detach event.
  745. *
  746. * This event is sent each time a window is removed from the application
  747. * with {@link com.vaadin.Application#removeWindow(Window)}.
  748. */
  749. public class WindowDetachEvent extends EventObject {
  750. private final Window window;
  751. /**
  752. * Creates a event.
  753. *
  754. * @param window
  755. * the Detached window.
  756. */
  757. public WindowDetachEvent(Window window) {
  758. super(Application.this);
  759. this.window = window;
  760. }
  761. /**
  762. * Gets the detached window.
  763. *
  764. * @return the detached window.
  765. */
  766. public Window getWindow() {
  767. return window;
  768. }
  769. /**
  770. * Gets the application from which the window was detached.
  771. *
  772. * @return the Application.
  773. */
  774. public Application getApplication() {
  775. return (Application) getSource();
  776. }
  777. }
  778. /**
  779. * Window attach event.
  780. *
  781. * This event is sent each time a window is attached tothe application with
  782. * {@link com.vaadin.Application#addWindow(Window)}.
  783. */
  784. public class WindowAttachEvent extends EventObject {
  785. private final Window window;
  786. /**
  787. * Creates a event.
  788. *
  789. * @param window
  790. * the Attached window.
  791. */
  792. public WindowAttachEvent(Window window) {
  793. super(Application.this);
  794. this.window = window;
  795. }
  796. /**
  797. * Gets the attached window.
  798. *
  799. * @return the attached window.
  800. */
  801. public Window getWindow() {
  802. return window;
  803. }
  804. /**
  805. * Gets the application to which the window was attached.
  806. *
  807. * @return the Application.
  808. */
  809. public Application getApplication() {
  810. return (Application) getSource();
  811. }
  812. }
  813. /**
  814. * Window attach listener interface.
  815. */
  816. public interface WindowAttachListener extends Serializable {
  817. /**
  818. * Window attached
  819. *
  820. * @param event
  821. * the window attach event.
  822. */
  823. public void windowAttached(WindowAttachEvent event);
  824. }
  825. /**
  826. * Window detach listener interface.
  827. */
  828. public interface WindowDetachListener extends Serializable {
  829. /**
  830. * Window detached.
  831. *
  832. * @param event
  833. * the window detach event.
  834. */
  835. public void windowDetached(WindowDetachEvent event);
  836. }
  837. /**
  838. * Returns the URL user is redirected to on application close. If the URL is
  839. * <code>null</code>, the application is closed normally as defined by the
  840. * application running environment.
  841. * <p>
  842. * Desktop application just closes the application window and
  843. * web-application redirects the browser to application main URL.
  844. * </p>
  845. *
  846. * @return the URL.
  847. */
  848. public String getLogoutURL() {
  849. return logoutURL;
  850. }
  851. /**
  852. * Sets the URL user is redirected to on application close. If the URL is
  853. * <code>null</code>, the application is closed normally as defined by the
  854. * application running environment: Desktop application just closes the
  855. * application window and web-application redirects the browser to
  856. * application main URL.
  857. *
  858. * @param logoutURL
  859. * the logoutURL to set.
  860. */
  861. public void setLogoutURL(String logoutURL) {
  862. this.logoutURL = logoutURL;
  863. }
  864. /**
  865. * Gets the SystemMessages for this application. SystemMessages are used to
  866. * notify the user of various critical situations that can occur, such as
  867. * session expiration, client/server out of sync, and internal server error.
  868. *
  869. * You can customize the messages by "overriding" this method and returning
  870. * {@link CustomizedSystemMessages}. To "override" this method, re-implement
  871. * this method in your application (the class that extends
  872. * {@link Application}). Even though overriding static methods is not
  873. * possible in Java, Vaadin selects to call the static method from the
  874. * subclass instead of the original {@link #getSystemMessages()} if such a
  875. * method exists.
  876. *
  877. * @return the SystemMessages for this application
  878. */
  879. public static SystemMessages getSystemMessages() {
  880. return DEFAULT_SYSTEM_MESSAGES;
  881. }
  882. /**
  883. * <p>
  884. * Invoked by the terminal on any exception that occurs in application and
  885. * is thrown by the <code>setVariable</code> to the terminal. The default
  886. * implementation sets the exceptions as <code>ComponentErrors</code> to the
  887. * component that initiated the exception and prints stack trace to standard
  888. * error stream.
  889. * </p>
  890. * <p>
  891. * You can safely override this method in your application in order to
  892. * direct the errors to some other destination (for example log).
  893. * </p>
  894. *
  895. * @param event
  896. * the change event.
  897. * @see com.vaadin.terminal.Terminal.ErrorListener#terminalError(com.vaadin.terminal.Terminal.ErrorEvent)
  898. */
  899. public void terminalError(Terminal.ErrorEvent event) {
  900. final Throwable t = event.getThrowable();
  901. if (t instanceof SocketException) {
  902. // Most likely client browser closed socket
  903. logger.info("SocketException in CommunicationManager."
  904. + " Most likely client (browser) closed socket.");
  905. return;
  906. }
  907. // Finds the original source of the error/exception
  908. Object owner = null;
  909. if (event instanceof VariableOwner.ErrorEvent) {
  910. owner = ((VariableOwner.ErrorEvent) event).getVariableOwner();
  911. } else if (event instanceof ChangeVariablesErrorEvent) {
  912. owner = ((ChangeVariablesErrorEvent) event).getComponent();
  913. }
  914. // Shows the error in AbstractComponent
  915. if (owner instanceof AbstractComponent) {
  916. ((AbstractComponent) owner).setComponentError(AbstractErrorMessage
  917. .getErrorMessageForException(t));
  918. }
  919. // also print the error on console
  920. logger.log(Level.SEVERE, "Terminal error:", t);
  921. }
  922. /**
  923. * Gets the application context.
  924. * <p>
  925. * The application context is the environment where the application is
  926. * running in. The actual implementation class of may contains quite a lot
  927. * more functionality than defined in the {@link ApplicationContext}
  928. * interface.
  929. * </p>
  930. * <p>
  931. * By default, when you are deploying your application to a servlet
  932. * container, the implementation class is {@link WebApplicationContext} -
  933. * you can safely cast to this class and use the methods from there. When
  934. * you are deploying your application as a portlet, context implementation
  935. * is {@link PortletApplicationContext}.
  936. * </p>
  937. *
  938. * @return the application context.
  939. */
  940. public ApplicationContext getContext() {
  941. return context;
  942. }
  943. /**
  944. * Override this method to return correct version number of your
  945. * Application. Version information is delivered for example to Testing
  946. * Tools test results. By default this returns a string "NONVERSIONED".
  947. *
  948. * @return version string
  949. */
  950. public String getVersion() {
  951. return "NONVERSIONED";
  952. }
  953. /**
  954. * Gets the application error handler.
  955. *
  956. * The default error handler is the application itself.
  957. *
  958. * @return Application error handler
  959. */
  960. public Terminal.ErrorListener getErrorHandler() {
  961. return errorHandler;
  962. }
  963. /**
  964. * Sets the application error handler.
  965. *
  966. * The default error handler is the application itself. By overriding this,
  967. * you can redirect the error messages to your selected target (log for
  968. * example).
  969. *
  970. * @param errorHandler
  971. */
  972. public void setErrorHandler(Terminal.ErrorListener errorHandler) {
  973. this.errorHandler = errorHandler;
  974. }
  975. /**
  976. * Gets the {@link ConverterFactory} used to locate a suitable
  977. * {@link Converter} for fields in the application.
  978. *
  979. * See {@link #setConverterFactory(ConverterFactory)} for more details
  980. *
  981. * @return The converter factory used in the application
  982. */
  983. public ConverterFactory getConverterFactory() {
  984. return converterFactory;
  985. }
  986. /**
  987. * Sets the {@link ConverterFactory} used to locate a suitable
  988. * {@link Converter} for fields in the application.
  989. * <p>
  990. * The {@link ConverterFactory} is used to find a suitable converter when
  991. * binding data to a UI component and the data type does not match the UI
  992. * component type, e.g. binding a Double to a TextField (which is based on a
  993. * String).
  994. * </p>
  995. * <p>
  996. * The {@link Converter} for an individual field can be overridden using
  997. * {@link AbstractField#setConverter(Converter)} and for individual property
  998. * ids in a {@link Table} using
  999. * {@link Table#setConverter(Object, Converter)}.
  1000. * </p>
  1001. * <p>
  1002. * The converter factory must never be set to null.
  1003. *
  1004. * @param converterFactory
  1005. * The converter factory used in the application
  1006. */
  1007. public void setConverterFactory(ConverterFactory converterFactory) {
  1008. this.converterFactory = converterFactory;
  1009. }
  1010. /**
  1011. * Contains the system messages used to notify the user about various
  1012. * critical situations that can occur.
  1013. * <p>
  1014. * Customize by overriding the static
  1015. * {@link Application#getSystemMessages()} and returning
  1016. * {@link CustomizedSystemMessages}.
  1017. * </p>
  1018. * <p>
  1019. * The defaults defined in this class are:
  1020. * <ul>
  1021. * <li><b>sessionExpiredURL</b> = null</li>
  1022. * <li><b>sessionExpiredNotificationEnabled</b> = true</li>
  1023. * <li><b>sessionExpiredCaption</b> = ""</li>
  1024. * <li><b>sessionExpiredMessage</b> =
  1025. * "Take note of any unsaved data, and <u>click here</u> to continue."</li>
  1026. * <li><b>communicationErrorURL</b> = null</li>
  1027. * <li><b>communicationErrorNotificationEnabled</b> = true</li>
  1028. * <li><b>communicationErrorCaption</b> = "Communication problem"</li>
  1029. * <li><b>communicationErrorMessage</b> =
  1030. * "Take note of any unsaved data, and <u>click here</u> to continue."</li>
  1031. * <li><b>internalErrorURL</b> = null</li>
  1032. * <li><b>internalErrorNotificationEnabled</b> = true</li>
  1033. * <li><b>internalErrorCaption</b> = "Internal error"</li>
  1034. * <li><b>internalErrorMessage</b> = "Please notify the administrator.<br/>
  1035. * Take note of any unsaved data, and <u>click here</u> to continue."</li>
  1036. * <li><b>outOfSyncURL</b> = null</li>
  1037. * <li><b>outOfSyncNotificationEnabled</b> = true</li>
  1038. * <li><b>outOfSyncCaption</b> = "Out of sync"</li>
  1039. * <li><b>outOfSyncMessage</b> = "Something has caused us to be out of sync
  1040. * with the server.<br/>
  1041. * Take note of any unsaved data, and <u>click here</u> to re-sync."</li>
  1042. * <li><b>cookiesDisabledURL</b> = null</li>
  1043. * <li><b>cookiesDisabledNotificationEnabled</b> = true</li>
  1044. * <li><b>cookiesDisabledCaption</b> = "Cookies disabled"</li>
  1045. * <li><b>cookiesDisabledMessage</b> = "This application requires cookies to
  1046. * function.<br/>
  1047. * Please enable cookies in your browser and <u>click here</u> to try again.
  1048. * </li>
  1049. * </ul>
  1050. * </p>
  1051. *
  1052. */
  1053. public static class SystemMessages implements Serializable {
  1054. protected String sessionExpiredURL = null;
  1055. protected boolean sessionExpiredNotificationEnabled = true;
  1056. protected String sessionExpiredCaption = "Session Expired";
  1057. protected String sessionExpiredMessage = "Take note of any unsaved data, and <u>click here</u> to continue.";
  1058. protected String communicationErrorURL = null;
  1059. protected boolean communicationErrorNotificationEnabled = true;
  1060. protected String communicationErrorCaption = "Communication problem";
  1061. protected String communicationErrorMessage = "Take note of any unsaved data, and <u>click here</u> to continue.";
  1062. protected String authenticationErrorURL = null;
  1063. protected boolean authenticationErrorNotificationEnabled = true;
  1064. protected String authenticationErrorCaption = "Authentication problem";
  1065. protected String authenticationErrorMessage = "Take note of any unsaved data, and <u>click here</u> to continue.";
  1066. protected String internalErrorURL = null;
  1067. protected boolean internalErrorNotificationEnabled = true;
  1068. protected String internalErrorCaption = "Internal error";
  1069. protected String internalErrorMessage = "Please notify the administrator.<br/>Take note of any unsaved data, and <u>click here</u> to continue.";
  1070. protected String outOfSyncURL = null;
  1071. protected boolean outOfSyncNotificationEnabled = true;
  1072. protected String outOfSyncCaption = "Out of sync";
  1073. 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.";
  1074. protected String cookiesDisabledURL = null;
  1075. protected boolean cookiesDisabledNotificationEnabled = true;
  1076. protected String cookiesDisabledCaption = "Cookies disabled";
  1077. protected String cookiesDisabledMessage = "This application requires cookies to function.<br/>Please enable cookies in your browser and <u>click here</u> to try again.";
  1078. /**
  1079. * Use {@link CustomizedSystemMessages} to customize
  1080. */
  1081. private SystemMessages() {
  1082. }
  1083. /**
  1084. * @return null to indicate that the application will be restarted after
  1085. * session expired message has been shown.
  1086. */
  1087. public String getSessionExpiredURL() {
  1088. return sessionExpiredURL;
  1089. }
  1090. /**
  1091. * @return true to show session expiration message.
  1092. */
  1093. public boolean isSessionExpiredNotificationEnabled() {
  1094. return sessionExpiredNotificationEnabled;
  1095. }
  1096. /**
  1097. * @return "" to show no caption.
  1098. */
  1099. public String getSessionExpiredCaption() {
  1100. return (sessionExpiredNotificationEnabled ? sessionExpiredCaption
  1101. : null);
  1102. }
  1103. /**
  1104. * @return
  1105. * "Take note of any unsaved data, and <u>click here</u> to continue."
  1106. */
  1107. public String getSessionExpiredMessage() {
  1108. return (sessionExpiredNotificationEnabled ? sessionExpiredMessage
  1109. : null);
  1110. }
  1111. /**
  1112. * @return null to reload the application after communication error
  1113. * message.
  1114. */
  1115. public String getCommunicationErrorURL() {
  1116. return communicationErrorURL;
  1117. }
  1118. /**
  1119. * @return true to show the communication error message.
  1120. */
  1121. public boolean isCommunicationErrorNotificationEnabled() {
  1122. return communicationErrorNotificationEnabled;
  1123. }
  1124. /**
  1125. * @return "Communication problem"
  1126. */
  1127. public String getCommunicationErrorCaption() {
  1128. return (communicationErrorNotificationEnabled ? communicationErrorCaption
  1129. : null);
  1130. }
  1131. /**
  1132. * @return
  1133. * "Take note of any unsaved data, and <u>click here</u> to continue."
  1134. */
  1135. public String getCommunicationErrorMessage() {
  1136. return (communicationErrorNotificationEnabled ? communicationErrorMessage
  1137. : null);
  1138. }
  1139. /**
  1140. * @return null to reload the application after authentication error
  1141. * message.
  1142. */
  1143. public String getAuthenticationErrorURL() {
  1144. return authenticationErrorURL;
  1145. }
  1146. /**
  1147. * @return true to show the authentication error message.
  1148. */
  1149. public boolean isAuthenticationErrorNotificationEnabled() {
  1150. return authenticationErrorNotificationEnabled;
  1151. }
  1152. /**
  1153. * @return "Authentication problem"
  1154. */
  1155. public String getAuthenticationErrorCaption() {
  1156. return (authenticationErrorNotificationEnabled ? authenticationErrorCaption
  1157. : null);
  1158. }
  1159. /**
  1160. * @return
  1161. * "Take note of any unsaved data, and <u>click here</u> to continue."
  1162. */
  1163. public String getAuthenticationErrorMessage() {
  1164. return (authenticationErrorNotificationEnabled ? authenticationErrorMessage
  1165. : null);
  1166. }
  1167. /**
  1168. * @return null to reload the current URL after internal error message
  1169. * has been shown.
  1170. */
  1171. public String getInternalErrorURL() {
  1172. return internalErrorURL;
  1173. }
  1174. /**
  1175. * @return true to enable showing of internal error message.
  1176. */
  1177. public boolean isInternalErrorNotificationEnabled() {
  1178. return internalErrorNotificationEnabled;
  1179. }
  1180. /**
  1181. * @return "Internal error"
  1182. */
  1183. public String getInternalErrorCaption() {
  1184. return (internalErrorNotificationEnabled ? internalErrorCaption
  1185. : null);
  1186. }
  1187. /**
  1188. * @return "Please notify the administrator.<br/>
  1189. * Take note of any unsaved data, and <u>click here</u> to
  1190. * continue."
  1191. */
  1192. public String getInternalErrorMessage() {
  1193. return (internalErrorNotificationEnabled ? internalErrorMessage
  1194. : null);
  1195. }
  1196. /**
  1197. * @return null to reload the application after out of sync message.
  1198. */
  1199. public String getOutOfSyncURL() {
  1200. return outOfSyncURL;
  1201. }
  1202. /**
  1203. * @return true to enable showing out of sync message
  1204. */
  1205. public boolean isOutOfSyncNotificationEnabled() {
  1206. return outOfSyncNotificationEnabled;
  1207. }
  1208. /**
  1209. * @return "Out of sync"
  1210. */
  1211. public String getOutOfSyncCaption() {
  1212. return (outOfSyncNotificationEnabled ? outOfSyncCaption : null);
  1213. }
  1214. /**
  1215. * @return "Something has caused us to be out of sync with the server.<br/>
  1216. * Take note of any unsaved data, and <u>click here</u> to
  1217. * re-sync."
  1218. */
  1219. public String getOutOfSyncMessage() {
  1220. return (outOfSyncNotificationEnabled ? outOfSyncMessage : null);
  1221. }
  1222. /**
  1223. * Returns the URL the user should be redirected to after dismissing the
  1224. * "you have to enable your cookies" message. Typically null.
  1225. *
  1226. * @return A URL the user should be redirected to after dismissing the
  1227. * message or null to reload the current URL.
  1228. */
  1229. public String getCookiesDisabledURL() {
  1230. return cookiesDisabledURL;
  1231. }
  1232. /**
  1233. * Determines if "cookies disabled" messages should be shown to the end
  1234. * user or not. If the notification is disabled the user will be
  1235. * immediately redirected to the URL returned by
  1236. * {@link #getCookiesDisabledURL()}.
  1237. *
  1238. * @return true to show "cookies disabled" messages to the end user,
  1239. * false to redirect to the given URL directly
  1240. */
  1241. public boolean isCookiesDisabledNotificationEnabled() {
  1242. return cookiesDisabledNotificationEnabled;
  1243. }
  1244. /**
  1245. * Returns the caption of the message shown to the user when cookies are
  1246. * disabled in the browser.
  1247. *
  1248. * @return The caption of the "cookies disabled" message
  1249. */
  1250. public String getCookiesDisabledCaption() {
  1251. return (cookiesDisabledNotificationEnabled ? cookiesDisabledCaption
  1252. : null);
  1253. }
  1254. /**
  1255. * Returns the message shown to the user when cookies are disabled in
  1256. * the browser.
  1257. *
  1258. * @return The "cookies disabled" message
  1259. */
  1260. public String getCookiesDisabledMessage() {
  1261. return (cookiesDisabledNotificationEnabled ? cookiesDisabledMessage
  1262. : null);
  1263. }
  1264. }
  1265. /**
  1266. * Contains the system messages used to notify the user about various
  1267. * critical situations that can occur.
  1268. * <p>
  1269. * Vaadin gets the SystemMessages from your application by calling a static
  1270. * getSystemMessages() method. By default the
  1271. * Application.getSystemMessages() is used. You can customize this by
  1272. * defining a static MyApplication.getSystemMessages() and returning
  1273. * CustomizedSystemMessages. Note that getSystemMessages() is static -
  1274. * changing the system messages will by default change the message for all
  1275. * users of the application.
  1276. * </p>
  1277. * <p>
  1278. * The default behavior is to show a notification, and restart the
  1279. * application the the user clicks the message. <br/>
  1280. * Instead of restarting the application, you can set a specific URL that
  1281. * the user is taken to.<br/>
  1282. * Setting both caption and message to null will restart the application (or
  1283. * go to the specified URL) without displaying a notification.
  1284. * set*NotificationEnabled(false) will achieve the same thing.
  1285. * </p>
  1286. * <p>
  1287. * The situations are:
  1288. * <li>Session expired: the user session has expired, usually due to
  1289. * inactivity.</li>
  1290. * <li>Communication error: the client failed to contact the server, or the
  1291. * server returned and invalid response.</li>
  1292. * <li>Internal error: unhandled critical server error (e.g out of memory,
  1293. * database crash)
  1294. * <li>Out of sync: the client is not in sync with the server. E.g the user
  1295. * opens two windows showing the same application, but the application does
  1296. * not support this and uses the same Window instance. When the user makes
  1297. * changes in one of the windows - the other window is no longer in sync,
  1298. * and (for instance) pressing a button that is no longer present in the UI
  1299. * will cause a out-of-sync -situation.
  1300. * </p>
  1301. */
  1302. public static class CustomizedSystemMessages extends SystemMessages
  1303. implements Serializable {
  1304. /**
  1305. * Sets the URL to go to when the session has expired.
  1306. *
  1307. * @param sessionExpiredURL
  1308. * the URL to go to, or null to reload current
  1309. */
  1310. public void setSessionExpiredURL(String sessionExpiredURL) {
  1311. this.sessionExpiredURL = sessionExpiredURL;
  1312. }
  1313. /**
  1314. * Enables or disables the notification. If disabled, the set URL (or
  1315. * current) is loaded directly when next transaction between server and
  1316. * client happens.
  1317. *
  1318. * @param sessionExpiredNotificationEnabled
  1319. * true = enabled, false = disabled
  1320. */
  1321. public void setSessionExpiredNotificationEnabled(
  1322. boolean sessionExpiredNotificationEnabled) {
  1323. this.sessionExpiredNotificationEnabled = sessionExpiredNotificationEnabled;
  1324. }
  1325. /**
  1326. * Sets the caption of the notification. Set to null for no caption. If
  1327. * both caption and message are null, client automatically forwards to
  1328. * sessionExpiredUrl after timeout timer expires. Timer uses value read
  1329. * from HTTPSession.getMaxInactiveInterval()
  1330. *
  1331. * @param sessionExpiredCaption
  1332. * the caption
  1333. */
  1334. public void setSessionExpiredCaption(String sessionExpiredCaption) {
  1335. this.sessionExpiredCaption = sessionExpiredCaption;
  1336. }
  1337. /**
  1338. * Sets the message of the notification. Set to null for no message. If
  1339. * both caption and message are null, client automatically forwards to
  1340. * sessionExpiredUrl after timeout timer expires. Timer uses value read
  1341. * from HTTPSession.getMaxInactiveInterval()
  1342. *
  1343. * @param sessionExpiredMessage
  1344. * the message
  1345. */
  1346. public void setSessionExpiredMessage(String sessionExpiredMessage) {
  1347. this.sessionExpiredMessage = sessionExpiredMessage;
  1348. }
  1349. /**
  1350. * Sets the URL to go to when there is a authentication error.
  1351. *
  1352. * @param authenticationErrorURL
  1353. * the URL to go to, or null to reload current
  1354. */
  1355. public void setAuthenticationErrorURL(String authenticationErrorURL) {
  1356. this.authenticationErrorURL = authenticationErrorURL;
  1357. }
  1358. /**
  1359. * Enables or disables the notification. If disabled, the set URL (or
  1360. * current) is loaded directly.
  1361. *
  1362. * @param authenticationErrorNotificationEnabled
  1363. * true = enabled, false = disabled
  1364. */
  1365. public void setAuthenticationErrorNotificationEnabled(
  1366. boolean authenticationErrorNotificationEnabled) {
  1367. this.authenticationErrorNotificationEnabled = authenticationErrorNotificationEnabled;
  1368. }
  1369. /**
  1370. * Sets the caption of the notification. Set to null for no caption. If
  1371. * both caption and message is null, the notification is disabled;
  1372. *
  1373. * @param authenticationErrorCaption
  1374. * the caption
  1375. */
  1376. public void setAuthenticationErrorCaption(
  1377. String authenticationErrorCaption) {
  1378. this.authenticationErrorCaption = authenticationErrorCaption;
  1379. }
  1380. /**
  1381. * Sets the message of the notification. Set to null for no message. If
  1382. * both caption and message is null, the notification is disabled;
  1383. *
  1384. * @param authenticationErrorMessage
  1385. * the message
  1386. */
  1387. public void setAuthenticationErrorMessage(
  1388. String authenticationErrorMessage) {
  1389. this.authenticationErrorMessage = authenticationErrorMessage;
  1390. }
  1391. /**
  1392. * Sets the URL to go to when there is a communication error.
  1393. *
  1394. * @param communicationErrorURL
  1395. * the URL to go to, or null to reload current
  1396. */
  1397. public void setCommunicationErrorURL(String communicationErrorURL) {
  1398. this.communicationErrorURL = communicationErrorURL;
  1399. }
  1400. /**
  1401. * Enables or disables the notification. If disabled, the set URL (or
  1402. * current) is loaded directly.
  1403. *
  1404. * @param communicationErrorNotificationEnabled
  1405. * true = enabled, false = disabled
  1406. */
  1407. public void setCommunicationErrorNotificationEnabled(
  1408. boolean communicationErrorNotificationEnabled) {
  1409. this.communicationErrorNotificationEnabled = communicationErrorNotificationEnabled;
  1410. }
  1411. /**
  1412. * Sets the caption of the notification. Set to null for no caption. If
  1413. * both caption and message is null, the notification is disabled;
  1414. *
  1415. * @param communicationErrorCaption
  1416. * the caption
  1417. */
  1418. public void setCommunicationErrorCaption(
  1419. String communicationErrorCaption) {
  1420. this.communicationErrorCaption = communicationErrorCaption;
  1421. }
  1422. /**
  1423. * Sets the message of the notification. Set to null for no message. If
  1424. * both caption and message is null, the notification is disabled;
  1425. *
  1426. * @param communicationErrorMessage
  1427. * the message
  1428. */
  1429. public void setCommunicationErrorMessage(
  1430. String communicationErrorMessage) {
  1431. this.communicationErrorMessage = communicationErrorMessage;
  1432. }
  1433. /**
  1434. * Sets the URL to go to when an internal error occurs.
  1435. *
  1436. * @param internalErrorURL
  1437. * the URL to go to, or null to reload current
  1438. */
  1439. public void setInternalErrorURL(String internalErrorURL) {
  1440. this.internalErrorURL = internalErrorURL;
  1441. }
  1442. /**
  1443. * Enables or disables the notification. If disabled, the set URL (or
  1444. * current) is loaded directly.
  1445. *
  1446. * @param internalErrorNotificationEnabled
  1447. * true = enabled, false = disabled
  1448. */
  1449. public void setInternalErrorNotificationEnabled(
  1450. boolean internalErrorNotificationEnabled) {
  1451. this.internalErrorNotificationEnabled = internalErrorNotificationEnabled;
  1452. }
  1453. /**
  1454. * Sets the caption of the notification. Set to null for no caption. If
  1455. * both caption and message is null, the notification is disabled;
  1456. *
  1457. * @param internalErrorCaption
  1458. * the caption
  1459. */
  1460. public void setInternalErrorCaption(String internalErrorCaption) {
  1461. this.internalErrorCaption = internalErrorCaption;
  1462. }
  1463. /**
  1464. * Sets the message of the notification. Set to null for no message. If
  1465. * both caption and message is null, the notification is disabled;
  1466. *
  1467. * @param internalErrorMessage
  1468. * the message
  1469. */
  1470. public void setInternalErrorMessage(String internalErrorMessage) {
  1471. this.internalErrorMessage = internalErrorMessage;
  1472. }
  1473. /**
  1474. * Sets the URL to go to when the client is out-of-sync.
  1475. *
  1476. * @param outOfSyncURL
  1477. * the URL to go to, or null to reload current
  1478. */
  1479. public void setOutOfSyncURL(String outOfSyncURL) {
  1480. this.outOfSyncURL = outOfSyncURL;
  1481. }
  1482. /**
  1483. * Enables or disables the notification. If disabled, the set URL (or
  1484. * current) is loaded directly.
  1485. *
  1486. * @param outOfSyncNotificationEnabled
  1487. * true = enabled, false = disabled
  1488. */
  1489. public void setOutOfSyncNotificationEnabled(
  1490. boolean outOfSyncNotificationEnabled) {
  1491. this.outOfSyncNotificationEnabled = outOfSyncNotificationEnabled;
  1492. }
  1493. /**
  1494. * Sets the caption of the notification. Set to null for no caption. If
  1495. * both caption and message is null, the notification is disabled;
  1496. *
  1497. * @param outOfSyncCaption
  1498. * the caption
  1499. */
  1500. public void setOutOfSyncCaption(String outOfSyncCaption) {
  1501. this.outOfSyncCaption = outOfSyncCaption;
  1502. }
  1503. /**
  1504. * Sets the message of the notification. Set to null for no message. If
  1505. * both caption and message is null, the notification is disabled;
  1506. *
  1507. * @param outOfSyncMessage
  1508. * the message
  1509. */
  1510. public void setOutOfSyncMessage(String outOfSyncMessage) {
  1511. this.outOfSyncMessage = outOfSyncMessage;
  1512. }
  1513. /**
  1514. * Sets the URL to redirect to when the browser has cookies disabled.
  1515. *
  1516. * @param cookiesDisabledURL
  1517. * the URL to redirect to, or null to reload the current URL
  1518. */
  1519. public void setCookiesDisabledURL(String cookiesDisabledURL) {
  1520. this.cookiesDisabledURL = cookiesDisabledURL;
  1521. }
  1522. /**
  1523. * Enables or disables the notification for "cookies disabled" messages.
  1524. * If disabled, the URL returned by {@link #getCookiesDisabledURL()} is
  1525. * loaded directly.
  1526. *
  1527. * @param cookiesDisabledNotificationEnabled
  1528. * true to enable "cookies disabled" messages, false
  1529. * otherwise
  1530. */
  1531. public void setCookiesDisabledNotificationEnabled(
  1532. boolean cookiesDisabledNotificationEnabled) {
  1533. this.cookiesDisabledNotificationEnabled = cookiesDisabledNotificationEnabled;
  1534. }
  1535. /**
  1536. * Sets the caption of the "cookies disabled" notification. Set to null
  1537. * for no caption. If both caption and message is null, the notification
  1538. * is disabled.
  1539. *
  1540. * @param cookiesDisabledCaption
  1541. * the caption for the "cookies disabled" notification
  1542. */
  1543. public void setCookiesDisabledCaption(String cookiesDisabledCaption) {
  1544. this.cookiesDisabledCaption = cookiesDisabledCaption;
  1545. }
  1546. /**
  1547. * Sets the message of the "cookies disabled" notification. Set to null
  1548. * for no message. If both caption and message is null, the notification
  1549. * is disabled.
  1550. *
  1551. * @param cookiesDisabledMessage
  1552. * the message for the "cookies disabled" notification
  1553. */
  1554. public void setCookiesDisabledMessage(String cookiesDisabledMessage) {
  1555. this.cookiesDisabledMessage = cookiesDisabledMessage;
  1556. }
  1557. }
  1558. /**
  1559. * Application error is an error message defined on the application level.
  1560. *
  1561. * When an error occurs on the application level, this error message type
  1562. * should be used. This indicates that the problem is caused by the
  1563. * application - not by the user.
  1564. */
  1565. public class ApplicationError implements Terminal.ErrorEvent {
  1566. private final Throwable throwable;
  1567. public ApplicationError(Throwable throwable) {
  1568. this.throwable = throwable;
  1569. }
  1570. public Throwable getThrowable() {
  1571. return throwable;
  1572. }
  1573. }
  1574. /**
  1575. * Gets a root for a request for which no root is already known. This method
  1576. * is called when the framework processes a request that does not originate
  1577. * from an existing root instance. This typically happens when a host page
  1578. * is requested.
  1579. *
  1580. * <p>
  1581. * Subclasses of Application may override this method to provide custom
  1582. * logic for choosing how to create a suitable root or for picking an
  1583. * already created root. If an existing root is picked, care should be taken
  1584. * to avoid keeping the same root open in multiple browser windows, as that
  1585. * will cause the states to go out of sync.
  1586. * </p>
  1587. *
  1588. * <p>
  1589. * If {@link BrowserDetails} are required to create a Root, the
  1590. * implementation can throw a {@link RootRequiresMoreInformationException}
  1591. * exception. In this case, the framework will instruct the browser to send
  1592. * the additional details, whereupon this method is invoked again with the
  1593. * browser details present in the wrapped request. Throwing the exception if
  1594. * the browser details are already available is not supported.
  1595. * </p>
  1596. *
  1597. * <p>
  1598. * The default implementation in {@link Application} creates a new instance
  1599. * of the Root class returned by {@link #getRootClassName(WrappedRequest},
  1600. * which in turn uses the {@value #ROOT_PARAMETER} parameter from web.xml.
  1601. * </p>
  1602. *
  1603. * @param request
  1604. * the wrapped request for which a root is needed
  1605. * @return a root instance to use for the request
  1606. * @throws RootRequiresMoreInformationException
  1607. * may be thrown by an implementation to indicate that
  1608. * {@link BrowserDetails} are required to create a root
  1609. *
  1610. * @see #getRootClassName(WrappedRequest)
  1611. * @see Root
  1612. * @see RootRequiresMoreInformationException
  1613. * @see WrappedRequest#getBrowserDetails()
  1614. *
  1615. * @since 7.0
  1616. */
  1617. protected Root getRoot(WrappedRequest request)
  1618. throws RootRequiresMoreInformationException {
  1619. String rootClassName = getRootClassName(request);
  1620. try {
  1621. Class<? extends Root> rootClass = Class.forName(rootClassName)
  1622. .asSubclass(Root.class);
  1623. try {
  1624. Root root = rootClass.newInstance();
  1625. return root;
  1626. } catch (Exception e) {
  1627. throw new RuntimeException("Could not instantiate root class "
  1628. + rootClassName, e);
  1629. }
  1630. } catch (ClassNotFoundException e) {
  1631. throw new RuntimeException("Could not load root class "
  1632. + rootClassName, e);
  1633. }
  1634. }
  1635. /**
  1636. * Provides the name of the <code>Root</code> class that should be used for
  1637. * a request. The class must have an accessible no-args constructor.
  1638. * <p>
  1639. * The default implementation uses the {@value #ROOT_PARAMETER} parameter
  1640. * from web.xml.
  1641. * </p>
  1642. * <p>
  1643. * This method is mainly used by the default implementation of
  1644. * {@link #getRoot(WrappedRequest)}. If you override that method with your
  1645. * own functionality, the results of this method might not be used.
  1646. * </p>
  1647. *
  1648. * @param request
  1649. * the request for which a new root is required
  1650. * @return the name of the root class to use
  1651. *
  1652. * @since 7.0
  1653. */
  1654. protected String getRootClassName(WrappedRequest request) {
  1655. Object rootClassNameObj = properties.get(ROOT_PARAMETER);
  1656. if (rootClassNameObj instanceof String) {
  1657. return (String) rootClassNameObj;
  1658. } else {
  1659. throw new RuntimeException("No " + ROOT_PARAMETER
  1660. + " defined in web.xml");
  1661. }
  1662. }
  1663. /**
  1664. * Finds the theme to use for a specific root. If no specific theme is
  1665. * required, <code>null</code> is returned.
  1666. *
  1667. * TODO Tell what the default implementation does once it does something.
  1668. *
  1669. * @param root
  1670. * the root to get a theme for
  1671. * @return the name of the theme, or <code>null</code> if the default theme
  1672. * should be used
  1673. *
  1674. * @since 7.0
  1675. */
  1676. public String getThemeForRoot(Root root) {
  1677. Theme rootTheme = getAnnotationFor(root.getClass(), Theme.class);
  1678. if (rootTheme != null) {
  1679. return rootTheme.value();
  1680. } else {
  1681. return null;
  1682. }
  1683. }
  1684. /**
  1685. * Finds the widgetset to use for a specific root. If no specific widgetset
  1686. * is required, <code>null</code> is returned.
  1687. *
  1688. * TODO Tell what the default implementation does once it does something.
  1689. *
  1690. * @param root
  1691. * the root to get a widgetset for
  1692. * @return the name of the widgetset, or <code>null</code> if the default
  1693. * widgetset should be used
  1694. *
  1695. * @since 7.0
  1696. */
  1697. public String getWidgetsetForRoot(Root root) {
  1698. Widgetset rootWidgetset = getAnnotationFor(root.getClass(),
  1699. Widgetset.class);
  1700. if (rootWidgetset != null) {
  1701. return rootWidgetset.value();
  1702. } else {
  1703. return null;
  1704. }
  1705. }
  1706. /**
  1707. * Helper to get an annotation for a class. If the annotation is not present
  1708. * on the target class, it's superclasses and implemented interfaces are
  1709. * also searched for the annotation.
  1710. *
  1711. * @param type
  1712. * the target class from which the annotation should be found
  1713. * @param annotationType
  1714. * the annotation type to look for
  1715. * @return an annotation of the given type, or <code>null</code> if the
  1716. * annotation is not present on the class
  1717. */
  1718. private static <T extends Annotation> T getAnnotationFor(Class<?> type,
  1719. Class<T> annotationType) {
  1720. // Find from the class hierarchy
  1721. Class<?> currentType = type;
  1722. while (currentType != Object.class) {
  1723. T annotation = currentType.getAnnotation(annotationType);
  1724. if (annotation != null) {
  1725. return annotation;
  1726. } else {
  1727. currentType = currentType.getSuperclass();
  1728. }
  1729. }
  1730. // Find from an implemented interface
  1731. for (Class<?> iface : type.getInterfaces()) {
  1732. T annotation = iface.getAnnotation(annotationType);
  1733. if (annotation != null) {
  1734. return annotation;
  1735. }
  1736. }
  1737. return null;
  1738. }
  1739. /**
  1740. * Handles a request by passing it to each registered {@link RequestHandler}
  1741. * in turn until one produces a response. This method is used for requests
  1742. * that have not been handled by any specific functionality in the terminal
  1743. * implementation (e.g. {@link AbstractApplicationServlet}).
  1744. * <p>
  1745. * The request handlers are invoked in the revere order in which they were
  1746. * added to the application until a response has been produced. This means
  1747. * that the most recently added handler is used first and the first request
  1748. * handler that was added to the application is invoked towards the end
  1749. * unless any previous handler has already produced a response.
  1750. * </p>
  1751. *
  1752. * @param request
  1753. * the wrapped request to get information from
  1754. * @param response
  1755. * the response to which data can be written
  1756. * @return returns <code>true</code> if a {@link RequestHandler} has
  1757. * produced a response and <code>false</code> if no response has
  1758. * been written.
  1759. * @throws IOException
  1760. *
  1761. * @see #addRequestHandler(RequestHandler)
  1762. * @see RequestHandler
  1763. *
  1764. * @since 7.0
  1765. */
  1766. public boolean handleRequest(WrappedRequest request,
  1767. WrappedResponse response) throws IOException {
  1768. // Use a copy to avoid ConcurrentModificationException
  1769. for (RequestHandler handler : new ArrayList<RequestHandler>(
  1770. requestHandlers)) {
  1771. if (handler.handleRequest(this, request, response)) {
  1772. return true;
  1773. }
  1774. }
  1775. // If not handled
  1776. return false;
  1777. }
  1778. /**
  1779. * Adds a request handler to this application. Request handlers can be added
  1780. * to provide responses to requests that are not handled by the default
  1781. * functionality of the framework.
  1782. * <p>
  1783. * Handlers are called in reverse order of addition, so the most recently
  1784. * added handler will be called first.
  1785. * </p>
  1786. *
  1787. * @param handler
  1788. * the request handler to add
  1789. *
  1790. * @see #handleRequest(WrappedRequest, WrappedResponse)
  1791. * @see #removeRequestHandler(RequestHandler)
  1792. *
  1793. * @since 7.0
  1794. */
  1795. public void addRequestHandler(RequestHandler handler) {
  1796. requestHandlers.addFirst(handler);
  1797. }
  1798. /**
  1799. * Removes a request handler from the application.
  1800. *
  1801. * @param handler
  1802. * the request handler to remove
  1803. *
  1804. * @since 7.0
  1805. */
  1806. public void removeRequestHandler(RequestHandler handler) {
  1807. requestHandlers.remove(handler);
  1808. }
  1809. /**
  1810. * Gets the request handlers that are registered to the application. The
  1811. * iteration order of the returned collection is the same as the order in
  1812. * which the request handlers will be invoked when a request is handled.
  1813. *
  1814. * @return a collection of request handlers, with the iteration order
  1815. * according to the order they would be invoked
  1816. *
  1817. * @see #handleRequest(WrappedRequest, WrappedResponse)
  1818. * @see #addRequestHandler(RequestHandler)
  1819. * @see #removeRequestHandler(RequestHandler)
  1820. *
  1821. * @since 7.0
  1822. */
  1823. public Collection<RequestHandler> getRequestHandlers() {
  1824. return Collections.unmodifiableCollection(requestHandlers);
  1825. }
  1826. /**
  1827. * Find an application resource with a given key.
  1828. *
  1829. * @param key
  1830. * The key of the resource
  1831. * @return The application resource corresponding to the provided key, or
  1832. * <code>null</code> if no resource is registered for the key
  1833. *
  1834. * @since 7.0
  1835. */
  1836. public ApplicationResource getResource(String key) {
  1837. return keyResourceMap.get(key);
  1838. }
  1839. /**
  1840. * Thread local for keeping track of currently used application instance
  1841. *
  1842. * @since 7.0
  1843. */
  1844. private static final ThreadLocal<Application> currentApplication = new ThreadLocal<Application>();
  1845. private boolean rootPreserved = false;
  1846. /**
  1847. * Gets the currently used application. The current application is
  1848. * automatically defined when processing requests to the server. In other
  1849. * cases, (e.g. from background threads), the current application is not
  1850. * automatically defined.
  1851. *
  1852. * @return the current application instance if available, otherwise
  1853. * <code>null</code>
  1854. *
  1855. * @see #setCurrentApplication(Application)
  1856. *
  1857. * @since 7.0
  1858. */
  1859. public static Application getCurrentApplication() {
  1860. return currentApplication.get();
  1861. }
  1862. /**
  1863. * Sets the thread local for the current application. This method is used by
  1864. * the framework to set the current application whenever a new request is
  1865. * processed and it is cleared when the request has been processed.
  1866. * <p>
  1867. * The application developer can also use this method to define the current
  1868. * application outside the normal request handling, e.g. when initiating
  1869. * custom background threads.
  1870. * </p>
  1871. *
  1872. * @param application
  1873. *
  1874. * @see #getCurrentApplication()
  1875. * @see ThreadLocal
  1876. *
  1877. * @since 7.0
  1878. */
  1879. public static void setCurrentApplication(Application application) {
  1880. currentApplication.set(application);
  1881. }
  1882. /**
  1883. * Check whether this application is in production mode. If an application
  1884. * is in production mode, certain debugging facilities are not available.
  1885. *
  1886. * @return the status of the production mode flag
  1887. *
  1888. * @since 7.0
  1889. */
  1890. public boolean isProductionMode() {
  1891. return productionMode;
  1892. }
  1893. /**
  1894. * Finds the {@link Root} to which a particular request belongs. If the
  1895. * request originates from an existing Root, that root is returned. In other
  1896. * cases, the method attempts to create and initialize a new root and might
  1897. * throw a {@link RootRequiresMoreInformationException} if all required
  1898. * information is not available.
  1899. * <p>
  1900. * Please note that this method can also return a newly created
  1901. * <code>Root</code> which has not yet been initialized. You can use
  1902. * {@link #isRootInitPending(int)} with the root's id (
  1903. * {@link Root#getRootId()} to check whether the initialization is still
  1904. * pending.
  1905. * </p>
  1906. *
  1907. * @param request
  1908. * the request for which a root is desired
  1909. * @return a root belonging to the request
  1910. * @throws RootRequiresMoreInformationException
  1911. * if no existing root could be found and creating a new root
  1912. * requires additional information from the browser
  1913. *
  1914. * @see #getRoot(WrappedRequest)
  1915. * @see RootRequiresMoreInformationException
  1916. *
  1917. * @since 7.0
  1918. */
  1919. public Root getRootForRequest(WrappedRequest request)
  1920. throws RootRequiresMoreInformationException {
  1921. Root root = Root.getCurrentRoot();
  1922. if (root != null) {
  1923. return root;
  1924. }
  1925. Integer rootId = getRootId(request);
  1926. synchronized (this) {
  1927. BrowserDetails browserDetails = request.getBrowserDetails();
  1928. boolean hasBrowserDetails = browserDetails != null
  1929. && browserDetails.getUriFragment() != null;
  1930. root = roots.get(rootId);
  1931. if (root == null && isRootPreserved()) {
  1932. // Check for a known root
  1933. if (!retainOnRefreshRoots.isEmpty()) {
  1934. Integer retainedRootId;
  1935. if (!hasBrowserDetails) {
  1936. throw new RootRequiresMoreInformationException();
  1937. } else {
  1938. String windowName = browserDetails.getWindowName();
  1939. retainedRootId = retainOnRefreshRoots.get(windowName);
  1940. }
  1941. if (retainedRootId != null) {
  1942. rootId = retainedRootId;
  1943. root = roots.get(rootId);
  1944. }
  1945. }
  1946. }
  1947. if (root == null) {
  1948. // Throws exception if root can not yet be created
  1949. root = getRoot(request);
  1950. // Initialize some fields for a newly created root
  1951. if (root.getApplication() == null) {
  1952. root.setApplication(this);
  1953. }
  1954. if (root.getRootId() < 0) {
  1955. if (rootId == null) {
  1956. // Get the next id if none defined
  1957. rootId = Integer.valueOf(nextRootId++);
  1958. }
  1959. root.setRootId(rootId.intValue());
  1960. roots.put(rootId, root);
  1961. }
  1962. }
  1963. // Set thread local here so it is available in init
  1964. Root.setCurrentRoot(root);
  1965. if (!initedRoots.contains(rootId)) {
  1966. boolean initRequiresBrowserDetails = isRootPreserved()
  1967. || !root.getClass()
  1968. .isAnnotationPresent(EagerInit.class);
  1969. if (!initRequiresBrowserDetails || hasBrowserDetails) {
  1970. root.doInit(request);
  1971. // Remember that this root has been initialized
  1972. initedRoots.add(rootId);
  1973. // init() might turn on preserve so do this afterwards
  1974. if (isRootPreserved()) {
  1975. // Remember this root
  1976. String windowName = request.getBrowserDetails()
  1977. .getWindowName();
  1978. retainOnRefreshRoots.put(windowName, rootId);
  1979. }
  1980. }
  1981. }
  1982. } // end synchronized block
  1983. return root;
  1984. }
  1985. /**
  1986. * Internal helper to finds the root id for a request.
  1987. *
  1988. * @param request
  1989. * the request to get the root id for
  1990. * @return a root id, or <code>null</code> if no root id is defined
  1991. *
  1992. * @since 7.0
  1993. */
  1994. private static Integer getRootId(WrappedRequest request) {
  1995. if (request instanceof CombinedRequest) {
  1996. // Combined requests has the rootid parameter in the second request
  1997. CombinedRequest combinedRequest = (CombinedRequest) request;
  1998. request = combinedRequest.getSecondRequest();
  1999. }
  2000. String rootIdString = request
  2001. .getParameter(ApplicationConnection.ROOT_ID_PARAMETER);
  2002. Integer rootId = rootIdString == null ? null
  2003. : new Integer(rootIdString);
  2004. return rootId;
  2005. }
  2006. /**
  2007. * Sets whether the same Root state should be reused if the framework can
  2008. * detect that the application is opened in a browser window where it has
  2009. * previously been open. The framework attempts to discover this by checking
  2010. * the value of window.name in the browser.
  2011. * <p>
  2012. * NOTE that you should avoid turning this feature on/off on-the-fly when
  2013. * the UI is already shown, as it might not be retained as intended.
  2014. * </p>
  2015. *
  2016. * @param rootPreserved
  2017. * <code>true</code>if the same Root instance should be reused
  2018. * e.g. when the browser window is refreshed.
  2019. */
  2020. public void setRootPreserved(boolean rootPreserved) {
  2021. this.rootPreserved = rootPreserved;
  2022. if (!rootPreserved) {
  2023. retainOnRefreshRoots.clear();
  2024. }
  2025. }
  2026. /**
  2027. * Checks whether the same Root state should be reused if the framework can
  2028. * detect that the application is opened in a browser window where it has
  2029. * previously been open. The framework attempts to discover this by checking
  2030. * the value of window.name in the browser.
  2031. *
  2032. * @return <code>true</code>if the same Root instance should be reused e.g.
  2033. * when the browser window is refreshed.
  2034. */
  2035. public boolean isRootPreserved() {
  2036. return rootPreserved;
  2037. }
  2038. /**
  2039. * Checks whether there's a pending initialization for the root with the
  2040. * given id.
  2041. *
  2042. * @param rootId
  2043. * root id to check for
  2044. * @return <code>true</code> of the initialization is pending,
  2045. * <code>false</code> if the root id is not registered or if the
  2046. * root has already been initialized
  2047. *
  2048. * @see #getRootForRequest(WrappedRequest)
  2049. */
  2050. public boolean isRootInitPending(int rootId) {
  2051. return !initedRoots.contains(Integer.valueOf(rootId));
  2052. }
  2053. /**
  2054. * Gets all the roots of this application. This includes roots that have
  2055. * been requested but not yet initialized. Please note, that roots are not
  2056. * automatically removed e.g. if the browser window is closed and that there
  2057. * is no way to manually remove a root. Inactive roots will thus not be
  2058. * released for GC until the entire application is released when the session
  2059. * has timed out (unless there are dangling references). Improved support
  2060. * for releasing unused roots is planned for an upcoming alpha release of
  2061. * Vaadin 7.
  2062. *
  2063. * @return a collection of roots belonging to this application
  2064. *
  2065. * @since 7.0
  2066. */
  2067. public Collection<Root> getRoots() {
  2068. return Collections.unmodifiableCollection(roots.values());
  2069. }
  2070. private final HashMap<String, Connector> connectorIdToConnector = new HashMap<String, Connector>();
  2071. private int connectorIdSequence = 0;
  2072. /**
  2073. * Generate an id for the given Connector. Connectors must not call this
  2074. * method more than once, the first time they need an id.
  2075. *
  2076. * @param connector
  2077. * A connector that has not yet been assigned an id.
  2078. * @return A new id for the connector
  2079. */
  2080. public String createConnectorId(Connector connector) {
  2081. String connectorId = String.valueOf(connectorIdSequence++);
  2082. Connector oldReference = connectorIdToConnector.put(connectorId,
  2083. connector);
  2084. if (oldReference != null) {
  2085. throw new RuntimeException(
  2086. "An error occured while generating connector ids. A connector with id "
  2087. + connectorId + " was already found!");
  2088. }
  2089. return connectorId;
  2090. }
  2091. /**
  2092. * Gets a connector by its id.
  2093. *
  2094. * @param connectorId
  2095. * The connector id to look for
  2096. * @return The connector with the given id or null if no connector has the
  2097. * given id
  2098. */
  2099. public Connector getConnector(String connectorId) {
  2100. return connectorIdToConnector.get(connectorId);
  2101. }
  2102. /**
  2103. * Cleans the connector map from all connectors that are no longer attached
  2104. * to the application. This should only be called by the framework.
  2105. */
  2106. public void cleanConnectorMap() {
  2107. // remove detached components from paintableIdMap so they
  2108. // can be GC'ed
  2109. Iterator<String> iterator = connectorIdToConnector.keySet().iterator();
  2110. while (iterator.hasNext()) {
  2111. String connectorId = iterator.next();
  2112. Connector connector = connectorIdToConnector.get(connectorId);
  2113. if (connector instanceof Component) {
  2114. Component component = (Component) connector;
  2115. if (component.getApplication() != this) {
  2116. // If component is no longer part of this application,
  2117. // remove it from the map. If it is re-attached to the
  2118. // application at some point it will be re-added to this
  2119. // collection when sent to the client.
  2120. iterator.remove();
  2121. }
  2122. }
  2123. }
  2124. }
  2125. }