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

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