summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorLeif Åstrand <leif@vaadin.com>2014-03-18 14:24:04 +0200
committerArtur Signell <artur@vaadin.com>2014-04-02 15:27:12 +0000
commit8138be4b0352c5b2409b48ad600941afeed3b20b (patch)
treec7a1b4929c8d64c5c774a3f5fd03cf0eab8d7467 /server
parente1a987f88d624e6099e2ceae4c463506f1a4b52c (diff)
downloadvaadin-framework-8138be4b0352c5b2409b48ad600941afeed3b20b.tar.gz
vaadin-framework-8138be4b0352c5b2409b48ad600941afeed3b20b.zip
Make removeFromParent throw if the right session is not locked (#13473)
Change-Id: Id5ef40db07404d7cb41b26768d18e757b8cae2b3
Diffstat (limited to 'server')
-rw-r--r--server/src/com/vaadin/server/VaadinService.java24
-rw-r--r--server/src/com/vaadin/ui/AbstractSingleComponentContainer.java15
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/abstractsinglecomponentcontainer/TestRemoveFromParentLocking.java125
3 files changed, 161 insertions, 3 deletions
diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java
index 6fd0b23f7b..ba1224568a 100644
--- a/server/src/com/vaadin/server/VaadinService.java
+++ b/server/src/com/vaadin/server/VaadinService.java
@@ -1635,15 +1635,33 @@ public abstract class VaadinService implements Serializable {
* if the current thread holds the lock for another session
*/
public static void verifyNoOtherSessionLocked(VaadinSession session) {
- VaadinSession otherSession = VaadinSession.getCurrent();
- if (otherSession != null && otherSession != session
- && otherSession.hasLock()) {
+ if (isOtherSessionLocked(session)) {
throw new IllegalStateException(
"Can't access session while another session is locked by the same thread. This restriction is intended to help avoid deadlocks.");
}
}
/**
+ * Checks whether there might be some {@link VaadinSession} other than the
+ * provided one for which the current thread holds a lock. This method might
+ * not detect all cases where some other session is locked, but it should
+ * cover the most typical situations.
+ *
+ * @since 7.2
+ * @param session
+ * the session that is expected to be locked
+ * @return <code>true</code> if another session is also locked by the
+ * current thread; <code>false</code> if no such session was found
+ */
+ public static boolean isOtherSessionLocked(VaadinSession session) {
+ VaadinSession otherSession = VaadinSession.getCurrent();
+ if (otherSession == null || otherSession == session) {
+ return false;
+ }
+ return otherSession.hasLock();
+ }
+
+ /**
* Verifies that the given CSRF token (aka double submit cookie) is valid
* for the given session. This is used to protect against Cross Site Request
* Forgery attacks.
diff --git a/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java b/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java
index 8ad0d23351..534f01d4eb 100644
--- a/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java
+++ b/server/src/com/vaadin/ui/AbstractSingleComponentContainer.java
@@ -19,6 +19,8 @@ import java.util.Collections;
import java.util.Iterator;
import com.vaadin.server.ComponentSizeValidator;
+import com.vaadin.server.VaadinService;
+import com.vaadin.server.VaadinSession;
/**
* Abstract base class for component containers that have only one child
@@ -150,6 +152,19 @@ public abstract class AbstractSingleComponentContainer extends
// TODO move utility method elsewhere?
public static void removeFromParent(Component content)
throws IllegalArgumentException {
+ // Verify the appropriate session is locked
+ UI parentUI = content.getUI();
+ if (parentUI != null) {
+ VaadinSession parentSession = parentUI.getSession();
+ if (parentSession != null && !parentSession.hasLock()) {
+ String message = "Cannot remove from parent when the session is not locked.";
+ if (VaadinService.isOtherSessionLocked(parentSession)) {
+ message += " Furthermore, there is another locked session, indicating that the component might be about to be moved from one session to another.";
+ }
+ throw new IllegalStateException(message);
+ }
+ }
+
HasComponents parent = content.getParent();
if (parent instanceof ComponentContainer) {
// If the component already has a parent, try to remove it
diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractsinglecomponentcontainer/TestRemoveFromParentLocking.java b/server/tests/src/com/vaadin/tests/server/component/abstractsinglecomponentcontainer/TestRemoveFromParentLocking.java
new file mode 100644
index 0000000000..26443ead2b
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/server/component/abstractsinglecomponentcontainer/TestRemoveFromParentLocking.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.server.component.abstractsinglecomponentcontainer;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.VerticalLayout;
+
+public class TestRemoveFromParentLocking {
+
+ private static VerticalLayout createTestComponent() {
+ VaadinSession session = new VaadinSession(null) {
+ private final ReentrantLock lock = new ReentrantLock();
+
+ @Override
+ public Lock getLockInstance() {
+ return lock;
+ }
+ };
+
+ session.getLockInstance().lock();
+
+ UI ui = new UI() {
+ @Override
+ protected void init(VaadinRequest request) {
+ }
+ };
+ ui.setSession(session);
+
+ VerticalLayout layout = new VerticalLayout();
+ ui.setContent(layout);
+
+ session.getLockInstance().unlock();
+ return layout;
+ }
+
+ @Test
+ public void attachNoSessionLocked() {
+ VerticalLayout testComponent = createTestComponent();
+
+ VerticalLayout target = new VerticalLayout();
+
+ try {
+ target.addComponent(testComponent);
+ throw new AssertionError(
+ "Moving component when not holding its sessions's lock should throw");
+ } catch (IllegalStateException e) {
+ Assert.assertEquals(
+ "Cannot remove from parent when the session is not locked.",
+ e.getMessage());
+ }
+ }
+
+ @Test
+ public void attachSessionLocked() {
+ VerticalLayout testComponent = createTestComponent();
+
+ VerticalLayout target = new VerticalLayout();
+
+ testComponent.getUI().getSession().getLockInstance().lock();
+
+ target.addComponent(testComponent);
+ // OK if we get here without any exception
+ }
+
+ @Test
+ public void crossAttachOtherSessionLocked() {
+ VerticalLayout notLockedComponent = createTestComponent();
+
+ VerticalLayout lockedComponent = createTestComponent();
+
+ // Simulate the situation when attaching cross sessions
+ lockedComponent.getUI().getSession().getLockInstance().lock();
+ VaadinSession.setCurrent(lockedComponent.getUI().getSession());
+
+ try {
+ lockedComponent.addComponent(notLockedComponent);
+ throw new AssertionError(
+ "Moving component when not holding its sessions's lock should throw");
+ } catch (IllegalStateException e) {
+ Assert.assertEquals(
+ "Cannot remove from parent when the session is not locked."
+ + " Furthermore, there is another locked session, indicating that the component might be about to be moved from one session to another.",
+ e.getMessage());
+ }
+ }
+
+ @Test
+ public void crossAttachThisSessionLocked() {
+ VerticalLayout notLockedComponent = createTestComponent();
+
+ VerticalLayout lockedComponent = createTestComponent();
+
+ // Simulate the situation when attaching cross sessions
+ lockedComponent.getUI().getSession().getLockInstance().lock();
+ VaadinSession.setCurrent(lockedComponent.getUI().getSession());
+
+ try {
+ notLockedComponent.addComponent(lockedComponent);
+ } catch (AssertionError e) {
+ // All is fine, don't care about the exact wording in this case
+ }
+ }
+
+}