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.

UI.java 54KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629
  1. /*
  2. * Copyright 2000-2014 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.ui;
  17. import java.net.URI;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.Iterator;
  22. import java.util.LinkedHashSet;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.concurrent.Future;
  26. import java.util.logging.Level;
  27. import java.util.logging.Logger;
  28. import com.vaadin.annotations.PreserveOnRefresh;
  29. import com.vaadin.event.Action;
  30. import com.vaadin.event.Action.Handler;
  31. import com.vaadin.event.ActionManager;
  32. import com.vaadin.event.MouseEvents.ClickEvent;
  33. import com.vaadin.event.MouseEvents.ClickListener;
  34. import com.vaadin.event.UIEvents.PollEvent;
  35. import com.vaadin.event.UIEvents.PollListener;
  36. import com.vaadin.event.UIEvents.PollNotifier;
  37. import com.vaadin.navigator.Navigator;
  38. import com.vaadin.server.ClientConnector;
  39. import com.vaadin.server.ComponentSizeValidator;
  40. import com.vaadin.server.ComponentSizeValidator.InvalidLayout;
  41. import com.vaadin.server.DefaultErrorHandler;
  42. import com.vaadin.server.ErrorHandler;
  43. import com.vaadin.server.ErrorHandlingRunnable;
  44. import com.vaadin.server.LocaleService;
  45. import com.vaadin.server.Page;
  46. import com.vaadin.server.PaintException;
  47. import com.vaadin.server.PaintTarget;
  48. import com.vaadin.server.UIProvider;
  49. import com.vaadin.server.VaadinRequest;
  50. import com.vaadin.server.VaadinService;
  51. import com.vaadin.server.VaadinServlet;
  52. import com.vaadin.server.VaadinSession;
  53. import com.vaadin.server.VaadinSession.State;
  54. import com.vaadin.server.communication.PushConnection;
  55. import com.vaadin.shared.Connector;
  56. import com.vaadin.shared.EventId;
  57. import com.vaadin.shared.MouseEventDetails;
  58. import com.vaadin.shared.communication.PushMode;
  59. import com.vaadin.shared.ui.ui.DebugWindowClientRpc;
  60. import com.vaadin.shared.ui.ui.DebugWindowServerRpc;
  61. import com.vaadin.shared.ui.ui.ScrollClientRpc;
  62. import com.vaadin.shared.ui.ui.UIClientRpc;
  63. import com.vaadin.shared.ui.ui.UIConstants;
  64. import com.vaadin.shared.ui.ui.UIServerRpc;
  65. import com.vaadin.shared.ui.ui.UIState;
  66. import com.vaadin.ui.Component.Focusable;
  67. import com.vaadin.util.ConnectorHelper;
  68. import com.vaadin.util.CurrentInstance;
  69. /**
  70. * The topmost component in any component hierarchy. There is one UI for every
  71. * Vaadin instance in a browser window. A UI may either represent an entire
  72. * browser window (or tab) or some part of a html page where a Vaadin
  73. * application is embedded.
  74. * <p>
  75. * The UI is the server side entry point for various client side features that
  76. * are not represented as components added to a layout, e.g notifications, sub
  77. * windows, and executing javascript in the browser.
  78. * </p>
  79. * <p>
  80. * When a new UI instance is needed, typically because the user opens a URL in a
  81. * browser window which points to e.g. {@link VaadinServlet}, all
  82. * {@link UIProvider}s registered to the current {@link VaadinSession} are
  83. * queried for the UI class that should be used. The selection is by default
  84. * based on the <code>UI</code> init parameter from web.xml.
  85. * </p>
  86. * <p>
  87. * After a UI has been created by the application, it is initialized using
  88. * {@link #init(VaadinRequest)}. This method is intended to be overridden by the
  89. * developer to add components to the user interface and initialize
  90. * non-component functionality. The component hierarchy must be initialized by
  91. * passing a {@link Component} with the main layout or other content of the view
  92. * to {@link #setContent(Component)} or to the constructor of the UI.
  93. * </p>
  94. *
  95. * @see #init(VaadinRequest)
  96. * @see UIProvider
  97. *
  98. * @since 7.0
  99. */
  100. public abstract class UI extends AbstractSingleComponentContainer implements
  101. Action.Container, Action.Notifier, PollNotifier, LegacyComponent,
  102. Focusable {
  103. /**
  104. * The application to which this UI belongs
  105. */
  106. private volatile VaadinSession session;
  107. /**
  108. * List of windows in this UI.
  109. */
  110. private final LinkedHashSet<Window> windows = new LinkedHashSet<Window>();
  111. /**
  112. * The component that should be scrolled into view after the next repaint.
  113. * Null if nothing should be scrolled into view.
  114. */
  115. private Component scrollIntoView;
  116. /**
  117. * The id of this UI, used to find the server side instance of the UI form
  118. * which a request originates. A negative value indicates that the UI id has
  119. * not yet been assigned by the Application.
  120. *
  121. * @see VaadinSession#getNextUIid()
  122. */
  123. private int uiId = -1;
  124. /**
  125. * Keeps track of the Actions added to this component, and manages the
  126. * painting and handling as well.
  127. */
  128. protected ActionManager actionManager;
  129. /** Identifies the click event */
  130. private ConnectorTracker connectorTracker = new ConnectorTracker(this);
  131. private Page page = new Page(this, getState(false).pageState);
  132. private LoadingIndicatorConfiguration loadingIndicatorConfiguration = new LoadingIndicatorConfigurationImpl(
  133. this);
  134. /**
  135. * Scroll Y position.
  136. */
  137. private int scrollTop = 0;
  138. /**
  139. * Scroll X position
  140. */
  141. private int scrollLeft = 0;
  142. private UIServerRpc rpc = new UIServerRpc() {
  143. @Override
  144. public void click(MouseEventDetails mouseDetails) {
  145. fireEvent(new ClickEvent(UI.this, mouseDetails));
  146. }
  147. @Override
  148. public void resize(int viewWidth, int viewHeight, int windowWidth,
  149. int windowHeight) {
  150. // TODO We're not doing anything with the view dimensions
  151. getPage().updateBrowserWindowSize(windowWidth, windowHeight, true);
  152. }
  153. @Override
  154. public void scroll(int scrollTop, int scrollLeft) {
  155. UI.this.scrollTop = scrollTop;
  156. UI.this.scrollLeft = scrollLeft;
  157. }
  158. @Override
  159. public void poll() {
  160. fireEvent(new PollEvent(UI.this));
  161. }
  162. };
  163. private DebugWindowServerRpc debugRpc = new DebugWindowServerRpc() {
  164. @Override
  165. public void showServerDebugInfo(Connector connector) {
  166. String info = ConnectorHelper
  167. .getDebugInformation((ClientConnector) connector);
  168. getLogger().info(info);
  169. }
  170. @Override
  171. public void analyzeLayouts() {
  172. // TODO Move to client side
  173. List<InvalidLayout> invalidSizes = ComponentSizeValidator
  174. .validateLayouts(UI.this);
  175. StringBuilder json = new StringBuilder();
  176. json.append("{\"invalidLayouts\":");
  177. json.append("[");
  178. if (invalidSizes != null) {
  179. boolean first = true;
  180. for (InvalidLayout invalidSize : invalidSizes) {
  181. if (!first) {
  182. json.append(",");
  183. } else {
  184. first = false;
  185. }
  186. invalidSize.reportErrors(json, System.err);
  187. }
  188. }
  189. json.append("]}");
  190. getRpcProxy(DebugWindowClientRpc.class).reportLayoutProblems(
  191. json.toString());
  192. }
  193. };
  194. /**
  195. * Timestamp keeping track of the last heartbeat of this UI. Updated to the
  196. * current time whenever the application receives a heartbeat or UIDL
  197. * request from the client for this UI.
  198. */
  199. private long lastHeartbeatTimestamp = System.currentTimeMillis();
  200. private boolean closing = false;
  201. private TooltipConfiguration tooltipConfiguration = new TooltipConfigurationImpl(
  202. this);
  203. private PushConfiguration pushConfiguration = new PushConfigurationImpl(
  204. this);
  205. private NotificationConfiguration notificationConfiguration = new NotificationConfigurationImpl(
  206. this);
  207. /**
  208. * Creates a new empty UI without a caption. The content of the UI must be
  209. * set by calling {@link #setContent(Component)} before using the UI.
  210. */
  211. public UI() {
  212. this(null);
  213. }
  214. /**
  215. * Creates a new UI with the given component (often a layout) as its
  216. * content.
  217. *
  218. * @param content
  219. * the component to use as this UIs content.
  220. *
  221. * @see #setContent(Component)
  222. */
  223. public UI(Component content) {
  224. registerRpc(rpc);
  225. registerRpc(debugRpc);
  226. setSizeFull();
  227. setContent(content);
  228. }
  229. @Override
  230. protected UIState getState() {
  231. return (UIState) super.getState();
  232. }
  233. @Override
  234. protected UIState getState(boolean markAsDirty) {
  235. return (UIState) super.getState(markAsDirty);
  236. }
  237. @Override
  238. public Class<? extends UIState> getStateType() {
  239. // This is a workaround for a problem with creating the correct state
  240. // object during build
  241. return UIState.class;
  242. }
  243. /**
  244. * Overridden to return a value instead of referring to the parent.
  245. *
  246. * @return this UI
  247. *
  248. * @see com.vaadin.ui.AbstractComponent#getUI()
  249. */
  250. @Override
  251. public UI getUI() {
  252. return this;
  253. }
  254. /**
  255. * Gets the application object to which the component is attached.
  256. *
  257. * <p>
  258. * The method will return {@code null} if the component is not currently
  259. * attached to an application.
  260. * </p>
  261. *
  262. * <p>
  263. * Getting a null value is often a problem in constructors of regular
  264. * components and in the initializers of custom composite components. A
  265. * standard workaround is to use {@link VaadinSession#getCurrent()} to
  266. * retrieve the application instance that the current request relates to.
  267. * Another way is to move the problematic initialization to
  268. * {@link #attach()}, as described in the documentation of the method.
  269. * </p>
  270. *
  271. * @return the parent application of the component or <code>null</code>.
  272. * @see #attach()
  273. */
  274. @Override
  275. public VaadinSession getSession() {
  276. return session;
  277. }
  278. @Override
  279. public void paintContent(PaintTarget target) throws PaintException {
  280. page.paintContent(target);
  281. if (scrollIntoView != null) {
  282. target.addAttribute("scrollTo", scrollIntoView);
  283. scrollIntoView = null;
  284. }
  285. if (pendingFocus != null) {
  286. // ensure focused component is still attached to this main window
  287. if (pendingFocus.getUI() == this
  288. || (pendingFocus.getUI() != null && pendingFocus.getUI()
  289. .getParent() == this)) {
  290. target.addAttribute("focused", pendingFocus);
  291. }
  292. pendingFocus = null;
  293. }
  294. if (actionManager != null) {
  295. actionManager.paintActions(null, target);
  296. }
  297. if (isResizeLazy()) {
  298. target.addAttribute(UIConstants.RESIZE_LAZY, true);
  299. }
  300. }
  301. /**
  302. * Fire a click event to all click listeners.
  303. *
  304. * @param object
  305. * The raw "value" of the variable change from the client side.
  306. */
  307. private void fireClick(Map<String, Object> parameters) {
  308. MouseEventDetails mouseDetails = MouseEventDetails
  309. .deSerialize((String) parameters.get("mouseDetails"));
  310. fireEvent(new ClickEvent(this, mouseDetails));
  311. }
  312. @Override
  313. @SuppressWarnings("unchecked")
  314. public void changeVariables(Object source, Map<String, Object> variables) {
  315. if (variables.containsKey(EventId.CLICK_EVENT_IDENTIFIER)) {
  316. fireClick((Map<String, Object>) variables
  317. .get(EventId.CLICK_EVENT_IDENTIFIER));
  318. }
  319. // Actions
  320. if (actionManager != null) {
  321. actionManager.handleActions(variables, this);
  322. }
  323. if (variables.containsKey(UIConstants.LOCATION_VARIABLE)) {
  324. String location = (String) variables
  325. .get(UIConstants.LOCATION_VARIABLE);
  326. getPage().updateLocation(location, true);
  327. }
  328. }
  329. /*
  330. * (non-Javadoc)
  331. *
  332. * @see com.vaadin.ui.HasComponents#iterator()
  333. */
  334. @Override
  335. public Iterator<Component> iterator() {
  336. // TODO could directly create some kind of combined iterator instead of
  337. // creating a new ArrayList
  338. ArrayList<Component> components = new ArrayList<Component>();
  339. if (getContent() != null) {
  340. components.add(getContent());
  341. }
  342. components.addAll(windows);
  343. return components.iterator();
  344. }
  345. /*
  346. * (non-Javadoc)
  347. *
  348. * @see com.vaadin.ui.ComponentContainer#getComponentCount()
  349. */
  350. @Override
  351. public int getComponentCount() {
  352. return windows.size() + (getContent() == null ? 0 : 1);
  353. }
  354. /**
  355. * Sets the session to which this UI is assigned.
  356. * <p>
  357. * This method is for internal use by the framework. To explicitly close a
  358. * UI, see {@link #close()}.
  359. * </p>
  360. *
  361. * @param session
  362. * the session to set
  363. *
  364. * @throws IllegalStateException
  365. * if the session has already been set
  366. *
  367. * @see #getSession()
  368. */
  369. public void setSession(VaadinSession session) {
  370. if (session == null && this.session == null) {
  371. throw new IllegalStateException(
  372. "Session should never be set to null when UI.session is already null");
  373. } else if (session != null && this.session != null) {
  374. throw new IllegalStateException(
  375. "Session has already been set. Old session: "
  376. + getSessionDetails(this.session)
  377. + ". New session: " + getSessionDetails(session)
  378. + ".");
  379. } else {
  380. if (session == null) {
  381. detach();
  382. // Disable push when the UI is detached. Otherwise the
  383. // push connection and possibly VaadinSession will live on.
  384. getPushConfiguration().setPushMode(PushMode.DISABLED);
  385. setPushConnection(null);
  386. }
  387. this.session = session;
  388. }
  389. if (session != null) {
  390. attach();
  391. }
  392. }
  393. private static String getSessionDetails(VaadinSession session) {
  394. if (session == null) {
  395. return null;
  396. } else {
  397. return session.toString() + " for "
  398. + session.getService().getServiceName();
  399. }
  400. }
  401. /**
  402. * Gets the id of the UI, used to identify this UI within its application
  403. * when processing requests. The UI id should be present in every request to
  404. * the server that originates from this UI.
  405. * {@link VaadinService#findUI(VaadinRequest)} uses this id to find the
  406. * route to which the request belongs.
  407. * <p>
  408. * This method is not intended to be overridden. If it is overridden, care
  409. * should be taken since this method might be called in situations where
  410. * {@link UI#getCurrent()} does not return this UI.
  411. *
  412. * @return the id of this UI
  413. */
  414. public int getUIId() {
  415. return uiId;
  416. }
  417. /**
  418. * Adds a window as a subwindow inside this UI. To open a new browser window
  419. * or tab, you should instead use a {@link UIProvider}.
  420. *
  421. * @param window
  422. * @throws IllegalArgumentException
  423. * if the window is already added to an application
  424. * @throws NullPointerException
  425. * if the given <code>Window</code> is <code>null</code>.
  426. */
  427. public void addWindow(Window window) throws IllegalArgumentException,
  428. NullPointerException {
  429. if (window == null) {
  430. throw new NullPointerException("Argument must not be null");
  431. }
  432. if (window.isAttached()) {
  433. throw new IllegalArgumentException(
  434. "Window is already attached to an application.");
  435. }
  436. attachWindow(window);
  437. }
  438. /**
  439. * Helper method to attach a window.
  440. *
  441. * @param w
  442. * the window to add
  443. */
  444. private void attachWindow(Window w) {
  445. windows.add(w);
  446. w.setParent(this);
  447. markAsDirty();
  448. }
  449. /**
  450. * Remove the given subwindow from this UI.
  451. *
  452. * Since Vaadin 6.5, {@link Window.CloseListener}s are called also when
  453. * explicitly removing a window by calling this method.
  454. *
  455. * Since Vaadin 6.5, returns a boolean indicating if the window was removed
  456. * or not.
  457. *
  458. * @param window
  459. * Window to be removed.
  460. * @return true if the subwindow was removed, false otherwise
  461. */
  462. public boolean removeWindow(Window window) {
  463. if (!windows.remove(window)) {
  464. // Window window is not a subwindow of this UI.
  465. return false;
  466. }
  467. window.setParent(null);
  468. markAsDirty();
  469. window.fireClose();
  470. return true;
  471. }
  472. /**
  473. * Gets all the windows added to this UI.
  474. *
  475. * @return an unmodifiable collection of windows
  476. */
  477. public Collection<Window> getWindows() {
  478. return Collections.unmodifiableCollection(windows);
  479. }
  480. @Override
  481. public void focus() {
  482. super.focus();
  483. }
  484. /**
  485. * Component that should be focused after the next repaint. Null if no focus
  486. * change should take place.
  487. */
  488. private Focusable pendingFocus;
  489. private boolean resizeLazy = false;
  490. private String theme;
  491. private Navigator navigator;
  492. private PushConnection pushConnection = null;
  493. private LocaleService localeService = new LocaleService(this,
  494. getState(false).localeServiceState);
  495. private String embedId;
  496. /**
  497. * This method is used by Component.Focusable objects to request focus to
  498. * themselves. Focus renders must be handled at window level (instead of
  499. * Component.Focusable) due we want the last focused component to be focused
  500. * in client too. Not the one that is rendered last (the case we'd get if
  501. * implemented in Focusable only).
  502. *
  503. * To focus component from Vaadin application, use Focusable.focus(). See
  504. * {@link Focusable}.
  505. *
  506. * @param focusable
  507. * to be focused on next paint
  508. */
  509. public void setFocusedComponent(Focusable focusable) {
  510. pendingFocus = focusable;
  511. markAsDirty();
  512. }
  513. /**
  514. * Scrolls any component between the component and UI to a suitable position
  515. * so the component is visible to the user. The given component must belong
  516. * to this UI.
  517. *
  518. * @param component
  519. * the component to be scrolled into view
  520. * @throws IllegalArgumentException
  521. * if {@code component} does not belong to this UI
  522. */
  523. public void scrollIntoView(Component component)
  524. throws IllegalArgumentException {
  525. if (component.getUI() != this) {
  526. throw new IllegalArgumentException(
  527. "The component where to scroll must belong to this UI.");
  528. }
  529. scrollIntoView = component;
  530. markAsDirty();
  531. }
  532. /**
  533. * Internal initialization method, should not be overridden. This method is
  534. * not declared as final because that would break compatibility with e.g.
  535. * CDI.
  536. *
  537. * @param request
  538. * the initialization request
  539. * @param uiId
  540. * the id of the new ui
  541. * @param embedId
  542. * the embed id of this UI, or <code>null</code> if no id is
  543. * known
  544. *
  545. * @see #getUIId()
  546. * @see #getEmbedId()
  547. */
  548. public void doInit(VaadinRequest request, int uiId, String embedId) {
  549. if (this.uiId != -1) {
  550. String message = "This UI instance is already initialized (as UI id "
  551. + this.uiId
  552. + ") and can therefore not be initialized again (as UI id "
  553. + uiId + "). ";
  554. if (getSession() != null
  555. && !getSession().equals(VaadinSession.getCurrent())) {
  556. message += "Furthermore, it is already attached to another VaadinSession. ";
  557. }
  558. message += "Please make sure you are not accidentally reusing an old UI instance.";
  559. throw new IllegalStateException(message);
  560. }
  561. this.uiId = uiId;
  562. this.embedId = embedId;
  563. // Actual theme - used for finding CustomLayout templates
  564. theme = request.getParameter("theme");
  565. getPage().init(request);
  566. // Call the init overridden by the application developer
  567. init(request);
  568. Navigator navigator = getNavigator();
  569. if (navigator != null) {
  570. // Kickstart navigation if a navigator was attached in init()
  571. navigator.navigateTo(navigator.getState());
  572. }
  573. }
  574. /**
  575. * Initializes this UI. This method is intended to be overridden by
  576. * subclasses to build the view and configure non-component functionality.
  577. * Performing the initialization in a constructor is not suggested as the
  578. * state of the UI is not properly set up when the constructor is invoked.
  579. * <p>
  580. * The {@link VaadinRequest} can be used to get information about the
  581. * request that caused this UI to be created.
  582. * </p>
  583. *
  584. * @param request
  585. * the Vaadin request that caused this UI to be created
  586. */
  587. protected abstract void init(VaadinRequest request);
  588. /**
  589. * Internal reinitialization method, should not be overridden.
  590. *
  591. * @since 7.2
  592. * @param request
  593. * the request that caused this UI to be reloaded
  594. */
  595. public void doRefresh(VaadinRequest request) {
  596. // This is a horrible hack. We want to have the most recent location and
  597. // browser window size available in refresh(), but we want to call
  598. // listeners, if any, only after refresh(). So we momentarily assign the
  599. // old values back before setting the new values again to ensure the
  600. // events are properly fired.
  601. Page page = getPage();
  602. URI oldLocation = page.getLocation();
  603. int oldWidth = page.getBrowserWindowWidth();
  604. int oldHeight = page.getBrowserWindowHeight();
  605. page.init(request);
  606. refresh(request);
  607. URI newLocation = page.getLocation();
  608. int newWidth = page.getBrowserWindowWidth();
  609. int newHeight = page.getBrowserWindowHeight();
  610. page.updateLocation(oldLocation.toString(), false);
  611. page.updateBrowserWindowSize(oldWidth, oldHeight, false);
  612. page.updateLocation(newLocation.toString(), true);
  613. page.updateBrowserWindowSize(newWidth, newHeight, true);
  614. }
  615. /**
  616. * Reinitializes this UI after a browser refresh if the UI is set to be
  617. * preserved on refresh, typically using the {@link PreserveOnRefresh}
  618. * annotation. This method is intended to be overridden by subclasses if
  619. * needed; the default implementation is empty.
  620. * <p>
  621. * The {@link VaadinRequest} can be used to get information about the
  622. * request that caused this UI to be reloaded.
  623. *
  624. * @since 7.2
  625. * @param request
  626. * the request that caused this UI to be reloaded
  627. */
  628. protected void refresh(VaadinRequest request) {
  629. }
  630. /**
  631. * Sets the thread local for the current UI. This method is used by the
  632. * framework to set the current application whenever a new request is
  633. * processed and it is cleared when the request has been processed.
  634. * <p>
  635. * The application developer can also use this method to define the current
  636. * UI outside the normal request handling, e.g. when initiating custom
  637. * background threads.
  638. * </p>
  639. *
  640. * @param uI
  641. * the UI to register as the current UI
  642. *
  643. * @see #getCurrent()
  644. * @see ThreadLocal
  645. */
  646. public static void setCurrent(UI ui) {
  647. CurrentInstance.setInheritable(UI.class, ui);
  648. }
  649. /**
  650. * Gets the currently used UI. The current UI is automatically defined when
  651. * processing requests to the server. In other cases, (e.g. from background
  652. * threads), the current UI is not automatically defined.
  653. *
  654. * @return the current UI instance if available, otherwise <code>null</code>
  655. *
  656. * @see #setCurrent(UI)
  657. */
  658. public static UI getCurrent() {
  659. return CurrentInstance.get(UI.class);
  660. }
  661. /**
  662. * Set top offset to which the UI should scroll to.
  663. *
  664. * @param scrollTop
  665. */
  666. public void setScrollTop(int scrollTop) {
  667. if (scrollTop < 0) {
  668. throw new IllegalArgumentException(
  669. "Scroll offset must be at least 0");
  670. }
  671. if (this.scrollTop != scrollTop) {
  672. this.scrollTop = scrollTop;
  673. getRpcProxy(ScrollClientRpc.class).setScrollTop(scrollTop);
  674. }
  675. }
  676. public int getScrollTop() {
  677. return scrollTop;
  678. }
  679. /**
  680. * Set left offset to which the UI should scroll to.
  681. *
  682. * @param scrollLeft
  683. */
  684. public void setScrollLeft(int scrollLeft) {
  685. if (scrollLeft < 0) {
  686. throw new IllegalArgumentException(
  687. "Scroll offset must be at least 0");
  688. }
  689. if (this.scrollLeft != scrollLeft) {
  690. this.scrollLeft = scrollLeft;
  691. getRpcProxy(ScrollClientRpc.class).setScrollLeft(scrollLeft);
  692. }
  693. }
  694. public int getScrollLeft() {
  695. return scrollLeft;
  696. }
  697. @Override
  698. protected ActionManager getActionManager() {
  699. if (actionManager == null) {
  700. actionManager = new ActionManager(this);
  701. }
  702. return actionManager;
  703. }
  704. @Override
  705. public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
  706. T action) {
  707. getActionManager().addAction(action);
  708. }
  709. @Override
  710. public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
  711. T action) {
  712. if (actionManager != null) {
  713. actionManager.removeAction(action);
  714. }
  715. }
  716. @Override
  717. public void addActionHandler(Handler actionHandler) {
  718. getActionManager().addActionHandler(actionHandler);
  719. }
  720. @Override
  721. public void removeActionHandler(Handler actionHandler) {
  722. if (actionManager != null) {
  723. actionManager.removeActionHandler(actionHandler);
  724. }
  725. }
  726. /**
  727. * Should resize operations be lazy, i.e. should there be a delay before
  728. * layout sizes are recalculated and resize events are sent to the server.
  729. * Speeds up resize operations in slow UIs with the penalty of slightly
  730. * decreased usability.
  731. * <p>
  732. * Default value: <code>false</code>
  733. * </p>
  734. * <p>
  735. * When there are active window resize listeners, lazy resize mode should be
  736. * used to avoid a large number of events during resize.
  737. * </p>
  738. *
  739. * @param resizeLazy
  740. * true to use a delay before recalculating sizes, false to
  741. * calculate immediately.
  742. */
  743. public void setResizeLazy(boolean resizeLazy) {
  744. this.resizeLazy = resizeLazy;
  745. markAsDirty();
  746. }
  747. /**
  748. * Checks whether lazy resize is enabled.
  749. *
  750. * @return <code>true</code> if lazy resize is enabled, <code>false</code>
  751. * if lazy resize is not enabled
  752. */
  753. public boolean isResizeLazy() {
  754. return resizeLazy;
  755. }
  756. /**
  757. * Add a click listener to the UI. The listener is called whenever the user
  758. * clicks inside the UI. Also when the click targets a component inside the
  759. * UI, provided the targeted component does not prevent the click event from
  760. * propagating.
  761. *
  762. * Use {@link #removeListener(ClickListener)} to remove the listener.
  763. *
  764. * @param listener
  765. * The listener to add
  766. */
  767. public void addClickListener(ClickListener listener) {
  768. addListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class, listener,
  769. ClickListener.clickMethod);
  770. }
  771. /**
  772. * @deprecated As of 7.0, replaced by
  773. * {@link #addClickListener(ClickListener)}
  774. **/
  775. @Deprecated
  776. public void addListener(ClickListener listener) {
  777. addClickListener(listener);
  778. }
  779. /**
  780. * Remove a click listener from the UI. The listener should earlier have
  781. * been added using {@link #addListener(ClickListener)}.
  782. *
  783. * @param listener
  784. * The listener to remove
  785. */
  786. public void removeClickListener(ClickListener listener) {
  787. removeListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class,
  788. listener);
  789. }
  790. /**
  791. * @deprecated As of 7.0, replaced by
  792. * {@link #removeClickListener(ClickListener)}
  793. **/
  794. @Deprecated
  795. public void removeListener(ClickListener listener) {
  796. removeClickListener(listener);
  797. }
  798. @Override
  799. public boolean isConnectorEnabled() {
  800. // TODO How can a UI be invisible? What does it mean?
  801. return isVisible() && isEnabled();
  802. }
  803. public ConnectorTracker getConnectorTracker() {
  804. return connectorTracker;
  805. }
  806. public Page getPage() {
  807. return page;
  808. }
  809. /**
  810. * Returns the navigator attached to this UI or null if there is no
  811. * navigator.
  812. *
  813. * @return
  814. */
  815. public Navigator getNavigator() {
  816. return navigator;
  817. }
  818. /**
  819. * For internal use only.
  820. *
  821. * @param navigator
  822. */
  823. public void setNavigator(Navigator navigator) {
  824. this.navigator = navigator;
  825. }
  826. /**
  827. * Setting the caption of a UI is not supported. To set the title of the
  828. * HTML page, use Page.setTitle
  829. *
  830. * @deprecated As of 7.0, use {@link Page#setTitle(String)}
  831. */
  832. @Override
  833. @Deprecated
  834. public void setCaption(String caption) {
  835. throw new UnsupportedOperationException(
  836. "You can not set the title of a UI. To set the title of the HTML page, use Page.setTitle");
  837. }
  838. /**
  839. * Shows a notification message on the middle of the UI. The message
  840. * automatically disappears ("humanized message").
  841. *
  842. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  843. * rendered as html.
  844. *
  845. * @see #showNotification(Notification)
  846. * @see Notification
  847. *
  848. * @param caption
  849. * The message
  850. *
  851. * @deprecated As of 7.0, use Notification.show instead but be aware that
  852. * Notification.show does not allow HTML.
  853. */
  854. @Deprecated
  855. public void showNotification(String caption) {
  856. Notification notification = new Notification(caption);
  857. notification.setHtmlContentAllowed(true);// Backwards compatibility
  858. getPage().showNotification(notification);
  859. }
  860. /**
  861. * Shows a notification message the UI. The position and behavior of the
  862. * message depends on the type, which is one of the basic types defined in
  863. * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
  864. *
  865. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  866. * rendered as html.
  867. *
  868. * @see #showNotification(Notification)
  869. * @see Notification
  870. *
  871. * @param caption
  872. * The message
  873. * @param type
  874. * The message type
  875. *
  876. * @deprecated As of 7.0, use Notification.show instead but be aware that
  877. * Notification.show does not allow HTML.
  878. */
  879. @Deprecated
  880. public void showNotification(String caption, Notification.Type type) {
  881. Notification notification = new Notification(caption, type);
  882. notification.setHtmlContentAllowed(true);// Backwards compatibility
  883. getPage().showNotification(notification);
  884. }
  885. /**
  886. * Shows a notification consisting of a bigger caption and a smaller
  887. * description on the middle of the UI. The message automatically disappears
  888. * ("humanized message").
  889. *
  890. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  891. * description are rendered as html.
  892. *
  893. * @see #showNotification(Notification)
  894. * @see Notification
  895. *
  896. * @param caption
  897. * The caption of the message
  898. * @param description
  899. * The message description
  900. *
  901. * @deprecated As of 7.0, use new Notification(...).show(Page) instead but
  902. * be aware that HTML by default not allowed.
  903. */
  904. @Deprecated
  905. public void showNotification(String caption, String description) {
  906. Notification notification = new Notification(caption, description);
  907. notification.setHtmlContentAllowed(true);// Backwards compatibility
  908. getPage().showNotification(notification);
  909. }
  910. /**
  911. * Shows a notification consisting of a bigger caption and a smaller
  912. * description. The position and behavior of the message depends on the
  913. * type, which is one of the basic types defined in {@link Notification} ,
  914. * for instance Notification.TYPE_WARNING_MESSAGE.
  915. *
  916. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  917. * description are rendered as html.
  918. *
  919. * @see #showNotification(Notification)
  920. * @see Notification
  921. *
  922. * @param caption
  923. * The caption of the message
  924. * @param description
  925. * The message description
  926. * @param type
  927. * The message type
  928. *
  929. * @deprecated As of 7.0, use new Notification(...).show(Page) instead but
  930. * be aware that HTML by default not allowed.
  931. */
  932. @Deprecated
  933. public void showNotification(String caption, String description,
  934. Notification.Type type) {
  935. Notification notification = new Notification(caption, description, type);
  936. notification.setHtmlContentAllowed(true);// Backwards compatibility
  937. getPage().showNotification(notification);
  938. }
  939. /**
  940. * Shows a notification consisting of a bigger caption and a smaller
  941. * description. The position and behavior of the message depends on the
  942. * type, which is one of the basic types defined in {@link Notification} ,
  943. * for instance Notification.TYPE_WARNING_MESSAGE.
  944. *
  945. * Care should be taken to avoid XSS vulnerabilities if html content is
  946. * allowed.
  947. *
  948. * @see #showNotification(Notification)
  949. * @see Notification
  950. *
  951. * @param caption
  952. * The message caption
  953. * @param description
  954. * The message description
  955. * @param type
  956. * The type of message
  957. * @param htmlContentAllowed
  958. * Whether html in the caption and description should be
  959. * displayed as html or as plain text
  960. *
  961. * @deprecated As of 7.0, use new Notification(...).show(Page).
  962. */
  963. @Deprecated
  964. public void showNotification(String caption, String description,
  965. Notification.Type type, boolean htmlContentAllowed) {
  966. getPage()
  967. .showNotification(
  968. new Notification(caption, description, type,
  969. htmlContentAllowed));
  970. }
  971. /**
  972. * Shows a notification message.
  973. *
  974. * @see Notification
  975. * @see #showNotification(String)
  976. * @see #showNotification(String, int)
  977. * @see #showNotification(String, String)
  978. * @see #showNotification(String, String, int)
  979. *
  980. * @param notification
  981. * The notification message to show
  982. *
  983. * @deprecated As of 7.0, use Notification.show instead
  984. */
  985. @Deprecated
  986. public void showNotification(Notification notification) {
  987. getPage().showNotification(notification);
  988. }
  989. /**
  990. * Returns the timestamp of the last received heartbeat for this UI.
  991. * <p>
  992. * This method is not intended to be overridden. If it is overridden, care
  993. * should be taken since this method might be called in situations where
  994. * {@link UI#getCurrent()} does not return this UI.
  995. *
  996. * @see VaadinService#closeInactiveUIs(VaadinSession)
  997. *
  998. * @return The time the last heartbeat request occurred, in milliseconds
  999. * since the epoch.
  1000. */
  1001. public long getLastHeartbeatTimestamp() {
  1002. return lastHeartbeatTimestamp;
  1003. }
  1004. /**
  1005. * Sets the last heartbeat request timestamp for this UI. Called by the
  1006. * framework whenever the application receives a valid heartbeat request for
  1007. * this UI.
  1008. * <p>
  1009. * This method is not intended to be overridden. If it is overridden, care
  1010. * should be taken since this method might be called in situations where
  1011. * {@link UI#getCurrent()} does not return this UI.
  1012. *
  1013. * @param lastHeartbeat
  1014. * The time the last heartbeat request occurred, in milliseconds
  1015. * since the epoch.
  1016. */
  1017. public void setLastHeartbeatTimestamp(long lastHeartbeat) {
  1018. lastHeartbeatTimestamp = lastHeartbeat;
  1019. }
  1020. /**
  1021. * Gets the theme that was used when the UI was initialized.
  1022. *
  1023. * @return the theme name
  1024. */
  1025. public String getTheme() {
  1026. return theme;
  1027. }
  1028. /**
  1029. * Marks this UI to be {@link #detach() detached} from the session at the
  1030. * end of the current request, or the next request if there is no current
  1031. * request (if called from a background thread, for instance.)
  1032. * <p>
  1033. * The UI is detached after the response is sent, so in the current request
  1034. * it can still update the client side normally. However, after the response
  1035. * any new requests from the client side to this UI will cause an error, so
  1036. * usually the client should be asked, for instance, to reload the page
  1037. * (serving a fresh UI instance), to close the page, or to navigate
  1038. * somewhere else.
  1039. * <p>
  1040. * Note that this method is strictly for users to explicitly signal the
  1041. * framework that the UI should be detached. Overriding it is not a reliable
  1042. * way to catch UIs that are to be detached. Instead, {@code UI.detach()}
  1043. * should be overridden or a {@link DetachListener} used.
  1044. */
  1045. public void close() {
  1046. closing = true;
  1047. boolean sessionExpired = (session == null || session.getState() != State.OPEN);
  1048. getRpcProxy(UIClientRpc.class).uiClosed(sessionExpired);
  1049. if (getPushConnection() != null) {
  1050. // Push the Rpc to the client. The connection will be closed when
  1051. // the UI is detached and cleaned up.
  1052. // Can't use UI.push() directly since it checks for a valid session
  1053. if (session != null) {
  1054. session.getService().runPendingAccessTasks(session);
  1055. }
  1056. getPushConnection().push();
  1057. }
  1058. }
  1059. /**
  1060. * Returns whether this UI is marked as closed and is to be detached.
  1061. * <p>
  1062. * This method is not intended to be overridden. If it is overridden, care
  1063. * should be taken since this method might be called in situations where
  1064. * {@link UI#getCurrent()} does not return this UI.
  1065. *
  1066. * @see #close()
  1067. *
  1068. * @return whether this UI is closing.
  1069. */
  1070. public boolean isClosing() {
  1071. return closing;
  1072. }
  1073. /**
  1074. * Called after the UI is added to the session. A UI instance is attached
  1075. * exactly once, before its {@link #init(VaadinRequest) init} method is
  1076. * called.
  1077. *
  1078. * @see Component#attach
  1079. */
  1080. @Override
  1081. public void attach() {
  1082. super.attach();
  1083. getLocaleService().addLocale(getLocale());
  1084. }
  1085. /**
  1086. * Called before the UI is removed from the session. A UI instance is
  1087. * detached exactly once, either:
  1088. * <ul>
  1089. * <li>after it is explicitly {@link #close() closed}.
  1090. * <li>when its session is closed or expires
  1091. * <li>after three missed heartbeat requests.
  1092. * </ul>
  1093. * <p>
  1094. * Note that when a UI is detached, any changes made in the {@code detach}
  1095. * methods of any children or {@link DetachListener}s that would be
  1096. * communicated to the client are silently ignored.
  1097. */
  1098. @Override
  1099. public void detach() {
  1100. super.detach();
  1101. }
  1102. /*
  1103. * (non-Javadoc)
  1104. *
  1105. * @see
  1106. * com.vaadin.ui.AbstractSingleComponentContainer#setContent(com.vaadin.
  1107. * ui.Component)
  1108. */
  1109. @Override
  1110. public void setContent(Component content) {
  1111. if (content instanceof Window) {
  1112. throw new IllegalArgumentException(
  1113. "A Window cannot be added using setContent. Use addWindow(Window window) instead");
  1114. }
  1115. super.setContent(content);
  1116. }
  1117. @Override
  1118. public void setTabIndex(int tabIndex) {
  1119. getState().tabIndex = tabIndex;
  1120. }
  1121. @Override
  1122. public int getTabIndex() {
  1123. return getState(false).tabIndex;
  1124. }
  1125. /**
  1126. * Locks the session of this UI and runs the provided Runnable right away.
  1127. * <p>
  1128. * It is generally recommended to use {@link #access(Runnable)} instead of
  1129. * this method for accessing a session from a different thread as
  1130. * {@link #access(Runnable)} can be used while holding the lock of another
  1131. * session. To avoid causing deadlocks, this methods throws an exception if
  1132. * it is detected than another session is also locked by the current thread.
  1133. * </p>
  1134. * <p>
  1135. * This method behaves differently than {@link #access(Runnable)} in some
  1136. * situations:
  1137. * <ul>
  1138. * <li>If the current thread is currently holding the lock of the session,
  1139. * {@link #accessSynchronously(Runnable)} runs the task right away whereas
  1140. * {@link #access(Runnable)} defers the task to a later point in time.</li>
  1141. * <li>If some other thread is currently holding the lock for the session,
  1142. * {@link #accessSynchronously(Runnable)} blocks while waiting for the lock
  1143. * to be available whereas {@link #access(Runnable)} defers the task to a
  1144. * later point in time.</li>
  1145. * </ul>
  1146. * </p>
  1147. *
  1148. * @since 7.1
  1149. *
  1150. * @param runnable
  1151. * the runnable which accesses the UI
  1152. * @throws UIDetachedException
  1153. * if the UI is not attached to a session (and locking can
  1154. * therefore not be done)
  1155. * @throws IllegalStateException
  1156. * if the current thread holds the lock for another session
  1157. *
  1158. * @see #access(Runnable)
  1159. * @see VaadinSession#accessSynchronously(Runnable)
  1160. */
  1161. public void accessSynchronously(Runnable runnable)
  1162. throws UIDetachedException {
  1163. Map<Class<?>, CurrentInstance> old = null;
  1164. VaadinSession session = getSession();
  1165. if (session == null) {
  1166. throw new UIDetachedException();
  1167. }
  1168. VaadinService.verifyNoOtherSessionLocked(session);
  1169. session.lock();
  1170. try {
  1171. if (getSession() == null) {
  1172. // UI was detached after fetching the session but before we
  1173. // acquired the lock.
  1174. throw new UIDetachedException();
  1175. }
  1176. old = CurrentInstance.setCurrent(this);
  1177. runnable.run();
  1178. } finally {
  1179. session.unlock();
  1180. if (old != null) {
  1181. CurrentInstance.restoreInstances(old);
  1182. }
  1183. }
  1184. }
  1185. /**
  1186. * Provides exclusive access to this UI from outside a request handling
  1187. * thread.
  1188. * <p>
  1189. * The given runnable is executed while holding the session lock to ensure
  1190. * exclusive access to this UI. If the session is not locked, the lock will
  1191. * be acquired and the runnable is run right away. If the session is
  1192. * currently locked, the runnable will be run before that lock is released.
  1193. * </p>
  1194. * <p>
  1195. * RPC handlers for components inside this UI do not need to use this method
  1196. * as the session is automatically locked by the framework during RPC
  1197. * handling.
  1198. * </p>
  1199. * <p>
  1200. * Please note that the runnable might be invoked on a different thread or
  1201. * later on the current thread, which means that custom thread locals might
  1202. * not have the expected values when the runnable is executed. Inheritable
  1203. * values in {@link CurrentInstance} will have the same values as when this
  1204. * method was invoked. {@link UI#getCurrent()},
  1205. * {@link VaadinSession#getCurrent()} and {@link VaadinService#getCurrent()}
  1206. * are set according to this UI before executing the runnable.
  1207. * Non-inheritable CurrentInstance values including
  1208. * {@link VaadinService#getCurrentRequest()} and
  1209. * {@link VaadinService#getCurrentResponse()} will not be defined.
  1210. * </p>
  1211. * <p>
  1212. * The returned future can be used to check for task completion and to
  1213. * cancel the task.
  1214. * </p>
  1215. *
  1216. * @see #getCurrent()
  1217. * @see #accessSynchronously(Runnable)
  1218. * @see VaadinSession#access(Runnable)
  1219. * @see VaadinSession#lock()
  1220. *
  1221. * @since 7.1
  1222. *
  1223. * @param runnable
  1224. * the runnable which accesses the UI
  1225. * @throws UIDetachedException
  1226. * if the UI is not attached to a session (and locking can
  1227. * therefore not be done)
  1228. * @return a future that can be used to check for task completion and to
  1229. * cancel the task
  1230. */
  1231. public Future<Void> access(final Runnable runnable) {
  1232. VaadinSession session = getSession();
  1233. if (session == null) {
  1234. throw new UIDetachedException();
  1235. }
  1236. return session.access(new ErrorHandlingRunnable() {
  1237. @Override
  1238. public void run() {
  1239. accessSynchronously(runnable);
  1240. }
  1241. @Override
  1242. public void handleError(Exception exception) {
  1243. try {
  1244. if (runnable instanceof ErrorHandlingRunnable) {
  1245. ErrorHandlingRunnable errorHandlingRunnable = (ErrorHandlingRunnable) runnable;
  1246. errorHandlingRunnable.handleError(exception);
  1247. } else {
  1248. ConnectorErrorEvent errorEvent = new ConnectorErrorEvent(
  1249. UI.this, exception);
  1250. ErrorHandler errorHandler = com.vaadin.server.ErrorEvent
  1251. .findErrorHandler(UI.this);
  1252. if (errorHandler == null) {
  1253. errorHandler = new DefaultErrorHandler();
  1254. }
  1255. errorHandler.error(errorEvent);
  1256. }
  1257. } catch (Exception e) {
  1258. getLogger().log(Level.SEVERE, e.getMessage(), e);
  1259. }
  1260. }
  1261. });
  1262. }
  1263. /**
  1264. * Retrieves the object used for configuring tooltips.
  1265. *
  1266. * @return The instance used for tooltip configuration
  1267. */
  1268. public TooltipConfiguration getTooltipConfiguration() {
  1269. return tooltipConfiguration;
  1270. }
  1271. /**
  1272. * Retrieves the object used for configuring notifications.
  1273. *
  1274. * @return The instance used for notification configuration
  1275. */
  1276. public NotificationConfiguration getNotificationConfiguration() {
  1277. return notificationConfiguration;
  1278. }
  1279. /**
  1280. * Retrieves the object used for configuring the loading indicator.
  1281. *
  1282. * @return The instance used for configuring the loading indicator
  1283. */
  1284. public LoadingIndicatorConfiguration getLoadingIndicatorConfiguration() {
  1285. return loadingIndicatorConfiguration;
  1286. }
  1287. /**
  1288. * Pushes the pending changes and client RPC invocations of this UI to the
  1289. * client-side.
  1290. * <p>
  1291. * If push is enabled, but the push connection is not currently open, the
  1292. * push will be done when the connection is established.
  1293. * <p>
  1294. * As with all UI methods, the session must be locked when calling this
  1295. * method. It is also recommended that {@link UI#getCurrent()} is set up to
  1296. * return this UI since writing the response may invoke logic in any
  1297. * attached component or extension. The recommended way of fulfilling these
  1298. * conditions is to use {@link #access(Runnable)}.
  1299. *
  1300. * @throws IllegalStateException
  1301. * if push is disabled.
  1302. * @throws UIDetachedException
  1303. * if this UI is not attached to a session.
  1304. *
  1305. * @see #getPushConfiguration()
  1306. *
  1307. * @since 7.1
  1308. */
  1309. public void push() {
  1310. VaadinSession session = getSession();
  1311. if (session == null) {
  1312. throw new UIDetachedException("Cannot push a detached UI");
  1313. }
  1314. assert session.hasLock();
  1315. if (!getPushConfiguration().getPushMode().isEnabled()) {
  1316. throw new IllegalStateException("Push not enabled");
  1317. }
  1318. assert pushConnection != null;
  1319. /*
  1320. * Purge the pending access queue as it might mark a connector as dirty
  1321. * when the push would otherwise be ignored because there are no changes
  1322. * to push.
  1323. */
  1324. session.getService().runPendingAccessTasks(session);
  1325. if (!getConnectorTracker().hasDirtyConnectors()) {
  1326. // Do not push if there is nothing to push
  1327. return;
  1328. }
  1329. pushConnection.push();
  1330. }
  1331. /**
  1332. * Returns the internal push connection object used by this UI. This method
  1333. * should only be called by the framework.
  1334. * <p>
  1335. * This method is not intended to be overridden. If it is overridden, care
  1336. * should be taken since this method might be called in situations where
  1337. * {@link UI#getCurrent()} does not return this UI.
  1338. *
  1339. * @return the push connection used by this UI, or {@code null} if push is
  1340. * not available.
  1341. */
  1342. public PushConnection getPushConnection() {
  1343. assert !(getPushConfiguration().getPushMode().isEnabled() && pushConnection == null);
  1344. return pushConnection;
  1345. }
  1346. /**
  1347. * Sets the internal push connection object used by this UI. This method
  1348. * should only be called by the framework.
  1349. * <p>
  1350. * The {@code pushConnection} argument must be non-null if and only if
  1351. * {@code getPushConfiguration().getPushMode().isEnabled()}.
  1352. *
  1353. * @param pushConnection
  1354. * the push connection to use for this UI
  1355. */
  1356. public void setPushConnection(PushConnection pushConnection) {
  1357. // If pushMode is disabled then there should never be a pushConnection;
  1358. // if enabled there should always be
  1359. assert (pushConnection == null)
  1360. ^ getPushConfiguration().getPushMode().isEnabled();
  1361. if (pushConnection == this.pushConnection) {
  1362. return;
  1363. }
  1364. if (this.pushConnection != null && this.pushConnection.isConnected()) {
  1365. this.pushConnection.disconnect();
  1366. }
  1367. this.pushConnection = pushConnection;
  1368. }
  1369. /**
  1370. * Sets the interval with which the UI should poll the server to see if
  1371. * there are any changes. Polling is disabled by default.
  1372. * <p>
  1373. * Note that it is possible to enable push and polling at the same time but
  1374. * it should not be done to avoid excessive server traffic.
  1375. * </p>
  1376. * <p>
  1377. * Add-on developers should note that this method is only meant for the
  1378. * application developer. An add-on should not set the poll interval
  1379. * directly, rather instruct the user to set it.
  1380. * </p>
  1381. *
  1382. * @param intervalInMillis
  1383. * The interval (in ms) with which the UI should poll the server
  1384. * or -1 to disable polling
  1385. */
  1386. public void setPollInterval(int intervalInMillis) {
  1387. getState().pollInterval = intervalInMillis;
  1388. }
  1389. /**
  1390. * Returns the interval with which the UI polls the server.
  1391. *
  1392. * @return The interval (in ms) with which the UI polls the server or -1 if
  1393. * polling is disabled
  1394. */
  1395. public int getPollInterval() {
  1396. return getState(false).pollInterval;
  1397. }
  1398. @Override
  1399. public void addPollListener(PollListener listener) {
  1400. addListener(EventId.POLL, PollEvent.class, listener,
  1401. PollListener.POLL_METHOD);
  1402. }
  1403. @Override
  1404. public void removePollListener(PollListener listener) {
  1405. removeListener(EventId.POLL, PollEvent.class, listener);
  1406. }
  1407. /**
  1408. * Retrieves the object used for configuring the push channel.
  1409. *
  1410. * @since 7.1
  1411. * @return The instance used for push configuration
  1412. */
  1413. public PushConfiguration getPushConfiguration() {
  1414. return pushConfiguration;
  1415. }
  1416. /**
  1417. * Get the label that is added to the container element, where tooltip,
  1418. * notification and dialogs are added to.
  1419. *
  1420. * @return the label of the container
  1421. */
  1422. public String getOverlayContainerLabel() {
  1423. return getState(false).overlayContainerLabel;
  1424. }
  1425. /**
  1426. * Sets the label that is added to the container element, where tooltip,
  1427. * notifications and dialogs are added to.
  1428. * <p>
  1429. * This is helpful for users of assistive devices, as this element is
  1430. * reachable for them.
  1431. * </p>
  1432. *
  1433. * @param overlayContainerLabel
  1434. * label to use for the container
  1435. */
  1436. public void setOverlayContainerLabel(String overlayContainerLabel) {
  1437. getState().overlayContainerLabel = overlayContainerLabel;
  1438. }
  1439. /**
  1440. * Returns the locale service which handles transmission of Locale data to
  1441. * the client.
  1442. *
  1443. * @since 7.1
  1444. * @return The LocaleService for this UI
  1445. */
  1446. public LocaleService getLocaleService() {
  1447. return localeService;
  1448. }
  1449. private static Logger getLogger() {
  1450. return Logger.getLogger(UI.class.getName());
  1451. }
  1452. /**
  1453. * Gets a string the uniquely distinguishes this UI instance based on where
  1454. * it is embedded. The embed identifier is based on the
  1455. * <code>window.name</code> DOM attribute of the browser window where the UI
  1456. * is displayed and the id of the div element where the UI is embedded.
  1457. *
  1458. * @since 7.2
  1459. * @return the embed id for this UI, or <code>null</code> if no id known
  1460. */
  1461. public String getEmbedId() {
  1462. return embedId;
  1463. }
  1464. }