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.

VaadinSessionTest.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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.ByteArrayInputStream;
  18. import java.io.ByteArrayOutputStream;
  19. import java.io.IOException;
  20. import java.io.ObjectInputStream;
  21. import java.io.ObjectOutputStream;
  22. import java.io.Serializable;
  23. import java.util.concurrent.TimeUnit;
  24. import java.util.concurrent.atomic.AtomicBoolean;
  25. import java.util.concurrent.locks.Lock;
  26. import java.util.concurrent.locks.ReentrantLock;
  27. import javax.servlet.ServletConfig;
  28. import javax.servlet.http.HttpServletRequest;
  29. import javax.servlet.http.HttpSession;
  30. import javax.servlet.http.HttpSessionBindingEvent;
  31. import org.easymock.EasyMock;
  32. import org.junit.Assert;
  33. import org.junit.Before;
  34. import org.junit.Test;
  35. import com.vaadin.server.ClientConnector.DetachEvent;
  36. import com.vaadin.server.communication.UIInitHandler;
  37. import com.vaadin.ui.Label;
  38. import com.vaadin.ui.UI;
  39. import com.vaadin.util.CurrentInstance;
  40. public class VaadinSessionTest implements Serializable {
  41. private transient VaadinSession session;
  42. private transient VaadinServlet mockServlet;
  43. private transient VaadinServletService mockService;
  44. private transient ServletConfig mockServletConfig;
  45. private transient HttpSession mockHttpSession;
  46. private transient WrappedSession mockWrappedSession;
  47. private transient VaadinServletRequest vaadinRequest;
  48. private transient UI ui;
  49. private transient Lock httpSessionLock;
  50. @Before
  51. public void setup() throws Exception {
  52. httpSessionLock = new ReentrantLock();
  53. mockServletConfig = new MockServletConfig();
  54. mockServlet = new VaadinServlet();
  55. mockServlet.init(mockServletConfig);
  56. mockService = mockServlet.getService();
  57. mockHttpSession = EasyMock.createMock(HttpSession.class);
  58. mockWrappedSession = new WrappedHttpSession(mockHttpSession) {
  59. final ReentrantLock lock = new ReentrantLock();
  60. {
  61. lock.lock();
  62. }
  63. @Override
  64. public Object getAttribute(String name) {
  65. Object res;
  66. try {
  67. Thread.sleep(100); // for deadlock testing
  68. org.junit.Assert.assertTrue("Deadlock detected",
  69. httpSessionLock.tryLock(5, TimeUnit.SECONDS)); // simulates
  70. // servlet
  71. // container's
  72. // session
  73. // locking
  74. String lockAttribute = mockService.getServiceName()
  75. + ".lock";
  76. if (lockAttribute.equals(name)) {
  77. res = lock;
  78. } else if ("com.vaadin.server.VaadinSession.Mock Servlet"
  79. .equals(name)) {
  80. res = session;
  81. } else {
  82. res = super.getAttribute(name);
  83. }
  84. httpSessionLock.unlock();
  85. } catch (InterruptedException e) {
  86. throw new RuntimeException(e);
  87. }
  88. return res;
  89. }
  90. };
  91. session = new VaadinSession(mockService);
  92. mockService.storeSession(session, mockWrappedSession);
  93. ui = new MockPageUI();
  94. vaadinRequest = new VaadinServletRequest(
  95. EasyMock.createMock(HttpServletRequest.class), mockService) {
  96. @Override
  97. public String getParameter(String name) {
  98. if ("theme".equals(name) || "restartApplication".equals(name)
  99. || "ignoreRestart".equals(name)
  100. || "closeApplication".equals(name)) {
  101. return null;
  102. } else if (UIInitHandler.BROWSER_DETAILS_PARAMETER
  103. .equals(name)) {
  104. return "1";
  105. }
  106. return super.getParameter(name);
  107. }
  108. @Override
  109. public String getMethod() {
  110. return "POST";
  111. }
  112. @Override
  113. public WrappedSession getWrappedSession(
  114. boolean allowSessionCreation) {
  115. return mockWrappedSession;
  116. }
  117. };
  118. ui.doInit(vaadinRequest, session.getNextUIid(), null);
  119. ui.setSession(session);
  120. session.addUI(ui);
  121. }
  122. /**
  123. * This reproduces #14452 situation with deadlock - see diagram
  124. */
  125. @Test
  126. public void testInvalidationDeadlock() {
  127. // this simulates servlet container's session invalidation from another
  128. // thread
  129. new Thread(() -> {
  130. try {
  131. Thread.sleep(150); // delay selected so that VaadinSession
  132. // will be already locked by the main
  133. // thread
  134. // when we get here
  135. httpSessionLock.lock();// simulating servlet container's
  136. // session lock
  137. try {
  138. mockService.fireSessionDestroy(session);
  139. } finally {
  140. httpSessionLock.unlock();
  141. }
  142. } catch (InterruptedException e) {
  143. throw new RuntimeException(e);
  144. }
  145. }).start();
  146. try {
  147. mockService.findVaadinSession(vaadinRequest);
  148. } catch (Exception e) {
  149. throw new RuntimeException(e);
  150. }
  151. }
  152. @Test
  153. public void threadLocalsAfterUnderlyingSessionTimeout()
  154. throws InterruptedException {
  155. final AtomicBoolean detachCalled = new AtomicBoolean(false);
  156. ui.addDetachListener((DetachEvent event) -> {
  157. detachCalled.set(true);
  158. Assert.assertEquals(ui, UI.getCurrent());
  159. Assert.assertEquals(ui.getPage(), Page.getCurrent());
  160. Assert.assertEquals(session, VaadinSession.getCurrent());
  161. Assert.assertEquals(mockService, VaadinService.getCurrent());
  162. Assert.assertEquals(mockServlet, VaadinServlet.getCurrent());
  163. });
  164. session.valueUnbound(
  165. EasyMock.createMock(HttpSessionBindingEvent.class));
  166. mockService.runPendingAccessTasks(session); // as soon as we changed
  167. // session.accessSynchronously
  168. // to session.access in
  169. // VaadinService.fireSessionDestroy,
  170. // we need to run the
  171. // pending task ourselves
  172. Assert.assertTrue(detachCalled.get());
  173. }
  174. @Test
  175. public void threadLocalsAfterSessionDestroy() throws InterruptedException {
  176. final AtomicBoolean detachCalled = new AtomicBoolean(false);
  177. ui.addDetachListener((DetachEvent event) -> {
  178. detachCalled.set(true);
  179. Assert.assertEquals(ui, UI.getCurrent());
  180. Assert.assertEquals(ui.getPage(), Page.getCurrent());
  181. Assert.assertEquals(session, VaadinSession.getCurrent());
  182. Assert.assertEquals(mockService, VaadinService.getCurrent());
  183. Assert.assertEquals(mockServlet, VaadinServlet.getCurrent());
  184. });
  185. CurrentInstance.clearAll();
  186. session.close();
  187. mockService.cleanupSession(session);
  188. mockService.runPendingAccessTasks(session); // as soon as we changed
  189. // session.accessSynchronously
  190. // to session.access in
  191. // VaadinService.fireSessionDestroy,
  192. // we need to run the
  193. // pending task ourselves
  194. Assert.assertTrue(detachCalled.get());
  195. }
  196. @Test
  197. public void testValueUnbound() {
  198. MockVaadinSession vaadinSession = new MockVaadinSession(mockService);
  199. vaadinSession.valueUnbound(
  200. EasyMock.createMock(HttpSessionBindingEvent.class));
  201. org.junit.Assert.assertEquals(
  202. "'valueUnbound' method doesn't call 'close' for the session", 1,
  203. vaadinSession.getCloseCount());
  204. vaadinSession.valueUnbound(
  205. EasyMock.createMock(HttpSessionBindingEvent.class));
  206. org.junit.Assert.assertEquals(
  207. "'valueUnbound' method may not call 'close' "
  208. + "method for closing session",
  209. 1, vaadinSession.getCloseCount());
  210. }
  211. // Can't define as an anonymous class since it would have a reference to
  212. // VaadinSessionTest.this which isn't serializable
  213. private static class MockPageUI extends UI {
  214. Page page = new Page(this, getState(false).pageState) {
  215. @Override
  216. public void init(VaadinRequest request) {
  217. }
  218. };
  219. @Override
  220. protected void init(VaadinRequest request) {
  221. }
  222. @Override
  223. public Page getPage() {
  224. return page;
  225. }
  226. }
  227. private static class SerializationTestLabel extends Label {
  228. private transient VaadinSession session = VaadinSession.getCurrent();
  229. private void readObject(ObjectInputStream in)
  230. throws IOException, ClassNotFoundException {
  231. in.defaultReadObject();
  232. session = VaadinSession.getCurrent();
  233. }
  234. }
  235. @Test
  236. public void threadLocalsWhenDeserializing() throws Exception {
  237. VaadinSession.setCurrent(session);
  238. session.lock();
  239. SerializationTestLabel label = new SerializationTestLabel();
  240. Assert.assertEquals("Session should be set when instance is created",
  241. session, label.session);
  242. ui.setContent(label);
  243. int uiId = ui.getUIId();
  244. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  245. try (ObjectOutputStream out = new ObjectOutputStream(bos)) {
  246. out.writeObject(session);
  247. }
  248. session.unlock();
  249. CurrentInstance.clearAll();
  250. ObjectInputStream in = new ObjectInputStream(
  251. new ByteArrayInputStream(bos.toByteArray()));
  252. VaadinSession deserializedSession = (VaadinSession) in.readObject();
  253. Assert.assertNull("Current session shouldn't leak from deserialisation",
  254. VaadinSession.getCurrent());
  255. Assert.assertNotSame("Should get a new session", session,
  256. deserializedSession);
  257. // Restore http session and service instance so the session can be
  258. // locked
  259. deserializedSession.refreshTransients(mockWrappedSession, mockService);
  260. deserializedSession.lock();
  261. UI deserializedUi = deserializedSession.getUIById(uiId);
  262. SerializationTestLabel deserializedLabel = (SerializationTestLabel) deserializedUi
  263. .getContent();
  264. Assert.assertEquals(
  265. "Current session should be available in SerializationTestLabel.readObject",
  266. deserializedSession, deserializedLabel.session);
  267. deserializedSession.unlock();
  268. }
  269. @Test
  270. public void lockedDuringSerialization() throws IOException {
  271. final AtomicBoolean lockChecked = new AtomicBoolean(false);
  272. ui.setContent(new Label() {
  273. private void writeObject(ObjectOutputStream out)
  274. throws IOException {
  275. Assert.assertTrue(session.hasLock());
  276. lockChecked.set(true);
  277. out.defaultWriteObject();
  278. }
  279. });
  280. session.unlock();
  281. Assert.assertFalse(session.hasLock());
  282. ObjectOutputStream out = new ObjectOutputStream(
  283. new ByteArrayOutputStream());
  284. out.writeObject(session);
  285. Assert.assertFalse(session.hasLock());
  286. Assert.assertTrue(lockChecked.get());
  287. }
  288. }