You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UI.java 54KB

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