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

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