/* * 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.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.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; 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; public class CurrentInstanceTest { @Before public void clearExistingThreadLocals() { // Ensure no previous test left some thread locals hanging CurrentInstance.clearAll(); } @Test public void testInitiallyCleared() throws Exception { assertCleared(); } @Test public void testClearedAfterRemove() throws Exception { CurrentInstance.set(CurrentInstanceTest.class, this); Assert.assertEquals(this, CurrentInstance.get(CurrentInstanceTest.class)); CurrentInstance.set(CurrentInstanceTest.class, null); assertCleared(); } @Test public void testClearedWithClearAll() throws Exception { CurrentInstance.set(CurrentInstanceTest.class, this); Assert.assertEquals(this, CurrentInstance.get(CurrentInstanceTest.class)); CurrentInstance.clearAll(); assertCleared(); } private void assertCleared() throws SecurityException, NoSuchFieldException, IllegalAccessException { Assert.assertNull(getInternalCurrentInstanceVariable().get()); } private ThreadLocal<Map<Class<?>, CurrentInstance>> getInternalCurrentInstanceVariable() throws SecurityException, NoSuchFieldException, IllegalAccessException { Field f = CurrentInstance.class.getDeclaredField("instances"); f.setAccessible(true); return (ThreadLocal<Map<Class<?>, CurrentInstance>>) f.get(null); } public void testInheritedClearedAfterRemove() { } private static class UIStoredInCurrentInstance extends UI { @Override protected void init(VaadinRequest request) { } } private static class SessionStoredInCurrentInstance extends VaadinSession { public SessionStoredInCurrentInstance(VaadinService service) { super(service); } } @Test public void testRestoringNullUIWorks() throws Exception { // First make sure current instance is empty CurrentInstance.clearAll(); // Then store a new UI in there Map<Class<?>, CurrentInstance> old = CurrentInstance .setCurrent(new UIStoredInCurrentInstance()); // Restore the old values and assert that the UI is null again CurrentInstance.restoreInstances(old); assertNull(CurrentInstance.get(UI.class)); } @Test public void testRestoringNullSessionWorks() throws Exception { // First make sure current instance is empty CurrentInstance.clearAll(); // Then store a new session in there Map<Class<?>, CurrentInstance> old = CurrentInstance .setCurrent(new SessionStoredInCurrentInstance( EasyMock.createNiceMock(VaadinService.class))); // Restore the old values and assert that the session is null again CurrentInstance.restoreInstances(old); assertNull(CurrentInstance.get(VaadinSession.class)); assertNull(CurrentInstance.get(VaadinService.class)); } @Test public void testRestoreWithGarbageCollectedValue() throws InterruptedException { VaadinSession session1 = new VaadinSession(null) { @Override public String toString() { return "First session"; } }; VaadinSession session2 = new VaadinSession(null) { @Override public String toString() { return "Second session"; } }; VaadinSession.setCurrent(session1); Map<Class<?>, CurrentInstance> previous = CurrentInstance .setCurrent(session2); // Use weak ref to verify object is collected WeakReference<VaadinSession> ref = new WeakReference<>(session1); session1 = null; waitUntilGarbageCollected(ref); CurrentInstance.restoreInstances(previous); Assert.assertNull(VaadinSession.getCurrent()); } private static void waitUntilGarbageCollected(WeakReference<?> ref) throws InterruptedException { for (int i = 0; i < 50; i++) { System.gc(); if (ref.get() == null) { return; } Thread.sleep(100); } 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(); } }