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

VaadinSession.java 48KB

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