*/
package com.vaadin.server;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.ServletConfig;
import com.vaadin.server.ClientConnector.DetachEvent;
import com.vaadin.server.ClientConnector.DetachListener;
+import com.vaadin.server.communication.UIInitHandler;
import com.vaadin.ui.UI;
import com.vaadin.util.CurrentInstance;
private WrappedSession mockWrappedSession;
private VaadinServletRequest vaadinRequest;
private UI ui;
+ private Lock httpSessionLock;
@Before
public void setup() throws Exception {
+ httpSessionLock = new ReentrantLock();
mockServletConfig = new MockServletConfig();
mockServlet = new VaadinServlet();
mockServlet.init(mockServletConfig);
@Override
public Object getAttribute(String name) {
- String lockAttribute = mockService.getServiceName() + ".lock";
- if (lockAttribute.equals(name)) {
- return lock;
+ Object res;
+ try {
+ Thread.sleep(100); // for deadlock testing
+ org.junit.Assert.assertTrue("Deadlock detected",
+ httpSessionLock.tryLock(5, TimeUnit.SECONDS)); // simulates
+ // servlet
+ // container's
+ // session
+ // locking
+ String lockAttribute = mockService.getServiceName()
+ + ".lock";
+ if (lockAttribute.equals(name)) {
+ res = lock;
+ } else if ("com.vaadin.server.VaadinSession.Mock Servlet"
+ .equals(name)) {
+ res = session;
+ } else {
+ res = super.getAttribute(name);
+ }
+ httpSessionLock.unlock();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
}
- return super.getAttribute(name);
+ return res;
}
};
EasyMock.createMock(HttpServletRequest.class), mockService) {
@Override
public String getParameter(String name) {
- if ("theme".equals(name)) {
+ if ("theme".equals(name) || "restartApplication".equals(name)
+ || "ignoreRestart".equals(name)
+ || "closeApplication".equals(name)) {
return null;
+ } else if (UIInitHandler.BROWSER_DETAILS_PARAMETER.equals(name)) {
+ return "1";
}
-
return super.getParameter(name);
}
+
+ @Override
+ public String getMethod() {
+ return "POST";
+ }
+
+ @Override
+ public WrappedSession getWrappedSession(boolean allowSessionCreation) {
+ return mockWrappedSession;
+ }
+
};
ui.doInit(vaadinRequest, session.getNextUIid(), null);
}
+ /**
+ * This reproduces #14452 situation with deadlock - see diagram
+ */
+ @Test
+ public void testInvalidationDeadlock() {
+
+ // this simulates servlet container's session invalidation from another
+ // thread
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(150); // delay selected so that VaadinSession
+ // will be already locked by the main
+ // thread
+ // when we get here
+ httpSessionLock.lock();// simulating servlet container's
+ // session lock
+ mockService.fireSessionDestroy(session);
+ httpSessionLock.unlock();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }).start();
+
+ try {
+ mockService.findVaadinSession(vaadinRequest);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
@Test
- public void threadLocalsAfterUnderlyingSessionTimeout() {
+ public void threadLocalsAfterUnderlyingSessionTimeout()
+ throws InterruptedException {
final AtomicBoolean detachCalled = new AtomicBoolean(false);
ui.addDetachListener(new DetachListener() {
});
session.valueUnbound(EasyMock.createMock(HttpSessionBindingEvent.class));
+ mockService.runPendingAccessTasks(session); // as soon as we changed
+ // session.accessSynchronously
+ // to session.access in
+ // VaadinService.fireSessionDestroy,
+ // we need to run the
+ // pending task ourselves
Assert.assertTrue(detachCalled.get());
}
@Test
- public void threadLocalsAfterSessionDestroy() {
+ public void threadLocalsAfterSessionDestroy() throws InterruptedException {
final AtomicBoolean detachCalled = new AtomicBoolean(false);
ui.addDetachListener(new DetachListener() {
@Override
CurrentInstance.clearAll();
session.close();
mockService.cleanupSession(session);
+ mockService.runPendingAccessTasks(session); // as soon as we changed
+ // session.accessSynchronously
+ // to session.access in
+ // VaadinService.fireSessionDestroy,
+ // we need to run the
+ // pending task ourselves
Assert.assertTrue(detachCalled.get());
}