/**
* Gets the currently used Vaadin portlet. The current portlet is
- * automatically defined when initializing the portlet and when processing
- * requests to the server and in threads started at a point when the current
- * portlet is defined (see {@link InheritableThreadLocal}). In other cases,
- * (e.g. from background threads started in some other way), the current
- * portlet is not automatically defined.
- * <p>
+ * automatically defined when processing requests related to the service
+ * (see {@link ThreadLocal}) and in {@link VaadinSession#access(Command)}
+ * and {@link UI#access(Command)}. In other cases, (e.g. from background
+ * threads, the current service is not automatically defined.
+ *
* The current portlet is derived from the current service using
* {@link VaadinService#getCurrent()}
*
/**
* Gets the currently used Vaadin service. The current service is
- * automatically defined when processing requests related to the service and
- * in threads started at a point when the current service is defined (see
- * {@link InheritableThreadLocal}). In other cases, (e.g. from background
- * threads started in some other way), the current service is not
- * automatically defined.
+ * automatically defined when processing requests related to the service
+ * (see {@link ThreadLocal}) and in {@link VaadinSession#access(Command)}
+ * and {@link UI#access(Command)}. In other cases, (e.g. from background
+ * threads, the current service is not automatically defined.
*
* @return the current Vaadin service instance if available, otherwise
* <code>null</code>
* @param service
*/
public static void setCurrent(VaadinService service) {
- CurrentInstance.setInheritable(VaadinService.class, service);
+ CurrentInstance.set(VaadinService.class, service);
}
/**
return;
}
- Map<Class<?>, CurrentInstance> oldInstances = CurrentInstance
- .getInstances(false);
-
FutureAccess pendingAccess;
+ Map<Class<?>, CurrentInstance> oldInstances = CurrentInstance
+ .setCurrent(session);
try {
while ((pendingAccess = session.getPendingAccessQueue()
.poll()) != null) {
if (!pendingAccess.isCancelled()) {
- CurrentInstance.clearAll();
- CurrentInstance.restoreInstances(
- pendingAccess.getCurrentInstances());
- CurrentInstance.setCurrent(session);
pendingAccess.run();
try {
/**
* Gets the currently used Vaadin servlet. The current servlet is
* automatically defined when initializing the servlet and when processing
- * requests to the server and in threads started at a point when the current
- * servlet is defined (see {@link InheritableThreadLocal}). In other cases,
- * (e.g. from background threads started in some other way), the current
- * servlet is not automatically defined.
+ * requests to the server (see {@link ThreadLocal}) and in
+ * {@link VaadinSession#access(Runnable)} and {@link UI#access(Runnable)}. In
+ * other cases, (e.g. from background threads), the current servlet is not
+ * automatically defined.
* <p>
* The current servlet is derived from the current service using
* {@link VaadinService#getCurrent()}
* @author Vaadin Ltd
*/
public static class FutureAccess extends FutureTask<Void> {
- /**
- * Snapshot of all non-inheritable current instances at the time this
- * object was created.
- */
- private final Map<Class<?>, CurrentInstance> instances = CurrentInstance
- .getInstances(true);
private final VaadinSession session;
private final Runnable runnable;
return super.get();
}
- /**
- * Gets the current instance values that should be used when running
- * this task.
- *
- * @see CurrentInstance#restoreInstances(Map)
- *
- * @return a map of current instances.
- */
- public Map<Class<?>, CurrentInstance> getCurrentInstances() {
- return instances;
- }
-
/**
* Handles exceptions thrown during the execution of this task.
*
}
/**
- * Gets the currently used session. The current session is automatically
- * defined when processing requests to the server and in threads started at
- * a point when the current session is defined (see
- * {@link InheritableThreadLocal}). In other cases, (e.g. from background
- * threads started in some other way), the current session is not
- * automatically defined.
+ * Gets the currently used session. The current session is
+ * automatically defined when processing requests related to the session
+ * (see {@link ThreadLocal}) and in {@link VaadinSession#access(Command)}
+ * and {@link UI#access(Command)}. In other cases, (e.g. from background
+ * threads, the current session is not automatically defined.
* <p>
* The session is stored using a weak reference to avoid leaking memory in
* case it is not explicitly cleared.
* @since 7.0
*/
public static void setCurrent(VaadinSession session) {
- CurrentInstance.setInheritable(VaadinSession.class, session);
+ CurrentInstance.set(VaadinSession.class, session);
}
/**
* <p>
* Please note that the runnable might be invoked on a different thread or
* later on the current thread, which means that custom thread locals might
- * not have the expected values when the runnable is executed. Inheritable
- * values in {@link CurrentInstance} will have the same values as when this
- * method was invoked. {@link VaadinSession#getCurrent()} and
- * {@link VaadinService#getCurrent()} are set according to this session
- * before executing the runnable. Non-inheritable CurrentInstance values
- * including {@link VaadinService#getCurrentRequest()} and
+ * not have the expected values when the command is executed.
+ * {@link VaadinSession#getCurrent()} and {@link VaadinService#getCurrent()}
+ * are set according to this session before executing the command. Other
+ * standard CurrentInstance values such as
+ * {@link VaadinService#getCurrentRequest()} and
* {@link VaadinService#getCurrentResponse()} will not be defined.
* </p>
* <p>
* @see ThreadLocal
*/
public static void setCurrent(UI ui) {
- CurrentInstance.setInheritable(UI.class, ui);
+ CurrentInstance.set(UI.class, ui);
}
/**
* <p>
* Please note that the runnable might be invoked on a different thread or
* later on the current thread, which means that custom thread locals might
- * not have the expected values when the runnable is executed. Inheritable
- * values in {@link CurrentInstance} will have the same values as when this
- * method was invoked. {@link UI#getCurrent()},
- * {@link VaadinSession#getCurrent()} and {@link VaadinService#getCurrent()}
- * are set according to this UI before executing the runnable.
- * Non-inheritable CurrentInstance values including
+ * not have the expected values when the command is executed.
+ * {@link UI#getCurrent()}, {@link VaadinSession#getCurrent()} and
+ * {@link VaadinService#getCurrent()} are set according to this UI before
+ * executing the command. Other standard CurrentInstance values such as
* {@link VaadinService#getCurrentRequest()} and
* {@link VaadinService#getCurrentResponse()} will not be defined.
* </p>
/**
* Keeps track of various current instances for the current thread. All the
* instances are automatically cleared after handling a request from the client
- * to avoid leaking memory. The inheritable values are also maintained when
- * execution is moved to another thread, both when a new thread is created and
- * when {@link VaadinSession#access(Runnable)} or {@link UI#access(Runnable)} is
- * used.
+ * to avoid leaking memory.
* <p>
* Please note that the instances are stored using {@link WeakReference}. This
* means that the a current instance value may suddenly disappear if there a no
* Currently the framework uses the following instances:
* </p>
* <p>
- * Inheritable: {@link UI}, {@link VaadinService}, {@link VaadinSession}.
- * </p>
- * <p>
- * Non-inheritable: {@link VaadinRequest}, {@link VaadinResponse}.
+ * {@link UI}, {@link VaadinService}, {@link VaadinSession},
+ * {@link VaadinRequest}, {@link VaadinResponse}.
* </p>
*
* @author Vaadin Ltd
*/
public class CurrentInstance implements Serializable {
private static final Object NULL_OBJECT = new Object();
- private static final CurrentInstance CURRENT_INSTANCE_NULL = new CurrentInstance(
- NULL_OBJECT, true);
+ private static final CurrentInstance CURRENT_INSTANCE_NULL = new CurrentInstance(NULL_OBJECT);
private final WeakReference<Object> instance;
- private final boolean inheritable;
-
- private static final InheritableThreadLocal<Map<Class<?>, CurrentInstance>> instances = new InheritableThreadLocal<Map<Class<?>, CurrentInstance>>() {
- @Override
- protected Map<Class<?>, CurrentInstance> childValue(
- Map<Class<?>, CurrentInstance> parentValue) {
- if (parentValue == null) {
- return null;
- }
-
- Map<Class<?>, CurrentInstance> value = new HashMap<>();
- // Copy all inheritable values to child map
- for (Entry<Class<?>, CurrentInstance> e : parentValue.entrySet()) {
- if (e.getValue().inheritable) {
- value.put(e.getKey(), e.getValue());
- }
- }
-
- return value;
- }
- };
+ private static final ThreadLocal<Map<Class<?>, CurrentInstance>> instances = new ThreadLocal<>();
- private CurrentInstance(Object instance, boolean inheritable) {
+ private CurrentInstance(Object instance) {
this.instance = new WeakReference<>(instance);
- this.inheritable = inheritable;
}
/**
/**
* Sets the current instance of the given type.
*
- * @see #setInheritable(Class, Object)
* @see ThreadLocal
*
* @param type
* @param instance
* the actual instance
*/
- public static <T> void set(Class<T> type, T instance) {
- set(type, instance, false);
- }
-
- /**
- * Sets the current inheritable instance of the given type. A current
- * instance that is inheritable will be available for child threads and in
- * code run by {@link VaadinSession#access(Runnable)} and
- * {@link UI#access(Runnable)}.
- *
- * @see #set(Class, Object)
- * @see InheritableThreadLocal
- *
- * @param type
- * the class that should be used when getting the current
- * instance back
- * @param instance
- * the actual instance
- */
- public static <T> void setInheritable(Class<T> type, T instance) {
- set(type, instance, true);
- }
-
- private static <T> CurrentInstance set(Class<T> type, T instance,
- boolean inheritable) {
+ public static <T> CurrentInstance set(Class<T> type, T instance) {
Map<Class<?>, CurrentInstance> map = instances.get();
CurrentInstance previousInstance = null;
if (instance == null) {
instances.set(map);
}
- previousInstance = map.put(type,
- new CurrentInstance(instance, inheritable));
- if (previousInstance != null) {
- assert previousInstance.inheritable == inheritable : "Inheritable status mismatch for "
- + type + " (previous was "
- + previousInstance.inheritable + ", new is "
- + inheritable + ")";
- }
+ previousInstance = map.put(type, new CurrentInstance(instance));
}
if (previousInstance == null) {
previousInstance = CURRENT_INSTANCE_NULL;
*/
v = null;
}
- set(c, v, ci.inheritable);
+ set(c, v);
}
if (removeStale) {
*
* @since 7.1
*
- * @param onlyInheritable
- * <code>true</code> if only the inheritable instances should be
- * included; <code>false</code> to get all instances.
* @return a map containing the current instances
*/
- public static Map<Class<?>, CurrentInstance> getInstances(
- boolean onlyInheritable) {
+ public static Map<Class<?>, CurrentInstance> getInstances() {
Map<Class<?>, CurrentInstance> map = instances.get();
if (map == null) {
return Collections.emptyMap();
CurrentInstance ci = map.get(c);
if (ci.instance.get() == null) {
removeStale = true;
- } else if (ci.inheritable || !onlyInheritable) {
+ } else {
copy.put(c, ci);
}
}
*/
public static Map<Class<?>, CurrentInstance> setCurrent(UI ui) {
Map<Class<?>, CurrentInstance> old = setCurrent(ui.getSession());
- old.put(UI.class, set(UI.class, ui, true));
+ old.put(UI.class, set(UI.class, ui));
return old;
}
public static Map<Class<?>, CurrentInstance> setCurrent(
VaadinSession session) {
Map<Class<?>, CurrentInstance> old = new HashMap<>();
- old.put(VaadinSession.class, set(VaadinSession.class, session, true));
+ old.put(VaadinSession.class, set(VaadinSession.class, session));
VaadinService service = null;
if (session != null) {
service = session.getService();
}
- old.put(VaadinService.class, set(VaadinService.class, service, true));
+ old.put(VaadinService.class, set(VaadinService.class, service));
return old;
}
*/
package com.vaadin.util;
-import static org.junit.Assert.assertNull;
-
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
-
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinService;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.ui.UI;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
-import com.vaadin.server.VaadinRequest;
-import com.vaadin.server.VaadinService;
-import com.vaadin.server.VaadinSession;
-import com.vaadin.ui.UI;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import static org.junit.Assert.assertNull;
public class CurrentInstanceTest {
assertCleared();
}
- @Test
- public void testClearedAfterRemoveInheritable() throws Exception {
- CurrentInstance.clearAll();
-
- CurrentInstance.setInheritable(CurrentInstanceTest.class, this);
- Assert.assertEquals(this,
- CurrentInstance.get(CurrentInstanceTest.class));
- CurrentInstance.setInheritable(CurrentInstanceTest.class, null);
-
- assertCleared();
- }
-
- @Test
- public void testInheritableThreadLocal() throws Exception {
- final AtomicBoolean threadFailed = new AtomicBoolean(true);
-
- CurrentInstance.setInheritable(CurrentInstanceTest.class, this);
- Assert.assertEquals(this,
- CurrentInstance.get(CurrentInstanceTest.class));
- Thread t = new Thread() {
- @Override
- public void run() {
- Assert.assertEquals(CurrentInstanceTest.this,
- CurrentInstance.get(CurrentInstanceTest.class));
- threadFailed.set(false);
- }
- };
- t.start();
- CurrentInstance.set(CurrentInstanceTest.class, null);
-
- assertCleared();
- while (t.isAlive()) {
- Thread.sleep(1000);
- }
- Assert.assertFalse("Thread failed", threadFailed.get());
-
- }
-
- @Test
- public void testClearedAfterRemoveInSeparateThread() throws Exception {
- final AtomicBoolean threadFailed = new AtomicBoolean(true);
-
- CurrentInstance.setInheritable(CurrentInstanceTest.class, this);
- Assert.assertEquals(this,
- CurrentInstance.get(CurrentInstanceTest.class));
- Thread t = new Thread() {
- @Override
- public void run() {
- try {
- Assert.assertEquals(CurrentInstanceTest.this,
- CurrentInstance.get(CurrentInstanceTest.class));
- CurrentInstance.set(CurrentInstanceTest.class, null);
- assertCleared();
-
- threadFailed.set(false);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- };
- t.start();
-
- while (t.isAlive()) {
- Thread.sleep(1000);
- }
- Assert.assertFalse("Thread failed", threadFailed.get());
-
- // Clearing the threadlocal in the thread should not have cleared it
- // here
- Assert.assertEquals(this,
- CurrentInstance.get(CurrentInstanceTest.class));
-
- // Clearing the only remaining threadlocal should free all memory
- CurrentInstance.set(CurrentInstanceTest.class, null);
- assertCleared();
- }
-
@Test
public void testClearedWithClearAll() throws Exception {
CurrentInstance.set(CurrentInstanceTest.class, this);
Assert.assertNull(getInternalCurrentInstanceVariable().get());
}
- private InheritableThreadLocal<Map<Class<?>, CurrentInstance>> getInternalCurrentInstanceVariable()
+ private ThreadLocal<Map<Class<?>, CurrentInstance>> getInternalCurrentInstanceVariable()
throws SecurityException, NoSuchFieldException,
IllegalAccessException {
Field f = CurrentInstance.class.getDeclaredField("instances");
f.setAccessible(true);
- return (InheritableThreadLocal<Map<Class<?>, CurrentInstance>>) f
+ return (ThreadLocal<Map<Class<?>, CurrentInstance>>) f
.get(null);
}
}
Assert.fail("Value was not garbage collected.");
}
+
+ @Test
+ public void nonInheritableThreadLocals()
+ throws InterruptedException, ExecutionException {
+ CurrentInstance.clearAll();
+ CurrentInstance.set(CurrentInstanceTest.class, this);
+
+ Assert.assertNotNull(CurrentInstance.get(CurrentInstanceTest.class));
+
+ Callable<Void> runnable = () -> {
+ Assert.assertNull(CurrentInstance.get(CurrentInstanceTest.class));
+ return null;
+ };
+ ExecutorService service = Executors.newSingleThreadExecutor();
+ Future<Void> future = service.submit(runnable);
+ future.get();
+ }
}
+++ /dev/null
-/*
- * Copyright 2000-2016 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.components.ui;
-
-import java.util.ArrayList;
-
-import com.vaadin.server.VaadinRequest;
-import com.vaadin.tests.components.AbstractTestUIWithLog;
-import com.vaadin.ui.Button;
-import com.vaadin.ui.Button.ClickEvent;
-import com.vaadin.ui.UI;
-import com.vaadin.util.CurrentInstance;
-
-public class CurrentUiRetained extends AbstractTestUIWithLog {
- public static class GcProbe {
-
- }
-
- @Override
- protected void setup(VaadinRequest request) {
- final ArrayList<UI> uiLog = new ArrayList<>();
- final ArrayList<Boolean> probeLog = new ArrayList<>();
-
- final Thread thread = new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- uiLog.add(UI.getCurrent());
-
- GcProbe gcProbe = new GcProbe();
- CurrentInstance.set(GcProbe.class, gcProbe);
- probeLog.add(CurrentInstance.get(GcProbe.class) != null);
- gcProbe = null;
-
- Thread.sleep(500l);
- System.gc();
- Thread.sleep(500l);
-
- probeLog.add(CurrentInstance.get(GcProbe.class) != null);
- uiLog.add(UI.getCurrent());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
- thread.start();
-
- addComponent(new Button("Show result", new Button.ClickListener() {
- @Override
- public void buttonClick(ClickEvent event) {
- try {
- thread.join();
-
- log("Correct UI.getCurrent before GC: "
- + (uiLog.get(0) == CurrentUiRetained.this));
- log("Correct UI.getCurrent after GC: "
- + (uiLog.get(1) == CurrentUiRetained.this));
-
- log("GC probe available before GC: " + probeLog.get(0));
- log("GC probe available after GC: " + probeLog.get(1));
-
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }));
- }
-
- @Override
- protected String getTestDescription() {
- return "Tests that garbage collection removes stale CurrentInstance values while retaining values not collected.";
- }
-
- @Override
- protected Integer getTicketNumber() {
- return Integer.valueOf(12509);
- }
-
-}
public void buttonClick(ClickEvent event) {
log.clear();
// accessSynchronously should maintain values
- CurrentInstance.setInheritable(
+ CurrentInstance.set(
CurrentInstanceTestType.class,
new CurrentInstanceTestType(
"Set before access"));
log.log("Test value in access: "
+ CurrentInstance.get(
CurrentInstanceTestType.class));
- CurrentInstance.setInheritable(
+ CurrentInstance.set(
CurrentInstanceTestType.class,
new CurrentInstanceTestType(
"Set in access"));
}
});
- CurrentInstance.setInheritable(
+ CurrentInstance.set(
CurrentInstanceTestType.class,
new CurrentInstanceTestType(
"Set before run pending"));
.getCurrent() == UIAccessExceptionHandling.this;
Map<Class<?>, CurrentInstance> instances = CurrentInstance
- .getInstances(false);
+ .getInstances();
CurrentInstance.clearAll();
assert UI.getCurrent() == null;
assertLogText("8. this root in root init", 8);
assertLogText("9. some app in root paint", 7);
assertLogText("10. this root in root paint", 6);
- assertLogText("11. some app in background thread", 5);
- assertLogText("12. this root in background thread", 4);
+ assertLogText("11. null app in background thread", 5);
+ assertLogText("12. null root in background thread", 4);
assertLogText("13. some app in resource handler", 3);
assertLogText("14. this root in resource handler", 2);
assertLogText("15. some app in button listener", 1);
+++ /dev/null
-package com.vaadin.tests.components.ui;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import com.vaadin.testbench.elements.ButtonElement;
-import com.vaadin.tests.tb3.MultiBrowserTest;
-
-public class CurrentUiRetainedTest extends MultiBrowserTest {
- @Test
- public void testCurrentUiRetained() throws Exception {
- openTestURL();
- $(ButtonElement.class).first().click();
- assertLogText(3, "1. Correct UI.getCurrent before GC: true");
- assertLogText(2, "2. Correct UI.getCurrent after GC: true");
- assertLogText(1, "3. GC probe available before GC: true");
- assertLogText(0, "4. GC probe available after GC: false");
- }
-
- private void assertLogText(int index, String expected) {
- Assert.assertEquals("Unexpected log contents,", expected,
- getLogRow(index));
- }
-}
"3. Test value after accessSynchornously: Set in accessSynchronosly"));
}
- @Test
- public void currentInstanceCanAccessValue() {
- $(ButtonElement.class).get(6).click();
-
- assertTrue(logContainsText("0. access has request? false"));
- assertTrue(
- logContainsText("1. Test value in access: Set before access"));
- assertTrue(logContainsText("2. has request after access? true"));
- assertTrue(logContainsText(
- "3. Test value after access: Set before run pending"));
- }
-
}