/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.threadlocals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import java.util.HashMap; 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.geoserver.catalog.LayerInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.impl.LayerInfoImpl; import org.geoserver.catalog.impl.WorkspaceInfoImpl; import org.geoserver.data.test.SystemTestData; import org.geoserver.ows.Dispatcher; import org.geoserver.ows.LocalPublished; import org.geoserver.ows.LocalWorkspace; import org.geoserver.ows.Request; import org.geoserver.security.AdminRequest; import org.geoserver.test.GeoServerSystemTestSupport; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; public class ThreadLocalsTransferTest extends GeoServerSystemTestSupport { protected ExecutorService executor; @Override protected void setUpTestData(SystemTestData testData) throws Exception { // no test data needed for this test, only the spring context testData.setUpSecurity(); } @Before public void setupExecutor() { executor = Executors.newCachedThreadPool(); } @After public void stopExecutor() { executor.shutdown(); } @After public void cleanupThreadLocals() { Dispatcher.REQUEST.remove(); AdminRequest.finish(); LocalPublished.remove(); LocalWorkspace.remove(); SecurityContextHolder.getContext().setAuthentication(null); } @Test public void testThreadLocalTransfer() throws InterruptedException, ExecutionException { final Request request = new Request(); Dispatcher.REQUEST.set(request); final LayerInfo layer = new LayerInfoImpl(); LocalPublished.set(layer); final WorkspaceInfo ws = new WorkspaceInfoImpl(); LocalWorkspace.set(ws); final Object myState = new Object(); AdminRequest.start(myState); final Authentication auth = new UsernamePasswordAuthenticationToken("user", "password"); SecurityContextHolder.getContext().setAuthentication(auth); final ThreadLocalsTransfer transfer = new ThreadLocalsTransfer(); Future<Void> future = executor.submit(new Callable<Void>() { @Override public Void call() throws Exception { testApply(); testCleanup(); return null; } private void testApply() { transfer.apply(); // check all thread locals have been applied to the current thread assertSame(request, Dispatcher.REQUEST.get()); assertSame(myState, AdminRequest.get()); assertSame(layer, LocalPublished.get()); assertSame(ws, LocalWorkspace.get()); assertSame(auth, SecurityContextHolder.getContext().getAuthentication()); } private void testCleanup() { transfer.cleanup(); // check all thread locals have been cleaned up from the current thread assertNull(Dispatcher.REQUEST.get()); assertNull(AdminRequest.get()); assertNull(LocalPublished.get()); assertNull(LocalWorkspace.get()); assertNull(SecurityContextHolder.getContext().getAuthentication()); } }); future.get(); } protected abstract static class ThreadLocalTransferCallable implements Callable<Void> { Thread originalThread; ThreadLocalTransfer transfer; Map<String, Object> storage = new HashMap<String, Object>(); public ThreadLocalTransferCallable(ThreadLocalTransfer transfer) { this.originalThread = Thread.currentThread(); this.transfer = transfer; this.transfer.collect(storage); } @Override public Void call() throws Exception { // this is the the main thread, we are actually running inside the thread pool assertNotEquals(originalThread, Thread.currentThread()); // apply the thread local, check it has been applied correctly transfer.apply(storage); assertThreadLocalApplied(); // clean up, check the therad local is now empty transfer.cleanup(); assertThreadLocalCleaned(); return null; } abstract void assertThreadLocalCleaned(); abstract void assertThreadLocalApplied(); }; }