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 49KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin;
  5. import java.io.Serializable;
  6. import java.net.SocketException;
  7. import java.net.URL;
  8. import java.util.Collection;
  9. import java.util.Collections;
  10. import java.util.Enumeration;
  11. import java.util.EventListener;
  12. import java.util.EventObject;
  13. import java.util.Hashtable;
  14. import java.util.Iterator;
  15. import java.util.LinkedList;
  16. import java.util.Locale;
  17. import java.util.Properties;
  18. import java.util.Random;
  19. import com.vaadin.service.ApplicationContext;
  20. import com.vaadin.terminal.ApplicationResource;
  21. import com.vaadin.terminal.DownloadStream;
  22. import com.vaadin.terminal.ErrorMessage;
  23. import com.vaadin.terminal.ParameterHandler;
  24. import com.vaadin.terminal.SystemError;
  25. import com.vaadin.terminal.Terminal;
  26. import com.vaadin.terminal.URIHandler;
  27. import com.vaadin.terminal.VariableOwner;
  28. import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent;
  29. import com.vaadin.ui.AbstractComponent;
  30. import com.vaadin.ui.Window;
  31. /**
  32. * <p>
  33. * Base class required for all IT Mill Toolkit applications. This class provides
  34. * all the basic services required by the toolkit. These services allow external
  35. * discovery and manipulation of the user, {@link com.vaadin.ui.Window
  36. * windows} and themes, and starting and stopping the application.
  37. * </p>
  38. *
  39. * <p>
  40. * As mentioned, all IT Mill Toolkit applications must inherit this class.
  41. * However, this is almost all of what one needs to do to create a fully
  42. * functional application. The only thing a class inheriting the
  43. * <code>Application</code> needs to do is implement the <code>init</code>
  44. * method where it creates the windows it needs to perform its function. Note
  45. * that all applications must have at least one window: the main window. The
  46. * first unnamed window constructed by an application automatically becomes the
  47. * main window which behaves just like other windows with one exception: when
  48. * accessing windows using URLs the main window corresponds to the application
  49. * URL whereas other windows correspond to a URL gotten by catenating the
  50. * window's name to the application URL.
  51. * </p>
  52. *
  53. * <p>
  54. * See the class <code>com.vaadin.demo.HelloWorld</code> for a simple
  55. * example of a fully working application.
  56. * </p>
  57. *
  58. * <p>
  59. * <strong>Window access.</strong> <code>Application</code> provides methods to
  60. * list, add and remove the windows it contains.
  61. * </p>
  62. *
  63. * <p>
  64. * <strong>Execution control.</strong> This class includes method to start and
  65. * finish the execution of the application. Being finished means basically that
  66. * no windows will be available from the application anymore.
  67. * </p>
  68. *
  69. * <p>
  70. * <strong>Theme selection.</strong> The theme selection process allows a theme
  71. * to be specified at three different levels. When a window's theme needs to be
  72. * found out, the window itself is queried for a preferred theme. If the window
  73. * does not prefer a specific theme, the application containing the window is
  74. * queried. If neither the application prefers a theme, the default theme for
  75. * the {@link com.vaadin.terminal.Terminal terminal} is used. The
  76. * terminal always defines a default theme.
  77. * </p>
  78. *
  79. * @author IT Mill Ltd.
  80. * @version
  81. * @VERSION@
  82. * @since 3.0
  83. */
  84. @SuppressWarnings("serial")
  85. public abstract class Application implements URIHandler,
  86. Terminal.ErrorListener, Serializable {
  87. /**
  88. * Random window name generator.
  89. */
  90. private static Random nameGenerator = new Random();
  91. /**
  92. * Application context the application is running in.
  93. */
  94. private ApplicationContext context;
  95. /**
  96. * The current user or <code>null</code> if no user has logged in.
  97. */
  98. private Object user;
  99. /**
  100. * Mapping from window name to window instance.
  101. */
  102. private final Hashtable windows = new Hashtable();
  103. /**
  104. * Main window of the application.
  105. */
  106. private Window mainWindow = null;
  107. /**
  108. * The application's URL.
  109. */
  110. private URL applicationUrl;
  111. /**
  112. * Name of the theme currently used by the application.
  113. */
  114. private String theme = null;
  115. /**
  116. * Application status.
  117. */
  118. private boolean applicationIsRunning = false;
  119. /**
  120. * Application properties.
  121. */
  122. private Properties properties;
  123. /**
  124. * Default locale of the application.
  125. */
  126. private Locale locale;
  127. /**
  128. * List of listeners listening user changes.
  129. */
  130. private LinkedList userChangeListeners = null;
  131. /**
  132. * Window attach listeners.
  133. */
  134. private LinkedList windowAttachListeners = null;
  135. /**
  136. * Window detach listeners.
  137. */
  138. private LinkedList windowDetachListeners = null;
  139. /**
  140. * Application resource mapping: key <-> resource.
  141. */
  142. private final Hashtable resourceKeyMap = new Hashtable();
  143. private final Hashtable keyResourceMap = new Hashtable();
  144. private long lastResourceKeyNumber = 0;
  145. /**
  146. * URL where the user is redirected to on application close, or null if
  147. * application is just closed without redirection.
  148. */
  149. private String logoutURL = null;
  150. /**
  151. * The default SystemMessages (read-only). Change by overriding
  152. * getSystemMessages() and returning CustomizedSystemMessages
  153. */
  154. private static final SystemMessages DEFAULT_SYSTEM_MESSAGES = new SystemMessages();
  155. /**
  156. * Application wide error handler which is used by default if an error is
  157. * left unhandled.
  158. */
  159. private Terminal.ErrorListener errorHandler = this;
  160. /**
  161. * <p>
  162. * Gets a window by name. Returns <code>null</code> if the application is
  163. * not running or it does not contain a window corresponding to the name.
  164. * </p>
  165. *
  166. * <p>
  167. * All windows can be referenced by their names in url
  168. * <code>http://host:port/foo/bar/</code> where
  169. * <code>http://host:port/foo/</code> is the application url as returned by
  170. * getURL() and <code>bar</code> is the name of the window.
  171. * </p>
  172. *
  173. * <p>
  174. * One should note that this method can, as a side effect create new windows
  175. * if needed by the application. This can be achieved by overriding the
  176. * default implementation.
  177. * </p>
  178. *
  179. * <p>
  180. * If for some reason user opens another window with same url that is
  181. * already open, name is modified by adding "_12345678" postfix to the name,
  182. * where 12345678 is a random number. One can decide to create another
  183. * window-object for those windows (recommended) or to discard the postfix.
  184. * If the user has two browser windows pointing to the same window-object on
  185. * server, synchronization errors are likely to occur.
  186. * </p>
  187. *
  188. * <p>
  189. * If no browser-level windowing is used, all defaults are fine and this
  190. * method can be left as is. In case browser-level windows are needed, it is
  191. * recommended to create new window-objects on this method from their names
  192. * if the super.getWindow() does not find existing windows. See below for
  193. * implementation example: <code><pre>
  194. // If we already have the requested window, use it
  195. Window w = super.getWindow(name);
  196. if (w == null) {
  197. // If no window found, create it
  198. w = new Window(name);
  199. // set windows name to the one requested
  200. w.setName(name);
  201. // add it to this application
  202. addWindow(w);
  203. // add some content
  204. w.addComponent(new Label("Test window"));
  205. }
  206. return w;</pre></code>
  207. * </p>
  208. *
  209. * <p>
  210. * <strong>Note</strong> that all returned Window objects must be added to
  211. * this application instance.
  212. *
  213. * <p>
  214. * The method should return null if the window does not exists (and is not
  215. * created as a side-effect) or if the application is not running anymore.
  216. * </p>
  217. *
  218. * @param name
  219. * the name of the window.
  220. * @return the window associated with the given URI or <code>null</code>
  221. */
  222. public Window getWindow(String name) {
  223. // For closed app, do not give any windows
  224. if (!isRunning()) {
  225. return null;
  226. }
  227. // Gets the window by name
  228. final Window window = (Window) windows.get(name);
  229. return window;
  230. }
  231. /**
  232. * Adds a new window to the application.
  233. *
  234. * <p>
  235. * This implicitly invokes the
  236. * {@link com.vaadin.ui.Window#setApplication(Application)} method.
  237. * </p>
  238. *
  239. * <p>
  240. * Note that all application-level windows can be accessed by their names in
  241. * url <code>http://host:port/foo/bar/</code> where
  242. * <code>http://host:port/foo/</code> is the application url as returned by
  243. * getURL() and <code>bar</code> is the name of the window. Also note that
  244. * not all windows should be added to application - one can also add windows
  245. * inside other windows - these windows show as smaller windows inside those
  246. * windows.
  247. * </p>
  248. *
  249. * @param window
  250. * the new <code>Window</code> to add. If the name of the window
  251. * is <code>null</code>, an unique name is automatically given
  252. * for the window.
  253. * @throws IllegalArgumentException
  254. * if a window with the same name as the new window already
  255. * exists in the application.
  256. * @throws NullPointerException
  257. * if the given <code>Window</code> is <code>null</code>.
  258. */
  259. public void addWindow(Window window) throws IllegalArgumentException,
  260. NullPointerException {
  261. // Nulls can not be added to application
  262. if (window == null) {
  263. return;
  264. }
  265. // Check that one is not adding a sub-window to application
  266. if (window.getParent() != null) {
  267. throw new IllegalArgumentException(
  268. "Window was already added inside another window"
  269. + " - it can not be added to application also.");
  270. }
  271. // Gets the naming proposal from window
  272. String name = window.getName();
  273. // Checks that the application does not already contain
  274. // window having the same name
  275. if (name != null && windows.containsKey(name)) {
  276. // If the window is already added
  277. if (window == windows.get(name)) {
  278. return;
  279. }
  280. // Otherwise complain
  281. throw new IllegalArgumentException("Window with name '"
  282. + window.getName()
  283. + "' is already present in the application");
  284. }
  285. // If the name of the window is null, the window is automatically named
  286. if (name == null) {
  287. boolean accepted = false;
  288. while (!accepted) {
  289. // Try another name
  290. name = String.valueOf(Math.abs(nameGenerator.nextInt()));
  291. if (!windows.containsKey(name)) {
  292. accepted = true;
  293. }
  294. }
  295. window.setName(name);
  296. }
  297. // Adds the window to application
  298. windows.put(name, window);
  299. window.setApplication(this);
  300. fireWindowAttachEvent(window);
  301. // If no main window is set, declare the window to be main window
  302. if (getMainWindow() == null) {
  303. mainWindow = window;
  304. }
  305. }
  306. /**
  307. * Send information to all listeners about new Windows associated with this
  308. * application.
  309. *
  310. * @param window
  311. */
  312. private void fireWindowAttachEvent(Window window) {
  313. // Fires the window attach event
  314. if (windowAttachListeners != null) {
  315. final Object[] listeners = windowAttachListeners.toArray();
  316. final WindowAttachEvent event = new WindowAttachEvent(window);
  317. for (int i = 0; i < listeners.length; i++) {
  318. ((WindowAttachListener) listeners[i]).windowAttached(event);
  319. }
  320. }
  321. }
  322. /**
  323. * Removes the specified window from the application.
  324. *
  325. * @param window
  326. * the window to be removed.
  327. */
  328. public void removeWindow(Window window) {
  329. if (window != null && windows.contains(window)) {
  330. // Removes the window from application
  331. windows.remove(window.getName());
  332. // If the window was main window, clear it
  333. if (getMainWindow() == window) {
  334. setMainWindow(null);
  335. }
  336. // Removes the application from window
  337. if (window.getApplication() == this) {
  338. window.setApplication(null);
  339. }
  340. fireWindowDetachEvent(window);
  341. }
  342. }
  343. private void fireWindowDetachEvent(Window window) {
  344. // Fires the window detach event
  345. if (windowDetachListeners != null) {
  346. final Object[] listeners = windowDetachListeners.toArray();
  347. final WindowDetachEvent event = new WindowDetachEvent(window);
  348. for (int i = 0; i < listeners.length; i++) {
  349. ((WindowDetachListener) listeners[i]).windowDetached(event);
  350. }
  351. }
  352. }
  353. /**
  354. * Gets the user of the application.
  355. *
  356. * @return the User of the application.
  357. */
  358. public Object getUser() {
  359. return user;
  360. }
  361. /**
  362. * <p>
  363. * Sets the user of the application instance. An application instance may
  364. * have a user associated to it. This can be set in login procedure or
  365. * application initialization.
  366. * </p>
  367. * <p>
  368. * A component performing the user login procedure can assign the user
  369. * property of the application and make the user object available to other
  370. * components of the application.
  371. * </p>
  372. *
  373. * @param user
  374. * the new user.
  375. */
  376. public void setUser(Object user) {
  377. final Object prevUser = this.user;
  378. if (user == prevUser || (user != null && user.equals(prevUser))) {
  379. return;
  380. }
  381. this.user = user;
  382. if (userChangeListeners != null) {
  383. final Object[] listeners = userChangeListeners.toArray();
  384. final UserChangeEvent event = new UserChangeEvent(this, user,
  385. prevUser);
  386. for (int i = 0; i < listeners.length; i++) {
  387. ((UserChangeListener) listeners[i])
  388. .applicationUserChanged(event);
  389. }
  390. }
  391. }
  392. /**
  393. * Gets the URL of the application.
  394. *
  395. * @return the application's URL.
  396. */
  397. public URL getURL() {
  398. return applicationUrl;
  399. }
  400. /**
  401. * Ends the Application. In effect this will cause the application stop
  402. * returning any windows when asked.
  403. */
  404. public void close() {
  405. applicationIsRunning = false;
  406. }
  407. /**
  408. * Starts the application on the given URL.After this call the application
  409. * corresponds to the given URL and it will return windows when asked for
  410. * them.
  411. *
  412. * Application properties are defined by servlet configuration object
  413. * {@link javax.servlet.ServletConfig} and they are overridden by
  414. * context-wide initialization parameters
  415. * {@link javax.servlet.ServletContext}.
  416. *
  417. * @param applicationUrl
  418. * the URL the application should respond to.
  419. * @param applicationProperties
  420. * the Application properties as specified by the servlet
  421. * configuration.
  422. * @param context
  423. * the context application will be running in.
  424. *
  425. */
  426. public void start(URL applicationUrl, Properties applicationProperties,
  427. ApplicationContext context) {
  428. this.applicationUrl = applicationUrl;
  429. properties = applicationProperties;
  430. this.context = context;
  431. init();
  432. applicationIsRunning = true;
  433. }
  434. /**
  435. * Tests if the application is running or if it has been finished.
  436. *
  437. * @return <code>true</code> if the application is running,
  438. * <code>false</code> if not.
  439. */
  440. public boolean isRunning() {
  441. return applicationIsRunning;
  442. }
  443. /**
  444. * Gets the set of windows contained by the application.
  445. *
  446. * @return the Unmodifiable collection of windows.
  447. */
  448. public Collection getWindows() {
  449. return Collections.unmodifiableCollection(windows.values());
  450. }
  451. /**
  452. * <p>
  453. * Main initializer of the application. The <code>init</code> method is
  454. * called by the framework when the application is started, and it should
  455. * perform whatever initialization operations the application needs, such as
  456. * creating windows and adding components to them.
  457. * </p>
  458. */
  459. public abstract void init();
  460. /**
  461. * Gets the application's theme. The application's theme is the default
  462. * theme used by all the windows in it that do not explicitly specify a
  463. * theme. If the application theme is not explicitly set, the
  464. * <code>null</code> is returned.
  465. *
  466. * @return the name of the application's theme.
  467. */
  468. public String getTheme() {
  469. return theme;
  470. }
  471. /**
  472. * Sets the application's theme.
  473. * <p>
  474. * Note that this theme can be overridden by the windows. <code>null</code>
  475. * implies the default terminal theme.
  476. * </p>
  477. *
  478. * @param theme
  479. * the new theme for this application.
  480. */
  481. public void setTheme(String theme) {
  482. // Collect list of windows not having the current or future theme
  483. final LinkedList toBeUpdated = new LinkedList();
  484. final String oldAppTheme = getTheme();
  485. for (final Iterator i = getWindows().iterator(); i.hasNext();) {
  486. final Window w = (Window) i.next();
  487. final String windowTheme = w.getTheme();
  488. if ((windowTheme == null)
  489. || (!windowTheme.equals(theme) && windowTheme
  490. .equals(oldAppTheme))) {
  491. toBeUpdated.add(w);
  492. }
  493. }
  494. // Updates the theme
  495. this.theme = theme;
  496. // Ask windows to update themselves
  497. for (final Iterator i = toBeUpdated.iterator(); i.hasNext();) {
  498. ((Window) i.next()).requestRepaint();
  499. }
  500. }
  501. /**
  502. * Gets the mainWindow of the application.
  503. *
  504. * @return the main window.
  505. */
  506. public Window getMainWindow() {
  507. return mainWindow;
  508. }
  509. /**
  510. * <p>
  511. * Sets the mainWindow. If the main window is not explicitly set, the main
  512. * window defaults to first created window. Setting window as a main window
  513. * of this application also adds the window to this application.
  514. * </p>
  515. *
  516. * @param mainWindow
  517. * the mainWindow to set.
  518. */
  519. public void setMainWindow(Window mainWindow) {
  520. addWindow(mainWindow);
  521. this.mainWindow = mainWindow;
  522. }
  523. /**
  524. * Returns an enumeration of all the names in this application.
  525. *
  526. * See {@link #start(URL, Properties, ApplicationContext)} how properties
  527. * are defined.
  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.
  583. *
  584. * @param resource
  585. * the resource to get relative location.
  586. * @return the relative uri of the resource.
  587. */
  588. public String getRelativeLocation(ApplicationResource resource) {
  589. // Gets the key
  590. final String key = (String) resourceKeyMap.get(resource);
  591. // If the resource is not registered, return null
  592. if (key == null) {
  593. return null;
  594. }
  595. final String filename = resource.getFilename();
  596. if (filename == null) {
  597. return "APP/" + key + "/";
  598. } else {
  599. return "APP/" + key + "/" + filename;
  600. }
  601. }
  602. /**
  603. * This method gets called by terminal. It has lots of duties like to pass
  604. * uri handler to proper uri handlers registered to windows etc.
  605. *
  606. * In most situations developers should NOT OVERRIDE this method. Instead
  607. * developers should implement and register uri handlers to windows.
  608. *
  609. * @see com.vaadin.terminal.URIHandler#handleURI(URL, String)
  610. */
  611. public DownloadStream handleURI(URL context, String relativeUri) {
  612. // If the relative uri is null, we are ready
  613. if (relativeUri == null) {
  614. return null;
  615. }
  616. // Resolves the prefix
  617. String prefix = relativeUri;
  618. final int index = relativeUri.indexOf('/');
  619. if (index >= 0) {
  620. prefix = relativeUri.substring(0, index);
  621. }
  622. // Handles the resource requests
  623. if (prefix.equals("APP")) {
  624. // Handles the resource request
  625. final int next = relativeUri.indexOf('/', index + 1);
  626. if (next < 0) {
  627. return null;
  628. }
  629. final String key = relativeUri.substring(index + 1, next);
  630. final ApplicationResource resource = (ApplicationResource) keyResourceMap
  631. .get(key);
  632. if (resource != null) {
  633. DownloadStream stream = resource.getStream();
  634. if (stream != null) {
  635. stream.setCacheTime(resource.getCacheTime());
  636. return stream;
  637. } else {
  638. return null;
  639. }
  640. } else {
  641. // Resource requests override uri handling
  642. return null;
  643. }
  644. } else {
  645. return null;
  646. }
  647. }
  648. /**
  649. * Gets the default locale for this application.
  650. *
  651. * @return the locale of this application.
  652. */
  653. public Locale getLocale() {
  654. if (locale != null) {
  655. return locale;
  656. }
  657. return Locale.getDefault();
  658. }
  659. /**
  660. * Sets the default locale for this application.
  661. *
  662. * @param locale
  663. * the Locale object.
  664. *
  665. */
  666. public void setLocale(Locale locale) {
  667. this.locale = locale;
  668. }
  669. /**
  670. * <p>
  671. * An event that characterizes a change in the current selection.
  672. * </p>
  673. * Application user change event sent when the setUser is called to change
  674. * the current user of the application.
  675. *
  676. * @version
  677. * @VERSION@
  678. * @since 3.0
  679. */
  680. public class UserChangeEvent extends java.util.EventObject {
  681. /**
  682. * New user of the application.
  683. */
  684. private final Object newUser;
  685. /**
  686. * Previous user of the application.
  687. */
  688. private final Object prevUser;
  689. /**
  690. * Constructor for user change event.
  691. *
  692. * @param source
  693. * the application source.
  694. * @param newUser
  695. * the new User.
  696. * @param prevUser
  697. * the previous User.
  698. */
  699. public UserChangeEvent(Application source, Object newUser,
  700. Object prevUser) {
  701. super(source);
  702. this.newUser = newUser;
  703. this.prevUser = prevUser;
  704. }
  705. /**
  706. * Gets the new user of the application.
  707. *
  708. * @return the new User.
  709. */
  710. public Object getNewUser() {
  711. return newUser;
  712. }
  713. /**
  714. * Gets the previous user of the application.
  715. *
  716. * @return the previous Toolkit user, if user has not changed ever on
  717. * application it returns <code>null</code>
  718. */
  719. public Object getPreviousUser() {
  720. return prevUser;
  721. }
  722. /**
  723. * Gets the application where the user change occurred.
  724. *
  725. * @return the Application.
  726. */
  727. public Application getApplication() {
  728. return (Application) getSource();
  729. }
  730. }
  731. /**
  732. * The <code>UserChangeListener</code> interface for listening application
  733. * user changes.
  734. *
  735. * @version
  736. * @VERSION@
  737. * @since 3.0
  738. */
  739. public interface UserChangeListener extends EventListener, Serializable {
  740. /**
  741. * The <code>applicationUserChanged</code> method Invoked when the
  742. * application user has changed.
  743. *
  744. * @param event
  745. * the change event.
  746. */
  747. public void applicationUserChanged(Application.UserChangeEvent event);
  748. }
  749. /**
  750. * Adds the user change listener.
  751. *
  752. * @param listener
  753. * the user change listener to add.
  754. */
  755. public void addListener(UserChangeListener listener) {
  756. if (userChangeListeners == null) {
  757. userChangeListeners = new LinkedList();
  758. }
  759. userChangeListeners.add(listener);
  760. }
  761. /**
  762. * Removes the user change listener.
  763. *
  764. * @param listener
  765. * the user change listener to remove.
  766. */
  767. public void removeListener(UserChangeListener listener) {
  768. if (userChangeListeners == null) {
  769. return;
  770. }
  771. userChangeListeners.remove(listener);
  772. if (userChangeListeners.isEmpty()) {
  773. userChangeListeners = null;
  774. }
  775. }
  776. /**
  777. * Window detach event.
  778. */
  779. public class WindowDetachEvent extends EventObject {
  780. private final Window window;
  781. /**
  782. * Creates a event.
  783. *
  784. * @param window
  785. * the Detached window.
  786. */
  787. public WindowDetachEvent(Window window) {
  788. super(Application.this);
  789. this.window = window;
  790. }
  791. /**
  792. * Gets the detached window.
  793. *
  794. * @return the detached window.
  795. */
  796. public Window getWindow() {
  797. return window;
  798. }
  799. /**
  800. * Gets the application from which the window was detached.
  801. *
  802. * @return the Application.
  803. */
  804. public Application getApplication() {
  805. return (Application) getSource();
  806. }
  807. }
  808. /**
  809. * Window attach event.
  810. */
  811. public class WindowAttachEvent extends EventObject {
  812. private final Window window;
  813. /**
  814. * Creates a event.
  815. *
  816. * @param window
  817. * the Attached window.
  818. */
  819. public WindowAttachEvent(Window window) {
  820. super(Application.this);
  821. this.window = window;
  822. }
  823. /**
  824. * Gets the attached window.
  825. *
  826. * @return the attached window.
  827. */
  828. public Window getWindow() {
  829. return window;
  830. }
  831. /**
  832. * Gets the application to which the window was attached.
  833. *
  834. * @return the Application.
  835. */
  836. public Application getApplication() {
  837. return (Application) getSource();
  838. }
  839. }
  840. /**
  841. * Window attach listener interface.
  842. */
  843. public interface WindowAttachListener extends Serializable {
  844. /**
  845. * Window attached
  846. *
  847. * @param event
  848. * the window attach event.
  849. */
  850. public void windowAttached(WindowAttachEvent event);
  851. }
  852. /**
  853. * Window detach listener interface.
  854. */
  855. public interface WindowDetachListener extends Serializable {
  856. /**
  857. * Window detached.
  858. *
  859. * @param event
  860. * the window detach event.
  861. */
  862. public void windowDetached(WindowDetachEvent event);
  863. }
  864. /**
  865. * Adds the window attach listener.
  866. *
  867. * @param listener
  868. * the window attach listener to add.
  869. */
  870. public void addListener(WindowAttachListener listener) {
  871. if (windowAttachListeners == null) {
  872. windowAttachListeners = new LinkedList();
  873. }
  874. windowAttachListeners.add(listener);
  875. }
  876. /**
  877. * Adds the window detach listener.
  878. *
  879. * @param listener
  880. * the window detach listener to add.
  881. */
  882. public void addListener(WindowDetachListener listener) {
  883. if (windowDetachListeners == null) {
  884. windowDetachListeners = new LinkedList();
  885. }
  886. windowDetachListeners.add(listener);
  887. }
  888. /**
  889. * Removes the window attach listener.
  890. *
  891. * @param listener
  892. * the window attach listener to remove.
  893. */
  894. public void removeListener(WindowAttachListener listener) {
  895. if (windowAttachListeners != null) {
  896. windowAttachListeners.remove(listener);
  897. if (windowAttachListeners.isEmpty()) {
  898. windowAttachListeners = null;
  899. }
  900. }
  901. }
  902. /**
  903. * Removes the window detach listener.
  904. *
  905. * @param listener
  906. * the window detach listener to remove.
  907. */
  908. public void removeListener(WindowDetachListener listener) {
  909. if (windowDetachListeners != null) {
  910. windowDetachListeners.remove(listener);
  911. if (windowDetachListeners.isEmpty()) {
  912. windowDetachListeners = null;
  913. }
  914. }
  915. }
  916. /**
  917. * Returns the URL user is redirected to on application close. If the URL is
  918. * <code>null</code>, the application is closed normally as defined by the
  919. * application running environment.
  920. * <p>
  921. * Desktop application just closes the application window and
  922. * web-application redirects the browser to application main URL.
  923. * </p>
  924. *
  925. * @return the URL.
  926. */
  927. public String getLogoutURL() {
  928. return logoutURL;
  929. }
  930. /**
  931. * Sets the URL user is redirected to on application close. If the URL is
  932. * <code>null</code>, the application is closed normally as defined by the
  933. * application running environment: Desktop application just closes the
  934. * application window and web-application redirects the browser to
  935. * application main URL.
  936. *
  937. * @param logoutURL
  938. * the logoutURL to set.
  939. */
  940. public void setLogoutURL(String logoutURL) {
  941. this.logoutURL = logoutURL;
  942. }
  943. /**
  944. * Gets the SystemMessages for this application. SystemMessages are used to
  945. * notify the user of various critical situations that can occur, such as
  946. * session expiration, client/server out of sync, and internal server error.
  947. *
  948. * You can customize the messages by overriding this method and returning
  949. * {@link CustomizedSystemMessages}
  950. *
  951. * @return the SystemMessages for this application
  952. */
  953. public static SystemMessages getSystemMessages() {
  954. return DEFAULT_SYSTEM_MESSAGES;
  955. }
  956. /**
  957. * <p>
  958. * Invoked by the terminal on any exception that occurs in application and
  959. * is thrown by the <code>setVariable</code> to the terminal. The default
  960. * implementation sets the exceptions as <code>ComponentErrors</code> to the
  961. * component that initiated the exception and prints stack trace to standard
  962. * error stream.
  963. * </p>
  964. * <p>
  965. * You can safely override this method in your application in order to
  966. * direct the errors to some other destination (for example log).
  967. * </p>
  968. *
  969. * @param event
  970. * the change event.
  971. * @see com.vaadin.terminal.Terminal.ErrorListener#terminalError(com.vaadin.terminal.Terminal.ErrorEvent)
  972. */
  973. public void terminalError(Terminal.ErrorEvent event) {
  974. final Throwable t = event.getThrowable();
  975. if (t instanceof SocketException) {
  976. // Most likely client browser closed socket
  977. System.err
  978. .println("Warning: SocketException in CommunicationManager."
  979. + " Most likely client (browser) closed socket.");
  980. return;
  981. }
  982. // Finds the original source of the error/exception
  983. Object owner = null;
  984. if (event instanceof VariableOwner.ErrorEvent) {
  985. owner = ((VariableOwner.ErrorEvent) event).getVariableOwner();
  986. } else if (event instanceof URIHandler.ErrorEvent) {
  987. owner = ((URIHandler.ErrorEvent) event).getURIHandler();
  988. } else if (event instanceof ParameterHandler.ErrorEvent) {
  989. owner = ((ParameterHandler.ErrorEvent) event).getParameterHandler();
  990. } else if (event instanceof ChangeVariablesErrorEvent) {
  991. owner = ((ChangeVariablesErrorEvent) event).getComponent();
  992. }
  993. // Shows the error in AbstractComponent
  994. if (owner instanceof AbstractComponent) {
  995. if (t instanceof ErrorMessage) {
  996. ((AbstractComponent) owner).setComponentError((ErrorMessage) t);
  997. } else {
  998. ((AbstractComponent) owner)
  999. .setComponentError(new SystemError(t));
  1000. }
  1001. }
  1002. // also print the error on console
  1003. t.printStackTrace();
  1004. }
  1005. /**
  1006. * Gets the application context.
  1007. * <p>
  1008. * The application context is the environment where the application is
  1009. * running in.
  1010. * </p>
  1011. *
  1012. * @return the application context.
  1013. */
  1014. public ApplicationContext getContext() {
  1015. return context;
  1016. }
  1017. /**
  1018. * Override this method to return correct version number of your
  1019. * Application. Version information is delivered for example to Testing
  1020. * Tools test results.
  1021. *
  1022. * @return version string
  1023. */
  1024. public String getVersion() {
  1025. return "NONVERSIONED";
  1026. }
  1027. /**
  1028. * Gets the application error handler.
  1029. *
  1030. * The default error handler is the application itself.
  1031. *
  1032. * @return Application error handler
  1033. */
  1034. public Terminal.ErrorListener getErrorHandler() {
  1035. return errorHandler;
  1036. }
  1037. /**
  1038. * Sets the application error handler.
  1039. *
  1040. * The default error handler is the application itself.
  1041. *
  1042. * @param errorHandler
  1043. */
  1044. public void setErrorHandler(Terminal.ErrorListener errorHandler) {
  1045. this.errorHandler = errorHandler;
  1046. }
  1047. /**
  1048. * Contains the system messages used to notify the user about various
  1049. * critical situations that can occur.
  1050. * <p>
  1051. * Customize by overriding the static Application.getSystemMessages() and
  1052. * returning {@link CustomizedSystemMessages}
  1053. * </p>
  1054. *
  1055. */
  1056. public static class SystemMessages implements Serializable {
  1057. protected String sessionExpiredURL = null;
  1058. protected boolean sessionExpiredNotificationEnabled = true;
  1059. protected String sessionExpiredCaption = "Session Expired";
  1060. protected String sessionExpiredMessage = "Take note of any unsaved data, and <u>click here</u> to continue.";
  1061. protected String communicationErrorURL = null;
  1062. protected boolean communicationErrorNotificationEnabled = true;
  1063. protected String communicationErrorCaption = "Communication problem";
  1064. protected String communicationErrorMessage = "Take note of any unsaved data, and <u>click here</u> to continue.";
  1065. protected String internalErrorURL = null;
  1066. protected boolean internalErrorNotificationEnabled = true;
  1067. protected String internalErrorCaption = "Internal Error";
  1068. protected String internalErrorMessage = "Please notify the administrator.<br/>Take note of any unsaved data, and <u>click here</u> to continue.";
  1069. protected String outOfSyncURL = null;
  1070. protected boolean outOfSyncNotificationEnabled = true;
  1071. protected String outOfSyncCaption = "Out of sync";
  1072. 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.";
  1073. /**
  1074. * Use {@link CustomizedSystemMessages} to customize
  1075. */
  1076. private SystemMessages() {
  1077. }
  1078. /**
  1079. * @return the URL to load on null for restart
  1080. */
  1081. public String getSessionExpiredURL() {
  1082. return sessionExpiredURL;
  1083. }
  1084. /**
  1085. * @return true = notification enabled, false = notification disabled
  1086. */
  1087. public boolean isSessionExpiredNotificationEnabled() {
  1088. return sessionExpiredNotificationEnabled;
  1089. }
  1090. /**
  1091. * @return the notification caption, or null for no caption
  1092. */
  1093. public String getSessionExpiredCaption() {
  1094. return (sessionExpiredNotificationEnabled ? sessionExpiredCaption
  1095. : null);
  1096. }
  1097. /**
  1098. * @return the notification message, or null for no message
  1099. */
  1100. public String getSessionExpiredMessage() {
  1101. return (sessionExpiredNotificationEnabled ? sessionExpiredMessage
  1102. : null);
  1103. }
  1104. /**
  1105. * @return the URL to load on null for restart
  1106. */
  1107. public String getCommunicationErrorURL() {
  1108. return communicationErrorURL;
  1109. }
  1110. /**
  1111. * @return true = notification enabled, false = notification disabled
  1112. */
  1113. public boolean isCommunicationErrorNotificationEnabled() {
  1114. return communicationErrorNotificationEnabled;
  1115. }
  1116. /**
  1117. * @return the notification caption, or null for no caption
  1118. */
  1119. public String getCommunicationErrorCaption() {
  1120. return (communicationErrorNotificationEnabled ? communicationErrorCaption
  1121. : null);
  1122. }
  1123. /**
  1124. * @return the notification message, or null for no message
  1125. */
  1126. public String getCommunicationErrorMessage() {
  1127. return (communicationErrorNotificationEnabled ? communicationErrorMessage
  1128. : null);
  1129. }
  1130. /**
  1131. * @return the URL to load on null for restart
  1132. */
  1133. public String getInternalErrorURL() {
  1134. return internalErrorURL;
  1135. }
  1136. /**
  1137. * @return true = notification enabled, false = notification disabled
  1138. */
  1139. public boolean isInternalErrorNotificationEnabled() {
  1140. return internalErrorNotificationEnabled;
  1141. }
  1142. /**
  1143. * @return the notification caption, or null for no caption
  1144. */
  1145. public String getInternalErrorCaption() {
  1146. return (internalErrorNotificationEnabled ? internalErrorCaption
  1147. : null);
  1148. }
  1149. /**
  1150. * @return the notification message, or null for no message
  1151. */
  1152. public String getInternalErrorMessage() {
  1153. return (internalErrorNotificationEnabled ? internalErrorMessage
  1154. : null);
  1155. }
  1156. /**
  1157. * @return the URL to load on null for restart
  1158. */
  1159. public String getOutOfSyncURL() {
  1160. return outOfSyncURL;
  1161. }
  1162. /**
  1163. * @return true = notification enabled, false = notification disabled
  1164. */
  1165. public boolean isOutOfSyncNotificationEnabled() {
  1166. return outOfSyncNotificationEnabled;
  1167. }
  1168. /**
  1169. * @return the notification caption, or null for no caption
  1170. */
  1171. public String getOutOfSyncCaption() {
  1172. return (outOfSyncNotificationEnabled ? outOfSyncCaption : null);
  1173. }
  1174. /**
  1175. * @return the notification message, or null for no message
  1176. */
  1177. public String getOutOfSyncMessage() {
  1178. return (outOfSyncNotificationEnabled ? outOfSyncMessage : null);
  1179. }
  1180. }
  1181. /**
  1182. * Contains the system messages used to notify the user about various
  1183. * critical situations that can occur.
  1184. * <p>
  1185. * Customize by overriding the static Application.getSystemMessages() and
  1186. * returning CustomizedSystemMessages. Note that getSystemMessages() is
  1187. * static - changing the system messages will by default change the message
  1188. * for all users of the application.
  1189. * </p>
  1190. * <p>
  1191. * The default behavior is to show a notification, and restart the
  1192. * application the the user clicks the message. <br/>
  1193. * Instead of restarting the application, you can set a specific URL that
  1194. * the user is taken to.<br/>
  1195. * Setting both caption and message to null will restart the application (or
  1196. * go to the specified URL) without displaying a notification.
  1197. * set*NotificationEnabled(false) will achieve the same thing.
  1198. * </p>
  1199. * <p>
  1200. * The situations are:
  1201. * <li>Session expired: the user session has expired, usually due to
  1202. * inactivity.</li>
  1203. * <li>Communication error: the client failed to contact the server, or the
  1204. * server returned and invalid response.</li>
  1205. * <li>Internal error: unhandled critical server error (e.g out of memory,
  1206. * database crash)
  1207. * <li>Out of sync: the client is not in sync with the server. E.g the user
  1208. * opens two windows showing the same application, but the application does
  1209. * not support this and uses the same Window instance. When the user makes
  1210. * changes in one of the windows - the other window is no longer in sync,
  1211. * and (for instance) pressing a button that is no longer present in the UI
  1212. * will cause a out-of-sync -situation.
  1213. * </p>
  1214. */
  1215. public static class CustomizedSystemMessages extends SystemMessages
  1216. implements Serializable {
  1217. /**
  1218. * Sets the URL to go to when the session has expired.
  1219. *
  1220. * @param sessionExpiredURL
  1221. * the URL to go to, or null to reload current
  1222. */
  1223. public void setSessionExpiredURL(String sessionExpiredURL) {
  1224. this.sessionExpiredURL = sessionExpiredURL;
  1225. }
  1226. /**
  1227. * Enables or disables the notification. If disabled, the set URL (or
  1228. * current) is loaded directly when next transaction between server and
  1229. * client happens.
  1230. *
  1231. * @param sessionExpiredNotificationEnabled
  1232. * true = enabled, false = disabled
  1233. */
  1234. public void setSessionExpiredNotificationEnabled(
  1235. boolean sessionExpiredNotificationEnabled) {
  1236. this.sessionExpiredNotificationEnabled = sessionExpiredNotificationEnabled;
  1237. }
  1238. /**
  1239. * Sets the caption of the notification. Set to null for no caption. If
  1240. * both caption and message are null, client automatically forwards to
  1241. * sessionExpiredUrl after timeout timer expires. Timer uses value read
  1242. * from HTTPSession.getMaxInactiveInterval()
  1243. *
  1244. * @param sessionExpiredCaption
  1245. * the caption
  1246. */
  1247. public void setSessionExpiredCaption(String sessionExpiredCaption) {
  1248. this.sessionExpiredCaption = sessionExpiredCaption;
  1249. }
  1250. /**
  1251. * Sets the message of the notification. Set to null for no message. If
  1252. * both caption and message are null, client automatically forwards to
  1253. * sessionExpiredUrl after timeout timer expires. Timer uses value read
  1254. * from HTTPSession.getMaxInactiveInterval()
  1255. *
  1256. * @param sessionExpiredMessage
  1257. * the message
  1258. */
  1259. public void setSessionExpiredMessage(String sessionExpiredMessage) {
  1260. this.sessionExpiredMessage = sessionExpiredMessage;
  1261. }
  1262. /**
  1263. * Sets the URL to go to when there is a communication error.
  1264. *
  1265. * @param communicationErrorURL
  1266. * the URL to go to, or null to reload current
  1267. */
  1268. public void setCommunicationErrorURL(String communicationErrorURL) {
  1269. this.communicationErrorURL = communicationErrorURL;
  1270. }
  1271. /**
  1272. * Enables or disables the notification. If disabled, the set URL (or
  1273. * current) is loaded directly.
  1274. *
  1275. * @param communicationErrorNotificationEnabled
  1276. * true = enabled, false = disabled
  1277. */
  1278. public void setCommunicationErrorNotificationEnabled(
  1279. boolean communicationErrorNotificationEnabled) {
  1280. this.communicationErrorNotificationEnabled = communicationErrorNotificationEnabled;
  1281. }
  1282. /**
  1283. * Sets the caption of the notification. Set to null for no caption. If
  1284. * both caption and message is null, the notification is disabled;
  1285. *
  1286. * @param communicationErrorCaption
  1287. * the caption
  1288. */
  1289. public void setCommunicationErrorCaption(
  1290. String communicationErrorCaption) {
  1291. this.communicationErrorCaption = communicationErrorCaption;
  1292. }
  1293. /**
  1294. * Sets the message of the notification. Set to null for no message. If
  1295. * both caption and message is null, the notification is disabled;
  1296. *
  1297. * @param communicationErrorMessage
  1298. * the message
  1299. */
  1300. public void setCommunicationErrorMessage(
  1301. String communicationErrorMessage) {
  1302. this.communicationErrorMessage = communicationErrorMessage;
  1303. }
  1304. /**
  1305. * Sets the URL to go to when an internal error occurs.
  1306. *
  1307. * @param internalErrorURL
  1308. * the URL to go to, or null to reload current
  1309. */
  1310. public void setInternalErrorURL(String internalErrorURL) {
  1311. this.internalErrorURL = internalErrorURL;
  1312. }
  1313. /**
  1314. * Enables or disables the notification. If disabled, the set URL (or
  1315. * current) is loaded directly.
  1316. *
  1317. * @param internalErrorNotificationEnabled
  1318. * true = enabled, false = disabled
  1319. */
  1320. public void setInternalErrorNotificationEnabled(
  1321. boolean internalErrorNotificationEnabled) {
  1322. this.internalErrorNotificationEnabled = internalErrorNotificationEnabled;
  1323. }
  1324. /**
  1325. * Sets the caption of the notification. Set to null for no caption. If
  1326. * both caption and message is null, the notification is disabled;
  1327. *
  1328. * @param internalErrorCaption
  1329. * the caption
  1330. */
  1331. public void setInternalErrorCaption(String internalErrorCaption) {
  1332. this.internalErrorCaption = internalErrorCaption;
  1333. }
  1334. /**
  1335. * Sets the message of the notification. Set to null for no message. If
  1336. * both caption and message is null, the notification is disabled;
  1337. *
  1338. * @param internalErrorMessage
  1339. * the message
  1340. */
  1341. public void setInternalErrorMessage(String internalErrorMessage) {
  1342. this.internalErrorMessage = internalErrorMessage;
  1343. }
  1344. /**
  1345. * Sets the URL to go to when the client is out-of-sync.
  1346. *
  1347. * @param outOfSyncURL
  1348. * the URL to go to, or null to reload current
  1349. */
  1350. public void setOutOfSyncURL(String outOfSyncURL) {
  1351. this.outOfSyncURL = outOfSyncURL;
  1352. }
  1353. /**
  1354. * Enables or disables the notification. If disabled, the set URL (or
  1355. * current) is loaded directly.
  1356. *
  1357. * @param outOfSyncNotificationEnabled
  1358. * true = enabled, false = disabled
  1359. */
  1360. public void setOutOfSyncNotificationEnabled(
  1361. boolean outOfSyncNotificationEnabled) {
  1362. this.outOfSyncNotificationEnabled = outOfSyncNotificationEnabled;
  1363. }
  1364. /**
  1365. * Sets the caption of the notification. Set to null for no caption. If
  1366. * both caption and message is null, the notification is disabled;
  1367. *
  1368. * @param outOfSyncCaption
  1369. * the caption
  1370. */
  1371. public void setOutOfSyncCaption(String outOfSyncCaption) {
  1372. this.outOfSyncCaption = outOfSyncCaption;
  1373. }
  1374. /**
  1375. * Sets the message of the notification. Set to null for no message. If
  1376. * both caption and message is null, the notification is disabled;
  1377. *
  1378. * @param outOfSyncMessage
  1379. * the message
  1380. */
  1381. public void setOutOfSyncMessage(String outOfSyncMessage) {
  1382. this.outOfSyncMessage = outOfSyncMessage;
  1383. }
  1384. }
  1385. public class ApplicationError implements Terminal.ErrorEvent {
  1386. private final Throwable throwable;
  1387. public ApplicationError(Throwable throwable) {
  1388. this.throwable = throwable;
  1389. }
  1390. public Throwable getThrowable() {
  1391. return throwable;
  1392. }
  1393. }
  1394. }