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

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