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.

пре 12 година
пре 12 година
пре 12 година
пре 12 година
пре 11 година
пре 12 година
пре 12 година
пре 12 година
пре 12 година
пре 12 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 12 година
пре 12 година
пре 12 година
пре 12 година
пре 12 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
пре 11 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866
  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 acknowledge() {
  175. // Nothing to do, just need the message to be sent and processed
  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. if (variables.containsKey(UIConstants.LOCATION_VARIABLE)) {
  394. String location = (String) variables
  395. .get(UIConstants.LOCATION_VARIABLE);
  396. getPage().updateLocation(location, true);
  397. }
  398. }
  399. /*
  400. * (non-Javadoc)
  401. *
  402. * @see com.vaadin.ui.HasComponents#iterator()
  403. */
  404. @Override
  405. public Iterator<Component> iterator() {
  406. // TODO could directly create some kind of combined iterator instead of
  407. // creating a new ArrayList
  408. ArrayList<Component> components = new ArrayList<>();
  409. if (getContent() != null) {
  410. components.add(getContent());
  411. }
  412. components.addAll(windows);
  413. return components.iterator();
  414. }
  415. /*
  416. * (non-Javadoc)
  417. *
  418. * @see com.vaadin.ui.ComponentContainer#getComponentCount()
  419. */
  420. @Override
  421. public int getComponentCount() {
  422. return windows.size() + (getContent() == null ? 0 : 1);
  423. }
  424. /**
  425. * Sets the session to which this UI is assigned.
  426. * <p>
  427. * This method is for internal use by the framework. To explicitly close a
  428. * UI, see {@link #close()}.
  429. * </p>
  430. *
  431. * @param session
  432. * the session to set
  433. *
  434. * @throws IllegalStateException
  435. * if the session has already been set
  436. *
  437. * @see #getSession()
  438. */
  439. public void setSession(VaadinSession session) {
  440. if (session == null && this.session == null) {
  441. throw new IllegalStateException(
  442. "Session should never be set to null when UI.session is already null");
  443. } else if (session != null && this.session != null) {
  444. throw new IllegalStateException(
  445. "Session has already been set. Old session: "
  446. + getSessionDetails(this.session)
  447. + ". New session: " + getSessionDetails(session)
  448. + ".");
  449. } else {
  450. if (session == null) {
  451. try {
  452. detach();
  453. } catch (Exception e) {
  454. getLogger().log(Level.WARNING,
  455. "Error while detaching UI from session", e);
  456. }
  457. // Disable push when the UI is detached. Otherwise the
  458. // push connection and possibly VaadinSession will live
  459. // on.
  460. getPushConfiguration().setPushMode(PushMode.DISABLED);
  461. new Thread(new Runnable() {
  462. @Override
  463. public void run() {
  464. // This intentionally does disconnect without locking
  465. // the VaadinSession to avoid deadlocks where the server
  466. // uses a lock for the websocket connection
  467. // See https://dev.vaadin.com/ticket/18436
  468. // The underlying problem is
  469. // https://dev.vaadin.com/ticket/16919
  470. setPushConnection(null);
  471. }
  472. }).start();
  473. }
  474. this.session = session;
  475. }
  476. if (session != null) {
  477. attach();
  478. }
  479. }
  480. private static String getSessionDetails(VaadinSession session) {
  481. if (session == null) {
  482. return null;
  483. } else {
  484. return session.toString() + " for "
  485. + session.getService().getServiceName();
  486. }
  487. }
  488. /**
  489. * Gets the id of the UI, used to identify this UI within its application
  490. * when processing requests. The UI id should be present in every request to
  491. * the server that originates from this UI.
  492. * {@link VaadinService#findUI(VaadinRequest)} uses this id to find the
  493. * route to which the request belongs.
  494. * <p>
  495. * This method is not intended to be overridden. If it is overridden, care
  496. * should be taken since this method might be called in situations where
  497. * {@link UI#getCurrent()} does not return this UI.
  498. *
  499. * @return the id of this UI
  500. */
  501. public int getUIId() {
  502. return uiId;
  503. }
  504. /**
  505. * Adds a window as a subwindow inside this UI. To open a new browser window
  506. * or tab, you should instead use a {@link UIProvider}.
  507. *
  508. * @param window
  509. * @throws IllegalArgumentException
  510. * if the window is already added to an application
  511. * @throws NullPointerException
  512. * if the given <code>Window</code> is <code>null</code>.
  513. */
  514. public void addWindow(Window window)
  515. throws IllegalArgumentException, NullPointerException {
  516. if (window == null) {
  517. throw new NullPointerException("Argument must not be null");
  518. }
  519. if (window.isAttached()) {
  520. throw new IllegalArgumentException(
  521. "Window is already attached to an application.");
  522. }
  523. attachWindow(window);
  524. }
  525. /**
  526. * Helper method to attach a window.
  527. *
  528. * @param w
  529. * the window to add
  530. */
  531. private void attachWindow(Window w) {
  532. windows.add(w);
  533. w.setParent(this);
  534. fireComponentAttachEvent(w);
  535. markAsDirty();
  536. }
  537. /**
  538. * Remove the given subwindow from this UI.
  539. *
  540. * Since Vaadin 6.5, {@link Window.CloseListener}s are called also when
  541. * explicitly removing a window by calling this method.
  542. *
  543. * Since Vaadin 6.5, returns a boolean indicating if the window was removed
  544. * or not.
  545. *
  546. * @param window
  547. * Window to be removed.
  548. * @return true if the subwindow was removed, false otherwise
  549. */
  550. public boolean removeWindow(Window window) {
  551. if (!windows.remove(window)) {
  552. // Window window is not a subwindow of this UI.
  553. return false;
  554. }
  555. window.setParent(null);
  556. markAsDirty();
  557. window.fireClose();
  558. fireComponentDetachEvent(window);
  559. fireWindowOrder(Collections.singletonMap(-1, window));
  560. return true;
  561. }
  562. /**
  563. * Gets all the windows added to this UI.
  564. *
  565. * @return an unmodifiable collection of windows
  566. */
  567. public Collection<Window> getWindows() {
  568. return Collections.unmodifiableCollection(windows);
  569. }
  570. @Override
  571. public void focus() {
  572. super.focus();
  573. }
  574. /**
  575. * Component that should be focused after the next repaint. Null if no focus
  576. * change should take place.
  577. */
  578. private Focusable pendingFocus;
  579. private boolean resizeLazy = false;
  580. private Navigator navigator;
  581. private PushConnection pushConnection = null;
  582. private LocaleService localeService = new LocaleService(this,
  583. getState(false).localeServiceState);
  584. private String embedId;
  585. /**
  586. * This method is used by Component.Focusable objects to request focus to
  587. * themselves. Focus renders must be handled at window level (instead of
  588. * Component.Focusable) due we want the last focused component to be focused
  589. * in client too. Not the one that is rendered last (the case we'd get if
  590. * implemented in Focusable only).
  591. *
  592. * To focus component from Vaadin application, use Focusable.focus(). See
  593. * {@link Focusable}.
  594. *
  595. * @param focusable
  596. * to be focused on next paint
  597. */
  598. public void setFocusedComponent(Focusable focusable) {
  599. pendingFocus = focusable;
  600. markAsDirty();
  601. }
  602. /**
  603. * Scrolls any component between the component and UI to a suitable position
  604. * so the component is visible to the user. The given component must belong
  605. * to this UI.
  606. *
  607. * @param component
  608. * the component to be scrolled into view
  609. * @throws IllegalArgumentException
  610. * if {@code component} does not belong to this UI
  611. */
  612. public void scrollIntoView(Component component)
  613. throws IllegalArgumentException {
  614. if (component.getUI() != this) {
  615. throw new IllegalArgumentException(
  616. "The component where to scroll must belong to this UI.");
  617. }
  618. scrollIntoView = component;
  619. markAsDirty();
  620. }
  621. /**
  622. * Internal initialization method, should not be overridden. This method is
  623. * not declared as final because that would break compatibility with e.g.
  624. * CDI.
  625. *
  626. * @param request
  627. * the initialization request
  628. * @param uiId
  629. * the id of the new ui
  630. * @param embedId
  631. * the embed id of this UI, or <code>null</code> if no id is
  632. * known
  633. *
  634. * @see #getUIId()
  635. * @see #getEmbedId()
  636. */
  637. public void doInit(VaadinRequest request, int uiId, String embedId) {
  638. if (this.uiId != -1) {
  639. String message = "This UI instance is already initialized (as UI id "
  640. + this.uiId
  641. + ") and can therefore not be initialized again (as UI id "
  642. + uiId + "). ";
  643. if (getSession() != null
  644. && !getSession().equals(VaadinSession.getCurrent())) {
  645. message += "Furthermore, it is already attached to another VaadinSession. ";
  646. }
  647. message += "Please make sure you are not accidentally reusing an old UI instance.";
  648. throw new IllegalStateException(message);
  649. }
  650. this.uiId = uiId;
  651. this.embedId = embedId;
  652. // Actual theme - used for finding CustomLayout templates
  653. setTheme(request.getParameter("theme"));
  654. getPage().init(request);
  655. // Call the init overridden by the application developer
  656. init(request);
  657. Navigator navigator = getNavigator();
  658. if (navigator != null) {
  659. // Kickstart navigation if a navigator was attached in init()
  660. navigator.navigateTo(navigator.getState());
  661. }
  662. }
  663. /**
  664. * Initializes this UI. This method is intended to be overridden by
  665. * subclasses to build the view and configure non-component functionality.
  666. * Performing the initialization in a constructor is not suggested as the
  667. * state of the UI is not properly set up when the constructor is invoked.
  668. * <p>
  669. * The {@link VaadinRequest} can be used to get information about the
  670. * request that caused this UI to be created.
  671. * </p>
  672. *
  673. * @param request
  674. * the Vaadin request that caused this UI to be created
  675. */
  676. protected abstract void init(VaadinRequest request);
  677. /**
  678. * Internal reinitialization method, should not be overridden.
  679. *
  680. * @since 7.2
  681. * @param request
  682. * the request that caused this UI to be reloaded
  683. */
  684. public void doRefresh(VaadinRequest request) {
  685. // This is a horrible hack. We want to have the most recent location and
  686. // browser window size available in refresh(), but we want to call
  687. // listeners, if any, only after refresh(). So we momentarily assign the
  688. // old values back before setting the new values again to ensure the
  689. // events are properly fired.
  690. Page page = getPage();
  691. URI oldLocation = page.getLocation();
  692. int oldWidth = page.getBrowserWindowWidth();
  693. int oldHeight = page.getBrowserWindowHeight();
  694. page.init(request);
  695. // Reset heartbeat timeout to avoid surprise if it's almost expired
  696. setLastHeartbeatTimestamp(System.currentTimeMillis());
  697. refresh(request);
  698. URI newLocation = page.getLocation();
  699. int newWidth = page.getBrowserWindowWidth();
  700. int newHeight = page.getBrowserWindowHeight();
  701. page.updateLocation(oldLocation.toString(), false);
  702. page.updateBrowserWindowSize(oldWidth, oldHeight, false);
  703. page.updateLocation(newLocation.toString(), true);
  704. page.updateBrowserWindowSize(newWidth, newHeight, true);
  705. }
  706. /**
  707. * Reinitializes this UI after a browser refresh if the UI is set to be
  708. * preserved on refresh, typically using the {@link PreserveOnRefresh}
  709. * annotation. This method is intended to be overridden by subclasses if
  710. * needed; the default implementation is empty.
  711. * <p>
  712. * The {@link VaadinRequest} can be used to get information about the
  713. * request that caused this UI to be reloaded.
  714. *
  715. * @since 7.2
  716. * @param request
  717. * the request that caused this UI to be reloaded
  718. */
  719. protected void refresh(VaadinRequest request) {
  720. }
  721. /**
  722. * Sets the thread local for the current UI. This method is used by the
  723. * framework to set the current application whenever a new request is
  724. * processed and it is cleared when the request has been processed.
  725. * <p>
  726. * The application developer can also use this method to define the current
  727. * UI outside the normal request handling, e.g. when initiating custom
  728. * background threads.
  729. * <p>
  730. * The UI is stored using a weak reference to avoid leaking memory in case
  731. * it is not explicitly cleared.
  732. *
  733. * @param ui
  734. * the UI to register as the current UI
  735. *
  736. * @see #getCurrent()
  737. * @see ThreadLocal
  738. */
  739. public static void setCurrent(UI ui) {
  740. CurrentInstance.set(UI.class, ui);
  741. }
  742. /**
  743. * Gets the currently used UI. The current UI is automatically defined when
  744. * processing requests to the server. In other cases, (e.g. from background
  745. * threads), the current UI is not automatically defined.
  746. * <p>
  747. * The UI is stored using a weak reference to avoid leaking memory in case
  748. * it is not explicitly cleared.
  749. *
  750. * @return the current UI instance if available, otherwise <code>null</code>
  751. *
  752. * @see #setCurrent(UI)
  753. */
  754. public static UI getCurrent() {
  755. return CurrentInstance.get(UI.class);
  756. }
  757. /**
  758. * Set top offset to which the UI should scroll to.
  759. *
  760. * @param scrollTop
  761. */
  762. public void setScrollTop(int scrollTop) {
  763. if (scrollTop < 0) {
  764. throw new IllegalArgumentException(
  765. "Scroll offset must be at least 0");
  766. }
  767. if (this.scrollTop != scrollTop) {
  768. this.scrollTop = scrollTop;
  769. getRpcProxy(ScrollClientRpc.class).setScrollTop(scrollTop);
  770. }
  771. }
  772. public int getScrollTop() {
  773. return scrollTop;
  774. }
  775. /**
  776. * Set left offset to which the UI should scroll to.
  777. *
  778. * @param scrollLeft
  779. */
  780. public void setScrollLeft(int scrollLeft) {
  781. if (scrollLeft < 0) {
  782. throw new IllegalArgumentException(
  783. "Scroll offset must be at least 0");
  784. }
  785. if (this.scrollLeft != scrollLeft) {
  786. this.scrollLeft = scrollLeft;
  787. getRpcProxy(ScrollClientRpc.class).setScrollLeft(scrollLeft);
  788. }
  789. }
  790. public int getScrollLeft() {
  791. return scrollLeft;
  792. }
  793. @Override
  794. protected ActionManager getActionManager() {
  795. if (actionManager == null) {
  796. actionManager = new ActionManager(this);
  797. }
  798. return actionManager;
  799. }
  800. @Override
  801. public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
  802. T action) {
  803. getActionManager().addAction(action);
  804. }
  805. @Override
  806. public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
  807. T action) {
  808. if (actionManager != null) {
  809. actionManager.removeAction(action);
  810. }
  811. }
  812. @Override
  813. public void addActionHandler(Handler actionHandler) {
  814. getActionManager().addActionHandler(actionHandler);
  815. }
  816. @Override
  817. public void removeActionHandler(Handler actionHandler) {
  818. if (actionManager != null) {
  819. actionManager.removeActionHandler(actionHandler);
  820. }
  821. }
  822. /**
  823. * Should resize operations be lazy, i.e. should there be a delay before
  824. * layout sizes are recalculated and resize events are sent to the server.
  825. * Speeds up resize operations in slow UIs with the penalty of slightly
  826. * decreased usability.
  827. * <p>
  828. * Default value: <code>false</code>
  829. * </p>
  830. * <p>
  831. * When there are active window resize listeners, lazy resize mode should be
  832. * used to avoid a large number of events during resize.
  833. * </p>
  834. *
  835. * @param resizeLazy
  836. * true to use a delay before recalculating sizes, false to
  837. * calculate immediately.
  838. */
  839. public void setResizeLazy(boolean resizeLazy) {
  840. this.resizeLazy = resizeLazy;
  841. markAsDirty();
  842. }
  843. /**
  844. * Checks whether lazy resize is enabled.
  845. *
  846. * @return <code>true</code> if lazy resize is enabled, <code>false</code>
  847. * if lazy resize is not enabled
  848. */
  849. public boolean isResizeLazy() {
  850. return resizeLazy;
  851. }
  852. /**
  853. * Add a click listener to the UI. The listener is called whenever the user
  854. * clicks inside the UI. Also when the click targets a component inside the
  855. * UI, provided the targeted component does not prevent the click event from
  856. * propagating.
  857. *
  858. * @see Registration
  859. *
  860. * @param listener
  861. * The listener to add, not null
  862. * @return a registration object for removing the listener
  863. */
  864. public Registration addClickListener(ClickListener listener) {
  865. return addListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class,
  866. listener, ClickListener.clickMethod);
  867. }
  868. /**
  869. * Remove a click listener from the UI. The listener should earlier have
  870. * been added using {@link #addListener(ClickListener)}.
  871. *
  872. * @param listener
  873. * The listener to remove
  874. *
  875. * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the
  876. * registration object returned from
  877. * {@link #removeClickListener(ClickListener)}.
  878. */
  879. @Deprecated
  880. public void removeClickListener(ClickListener listener) {
  881. removeListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class,
  882. listener);
  883. }
  884. @Override
  885. public boolean isConnectorEnabled() {
  886. // TODO How can a UI be invisible? What does it mean?
  887. return isVisible() && isEnabled();
  888. }
  889. public ConnectorTracker getConnectorTracker() {
  890. return connectorTracker;
  891. }
  892. public Page getPage() {
  893. return page;
  894. }
  895. /**
  896. * Returns the navigator attached to this UI or null if there is no
  897. * navigator.
  898. *
  899. * @return
  900. */
  901. public Navigator getNavigator() {
  902. return navigator;
  903. }
  904. /**
  905. * For internal use only.
  906. *
  907. * @param navigator
  908. */
  909. public void setNavigator(Navigator navigator) {
  910. this.navigator = navigator;
  911. }
  912. /**
  913. * Setting the caption of a UI is not supported. To set the title of the
  914. * HTML page, use Page.setTitle
  915. *
  916. * @deprecated As of 7.0, use {@link Page#setTitle(String)}
  917. */
  918. @Override
  919. @Deprecated
  920. public void setCaption(String caption) {
  921. throw new UnsupportedOperationException(
  922. "You can not set the title of a UI. To set the title of the HTML page, use Page.setTitle");
  923. }
  924. /**
  925. * Shows a notification message on the middle of the UI. The message
  926. * automatically disappears ("humanized message").
  927. *
  928. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  929. * rendered as html.
  930. *
  931. * @see #showNotification(Notification)
  932. * @see Notification
  933. *
  934. * @param caption
  935. * The message
  936. *
  937. * @deprecated As of 7.0, use Notification.show instead but be aware that
  938. * Notification.show does not allow HTML.
  939. */
  940. @Deprecated
  941. public void showNotification(String caption) {
  942. Notification notification = new Notification(caption);
  943. notification.setHtmlContentAllowed(true);// Backwards compatibility
  944. getPage().showNotification(notification);
  945. }
  946. /**
  947. * Shows a notification message the UI. The position and behavior of the
  948. * message depends on the type, which is one of the basic types defined in
  949. * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
  950. *
  951. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  952. * rendered as html.
  953. *
  954. * @see #showNotification(Notification)
  955. * @see Notification
  956. *
  957. * @param caption
  958. * The message
  959. * @param type
  960. * The message type
  961. *
  962. * @deprecated As of 7.0, use Notification.show instead but be aware that
  963. * Notification.show does not allow HTML.
  964. */
  965. @Deprecated
  966. public void showNotification(String caption, Notification.Type type) {
  967. Notification notification = new Notification(caption, type);
  968. notification.setHtmlContentAllowed(true);// Backwards compatibility
  969. getPage().showNotification(notification);
  970. }
  971. /**
  972. * Shows a notification consisting of a bigger caption and a smaller
  973. * description on the middle of the UI. The message automatically disappears
  974. * ("humanized message").
  975. *
  976. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  977. * description are rendered as html.
  978. *
  979. * @see #showNotification(Notification)
  980. * @see Notification
  981. *
  982. * @param caption
  983. * The caption of the message
  984. * @param description
  985. * The message description
  986. *
  987. * @deprecated As of 7.0, use new Notification(...).show(Page) instead but
  988. * be aware that HTML by default not allowed.
  989. */
  990. @Deprecated
  991. public void showNotification(String caption, String description) {
  992. Notification notification = new Notification(caption, description);
  993. notification.setHtmlContentAllowed(true);// Backwards compatibility
  994. getPage().showNotification(notification);
  995. }
  996. /**
  997. * Shows a notification consisting of a bigger caption and a smaller
  998. * description. The position and behavior of the message depends on the
  999. * type, which is one of the basic types defined in {@link Notification} ,
  1000. * for instance Notification.TYPE_WARNING_MESSAGE.
  1001. *
  1002. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  1003. * description are rendered as html.
  1004. *
  1005. * @see #showNotification(Notification)
  1006. * @see Notification
  1007. *
  1008. * @param caption
  1009. * The caption of the message
  1010. * @param description
  1011. * The message description
  1012. * @param type
  1013. * The message type
  1014. *
  1015. * @deprecated As of 7.0, use new Notification(...).show(Page) instead but
  1016. * be aware that HTML by default not allowed.
  1017. */
  1018. @Deprecated
  1019. public void showNotification(String caption, String description,
  1020. Notification.Type type) {
  1021. Notification notification = new Notification(caption, description,
  1022. type);
  1023. notification.setHtmlContentAllowed(true);// Backwards compatibility
  1024. getPage().showNotification(notification);
  1025. }
  1026. /**
  1027. * Shows a notification consisting of a bigger caption and a smaller
  1028. * description. The position and behavior of the message depends on the
  1029. * type, which is one of the basic types defined in {@link Notification} ,
  1030. * for instance Notification.TYPE_WARNING_MESSAGE.
  1031. *
  1032. * Care should be taken to avoid XSS vulnerabilities if html content is
  1033. * allowed.
  1034. *
  1035. * @see #showNotification(Notification)
  1036. * @see Notification
  1037. *
  1038. * @param caption
  1039. * The message caption
  1040. * @param description
  1041. * The message description
  1042. * @param type
  1043. * The type of message
  1044. * @param htmlContentAllowed
  1045. * Whether html in the caption and description should be
  1046. * displayed as html or as plain text
  1047. *
  1048. * @deprecated As of 7.0, use new Notification(...).show(Page).
  1049. */
  1050. @Deprecated
  1051. public void showNotification(String caption, String description,
  1052. Notification.Type type, boolean htmlContentAllowed) {
  1053. getPage().showNotification(new Notification(caption, description, type,
  1054. htmlContentAllowed));
  1055. }
  1056. /**
  1057. * Shows a notification message.
  1058. *
  1059. * @see Notification
  1060. * @see #showNotification(String)
  1061. * @see #showNotification(String, int)
  1062. * @see #showNotification(String, String)
  1063. * @see #showNotification(String, String, int)
  1064. *
  1065. * @param notification
  1066. * The notification message to show
  1067. *
  1068. * @deprecated As of 7.0, use Notification.show instead
  1069. */
  1070. @Deprecated
  1071. public void showNotification(Notification notification) {
  1072. getPage().showNotification(notification);
  1073. }
  1074. /**
  1075. * Returns the timestamp of the last received heartbeat for this UI.
  1076. * <p>
  1077. * This method is not intended to be overridden. If it is overridden, care
  1078. * should be taken since this method might be called in situations where
  1079. * {@link UI#getCurrent()} does not return this UI.
  1080. *
  1081. * @see VaadinService#closeInactiveUIs(VaadinSession)
  1082. *
  1083. * @return The time the last heartbeat request occurred, in milliseconds
  1084. * since the epoch.
  1085. */
  1086. public long getLastHeartbeatTimestamp() {
  1087. return lastHeartbeatTimestamp;
  1088. }
  1089. /**
  1090. * Sets the last heartbeat request timestamp for this UI. Called by the
  1091. * framework whenever the application receives a valid heartbeat request for
  1092. * this UI.
  1093. * <p>
  1094. * This method is not intended to be overridden. If it is overridden, care
  1095. * should be taken since this method might be called in situations where
  1096. * {@link UI#getCurrent()} does not return this UI.
  1097. *
  1098. * @param lastHeartbeat
  1099. * The time the last heartbeat request occurred, in milliseconds
  1100. * since the epoch.
  1101. */
  1102. public void setLastHeartbeatTimestamp(long lastHeartbeat) {
  1103. lastHeartbeatTimestamp = lastHeartbeat;
  1104. }
  1105. /**
  1106. * Gets the theme currently in use by this UI
  1107. *
  1108. * @return the theme name
  1109. */
  1110. public String getTheme() {
  1111. return getState(false).theme;
  1112. }
  1113. /**
  1114. * Sets the theme currently in use by this UI
  1115. * <p>
  1116. * Calling this method will remove the old theme (CSS file) from the
  1117. * application and add the new theme.
  1118. * <p>
  1119. * Note that this method is NOT SAFE to call in a portal environment or
  1120. * other environment where there are multiple UIs on the same page. The old
  1121. * CSS file will be removed even if there are other UIs on the page which
  1122. * are still using it.
  1123. *
  1124. * @since 7.3
  1125. * @param theme
  1126. * The new theme name
  1127. */
  1128. public void setTheme(String theme) {
  1129. if (theme == null) {
  1130. getState().theme = null;
  1131. } else {
  1132. getState().theme = VaadinServlet.stripSpecialChars(theme);
  1133. }
  1134. }
  1135. /**
  1136. * Marks this UI to be {@link #detach() detached} from the session at the
  1137. * end of the current request, or the next request if there is no current
  1138. * request (if called from a background thread, for instance.)
  1139. * <p>
  1140. * The UI is detached after the response is sent, so in the current request
  1141. * it can still update the client side normally. However, after the response
  1142. * any new requests from the client side to this UI will cause an error, so
  1143. * usually the client should be asked, for instance, to reload the page
  1144. * (serving a fresh UI instance), to close the page, or to navigate
  1145. * somewhere else.
  1146. * <p>
  1147. * Note that this method is strictly for users to explicitly signal the
  1148. * framework that the UI should be detached. Overriding it is not a reliable
  1149. * way to catch UIs that are to be detached. Instead, {@code UI.detach()}
  1150. * should be overridden or a {@link DetachListener} used.
  1151. */
  1152. public void close() {
  1153. closing = true;
  1154. boolean sessionExpired = (session == null
  1155. || session.getState() != State.OPEN);
  1156. getRpcProxy(UIClientRpc.class).uiClosed(sessionExpired);
  1157. if (getPushConnection() != null) {
  1158. // Push the Rpc to the client. The connection will be closed when
  1159. // the UI is detached and cleaned up.
  1160. // Can't use UI.push() directly since it checks for a valid session
  1161. if (session != null) {
  1162. session.getService().runPendingAccessTasks(session);
  1163. }
  1164. getPushConnection().push();
  1165. }
  1166. }
  1167. /**
  1168. * Returns whether this UI is marked as closed and is to be detached.
  1169. * <p>
  1170. * This method is not intended to be overridden. If it is overridden, care
  1171. * should be taken since this method might be called in situations where
  1172. * {@link UI#getCurrent()} does not return this UI.
  1173. *
  1174. * @see #close()
  1175. *
  1176. * @return whether this UI is closing.
  1177. */
  1178. public boolean isClosing() {
  1179. return closing;
  1180. }
  1181. /**
  1182. * Called after the UI is added to the session. A UI instance is attached
  1183. * exactly once, before its {@link #init(VaadinRequest) init} method is
  1184. * called.
  1185. *
  1186. * @see Component#attach
  1187. */
  1188. @Override
  1189. public void attach() {
  1190. super.attach();
  1191. getLocaleService().addLocale(getLocale());
  1192. }
  1193. /**
  1194. * Called before the UI is removed from the session. A UI instance is
  1195. * detached exactly once, either:
  1196. * <ul>
  1197. * <li>after it is explicitly {@link #close() closed}.
  1198. * <li>when its session is closed or expires
  1199. * <li>after three missed heartbeat requests.
  1200. * </ul>
  1201. * <p>
  1202. * Note that when a UI is detached, any changes made in the {@code detach}
  1203. * methods of any children or {@link DetachListener}s that would be
  1204. * communicated to the client are silently ignored.
  1205. */
  1206. @Override
  1207. public void detach() {
  1208. super.detach();
  1209. }
  1210. /*
  1211. * (non-Javadoc)
  1212. *
  1213. * @see
  1214. * com.vaadin.ui.AbstractSingleComponentContainer#setContent(com.vaadin.
  1215. * ui.Component)
  1216. */
  1217. @Override
  1218. public void setContent(Component content) {
  1219. if (content instanceof Window) {
  1220. throw new IllegalArgumentException(
  1221. "A Window cannot be added using setContent. Use addWindow(Window window) instead");
  1222. }
  1223. super.setContent(content);
  1224. }
  1225. @Override
  1226. public void setTabIndex(int tabIndex) {
  1227. getState().tabIndex = tabIndex;
  1228. }
  1229. @Override
  1230. public int getTabIndex() {
  1231. return getState(false).tabIndex;
  1232. }
  1233. /**
  1234. * Locks the session of this UI and runs the provided Runnable right away.
  1235. * <p>
  1236. * It is generally recommended to use {@link #access(Runnable)} instead of
  1237. * this method for accessing a session from a different thread as
  1238. * {@link #access(Runnable)} can be used while holding the lock of another
  1239. * session. To avoid causing deadlocks, this methods throws an exception if
  1240. * it is detected than another session is also locked by the current thread.
  1241. * </p>
  1242. * <p>
  1243. * This method behaves differently than {@link #access(Runnable)} in some
  1244. * situations:
  1245. * <ul>
  1246. * <li>If the current thread is currently holding the lock of the session,
  1247. * {@link #accessSynchronously(Runnable)} runs the task right away whereas
  1248. * {@link #access(Runnable)} defers the task to a later point in time.</li>
  1249. * <li>If some other thread is currently holding the lock for the session,
  1250. * {@link #accessSynchronously(Runnable)} blocks while waiting for the lock
  1251. * to be available whereas {@link #access(Runnable)} defers the task to a
  1252. * later point in time.</li>
  1253. * </ul>
  1254. * </p>
  1255. *
  1256. * @since 7.1
  1257. *
  1258. * @param runnable
  1259. * the runnable which accesses the UI
  1260. * @throws UIDetachedException
  1261. * if the UI is not attached to a session (and locking can
  1262. * therefore not be done)
  1263. * @throws IllegalStateException
  1264. * if the current thread holds the lock for another session
  1265. *
  1266. * @see #access(Runnable)
  1267. * @see VaadinSession#accessSynchronously(Runnable)
  1268. */
  1269. public void accessSynchronously(Runnable runnable)
  1270. throws UIDetachedException {
  1271. Map<Class<?>, CurrentInstance> old = null;
  1272. VaadinSession session = getSession();
  1273. if (session == null) {
  1274. throw new UIDetachedException();
  1275. }
  1276. VaadinService.verifyNoOtherSessionLocked(session);
  1277. session.lock();
  1278. try {
  1279. if (getSession() == null) {
  1280. // UI was detached after fetching the session but before we
  1281. // acquired the lock.
  1282. throw new UIDetachedException();
  1283. }
  1284. old = CurrentInstance.setCurrent(this);
  1285. runnable.run();
  1286. } finally {
  1287. session.unlock();
  1288. if (old != null) {
  1289. CurrentInstance.restoreInstances(old);
  1290. }
  1291. }
  1292. }
  1293. /**
  1294. * Provides exclusive access to this UI from outside a request handling
  1295. * thread.
  1296. * <p>
  1297. * The given runnable is executed while holding the session lock to ensure
  1298. * exclusive access to this UI. If the session is not locked, the lock will
  1299. * be acquired and the runnable is run right away. If the session is
  1300. * currently locked, the runnable will be run before that lock is released.
  1301. * </p>
  1302. * <p>
  1303. * RPC handlers for components inside this UI do not need to use this method
  1304. * as the session is automatically locked by the framework during RPC
  1305. * handling.
  1306. * </p>
  1307. * <p>
  1308. * Please note that the runnable might be invoked on a different thread or
  1309. * later on the current thread, which means that custom thread locals might
  1310. * not have the expected values when the command is executed.
  1311. * {@link UI#getCurrent()}, {@link VaadinSession#getCurrent()} and
  1312. * {@link VaadinService#getCurrent()} are set according to this UI before
  1313. * executing the command. Other standard CurrentInstance values such as
  1314. * {@link VaadinService#getCurrentRequest()} and
  1315. * {@link VaadinService#getCurrentResponse()} will not be defined.
  1316. * </p>
  1317. * <p>
  1318. * The returned future can be used to check for task completion and to
  1319. * cancel the task.
  1320. * </p>
  1321. *
  1322. * @see #getCurrent()
  1323. * @see #accessSynchronously(Runnable)
  1324. * @see VaadinSession#access(Runnable)
  1325. * @see VaadinSession#lock()
  1326. *
  1327. * @since 7.1
  1328. *
  1329. * @param runnable
  1330. * the runnable which accesses the UI
  1331. * @throws UIDetachedException
  1332. * if the UI is not attached to a session (and locking can
  1333. * therefore not be done)
  1334. * @return a future that can be used to check for task completion and to
  1335. * cancel the task
  1336. */
  1337. public Future<Void> access(final Runnable runnable) {
  1338. VaadinSession session = getSession();
  1339. if (session == null) {
  1340. throw new UIDetachedException();
  1341. }
  1342. return session.access(new ErrorHandlingRunnable() {
  1343. @Override
  1344. public void run() {
  1345. accessSynchronously(runnable);
  1346. }
  1347. @Override
  1348. public void handleError(Exception exception) {
  1349. try {
  1350. if (runnable instanceof ErrorHandlingRunnable) {
  1351. ErrorHandlingRunnable errorHandlingRunnable = (ErrorHandlingRunnable) runnable;
  1352. errorHandlingRunnable.handleError(exception);
  1353. } else {
  1354. ConnectorErrorEvent errorEvent = new ConnectorErrorEvent(
  1355. UI.this, exception);
  1356. ErrorHandler errorHandler = com.vaadin.server.ErrorEvent
  1357. .findErrorHandler(UI.this);
  1358. if (errorHandler == null) {
  1359. errorHandler = new DefaultErrorHandler();
  1360. }
  1361. errorHandler.error(errorEvent);
  1362. }
  1363. } catch (Exception e) {
  1364. getLogger().log(Level.SEVERE, e.getMessage(), e);
  1365. }
  1366. }
  1367. });
  1368. }
  1369. /**
  1370. * Retrieves the object used for configuring tooltips.
  1371. *
  1372. * @return The instance used for tooltip configuration
  1373. */
  1374. public TooltipConfiguration getTooltipConfiguration() {
  1375. return tooltipConfiguration;
  1376. }
  1377. /**
  1378. * Retrieves the object used for configuring notifications.
  1379. *
  1380. * @return The instance used for notification configuration
  1381. */
  1382. public NotificationConfiguration getNotificationConfiguration() {
  1383. return notificationConfiguration;
  1384. }
  1385. /**
  1386. * Retrieves the object used for configuring the loading indicator.
  1387. *
  1388. * @return The instance used for configuring the loading indicator
  1389. */
  1390. public LoadingIndicatorConfiguration getLoadingIndicatorConfiguration() {
  1391. return loadingIndicatorConfiguration;
  1392. }
  1393. /**
  1394. * Pushes the pending changes and client RPC invocations of this UI to the
  1395. * client-side.
  1396. * <p>
  1397. * If push is enabled, but the push connection is not currently open, the
  1398. * push will be done when the connection is established.
  1399. * <p>
  1400. * As with all UI methods, the session must be locked when calling this
  1401. * method. It is also recommended that {@link UI#getCurrent()} is set up to
  1402. * return this UI since writing the response may invoke logic in any
  1403. * attached component or extension. The recommended way of fulfilling these
  1404. * conditions is to use {@link #access(Runnable)}.
  1405. *
  1406. * @throws IllegalStateException
  1407. * if push is disabled.
  1408. * @throws UIDetachedException
  1409. * if this UI is not attached to a session.
  1410. *
  1411. * @see #getPushConfiguration()
  1412. *
  1413. * @since 7.1
  1414. */
  1415. public void push() {
  1416. VaadinSession session = getSession();
  1417. if (session == null) {
  1418. throw new UIDetachedException("Cannot push a detached UI");
  1419. }
  1420. assert session.hasLock();
  1421. if (!getPushConfiguration().getPushMode().isEnabled()) {
  1422. throw new IllegalStateException("Push not enabled");
  1423. }
  1424. assert pushConnection != null;
  1425. /*
  1426. * Purge the pending access queue as it might mark a connector as dirty
  1427. * when the push would otherwise be ignored because there are no changes
  1428. * to push.
  1429. */
  1430. session.getService().runPendingAccessTasks(session);
  1431. if (!getConnectorTracker().hasDirtyConnectors()) {
  1432. // Do not push if there is nothing to push
  1433. return;
  1434. }
  1435. pushConnection.push();
  1436. }
  1437. /**
  1438. * Returns the internal push connection object used by this UI. This method
  1439. * should only be called by the framework.
  1440. * <p>
  1441. * This method is not intended to be overridden. If it is overridden, care
  1442. * should be taken since this method might be called in situations where
  1443. * {@link UI#getCurrent()} does not return this UI.
  1444. *
  1445. * @return the push connection used by this UI, or {@code null} if push is
  1446. * not available.
  1447. */
  1448. public PushConnection getPushConnection() {
  1449. assert !(getPushConfiguration().getPushMode().isEnabled()
  1450. && pushConnection == null);
  1451. return pushConnection;
  1452. }
  1453. /**
  1454. * Sets the internal push connection object used by this UI. This method
  1455. * should only be called by the framework.
  1456. * <p>
  1457. * The {@code pushConnection} argument must be non-null if and only if
  1458. * {@code getPushConfiguration().getPushMode().isEnabled()}.
  1459. *
  1460. * @param pushConnection
  1461. * the push connection to use for this UI
  1462. */
  1463. public void setPushConnection(PushConnection pushConnection) {
  1464. // If pushMode is disabled then there should never be a pushConnection;
  1465. // if enabled there should always be
  1466. assert (pushConnection == null)
  1467. ^ getPushConfiguration().getPushMode().isEnabled();
  1468. if (pushConnection == this.pushConnection) {
  1469. return;
  1470. }
  1471. if (this.pushConnection != null && this.pushConnection.isConnected()) {
  1472. this.pushConnection.disconnect();
  1473. }
  1474. this.pushConnection = pushConnection;
  1475. }
  1476. /**
  1477. * Sets the interval with which the UI should poll the server to see if
  1478. * there are any changes. Polling is disabled by default.
  1479. * <p>
  1480. * Note that it is possible to enable push and polling at the same time but
  1481. * it should not be done to avoid excessive server traffic.
  1482. * </p>
  1483. * <p>
  1484. * Add-on developers should note that this method is only meant for the
  1485. * application developer. An add-on should not set the poll interval
  1486. * directly, rather instruct the user to set it.
  1487. * </p>
  1488. *
  1489. * @param intervalInMillis
  1490. * The interval (in ms) with which the UI should poll the server
  1491. * or -1 to disable polling
  1492. */
  1493. public void setPollInterval(int intervalInMillis) {
  1494. getState().pollInterval = intervalInMillis;
  1495. }
  1496. /**
  1497. * Returns the interval with which the UI polls the server.
  1498. *
  1499. * @return The interval (in ms) with which the UI polls the server or -1 if
  1500. * polling is disabled
  1501. */
  1502. public int getPollInterval() {
  1503. return getState(false).pollInterval;
  1504. }
  1505. @Override
  1506. public Registration addPollListener(PollListener listener) {
  1507. return addListener(EventId.POLL, PollEvent.class, listener,
  1508. PollListener.POLL_METHOD);
  1509. }
  1510. @Override
  1511. @Deprecated
  1512. public void removePollListener(PollListener listener) {
  1513. removeListener(EventId.POLL, PollEvent.class, listener);
  1514. }
  1515. /**
  1516. * Retrieves the object used for configuring the push channel.
  1517. *
  1518. * @since 7.1
  1519. * @return The instance used for push configuration
  1520. */
  1521. public PushConfiguration getPushConfiguration() {
  1522. return pushConfiguration;
  1523. }
  1524. /**
  1525. * Retrieves the object used for configuring the reconnect dialog.
  1526. *
  1527. * @since 7.6
  1528. * @return The instance used for reconnect dialog configuration
  1529. */
  1530. public ReconnectDialogConfiguration getReconnectDialogConfiguration() {
  1531. return reconnectDialogConfiguration;
  1532. }
  1533. /**
  1534. * Get the label that is added to the container element, where tooltip,
  1535. * notification and dialogs are added to.
  1536. *
  1537. * @return the label of the container
  1538. */
  1539. public String getOverlayContainerLabel() {
  1540. return getState(false).overlayContainerLabel;
  1541. }
  1542. /**
  1543. * Sets the label that is added to the container element, where tooltip,
  1544. * notifications and dialogs are added to.
  1545. * <p>
  1546. * This is helpful for users of assistive devices, as this element is
  1547. * reachable for them.
  1548. * </p>
  1549. *
  1550. * @param overlayContainerLabel
  1551. * label to use for the container
  1552. */
  1553. public void setOverlayContainerLabel(String overlayContainerLabel) {
  1554. getState().overlayContainerLabel = overlayContainerLabel;
  1555. }
  1556. /**
  1557. * Returns the locale service which handles transmission of Locale data to
  1558. * the client.
  1559. *
  1560. * @since 7.1
  1561. * @return The LocaleService for this UI
  1562. */
  1563. public LocaleService getLocaleService() {
  1564. return localeService;
  1565. }
  1566. private static Logger getLogger() {
  1567. return Logger.getLogger(UI.class.getName());
  1568. }
  1569. /**
  1570. * Gets a string the uniquely distinguishes this UI instance based on where
  1571. * it is embedded. The embed identifier is based on the
  1572. * <code>window.name</code> DOM attribute of the browser window where the UI
  1573. * is displayed and the id of the div element where the UI is embedded.
  1574. *
  1575. * @since 7.2
  1576. * @return the embed id for this UI, or <code>null</code> if no id known
  1577. */
  1578. public String getEmbedId() {
  1579. return embedId;
  1580. }
  1581. /**
  1582. * Gets the last processed server message id.
  1583. *
  1584. * Used internally for communication tracking.
  1585. *
  1586. * @return lastProcessedServerMessageId the id of the last processed server
  1587. * message
  1588. * @since 7.6
  1589. */
  1590. public int getLastProcessedClientToServerId() {
  1591. return lastProcessedClientToServerId;
  1592. }
  1593. /**
  1594. * Sets the last processed server message id.
  1595. *
  1596. * Used internally for communication tracking.
  1597. *
  1598. * @param lastProcessedServerMessageId
  1599. * the id of the last processed server message
  1600. * @since 7.6
  1601. */
  1602. public void setLastProcessedClientToServerId(
  1603. int lastProcessedClientToServerId) {
  1604. this.lastProcessedClientToServerId = lastProcessedClientToServerId;
  1605. }
  1606. /**
  1607. * Adds a WindowOrderUpdateListener to the UI.
  1608. * <p>
  1609. * The WindowOrderUpdateEvent is fired when the order positions of windows
  1610. * are updated. It can happen when some window (this or other) is brought to
  1611. * front or detached.
  1612. * <p>
  1613. * The other way to listen window position for specific window is
  1614. * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)}
  1615. *
  1616. * @see Window#addWindowOrderChangeListener(WindowOrderChangeListener)
  1617. *
  1618. * @param listener
  1619. * the WindowModeChangeListener to add.
  1620. * @since 8.0.0
  1621. *
  1622. * @return a registration object for removing the listener
  1623. */
  1624. public Registration addWindowOrderUpdateListener(
  1625. WindowOrderUpdateListener listener) {
  1626. addListener(EventId.WINDOW_ORDER, WindowOrderUpdateEvent.class,
  1627. listener, WindowOrderUpdateListener.windowOrderUpdateMethod);
  1628. return () -> removeListener(EventId.WINDOW_ORDER,
  1629. WindowOrderUpdateEvent.class, listener);
  1630. }
  1631. /**
  1632. * Event which is fired when the ordering of the windows is updated.
  1633. * <p>
  1634. * The other way to listen window position for specific window is
  1635. * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)}
  1636. *
  1637. * @see Window.WindowOrderChangeEvent
  1638. *
  1639. * @author Vaadin Ltd
  1640. * @since 8.0.0
  1641. *
  1642. */
  1643. public static class WindowOrderUpdateEvent extends Component.Event {
  1644. private final Collection<Window> windows;
  1645. public WindowOrderUpdateEvent(Component source,
  1646. Collection<Window> windows) {
  1647. super(source);
  1648. this.windows = windows;
  1649. }
  1650. /**
  1651. * Gets the windows in the order they appear in the UI: top most window
  1652. * is first, bottom one last.
  1653. *
  1654. * @return the windows collection
  1655. */
  1656. public Collection<Window> getWindows() {
  1657. return windows;
  1658. }
  1659. }
  1660. /**
  1661. * An interface used for listening to Windows order update events.
  1662. *
  1663. * @since 8.0.0
  1664. *
  1665. * @see Window.WindowOrderChangeEvent
  1666. */
  1667. @FunctionalInterface
  1668. public interface WindowOrderUpdateListener extends ConnectorEventListener {
  1669. public static final Method windowOrderUpdateMethod = ReflectTools
  1670. .findMethod(WindowOrderUpdateListener.class,
  1671. "windowOrderUpdated", WindowOrderUpdateEvent.class);
  1672. /**
  1673. * Called when the windows order positions are changed. Use
  1674. * {@link WindowOrderUpdateEvent#getWindows()} to get a reference to the
  1675. * {@link Window}s whose order positions are updated. Use
  1676. * {@link Window#getOrderPosition()} to get window position for specific
  1677. * window.
  1678. *
  1679. * @param event
  1680. */
  1681. public void windowOrderUpdated(WindowOrderUpdateEvent event);
  1682. }
  1683. }