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

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