Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

VaadinSessionTest.java 12KB

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