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.

VaadinSession.java 48KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461
  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.server;
  17. import java.io.IOException;
  18. import java.io.ObjectInputStream;
  19. import java.io.Serializable;
  20. import java.lang.reflect.Method;
  21. import java.util.Collection;
  22. import java.util.Collections;
  23. import java.util.Enumeration;
  24. import java.util.HashMap;
  25. import java.util.HashSet;
  26. import java.util.LinkedList;
  27. import java.util.List;
  28. import java.util.Locale;
  29. import java.util.Map;
  30. import java.util.Queue;
  31. import java.util.Set;
  32. import java.util.UUID;
  33. import java.util.concurrent.ConcurrentLinkedQueue;
  34. import java.util.concurrent.ExecutionException;
  35. import java.util.concurrent.Future;
  36. import java.util.concurrent.FutureTask;
  37. import java.util.concurrent.locks.Lock;
  38. import java.util.concurrent.locks.ReentrantLock;
  39. import java.util.logging.Level;
  40. import java.util.logging.Logger;
  41. import javax.portlet.PortletSession;
  42. import javax.servlet.http.HttpSession;
  43. import javax.servlet.http.HttpSessionBindingEvent;
  44. import javax.servlet.http.HttpSessionBindingListener;
  45. import com.vaadin.data.util.converter.Converter;
  46. import com.vaadin.data.util.converter.ConverterFactory;
  47. import com.vaadin.data.util.converter.DefaultConverterFactory;
  48. import com.vaadin.event.EventRouter;
  49. import com.vaadin.shared.communication.PushMode;
  50. import com.vaadin.ui.AbstractField;
  51. import com.vaadin.ui.Table;
  52. import com.vaadin.ui.UI;
  53. import com.vaadin.util.CurrentInstance;
  54. import com.vaadin.util.ReflectTools;
  55. /**
  56. * Contains everything that Vaadin needs to store for a specific user. This is
  57. * typically stored in a {@link HttpSession} or {@link PortletSession}, but
  58. * others storage mechanisms might also be used.
  59. * <p>
  60. * Everything inside a {@link VaadinSession} should be serializable to ensure
  61. * compatibility with schemes using serialization for persisting the session
  62. * data.
  63. *
  64. * @author Vaadin Ltd
  65. * @since 7.0.0
  66. */
  67. @SuppressWarnings("serial")
  68. public class VaadinSession implements HttpSessionBindingListener, Serializable {
  69. /**
  70. * Encapsulates a {@link Runnable} submitted using
  71. * {@link VaadinSession#access(Runnable)}. This class is used internally by
  72. * the framework and is not intended to be directly used by application
  73. * developers.
  74. *
  75. * @since 7.1
  76. * @author Vaadin Ltd
  77. */
  78. public static class FutureAccess extends FutureTask<Void> {
  79. /**
  80. * Snapshot of all non-inheritable current instances at the time this
  81. * object was created.
  82. */
  83. private final Map<Class<?>, CurrentInstance> instances = CurrentInstance
  84. .getInstances(true);
  85. private final VaadinSession session;
  86. private Runnable runnable;
  87. /**
  88. * Creates an instance for the given runnable
  89. *
  90. * @param session
  91. * the session to which the task belongs
  92. *
  93. * @param runnable
  94. * the runnable to run when this task is purged from the
  95. * queue
  96. */
  97. public FutureAccess(VaadinSession session, Runnable runnable) {
  98. super(runnable, null);
  99. this.session = session;
  100. this.runnable = runnable;
  101. }
  102. @Override
  103. public Void get() throws InterruptedException, ExecutionException {
  104. /*
  105. * Help the developer avoid programming patterns that cause
  106. * deadlocks unless implemented very carefully. get(long, TimeUnit)
  107. * does not have the same detection since a sensible timeout should
  108. * avoid completely locking up the application.
  109. *
  110. * Even though no deadlock could occur after the runnable has been
  111. * run, the check is always done as the deterministic behavior makes
  112. * it easier to detect potential problems.
  113. */
  114. VaadinService.verifyNoOtherSessionLocked(session);
  115. return super.get();
  116. }
  117. /**
  118. * Gets the current instance values that should be used when running
  119. * this task.
  120. *
  121. * @see CurrentInstance#restoreInstances(Map)
  122. *
  123. * @return a map of current instances.
  124. */
  125. public Map<Class<?>, CurrentInstance> getCurrentInstances() {
  126. return instances;
  127. }
  128. /**
  129. * Handles exceptions thrown during the execution of this task.
  130. *
  131. * @since 7.1.8
  132. * @param exception
  133. * the thrown exception.
  134. */
  135. public void handleError(Exception exception) {
  136. try {
  137. if (runnable instanceof ErrorHandlingRunnable) {
  138. ErrorHandlingRunnable errorHandlingRunnable = (ErrorHandlingRunnable) runnable;
  139. errorHandlingRunnable.handleError(exception);
  140. } else {
  141. ErrorEvent errorEvent = new ErrorEvent(exception);
  142. ErrorHandler errorHandler = ErrorEvent
  143. .findErrorHandler(session);
  144. if (errorHandler == null) {
  145. errorHandler = new DefaultErrorHandler();
  146. }
  147. errorHandler.error(errorEvent);
  148. }
  149. } catch (Exception e) {
  150. getLogger().log(Level.SEVERE, e.getMessage(), e);
  151. }
  152. }
  153. }
  154. /**
  155. * The lifecycle state of a VaadinSession.
  156. *
  157. * @since 7.2
  158. */
  159. public enum State {
  160. /**
  161. * The session is active and accepting client requests.
  162. */
  163. OPEN,
  164. /**
  165. * The {@link VaadinSession#close() close} method has been called; the
  166. * session will be closed as soon as the current request ends.
  167. */
  168. CLOSING,
  169. /**
  170. * The session is closed; all the {@link UI}s have been removed and
  171. * {@link SessionDestroyListener}s have been called.
  172. */
  173. CLOSED;
  174. private boolean isValidChange(State newState) {
  175. return (this == OPEN && newState == CLOSING)
  176. || (this == CLOSING && newState == CLOSED);
  177. }
  178. }
  179. /**
  180. * The name of the parameter that is by default used in e.g. web.xml to
  181. * define the name of the default {@link UI} class.
  182. */
  183. // javadoc in UI should be updated if this value is changed
  184. public static final String UI_PARAMETER = "UI";
  185. private static final Method BOOTSTRAP_FRAGMENT_METHOD = ReflectTools
  186. .findMethod(BootstrapListener.class, "modifyBootstrapFragment",
  187. BootstrapFragmentResponse.class);
  188. private static final Method BOOTSTRAP_PAGE_METHOD = ReflectTools
  189. .findMethod(BootstrapListener.class, "modifyBootstrapPage",
  190. BootstrapPageResponse.class);
  191. /**
  192. * Configuration for the session.
  193. */
  194. private DeploymentConfiguration configuration;
  195. /**
  196. * Default locale of the session.
  197. */
  198. private Locale locale;
  199. /**
  200. * Session wide error handler which is used by default if an error is left
  201. * unhandled.
  202. */
  203. private ErrorHandler errorHandler = new DefaultErrorHandler();
  204. /**
  205. * The converter factory that is used to provide default converters for the
  206. * session.
  207. */
  208. private ConverterFactory converterFactory = new DefaultConverterFactory();
  209. private LinkedList<RequestHandler> requestHandlers = new LinkedList<RequestHandler>();
  210. private int nextUIId = 0;
  211. private Map<Integer, UI> uIs = new HashMap<Integer, UI>();
  212. private final Map<String, Integer> embedIdMap = new HashMap<String, Integer>();
  213. private final EventRouter eventRouter = new EventRouter();
  214. private GlobalResourceHandler globalResourceHandler;
  215. protected WebBrowser browser = new WebBrowser();
  216. private DragAndDropService dragAndDropService;
  217. private LegacyCommunicationManager communicationManager;
  218. private long cumulativeRequestDuration = 0;
  219. private long lastRequestDuration = -1;
  220. private long lastRequestTimestamp = System.currentTimeMillis();
  221. private State state = State.OPEN;
  222. private transient WrappedSession session;
  223. private final Map<String, Object> attributes = new HashMap<String, Object>();
  224. private LinkedList<UIProvider> uiProviders = new LinkedList<UIProvider>();
  225. private transient VaadinService service;
  226. private transient Lock lock;
  227. /*
  228. * Pending tasks can't be serialized and the queue should be empty when the
  229. * session is serialized as long as it doesn't happen while some other
  230. * thread has the lock.
  231. */
  232. private transient ConcurrentLinkedQueue<FutureAccess> pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
  233. /**
  234. * Creates a new VaadinSession tied to a VaadinService.
  235. *
  236. * @param service
  237. * the Vaadin service for the new session
  238. */
  239. public VaadinSession(VaadinService service) {
  240. this.service = service;
  241. }
  242. /**
  243. * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)
  244. */
  245. @Override
  246. public void valueBound(HttpSessionBindingEvent arg0) {
  247. // We are not interested in bindings
  248. }
  249. /**
  250. * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)
  251. */
  252. @Override
  253. public void valueUnbound(HttpSessionBindingEvent event) {
  254. // If we are going to be unbound from the session, the session must be
  255. // closing
  256. // Notify the service
  257. if (service == null) {
  258. getLogger()
  259. .warning(
  260. "A VaadinSession instance not associated to any service is getting unbound. "
  261. + "Session destroy events will not be fired and UIs in the session will not get detached. "
  262. + "This might happen if a session is deserialized but never used before it expires.");
  263. } else if (VaadinService.getCurrentRequest() != null
  264. && getCurrent() == this) {
  265. assert hasLock();
  266. // Ignore if the session is being moved to a different backing
  267. // session or if GAEVaadinServlet is doing its normal cleanup.
  268. if (getAttribute(VaadinService.PRESERVE_UNBOUND_SESSION_ATTRIBUTE) == Boolean.TRUE) {
  269. return;
  270. }
  271. // There is still a request in progress for this session. The
  272. // session will be destroyed after the response has been written.
  273. if (getState() == State.OPEN) {
  274. close();
  275. }
  276. } else {
  277. // We are not in a request related to this session so we can destroy
  278. // it as soon as we acquire the lock.
  279. service.fireSessionDestroy(this);
  280. }
  281. session = null;
  282. }
  283. /**
  284. * Get the web browser associated with this session.
  285. *
  286. * @return the web browser object
  287. *
  288. * @deprecated As of 7.0, use {@link Page#getWebBrowser()} instead.
  289. */
  290. @Deprecated
  291. public WebBrowser getBrowser() {
  292. assert hasLock();
  293. return browser;
  294. }
  295. /**
  296. * @return The total time spent servicing requests in this session, in
  297. * milliseconds.
  298. */
  299. public long getCumulativeRequestDuration() {
  300. assert hasLock();
  301. return cumulativeRequestDuration;
  302. }
  303. /**
  304. * Sets the time spent servicing the last request in the session and updates
  305. * the total time spent servicing requests in this session.
  306. *
  307. * @param time
  308. * The time spent in the last request, in milliseconds.
  309. */
  310. public void setLastRequestDuration(long time) {
  311. assert hasLock();
  312. lastRequestDuration = time;
  313. cumulativeRequestDuration += time;
  314. }
  315. /**
  316. * @return The time spent servicing the last request in this session, in
  317. * milliseconds.
  318. */
  319. public long getLastRequestDuration() {
  320. assert hasLock();
  321. return lastRequestDuration;
  322. }
  323. /**
  324. * Sets the time when the last UIDL request was serviced in this session.
  325. *
  326. * @param timestamp
  327. * The time when the last request was handled, in milliseconds
  328. * since the epoch.
  329. *
  330. */
  331. public void setLastRequestTimestamp(long timestamp) {
  332. assert hasLock();
  333. lastRequestTimestamp = timestamp;
  334. }
  335. /**
  336. * Returns the time when the last request was serviced in this session.
  337. *
  338. * @return The time when the last request was handled, in milliseconds since
  339. * the epoch.
  340. */
  341. public long getLastRequestTimestamp() {
  342. assert hasLock();
  343. return lastRequestTimestamp;
  344. }
  345. /**
  346. * Gets the underlying session to which this service session is currently
  347. * associated.
  348. *
  349. * @return the wrapped session for this context
  350. */
  351. public WrappedSession getSession() {
  352. /*
  353. * This is used to fetch the underlying session and there is no need for
  354. * having a lock when doing this. On the contrary this is sometimes done
  355. * to be able to lock the session.
  356. */
  357. return session;
  358. }
  359. /**
  360. * @return
  361. *
  362. * @deprecated As of 7.0. Will likely change or be removed in a future
  363. * version
  364. */
  365. @Deprecated
  366. public LegacyCommunicationManager getCommunicationManager() {
  367. assert hasLock();
  368. return communicationManager;
  369. }
  370. public DragAndDropService getDragAndDropService() {
  371. if (dragAndDropService == null) {
  372. dragAndDropService = new DragAndDropService(this);
  373. }
  374. return dragAndDropService;
  375. }
  376. /**
  377. * Loads the VaadinSession for the given service and WrappedSession from the
  378. * HTTP session.
  379. *
  380. * @param service
  381. * The service the VaadinSession is associated with
  382. * @param underlyingSession
  383. * The wrapped HTTP session for the user
  384. * @return A VaadinSession instance for the service, session combination or
  385. * null if none was found.
  386. * @deprecated As of 7.0. Should be moved to a separate session storage
  387. * class some day.
  388. */
  389. @Deprecated
  390. public static VaadinSession getForSession(VaadinService service,
  391. WrappedSession underlyingSession) {
  392. assert hasLock(service, underlyingSession);
  393. VaadinSession vaadinSession = (VaadinSession) underlyingSession
  394. .getAttribute(getSessionAttributeName(service));
  395. if (vaadinSession == null) {
  396. return null;
  397. }
  398. vaadinSession.session = underlyingSession;
  399. vaadinSession.service = service;
  400. vaadinSession.refreshLock();
  401. return vaadinSession;
  402. }
  403. /**
  404. * Retrieves all {@link VaadinSession}s which are stored in the given HTTP
  405. * session
  406. *
  407. * @since 7.2
  408. * @param httpSession
  409. * the HTTP session
  410. * @return the found VaadinSessions
  411. */
  412. public static Collection<VaadinSession> getAllSessions(
  413. HttpSession httpSession) {
  414. Set<VaadinSession> sessions = new HashSet<VaadinSession>();
  415. Enumeration<String> attributeNames = httpSession.getAttributeNames();
  416. while (attributeNames.hasMoreElements()) {
  417. String attributeName = attributeNames.nextElement();
  418. if (attributeName.startsWith(VaadinSession.class.getName() + ".")) {
  419. Object value = httpSession.getAttribute(attributeName);
  420. if (value instanceof VaadinSession) {
  421. sessions.add((VaadinSession) value);
  422. }
  423. }
  424. }
  425. return sessions;
  426. }
  427. /**
  428. * Removes this VaadinSession from the HTTP session.
  429. *
  430. * @param service
  431. * The service this session is associated with
  432. * @deprecated As of 7.0. Should be moved to a separate session storage
  433. * class some day.
  434. */
  435. @Deprecated
  436. public void removeFromSession(VaadinService service) {
  437. assert hasLock();
  438. session.removeAttribute(getSessionAttributeName(service));
  439. }
  440. /**
  441. * Retrieves the name of the attribute used for storing a VaadinSession for
  442. * the given service.
  443. *
  444. * @param service
  445. * The service associated with the sessio
  446. * @return The attribute name used for storing the session
  447. */
  448. private static String getSessionAttributeName(VaadinService service) {
  449. return VaadinSession.class.getName() + "." + service.getServiceName();
  450. }
  451. /**
  452. * Stores this VaadinSession in the HTTP session.
  453. *
  454. * @param service
  455. * The service this session is associated with
  456. * @param session
  457. * The HTTP session this VaadinSession should be stored in
  458. * @deprecated As of 7.0. Should be moved to a separate session storage
  459. * class some day.
  460. */
  461. @Deprecated
  462. public void storeInSession(VaadinService service, WrappedSession session) {
  463. assert hasLock(service, session);
  464. session.setAttribute(getSessionAttributeName(service), this);
  465. /*
  466. * GAEVaadinServlet passes newly deserialized sessions here, which means
  467. * that these transient fields need to be populated to avoid NPE from
  468. * refreshLock().
  469. */
  470. this.service = service;
  471. this.session = session;
  472. refreshLock();
  473. }
  474. /**
  475. * Updates the transient session lock from VaadinService.
  476. */
  477. private void refreshLock() {
  478. assert lock == null || lock == service.getSessionLock(session) : "Cannot change the lock from one instance to another";
  479. assert hasLock(service, session);
  480. lock = service.getSessionLock(session);
  481. }
  482. public void setCommunicationManager(
  483. LegacyCommunicationManager communicationManager) {
  484. assert hasLock();
  485. if (communicationManager == null) {
  486. throw new IllegalArgumentException("Can not set to null");
  487. }
  488. assert this.communicationManager == null : "Communication manager can only be set once";
  489. this.communicationManager = communicationManager;
  490. }
  491. public void setConfiguration(DeploymentConfiguration configuration) {
  492. assert hasLock();
  493. if (configuration == null) {
  494. throw new IllegalArgumentException("Can not set to null");
  495. }
  496. assert this.configuration == null : "Configuration can only be set once";
  497. this.configuration = configuration;
  498. }
  499. /**
  500. * Gets the configuration for this session
  501. *
  502. * @return the deployment configuration
  503. */
  504. public DeploymentConfiguration getConfiguration() {
  505. assert hasLock();
  506. return configuration;
  507. }
  508. /**
  509. * Gets the default locale for this session.
  510. *
  511. * By default this is the preferred locale of the user using the session. In
  512. * most cases it is read from the browser defaults.
  513. *
  514. * @return the locale of this session.
  515. */
  516. public Locale getLocale() {
  517. assert hasLock();
  518. if (locale != null) {
  519. return locale;
  520. }
  521. return Locale.getDefault();
  522. }
  523. /**
  524. * Sets the default locale for this session.
  525. *
  526. * By default this is the preferred locale of the user using the
  527. * application. In most cases it is read from the browser defaults.
  528. *
  529. * @param locale
  530. * the Locale object.
  531. *
  532. */
  533. public void setLocale(Locale locale) {
  534. assert hasLock();
  535. this.locale = locale;
  536. }
  537. /**
  538. * Gets the session's error handler.
  539. *
  540. * @return the current error handler
  541. */
  542. public ErrorHandler getErrorHandler() {
  543. assert hasLock();
  544. return errorHandler;
  545. }
  546. /**
  547. * Sets the session error handler.
  548. *
  549. * @param errorHandler
  550. */
  551. public void setErrorHandler(ErrorHandler errorHandler) {
  552. assert hasLock();
  553. this.errorHandler = errorHandler;
  554. }
  555. /**
  556. * Gets the {@link ConverterFactory} used to locate a suitable
  557. * {@link Converter} for fields in the session.
  558. *
  559. * See {@link #setConverterFactory(ConverterFactory)} for more details
  560. *
  561. * @return The converter factory used in the session
  562. */
  563. public ConverterFactory getConverterFactory() {
  564. assert hasLock();
  565. return converterFactory;
  566. }
  567. /**
  568. * Sets the {@link ConverterFactory} used to locate a suitable
  569. * {@link Converter} for fields in the session.
  570. * <p>
  571. * The {@link ConverterFactory} is used to find a suitable converter when
  572. * binding data to a UI component and the data type does not match the UI
  573. * component type, e.g. binding a Double to a TextField (which is based on a
  574. * String).
  575. * </p>
  576. * <p>
  577. * The {@link Converter} for an individual field can be overridden using
  578. * {@link AbstractField#setConverter(Converter)} and for individual property
  579. * ids in a {@link Table} using
  580. * {@link Table#setConverter(Object, Converter)}.
  581. * </p>
  582. * <p>
  583. * The converter factory must never be set to null.
  584. *
  585. * @param converterFactory
  586. * The converter factory used in the session
  587. */
  588. public void setConverterFactory(ConverterFactory converterFactory) {
  589. assert hasLock();
  590. this.converterFactory = converterFactory;
  591. }
  592. /**
  593. * Adds a request handler to this session. Request handlers can be added to
  594. * provide responses to requests that are not handled by the default
  595. * functionality of the framework.
  596. * <p>
  597. * Handlers are called in reverse order of addition, so the most recently
  598. * added handler will be called first.
  599. * </p>
  600. *
  601. * @param handler
  602. * the request handler to add
  603. *
  604. * @see #removeRequestHandler(RequestHandler)
  605. *
  606. * @since 7.0
  607. */
  608. public void addRequestHandler(RequestHandler handler) {
  609. assert hasLock();
  610. requestHandlers.addFirst(handler);
  611. }
  612. /**
  613. * Removes a request handler from the session.
  614. *
  615. * @param handler
  616. * the request handler to remove
  617. *
  618. * @since 7.0
  619. */
  620. public void removeRequestHandler(RequestHandler handler) {
  621. assert hasLock();
  622. requestHandlers.remove(handler);
  623. }
  624. /**
  625. * Gets the request handlers that are registered to the session. The
  626. * iteration order of the returned collection is the same as the order in
  627. * which the request handlers will be invoked when a request is handled.
  628. *
  629. * @return a collection of request handlers, with the iteration order
  630. * according to the order they would be invoked
  631. *
  632. * @see #addRequestHandler(RequestHandler)
  633. * @see #removeRequestHandler(RequestHandler)
  634. *
  635. * @since 7.0
  636. */
  637. public Collection<RequestHandler> getRequestHandlers() {
  638. assert hasLock();
  639. return Collections.unmodifiableCollection(requestHandlers);
  640. }
  641. /**
  642. * Gets the currently used session. The current session is automatically
  643. * defined when processing requests to the server and in threads started at
  644. * a point when the current session is defined (see
  645. * {@link InheritableThreadLocal}). In other cases, (e.g. from background
  646. * threads started in some other way), the current session is not
  647. * automatically defined.
  648. *
  649. * @return the current session instance if available, otherwise
  650. * <code>null</code>
  651. *
  652. * @see #setCurrent(VaadinSession)
  653. *
  654. * @since 7.0
  655. */
  656. public static VaadinSession getCurrent() {
  657. return CurrentInstance.get(VaadinSession.class);
  658. }
  659. /**
  660. * Sets the thread local for the current session. This method is used by the
  661. * framework to set the current session whenever a new request is processed
  662. * and it is cleared when the request has been processed.
  663. * <p>
  664. * The application developer can also use this method to define the current
  665. * session outside the normal request handling and treads started from
  666. * request handling threads, e.g. when initiating custom background threads.
  667. * </p>
  668. *
  669. * @param session
  670. *
  671. * @see #getCurrent()
  672. * @see ThreadLocal
  673. *
  674. * @since 7.0
  675. */
  676. public static void setCurrent(VaadinSession session) {
  677. CurrentInstance.setInheritable(VaadinSession.class, session);
  678. }
  679. /**
  680. * Gets all the UIs of this session. This includes UIs that have been
  681. * requested but not yet initialized. UIs that receive no heartbeat requests
  682. * from the client are eventually removed from the session.
  683. *
  684. * @return a collection of UIs belonging to this application
  685. *
  686. * @since 7.0
  687. */
  688. public Collection<UI> getUIs() {
  689. assert hasLock();
  690. return Collections.unmodifiableCollection(uIs.values());
  691. }
  692. private int connectorIdSequence = 0;
  693. private final String csrfToken = UUID.randomUUID().toString();
  694. /**
  695. * Generate an id for the given Connector. Connectors must not call this
  696. * method more than once, the first time they need an id.
  697. *
  698. * @param connector
  699. * A connector that has not yet been assigned an id.
  700. * @return A new id for the connector
  701. *
  702. * @deprecated As of 7.0. Will likely change or be removed in a future
  703. * version
  704. */
  705. @Deprecated
  706. public String createConnectorId(ClientConnector connector) {
  707. assert hasLock();
  708. return String.valueOf(connectorIdSequence++);
  709. }
  710. /**
  711. * Returns a UI with the given id.
  712. * <p>
  713. * This is meant for framework internal use.
  714. * </p>
  715. *
  716. * @param uiId
  717. * The UI id
  718. * @return The UI with the given id or null if not found
  719. */
  720. public UI getUIById(int uiId) {
  721. assert hasLock();
  722. return uIs.get(uiId);
  723. }
  724. /**
  725. * Checks if the current thread has exclusive access to this VaadinSession
  726. *
  727. * @return true if the thread has exclusive access, false otherwise
  728. */
  729. public boolean hasLock() {
  730. ReentrantLock l = ((ReentrantLock) getLockInstance());
  731. return l.isHeldByCurrentThread();
  732. }
  733. /**
  734. * Checks if the current thread has exclusive access to the given
  735. * WrappedSession.
  736. *
  737. * @return true if this thread has exclusive access, false otherwise
  738. */
  739. private static boolean hasLock(VaadinService service, WrappedSession session) {
  740. ReentrantLock l = (ReentrantLock) service.getSessionLock(session);
  741. return l.isHeldByCurrentThread();
  742. }
  743. /**
  744. * Adds a listener that will be invoked when the bootstrap HTML is about to
  745. * be generated. This can be used to modify the contents of the HTML that
  746. * loads the Vaadin application in the browser and the HTTP headers that are
  747. * included in the response serving the HTML.
  748. *
  749. * @see BootstrapListener#modifyBootstrapFragment(BootstrapFragmentResponse)
  750. * @see BootstrapListener#modifyBootstrapPage(BootstrapPageResponse)
  751. *
  752. * @param listener
  753. * the bootstrap listener to add
  754. */
  755. public void addBootstrapListener(BootstrapListener listener) {
  756. assert hasLock();
  757. eventRouter.addListener(BootstrapFragmentResponse.class, listener,
  758. BOOTSTRAP_FRAGMENT_METHOD);
  759. eventRouter.addListener(BootstrapPageResponse.class, listener,
  760. BOOTSTRAP_PAGE_METHOD);
  761. }
  762. /**
  763. * Remove a bootstrap listener that was previously added.
  764. *
  765. * @see #addBootstrapListener(BootstrapListener)
  766. *
  767. * @param listener
  768. * the bootstrap listener to remove
  769. */
  770. public void removeBootstrapListener(BootstrapListener listener) {
  771. assert hasLock();
  772. eventRouter.removeListener(BootstrapFragmentResponse.class, listener,
  773. BOOTSTRAP_FRAGMENT_METHOD);
  774. eventRouter.removeListener(BootstrapPageResponse.class, listener,
  775. BOOTSTRAP_PAGE_METHOD);
  776. }
  777. /**
  778. * Fires a bootstrap event to all registered listeners. There are currently
  779. * two supported events, both inheriting from {@link BootstrapResponse}:
  780. * {@link BootstrapFragmentResponse} and {@link BootstrapPageResponse}.
  781. *
  782. * @param response
  783. * the bootstrap response event for which listeners should be
  784. * fired
  785. *
  786. * @deprecated As of 7.0. Will likely change or be removed in a future
  787. * version
  788. */
  789. @Deprecated
  790. public void modifyBootstrapResponse(BootstrapResponse response) {
  791. assert hasLock();
  792. eventRouter.fireEvent(response);
  793. }
  794. /**
  795. * Called by the framework to remove an UI instance from the session because
  796. * it has been closed.
  797. *
  798. * @param ui
  799. * the UI to remove
  800. */
  801. public void removeUI(UI ui) {
  802. assert hasLock();
  803. assert UI.getCurrent() == ui;
  804. Integer id = Integer.valueOf(ui.getUIId());
  805. ui.setSession(null);
  806. uIs.remove(id);
  807. String embedId = ui.getEmbedId();
  808. if (embedId != null && id.equals(embedIdMap.get(embedId))) {
  809. embedIdMap.remove(embedId);
  810. }
  811. }
  812. /**
  813. * Gets this session's global resource handler that takes care of serving
  814. * connector resources that are not served by any single connector because
  815. * e.g. because they are served with strong caching or because of legacy
  816. * reasons.
  817. *
  818. * @param createOnDemand
  819. * <code>true</code> if a resource handler should be initialized
  820. * if there is no handler associated with this application.
  821. * </code>false</code> if </code>null</code> should be returned
  822. * if there is no registered handler.
  823. * @return this session's global resource handler, or <code>null</code> if
  824. * there is no handler and the createOnDemand parameter is
  825. * <code>false</code>.
  826. *
  827. * @since 7.0.0
  828. */
  829. public GlobalResourceHandler getGlobalResourceHandler(boolean createOnDemand) {
  830. assert hasLock();
  831. if (globalResourceHandler == null && createOnDemand) {
  832. globalResourceHandler = new GlobalResourceHandler();
  833. addRequestHandler(globalResourceHandler);
  834. }
  835. return globalResourceHandler;
  836. }
  837. /**
  838. * Gets the {@link Lock} instance that is used for protecting the data of
  839. * this session from concurrent access.
  840. * <p>
  841. * The <code>Lock</code> can be used to gain more control than what is
  842. * available only using {@link #lock()} and {@link #unlock()}. The returned
  843. * instance is not guaranteed to support any other features of the
  844. * <code>Lock</code> interface than {@link Lock#lock()} and
  845. * {@link Lock#unlock()}.
  846. *
  847. * @return the <code>Lock</code> that is used for synchronization, never
  848. * <code>null</code>
  849. *
  850. * @see #lock()
  851. * @see Lock
  852. */
  853. public Lock getLockInstance() {
  854. return lock;
  855. }
  856. /**
  857. * Locks this session to protect its data from concurrent access. Accessing
  858. * the UI state from outside the normal request handling should always lock
  859. * the session and unlock it when done. The preferred way to ensure locking
  860. * is done correctly is to wrap your code using {@link UI#access(Runnable)}
  861. * (or {@link VaadinSession#access(Runnable)} if you are only touching the
  862. * session and not any UI), e.g.:
  863. *
  864. * <pre>
  865. * myUI.access(new Runnable() {
  866. * &#064;Override
  867. * public void run() {
  868. * // Here it is safe to update the UI.
  869. * // UI.getCurrent can also be used
  870. * myUI.getContent().setCaption(&quot;Changed safely&quot;);
  871. * }
  872. * });
  873. * </pre>
  874. *
  875. * If you for whatever reason want to do locking manually, you should do it
  876. * like:
  877. *
  878. * <pre>
  879. * session.lock();
  880. * try {
  881. * doSomething();
  882. * } finally {
  883. * session.unlock();
  884. * }
  885. * </pre>
  886. *
  887. * This method will block until the lock can be retrieved.
  888. * <p>
  889. * {@link #getLockInstance()} can be used if more control over the locking
  890. * is required.
  891. *
  892. * @see #unlock()
  893. * @see #getLockInstance()
  894. * @see #hasLock()
  895. */
  896. public void lock() {
  897. getLockInstance().lock();
  898. }
  899. /**
  900. * Unlocks this session. This method should always be used in a finally
  901. * block after {@link #lock()} to ensure that the lock is always released.
  902. * <p>
  903. * For UIs in this session that have its push mode set to
  904. * {@link PushMode#AUTOMATIC automatic}, pending changes will be pushed to
  905. * their respective clients.
  906. *
  907. * @see #lock()
  908. * @see UI#push()
  909. */
  910. public void unlock() {
  911. assert hasLock();
  912. boolean ultimateRelease = false;
  913. try {
  914. /*
  915. * Run pending tasks and push if the reentrant lock will actually be
  916. * released by this unlock() invocation.
  917. */
  918. if (((ReentrantLock) getLockInstance()).getHoldCount() == 1) {
  919. ultimateRelease = true;
  920. getService().runPendingAccessTasks(this);
  921. for (UI ui : getUIs()) {
  922. if (ui.getPushConfiguration().getPushMode() == PushMode.AUTOMATIC) {
  923. Map<Class<?>, CurrentInstance> oldCurrent = CurrentInstance
  924. .setCurrent(ui);
  925. try {
  926. ui.push();
  927. } finally {
  928. CurrentInstance.restoreInstances(oldCurrent);
  929. }
  930. }
  931. }
  932. }
  933. } finally {
  934. getLockInstance().unlock();
  935. }
  936. /*
  937. * If the session is locked when a new access task is added, it is
  938. * assumed that the queue will be purged when the lock is released. This
  939. * might however not happen if a task is enqueued between the moment
  940. * when unlock() purges the queue and the moment when the lock is
  941. * actually released. This means that the queue should be purged again
  942. * if it is not empty after unlocking.
  943. */
  944. if (ultimateRelease && !getPendingAccessQueue().isEmpty()) {
  945. getService().ensureAccessQueuePurged(this);
  946. }
  947. }
  948. /**
  949. * Stores a value in this service session. This can be used to associate
  950. * data with the current user so that it can be retrieved at a later point
  951. * from some other part of the application. Setting the value to
  952. * <code>null</code> clears the stored value.
  953. *
  954. * @see #getAttribute(String)
  955. *
  956. * @param name
  957. * the name to associate the value with, can not be
  958. * <code>null</code>
  959. * @param value
  960. * the value to associate with the name, or <code>null</code> to
  961. * remove a previous association.
  962. */
  963. public void setAttribute(String name, Object value) {
  964. assert hasLock();
  965. if (name == null) {
  966. throw new IllegalArgumentException("name can not be null");
  967. }
  968. if (value != null) {
  969. attributes.put(name, value);
  970. } else {
  971. attributes.remove(name);
  972. }
  973. }
  974. /**
  975. * Stores a value in this service session. This can be used to associate
  976. * data with the current user so that it can be retrieved at a later point
  977. * from some other part of the application. Setting the value to
  978. * <code>null</code> clears the stored value.
  979. * <p>
  980. * The fully qualified name of the type is used as the name when storing the
  981. * value. The outcome of calling this method is thus the same as if calling<br />
  982. * <br />
  983. * <code>setAttribute(type.getName(), value);</code>
  984. *
  985. * @see #getAttribute(Class)
  986. * @see #setAttribute(String, Object)
  987. *
  988. * @param type
  989. * the type that the stored value represents, can not be null
  990. * @param value
  991. * the value to associate with the type, or <code>null</code> to
  992. * remove a previous association.
  993. */
  994. public <T> void setAttribute(Class<T> type, T value) {
  995. assert hasLock();
  996. if (type == null) {
  997. throw new IllegalArgumentException("type can not be null");
  998. }
  999. if (value != null && !type.isInstance(value)) {
  1000. throw new IllegalArgumentException("value of type "
  1001. + type.getName() + " expected but got "
  1002. + value.getClass().getName());
  1003. }
  1004. setAttribute(type.getName(), value);
  1005. }
  1006. /**
  1007. * Gets a stored attribute value. If a value has been stored for the
  1008. * session, that value is returned. If no value is stored for the name,
  1009. * <code>null</code> is returned.
  1010. *
  1011. * @see #setAttribute(String, Object)
  1012. *
  1013. * @param name
  1014. * the name of the value to get, can not be <code>null</code>.
  1015. * @return the value, or <code>null</code> if no value has been stored or if
  1016. * it has been set to null.
  1017. */
  1018. public Object getAttribute(String name) {
  1019. assert hasLock();
  1020. if (name == null) {
  1021. throw new IllegalArgumentException("name can not be null");
  1022. }
  1023. return attributes.get(name);
  1024. }
  1025. /**
  1026. * Gets a stored attribute value. If a value has been stored for the
  1027. * session, that value is returned. If no value is stored for the name,
  1028. * <code>null</code> is returned.
  1029. * <p>
  1030. * The fully qualified name of the type is used as the name when getting the
  1031. * value. The outcome of calling this method is thus the same as if calling<br />
  1032. * <br />
  1033. * <code>getAttribute(type.getName());</code>
  1034. *
  1035. * @see #setAttribute(Class, Object)
  1036. * @see #getAttribute(String)
  1037. *
  1038. * @param type
  1039. * the type of the value to get, can not be <code>null</code>.
  1040. * @return the value, or <code>null</code> if no value has been stored or if
  1041. * it has been set to null.
  1042. */
  1043. public <T> T getAttribute(Class<T> type) {
  1044. assert hasLock();
  1045. if (type == null) {
  1046. throw new IllegalArgumentException("type can not be null");
  1047. }
  1048. Object value = getAttribute(type.getName());
  1049. if (value == null) {
  1050. return null;
  1051. } else {
  1052. return type.cast(value);
  1053. }
  1054. }
  1055. /**
  1056. * Creates a new unique id for a UI.
  1057. *
  1058. * @return a unique UI id
  1059. */
  1060. public int getNextUIid() {
  1061. assert hasLock();
  1062. return nextUIId++;
  1063. }
  1064. /**
  1065. * Adds an initialized UI to this session.
  1066. *
  1067. * @param ui
  1068. * the initialized UI to add.
  1069. */
  1070. public void addUI(UI ui) {
  1071. assert hasLock();
  1072. if (ui.getUIId() == -1) {
  1073. throw new IllegalArgumentException(
  1074. "Can not add an UI that has not been initialized.");
  1075. }
  1076. if (ui.getSession() != this) {
  1077. throw new IllegalArgumentException(
  1078. "The UI belongs to a different session");
  1079. }
  1080. Integer uiId = Integer.valueOf(ui.getUIId());
  1081. uIs.put(uiId, ui);
  1082. String embedId = ui.getEmbedId();
  1083. if (embedId != null) {
  1084. Integer previousUiId = embedIdMap.put(embedId, uiId);
  1085. if (previousUiId != null) {
  1086. UI previousUi = uIs.get(previousUiId);
  1087. assert previousUi != null
  1088. && embedId.equals(previousUi.getEmbedId()) : "UI id map and embed id map not in sync";
  1089. // Will fire cleanup events at the end of the request handling.
  1090. previousUi.close();
  1091. }
  1092. }
  1093. }
  1094. /**
  1095. * Adds a UI provider to this session.
  1096. *
  1097. * @param uiProvider
  1098. * the UI provider that should be added
  1099. */
  1100. public void addUIProvider(UIProvider uiProvider) {
  1101. assert hasLock();
  1102. uiProviders.addFirst(uiProvider);
  1103. }
  1104. /**
  1105. * Removes a UI provider association from this session.
  1106. *
  1107. * @param uiProvider
  1108. * the UI provider that should be removed
  1109. */
  1110. public void removeUIProvider(UIProvider uiProvider) {
  1111. assert hasLock();
  1112. uiProviders.remove(uiProvider);
  1113. }
  1114. /**
  1115. * Gets the UI providers configured for this session.
  1116. *
  1117. * @return an unmodifiable list of UI providers
  1118. */
  1119. public List<UIProvider> getUIProviders() {
  1120. assert hasLock();
  1121. return Collections.unmodifiableList(uiProviders);
  1122. }
  1123. public VaadinService getService() {
  1124. return service;
  1125. }
  1126. /**
  1127. * Sets this session to be closed and all UI state to be discarded at the
  1128. * end of the current request, or at the end of the next request if there is
  1129. * no ongoing one.
  1130. * <p>
  1131. * After the session has been discarded, any UIs that have been left open
  1132. * will give a Session Expired error and a new session will be created for
  1133. * serving new UIs.
  1134. * <p>
  1135. * To avoid causing out of sync errors, you should typically redirect to
  1136. * some other page using {@link Page#setLocation(String)} to make the
  1137. * browser unload the invalidated UI.
  1138. *
  1139. * @see SystemMessages#getSessionExpiredCaption()
  1140. *
  1141. */
  1142. public void close() {
  1143. assert hasLock();
  1144. state = State.CLOSING;
  1145. }
  1146. /**
  1147. * Returns whether this session is marked to be closed. Note that this
  1148. * method also returns true if the session is actually already closed.
  1149. *
  1150. * @see #close()
  1151. *
  1152. * @deprecated As of 7.2, use
  1153. * <code>{@link #getState() getState() != State.OPEN}</code>
  1154. * instead.
  1155. *
  1156. * @return true if this session is marked to be closed, false otherwise
  1157. */
  1158. @Deprecated
  1159. public boolean isClosing() {
  1160. assert hasLock();
  1161. return state == State.CLOSING || state == State.CLOSED;
  1162. }
  1163. /**
  1164. * Returns the lifecycle state of this session.
  1165. *
  1166. * @since 7.2
  1167. * @return the current state
  1168. */
  1169. public State getState() {
  1170. assert hasLock();
  1171. return state;
  1172. }
  1173. /**
  1174. * Sets the lifecycle state of this session. The allowed transitions are
  1175. * OPEN to CLOSING and CLOSING to CLOSED.
  1176. *
  1177. * @since 7.2
  1178. * @param state
  1179. * the new state
  1180. */
  1181. protected void setState(State state) {
  1182. assert hasLock();
  1183. assert this.state.isValidChange(state) : "Invalid session state change "
  1184. + this.state + "->" + state;
  1185. this.state = state;
  1186. }
  1187. private static final Logger getLogger() {
  1188. return Logger.getLogger(VaadinSession.class.getName());
  1189. }
  1190. /**
  1191. * Locks this session and runs the provided Runnable right away.
  1192. * <p>
  1193. * It is generally recommended to use {@link #access(Runnable)} instead of
  1194. * this method for accessing a session from a different thread as
  1195. * {@link #access(Runnable)} can be used while holding the lock of another
  1196. * session. To avoid causing deadlocks, this methods throws an exception if
  1197. * it is detected than another session is also locked by the current thread.
  1198. * </p>
  1199. * <p>
  1200. * This method behaves differently than {@link #access(Runnable)} in some
  1201. * situations:
  1202. * <ul>
  1203. * <li>If the current thread is currently holding the lock of this session,
  1204. * {@link #accessSynchronously(Runnable)} runs the task right away whereas
  1205. * {@link #access(Runnable)} defers the task to a later point in time.</li>
  1206. * <li>If some other thread is currently holding the lock for this session,
  1207. * {@link #accessSynchronously(Runnable)} blocks while waiting for the lock
  1208. * to be available whereas {@link #access(Runnable)} defers the task to a
  1209. * later point in time.</li>
  1210. * </ul>
  1211. * </p>
  1212. *
  1213. * @param runnable
  1214. * the runnable which accesses the session
  1215. *
  1216. * @throws IllegalStateException
  1217. * if the current thread holds the lock for another session
  1218. *
  1219. * @since 7.1
  1220. *
  1221. * @see #lock()
  1222. * @see #getCurrent()
  1223. * @see #access(Runnable)
  1224. * @see UI#accessSynchronously(Runnable)
  1225. */
  1226. public void accessSynchronously(Runnable runnable) {
  1227. VaadinService.verifyNoOtherSessionLocked(this);
  1228. Map<Class<?>, CurrentInstance> old = null;
  1229. lock();
  1230. try {
  1231. old = CurrentInstance.setCurrent(this);
  1232. runnable.run();
  1233. } finally {
  1234. unlock();
  1235. if (old != null) {
  1236. CurrentInstance.restoreInstances(old);
  1237. }
  1238. }
  1239. }
  1240. /**
  1241. * Provides exclusive access to this session from outside a request handling
  1242. * thread.
  1243. * <p>
  1244. * The given runnable is executed while holding the session lock to ensure
  1245. * exclusive access to this session. If this session is not locked, the lock
  1246. * will be acquired and the runnable is run right away. If this session is
  1247. * currently locked, the runnable will be run before that lock is released.
  1248. * </p>
  1249. * <p>
  1250. * RPC handlers for components inside this session do not need to use this
  1251. * method as the session is automatically locked by the framework during RPC
  1252. * handling.
  1253. * </p>
  1254. * <p>
  1255. * Please note that the runnable might be invoked on a different thread or
  1256. * later on the current thread, which means that custom thread locals might
  1257. * not have the expected values when the runnable is executed. Inheritable
  1258. * values in {@link CurrentInstance} will have the same values as when this
  1259. * method was invoked. {@link VaadinSession#getCurrent()} and
  1260. * {@link VaadinService#getCurrent()} are set according to this session
  1261. * before executing the runnable. Non-inheritable CurrentInstance values
  1262. * including {@link VaadinService#getCurrentRequest()} and
  1263. * {@link VaadinService#getCurrentResponse()} will not be defined.
  1264. * </p>
  1265. * <p>
  1266. * The returned future can be used to check for task completion and to
  1267. * cancel the task. To help avoiding deadlocks, {@link Future#get()} throws
  1268. * an exception if it is detected that the current thread holds the lock for
  1269. * some other session.
  1270. * </p>
  1271. *
  1272. * @see #lock()
  1273. * @see #getCurrent()
  1274. * @see #accessSynchronously(Runnable)
  1275. * @see UI#access(Runnable)
  1276. *
  1277. * @since 7.1
  1278. *
  1279. * @param runnable
  1280. * the runnable which accesses the session
  1281. * @return a future that can be used to check for task completion and to
  1282. * cancel the task
  1283. */
  1284. public Future<Void> access(Runnable runnable) {
  1285. return getService().accessSession(this, runnable);
  1286. }
  1287. /**
  1288. * Gets the queue of tasks submitted using {@link #access(Runnable)}. It is
  1289. * safe to call this method and access the returned queue without holding
  1290. * the {@link #lock() session lock}.
  1291. *
  1292. * @since 7.1
  1293. *
  1294. * @return the queue of pending access tasks
  1295. */
  1296. public Queue<FutureAccess> getPendingAccessQueue() {
  1297. return pendingAccessQueue;
  1298. }
  1299. /**
  1300. * Gets the CSRF token (aka double submit cookie) that is used to protect
  1301. * against Cross Site Request Forgery attacks.
  1302. *
  1303. * @since 7.1
  1304. * @return the csrf token string
  1305. */
  1306. public String getCsrfToken() {
  1307. assert hasLock();
  1308. return csrfToken;
  1309. }
  1310. /**
  1311. * Override default deserialization logic to account for transient
  1312. * {@link #pendingAccessQueue}.
  1313. */
  1314. private void readObject(ObjectInputStream stream) throws IOException,
  1315. ClassNotFoundException {
  1316. stream.defaultReadObject();
  1317. pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
  1318. }
  1319. /**
  1320. * Finds the UI with the corresponding embed id.
  1321. *
  1322. * @since 7.2
  1323. * @param embedId
  1324. * the embed id
  1325. * @return the UI with the corresponding embed id, or <code>null</code> if
  1326. * no UI is found
  1327. *
  1328. * @see UI#getEmbedId()
  1329. */
  1330. public UI getUIByEmbedId(String embedId) {
  1331. Integer uiId = embedIdMap.get(embedId);
  1332. if (uiId == null) {
  1333. return null;
  1334. } else {
  1335. return getUIById(uiId.intValue());
  1336. }
  1337. }
  1338. }