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

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