From 51fd83060e14fec41818160367dff9a530467d5c Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 8 Nov 2012 22:20:01 -0600 Subject: [PATCH] SEC-2077: Concurrency support Provide abstractions for transferring a SecurityContext across threads. The main concepts are the DelegatingSecurityContextCallable and the DelegatingSecurityContextRunnable which contain a SecurityContext to establish before delegating to a Callable or Runnable. There are also wrapper implementations for each of the key java.util.concurrent and spring task interfaces to make using the DelegatingSecurityContextCallable and DelegatingSecurityContextRunnable transparent to users. For example a DelegatingSecurityContextTaskExecutor which can be injected with a specific SecurityContext or use the SecurityContext from the SecurityContextHolder at the time the task is submitted. There are similar implementations for each of the key java.util.concurrent and spring task interfaces. Note that in order to get DelegatingSecurityContextExecutorService to compile with JDK 5 or JDK 6 we could not use type safe methods. See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6267833 for details. --- core/core.gradle | 3 +- ...tractDelegatingSecurityContextSupport.java | 47 +++++ .../DelegatingSecurityContextCallable.java | 82 +++++++++ .../DelegatingSecurityContextExecutor.java | 61 +++++++ ...egatingSecurityContextExecutorService.java | 135 +++++++++++++++ .../DelegatingSecurityContextRunnable.java | 78 +++++++++ ...curityContextScheduledExecutorService.java | 80 +++++++++ ...SecurityContextSchedulingTaskExecutor.java | 62 +++++++ ...atingSecurityContextAsyncTaskExecutor.java | 72 ++++++++ ...DelegatingSecurityContextTaskExecutor.java | 47 +++++ ...ngSecurityContextExecutorServiceTests.java | 161 ++++++++++++++++++ ...elegatingSecurityContextExecutorTests.java | 59 +++++++ ...yContextScheduledExecutorServiceTests.java | 84 +++++++++ ...tDelegatingSecurityContextTestSupport.java | 88 ++++++++++ ...ngSecurityContextExecutorServiceTests.java | 35 ++++ ...elegatingSecurityContextExecutorTests.java | 36 ++++ ...yContextScheduledExecutorServiceTests.java | 37 ++++ ...elegatingSecurityContextCallableTests.java | 134 +++++++++++++++ ...elegatingSecurityContextRunnableTests.java | 134 +++++++++++++++ ...DelegatingSecurityContextSupportTests.java | 64 +++++++ ...ngSecurityContextExecutorServiceTests.java | 35 ++++ ...elegatingSecurityContextExecutorTests.java | 37 ++++ ...yContextScheduledExecutorServiceTests.java | 37 ++++ ...ityContextSchedulingTaskExecutorTests.java | 39 +++++ ...ityContextSchedulingTaskExecutorTests.java | 23 +++ ...ityContextSchedulingTaskExecutorTests.java | 35 ++++ ...SecurityContextAsyncTaskExecutorTests.java | 66 +++++++ ...SecurityContextAsyncTaskExecutorTests.java | 26 +++ ...atingSecurityContextTaskExecutorTests.java | 47 +++++ ...SecurityContextAsyncTaskExecutorTests.java | 26 +++ ...atingSecurityContextTaskExecutorTests.java | 48 ++++++ 31 files changed, 1917 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextSupport.java create mode 100644 core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java create mode 100644 core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutor.java create mode 100644 core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorService.java create mode 100644 core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnable.java create mode 100644 core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorService.java create mode 100644 core/src/main/java/org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutor.java create mode 100644 core/src/main/java/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutor.java create mode 100644 core/src/main/java/org/springframework/security/task/DelegatingSecurityContextTaskExecutor.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorServiceTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextScheduledExecutorServiceTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextTestSupport.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorServiceTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextScheduledExecutorServiceTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextCallableTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnableTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextSupportTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorServiceTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextScheduledExecutorServiceTests.java create mode 100644 core/src/test/java/org/springframework/security/scheduling/AbstractSecurityContextSchedulingTaskExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/scheduling/CurrentSecurityContextSchedulingTaskExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/scheduling/ExplicitSecurityContextSchedulingTaskExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/task/AbstractDelegatingSecurityContextAsyncTaskExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextAsyncTaskExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextTaskExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextAsyncTaskExecutorTests.java create mode 100644 core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextTaskExecutorTests.java diff --git a/core/core.gradle b/core/core.gradle index 7b986e138d..74499bc79b 100644 --- a/core/core.gradle +++ b/core/core.gradle @@ -19,7 +19,8 @@ dependencies { testCompile 'commons-collections:commons-collections:3.2', "org.springframework:spring-test:$springVersion", - "org.slf4j:jcl-over-slf4j:$slf4jVersion" + "org.slf4j:jcl-over-slf4j:$slf4jVersion", + powerMockDependencies testRuntime "hsqldb:hsqldb:$hsqlVersion", "cglib:cglib-nodep:$cglibVersion" diff --git a/core/src/main/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextSupport.java b/core/src/main/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextSupport.java new file mode 100644 index 0000000000..85b3c784f8 --- /dev/null +++ b/core/src/main/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextSupport.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import java.util.concurrent.Callable; + +import org.springframework.security.core.context.SecurityContext; + +/** + * An internal support class that wraps {@link Callable} with {@link DelegatingSecurityContextCallable} and + * {@link Runnable} with {@link DelegatingSecurityContextRunnable} + * + * @author Rob Winch + * @since 3.2 + */ +abstract class AbstractDelegatingSecurityContextSupport { + + private final SecurityContext securityContext; + + /** + * Creates a new {@link AbstractDelegatingSecurityContextSupport} that uses the specified {@link SecurityContext}. + * + * @param securityContext the {@link SecurityContext} to use for each {@link DelegatingSecurityContextRunnable} and + * each {@link DelegatingSecurityContextCallable} or null to default to the current {@link SecurityContext}. + */ + AbstractDelegatingSecurityContextSupport(SecurityContext securityContext) { + this.securityContext = securityContext; + } + + protected final Runnable wrap(Runnable delegate) { + return DelegatingSecurityContextRunnable.create(delegate, securityContext); + } + + protected final Callable wrap(Callable delegate) { + return DelegatingSecurityContextCallable.create(delegate, securityContext); + } +} diff --git a/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java new file mode 100644 index 0000000000..278b529a37 --- /dev/null +++ b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java @@ -0,0 +1,82 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import java.util.concurrent.Callable; + +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.Assert; + +/** + * Wraps a delegate {@link Callable} with logic for setting up a {@link SecurityContext} before invoking the delegate + * {@link Callable} and then removing the {@link SecurityContext} after the delegate has completed. + * + * @author Rob Winch + * @since 3.2 + */ +public final class DelegatingSecurityContextCallable implements Callable { + + private final Callable delegate; + + private final SecurityContext securityContext; + + /** + * Creates a new {@link DelegatingSecurityContextCallable} with a specific {@link SecurityContext}. + * @param delegate the delegate {@link DelegatingSecurityContextCallable} to run with the specified + * {@link SecurityContext}. Cannot be null. + * @param securityContext the {@link SecurityContext} to establish for the delegate {@link Callable}. Cannot be + * null. + */ + public DelegatingSecurityContextCallable(Callable delegate, SecurityContext securityContext) { + Assert.notNull(delegate, "delegate cannot be null"); + Assert.notNull(securityContext, "securityContext cannot be null"); + this.delegate = delegate; + this.securityContext = securityContext; + } + + /** + * Creates a new {@link DelegatingSecurityContextCallable} with the {@link SecurityContext} from the + * {@link SecurityContextHolder}. + * @param delegate the delegate {@link Callable} to run under the current {@link SecurityContext}. Cannot be null. + */ + public DelegatingSecurityContextCallable(Callable delegate) { + this(delegate, SecurityContextHolder.getContext()); + } + + public V call() throws Exception { + try { + SecurityContextHolder.setContext(securityContext); + return delegate.call(); + } + finally { + SecurityContextHolder.clearContext(); + } + } + + /** + * Creates a {@link DelegatingSecurityContextCallable} and with the given {@link Callable} and + * {@link SecurityContext}, but if the securityContext is null will defaults to the current {@link SecurityContext} + * on the {@link SecurityContextHolder} + * + * @param delegate the delegate {@link DelegatingSecurityContextCallable} to run with the specified + * {@link SecurityContext}. Cannot be null. + * @param securityContext the {@link SecurityContext} to establish for the delegate {@link Callable}. If null, + * defaults to {@link SecurityContextHolder#getContext()} + * @return + */ + public static Callable create(Callable delegate, SecurityContext securityContext) { + return securityContext == null ? new DelegatingSecurityContextCallable(delegate) + : new DelegatingSecurityContextCallable(delegate, securityContext); + } +} diff --git a/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutor.java b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutor.java new file mode 100644 index 0000000000..3cc27db0c5 --- /dev/null +++ b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutor.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import java.util.concurrent.Executor; + +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.Assert; + +/** + * An {@link Executor} which wraps each {@link Runnable} in a {@link DelegatingSecurityContextRunnable}. + * + * @author Rob Winch + * @since 3.2 + */ +public class DelegatingSecurityContextExecutor extends AbstractDelegatingSecurityContextSupport implements Executor { + private final Executor delegate; + + /** + * Creates a new {@link DelegatingSecurityContextExecutor} that uses the specified {@link SecurityContext}. + * + * @param delegateExecutor the {@link Executor} to delegate to. Cannot be null. + * @param securityContext the {@link SecurityContext} to use for each {@link DelegatingSecurityContextRunnable} or + * null to default to the current {@link SecurityContext} + */ + public DelegatingSecurityContextExecutor(Executor delegateExecutor, SecurityContext securityContext) { + super(securityContext); + Assert.notNull(delegateExecutor, "delegateExecutor cannot be null"); + this.delegate = delegateExecutor; + } + + /** + * Creates a new {@link DelegatingSecurityContextExecutor} that uses the current {@link SecurityContext} from the + * {@link SecurityContextHolder} at the time the task is submitted. + * + * @param delegate the {@link Executor} to delegate to. Cannot be null. + */ + public DelegatingSecurityContextExecutor(Executor delegate) { + this(delegate, null); + } + + public final void execute(Runnable task) { + task = wrap(task); + delegate.execute(task); + } + + protected final Executor getDelegateExecutor() { + return delegate; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorService.java b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorService.java new file mode 100644 index 0000000000..8dd5a6b3ea --- /dev/null +++ b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorService.java @@ -0,0 +1,135 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.springframework.core.task.TaskExecutor; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * An {@link ExecutorService} which wraps each {@link Runnable} in a {@link DelegatingSecurityContextRunnable} and each + * {@link Callable} in a {@link DelegatingSecurityContextCallable}. + * + * @author Rob Winch + * @since 3.2 + */ +public class DelegatingSecurityContextExecutorService extends DelegatingSecurityContextExecutor implements + ExecutorService { + /** + * Creates a new {@link DelegatingSecurityContextExecutorService} that uses the specified {@link SecurityContext}. + * + * @param delegateExecutorService the {@link ExecutorService} to delegate to. Cannot be null. + * @param securityContext the {@link SecurityContext} to use for each {@link DelegatingSecurityContextRunnable} and + * each {@link DelegatingSecurityContextCallable}. + */ + public DelegatingSecurityContextExecutorService(ExecutorService delegateExecutorService, + SecurityContext securityContext) { + super(delegateExecutorService, securityContext); + } + + /** + * Creates a new {@link DelegatingSecurityContextExecutorService} that uses the current {@link SecurityContext} from + * the {@link SecurityContextHolder}. + * + * @param delegateTaskExecutor the {@link TaskExecutor} to delegate to. Cannot be null. + */ + public DelegatingSecurityContextExecutorService(ExecutorService delegate) { + this(delegate, null); + } + + public final void shutdown() { + getDelegate().shutdown(); + } + + public final List shutdownNow() { + return getDelegate().shutdownNow(); + } + + public final boolean isShutdown() { + return getDelegate().isShutdown(); + } + + public final boolean isTerminated() { + return getDelegate().isTerminated(); + } + + public final boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return getDelegate().awaitTermination(timeout, unit); + } + + public final Future submit(Callable task) { + task = wrap(task); + return getDelegate().submit(task); + } + + public final Future submit(Runnable task, T result) { + task = wrap(task); + return getDelegate().submit(task, result); + } + + public final Future submit(Runnable task) { + task = wrap(task); + return getDelegate().submit(task); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public final List invokeAll(Collection tasks) throws InterruptedException { + tasks = createTasks(tasks); + return getDelegate().invokeAll(tasks); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public final List invokeAll(Collection tasks, long timeout, TimeUnit unit) + throws InterruptedException { + tasks = createTasks(tasks); + return getDelegate().invokeAll(tasks, timeout, unit); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public final Object invokeAny(Collection tasks) throws InterruptedException, ExecutionException { + tasks = createTasks(tasks); + return getDelegate().invokeAny(tasks); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public final Object invokeAny(Collection tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + tasks = createTasks(tasks); + return getDelegate().invokeAny(tasks, timeout, unit); + } + + private Collection> createTasks(Collection> tasks) { + if (tasks == null) { + return null; + } + List> results = new ArrayList>(tasks.size()); + for (Callable task : tasks) { + results.add(wrap(task)); + } + return results; + } + + private ExecutorService getDelegate() { + return (ExecutorService) getDelegateExecutor(); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnable.java b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnable.java new file mode 100644 index 0000000000..c9371af44f --- /dev/null +++ b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnable.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.Assert; + +/** + * Wraps a delegate {@link Runnable} with logic for setting up a {@link SecurityContext} before invoking the delegate + * {@link Runnable} and then removing the {@link SecurityContext} after the delegate has completed. + * + * @author Rob Winch + * @since 3.2 + */ +public final class DelegatingSecurityContextRunnable implements Runnable { + + private final Runnable delegate; + + private final SecurityContext securityContext; + + /** + * Creates a new {@link DelegatingSecurityContextRunnable} with a specific {@link SecurityContext}. + * @param delegate the delegate {@link Runnable} to run with the specified {@link SecurityContext}. Cannot be null. + * @param securityContext the {@link SecurityContext} to establish for the delegate {@link Runnable}. Cannot be + * null. + */ + public DelegatingSecurityContextRunnable(Runnable delegate, SecurityContext securityContext) { + Assert.notNull(delegate, "delegate cannot be null"); + Assert.notNull(securityContext, "securityContext cannot be null"); + this.delegate = delegate; + this.securityContext = securityContext; + } + + /** + * Creates a new {@link DelegatingSecurityContextRunnable} with the {@link SecurityContext} from the + * {@link SecurityContextHolder}. + * @param delegate the delegate {@link Runnable} to run under the current {@link SecurityContext}. Cannot be null. + */ + public DelegatingSecurityContextRunnable(Runnable delegate) { + this(delegate, SecurityContextHolder.getContext()); + } + + public void run() { + try { + SecurityContextHolder.setContext(securityContext); + delegate.run(); + } + finally { + SecurityContextHolder.clearContext(); + } + } + + /** + * Factory method for creating a {@link DelegatingSecurityContextRunnable}. + * + * @param delegate the original {@link Runnable} that will be delegated to after establishing a + * {@link SecurityContext} on the {@link SecurityContextHolder}. Cannot have null. + * @param securityContext the {@link SecurityContext} to establish before invoking the delegate {@link Runnable}. If + * null, the current {@link SecurityContext} from the {@link SecurityContextHolder} will be used. + * @return + */ + public static Runnable create(Runnable delegate, SecurityContext securityContext) { + Assert.notNull(delegate, "delegate cannot be null"); + return securityContext == null ? new DelegatingSecurityContextRunnable(delegate) + : new DelegatingSecurityContextRunnable(delegate, securityContext); + } +} diff --git a/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorService.java b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorService.java new file mode 100644 index 0000000000..94f61396ef --- /dev/null +++ b/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorService.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.springframework.core.task.TaskExecutor; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * An {@link ScheduledExecutorService} which wraps each {@link Runnable} in a {@link DelegatingSecurityContextRunnable} + * and each {@link Callable} in a {@link DelegatingSecurityContextCallable}. + * + * @author Rob Winch + * @since 3.2 + */ +public final class DelegatingSecurityContextScheduledExecutorService extends DelegatingSecurityContextExecutorService + implements ScheduledExecutorService { + /** + * Creates a new {@link DelegatingSecurityContextScheduledExecutorService} that uses the specified + * {@link SecurityContext}. + * + * @param delegateScheduledExecutorService the {@link ScheduledExecutorService} to delegate to. Cannot be null. + * @param securityContext the {@link SecurityContext} to use for each {@link DelegatingSecurityContextRunnable} and + * each {@link DelegatingSecurityContextCallable}. + */ + public DelegatingSecurityContextScheduledExecutorService(ScheduledExecutorService delegateScheduledExecutorService, + SecurityContext securityContext) { + super(delegateScheduledExecutorService, securityContext); + } + + /** + * Creates a new {@link DelegatingSecurityContextScheduledExecutorService} that uses the current + * {@link SecurityContext} from the {@link SecurityContextHolder}. + * + * @param delegateTaskExecutor the {@link TaskExecutor} to delegate to. Cannot be null. + */ + public DelegatingSecurityContextScheduledExecutorService(ScheduledExecutorService delegate) { + this(delegate, null); + } + + public final ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + command = wrap(command); + return getDelegate().schedule(command, delay, unit); + } + + public final ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { + callable = wrap(callable); + return getDelegate().schedule(callable, delay, unit); + } + + public final ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { + command = wrap(command); + return getDelegate().scheduleAtFixedRate(command, initialDelay, period, unit); + } + + public final ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, + TimeUnit unit) { + command = wrap(command); + return getDelegate().scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + + private ScheduledExecutorService getDelegate() { + return (ScheduledExecutorService) getDelegateExecutor(); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutor.java b/core/src/main/java/org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutor.java new file mode 100644 index 0000000000..40fc4bfb0a --- /dev/null +++ b/core/src/main/java/org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutor.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.scheduling; + +import java.util.concurrent.Callable; + +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.scheduling.SchedulingTaskExecutor; +import org.springframework.security.concurrent.DelegatingSecurityContextCallable; +import org.springframework.security.concurrent.DelegatingSecurityContextRunnable; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor; + +/** + * An {@link SchedulingTaskExecutor} which wraps each {@link Runnable} in a {@link DelegatingSecurityContextRunnable} and each + * {@link Callable} in a {@link DelegatingSecurityContextCallable}. + * + * @author Rob Winch + * @since 3.2 + */ +public class DelegatingSecurityContextSchedulingTaskExecutor extends DelegatingSecurityContextAsyncTaskExecutor + implements SchedulingTaskExecutor { + + /** + * Creates a new {@link DelegatingSecurityContextSchedulingTaskExecutor} that uses the specified {@link SecurityContext}. + * + * @param delegateSchedulingTaskExecutor the {@link SchedulingTaskExecutor} to delegate to. Cannot be null. + * @param securityContext the {@link SecurityContext} to use for each {@link DelegatingSecurityContextRunnable} and + * {@link DelegatingSecurityContextCallable} + */ + public DelegatingSecurityContextSchedulingTaskExecutor(SchedulingTaskExecutor delegateSchedulingTaskExecutor, + SecurityContext securityContext) { + super(delegateSchedulingTaskExecutor, securityContext); + } + + /** + * Creates a new {@link DelegatingSecurityContextSchedulingTaskExecutor} that uses the current {@link SecurityContext}. + * + * @param delegateAsyncTaskExecutor the {@link AsyncTaskExecutor} to delegate to. Cannot be null. + */ + public DelegatingSecurityContextSchedulingTaskExecutor(SchedulingTaskExecutor delegateAsyncTaskExecutor) { + this(delegateAsyncTaskExecutor, null); + } + + public boolean prefersShortLivedTasks() { + return getDelegate().prefersShortLivedTasks(); + } + + private SchedulingTaskExecutor getDelegate() { + return (SchedulingTaskExecutor) getDelegateExecutor(); + } +} diff --git a/core/src/main/java/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutor.java b/core/src/main/java/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutor.java new file mode 100644 index 0000000000..8e08ec30df --- /dev/null +++ b/core/src/main/java/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutor.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.task; + +import java.util.concurrent.Callable; +import java.util.concurrent.Future; + +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.security.concurrent.DelegatingSecurityContextCallable; +import org.springframework.security.concurrent.DelegatingSecurityContextRunnable; +import org.springframework.security.core.context.SecurityContext; + +/** + * An {@link AsyncTaskExecutor} which wraps each {@link Runnable} in a {@link DelegatingSecurityContextRunnable} and each + * {@link Callable} in a {@link DelegatingSecurityContextCallable}. + * + * @author Rob Winch + * @since 3.2 + */ +public class DelegatingSecurityContextAsyncTaskExecutor extends DelegatingSecurityContextTaskExecutor implements + AsyncTaskExecutor { + + /** + * Creates a new {@link DelegatingSecurityContextAsyncTaskExecutor} that uses the specified {@link SecurityContext}. + * + * @param delegateAsyncTaskExecutor the {@link AsyncTaskExecutor} to delegate to. Cannot be null. + * @param securityContext the {@link SecurityContext} to use for each {@link DelegatingSecurityContextRunnable} and + * {@link DelegatingSecurityContextCallable} + */ + public DelegatingSecurityContextAsyncTaskExecutor(AsyncTaskExecutor delegateAsyncTaskExecutor, + SecurityContext securityContext) { + super(delegateAsyncTaskExecutor, securityContext); + } + + /** + * Creates a new {@link DelegatingSecurityContextAsyncTaskExecutor} that uses the current {@link SecurityContext}. + * + * @param delegateAsyncTaskExecutor the {@link AsyncTaskExecutor} to delegate to. Cannot be null. + */ + public DelegatingSecurityContextAsyncTaskExecutor(AsyncTaskExecutor delegateAsyncTaskExecutor) { + this(delegateAsyncTaskExecutor, null); + } + + public final void execute(Runnable task, long startTimeout) { + task = wrap(task); + getDelegate().execute(task, startTimeout); + } + + public final Future submit(Runnable task) { + task = wrap(task); + return getDelegate().submit(task); + } + + public final Future submit(Callable task) { + task = wrap(task); + return getDelegate().submit(task); + } + + private AsyncTaskExecutor getDelegate() { + return (AsyncTaskExecutor) getDelegateExecutor(); + } +} diff --git a/core/src/main/java/org/springframework/security/task/DelegatingSecurityContextTaskExecutor.java b/core/src/main/java/org/springframework/security/task/DelegatingSecurityContextTaskExecutor.java new file mode 100644 index 0000000000..bd6023ed52 --- /dev/null +++ b/core/src/main/java/org/springframework/security/task/DelegatingSecurityContextTaskExecutor.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.task; + +import org.springframework.core.task.TaskExecutor; +import org.springframework.security.concurrent.DelegatingSecurityContextExecutor; +import org.springframework.security.concurrent.DelegatingSecurityContextRunnable; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * An {@link TaskExecutor} which wraps each {@link Runnable} in a {@link DelegatingSecurityContextRunnable}. + * + * @author Rob Winch + * @since 3.2 + */ +public class DelegatingSecurityContextTaskExecutor extends DelegatingSecurityContextExecutor implements TaskExecutor { + /** + * Creates a new {@link DelegatingSecurityContextTaskExecutor} that uses the specified {@link SecurityContext}. + * + * @param delegateTaskExecutor the {@link TaskExecutor} to delegate to. Cannot be null. + * @param securityContext the {@link SecurityContext} to use for each {@link DelegatingSecurityContextRunnable} + */ + public DelegatingSecurityContextTaskExecutor(TaskExecutor delegateTaskExecutor, SecurityContext securityContext) { + super(delegateTaskExecutor, securityContext); + } + + /** + * Creates a new {@link DelegatingSecurityContextTaskExecutor} that uses the current {@link SecurityContext} from + * the {@link SecurityContextHolder}. + * + * @param delegateTaskExecutor the {@link TaskExecutor} to delegate to. Cannot be null. + */ + public DelegatingSecurityContextTaskExecutor(TaskExecutor delegate) { + this(delegate, null); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorServiceTests.java b/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorServiceTests.java new file mode 100644 index 0000000000..f9d455b6ef --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorServiceTests.java @@ -0,0 +1,161 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + + +/** + * Abstract class for testing {@link DelegatingSecurityContextExecutorService} which allows customization of + * how {@link DelegatingSecurityContextExecutorService} and its mocks are created. + * + * @author Rob Winch + * @since 3.2 + * @see CurrentDelegatingSecurityContextExecutorServiceTests + * @see ExplicitDelegatingSecurityContextExecutorServiceTests + */ +public abstract class AbstractDelegatingSecurityContextExecutorServiceTests extends AbstractDelegatingSecurityContextExecutorTests { + @Mock + private Future expectedFutureObject; + @Mock + private Object resultArg; + + protected DelegatingSecurityContextExecutorService executor; + + @Before + public final void setUpExecutorService() { + executor = create(); + } + + @Test(expected = IllegalArgumentException.class) + public void constructorNullDelegate() { + new DelegatingSecurityContextExecutorService(null); + } + + @Test + public void shutdown() { + executor.shutdown(); + verify(delegate).shutdown(); + } + + @Test + public void shutdownNow() { + List result = executor.shutdownNow(); + verify(delegate).shutdownNow(); + assertThat(result).isEqualTo(delegate.shutdownNow()).isNotNull(); + } + + @Test + public void isShutdown() { + boolean result = executor.isShutdown(); + verify(delegate).isShutdown(); + assertThat(result).isEqualTo(delegate.isShutdown()).isNotNull(); + } + + @Test + public void isTerminated() { + boolean result = executor.isTerminated(); + verify(delegate).isTerminated(); + assertThat(result).isEqualTo(delegate.isTerminated()).isNotNull(); + } + + @Test + public void awaitTermination() throws InterruptedException { + boolean result = executor.awaitTermination(1, TimeUnit.SECONDS); + verify(delegate).awaitTermination(1, TimeUnit.SECONDS); + assertThat(result).isEqualTo(delegate.awaitTermination(1, TimeUnit.SECONDS)).isNotNull(); + } + + @Test + public void submitCallable() throws Exception { + when(delegate.submit(wrappedCallable)).thenReturn(expectedFutureObject); + Future result = executor.submit(callable); + verify(delegate).submit(wrappedCallable); + assertThat(result).isEqualTo(expectedFutureObject); + } + + @Test + public void submitRunnableWithResult() throws Exception { + when(delegate.submit(wrappedRunnable, resultArg)).thenReturn(expectedFutureObject); + Future result = executor.submit(runnable, resultArg); + verify(delegate).submit(wrappedRunnable, resultArg); + assertThat(result).isEqualTo(expectedFutureObject); + } + + @Test + @SuppressWarnings("unchecked") + public void submitRunnable() throws Exception { + when((Future)delegate.submit(wrappedRunnable)).thenReturn(expectedFutureObject); + Future result = executor.submit(runnable); + verify(delegate).submit(wrappedRunnable); + assertThat(result).isEqualTo(expectedFutureObject); + } + + @Test + @SuppressWarnings("unchecked") + public void invokeAll() throws Exception { + List> exectedResult = Arrays.asList(expectedFutureObject); + List> wrappedCallables = Arrays.asList(wrappedCallable); + when(delegate.invokeAll(wrappedCallables)).thenReturn(exectedResult); + List> result = executor.invokeAll(Arrays.asList(callable)); + verify(delegate).invokeAll(wrappedCallables); + assertThat(result).isEqualTo(exectedResult); + } + + @Test + @SuppressWarnings("unchecked") + public void invokeAllTimeout() throws Exception { + List> exectedResult = Arrays.asList(expectedFutureObject); + List> wrappedCallables = Arrays.asList(wrappedCallable); + when(delegate.invokeAll(wrappedCallables, 1, TimeUnit.SECONDS)).thenReturn(exectedResult); + List> result = executor.invokeAll(Arrays.asList(callable), 1, TimeUnit.SECONDS); + verify(delegate).invokeAll(wrappedCallables, 1, TimeUnit.SECONDS); + assertThat(result).isEqualTo(exectedResult); + } + + @Test + @SuppressWarnings("unchecked") + public void invokeAny() throws Exception { + List> exectedResult = Arrays.asList(expectedFutureObject); + List> wrappedCallables = Arrays.asList(wrappedCallable); + when(delegate.invokeAny(wrappedCallables)).thenReturn(exectedResult); + Object result = executor.invokeAny(Arrays.asList(callable)); + verify(delegate).invokeAny(wrappedCallables); + assertThat(result).isEqualTo(exectedResult); + } + + @Test + @SuppressWarnings("unchecked") + public void invokeAnyTimeout() throws Exception { + List> exectedResult = Arrays.asList(expectedFutureObject); + List> wrappedCallables = Arrays.asList(wrappedCallable); + when(delegate.invokeAny(wrappedCallables, 1, TimeUnit.SECONDS)).thenReturn(exectedResult); + Object result = executor.invokeAny(Arrays.asList(callable), 1, TimeUnit.SECONDS); + verify(delegate).invokeAny(wrappedCallables, 1, TimeUnit.SECONDS); + assertThat(result).isEqualTo(exectedResult); + } + + protected abstract DelegatingSecurityContextExecutorService create(); +} \ No newline at end of file diff --git a/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorTests.java b/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorTests.java new file mode 100644 index 0000000000..14209baa7b --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextExecutorTests.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import static org.mockito.Mockito.verify; + +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; + +import org.junit.Test; +import org.mockito.Mock; + +/** + * Abstract class for testing {@link DelegatingSecurityContextExecutor} which allows customization of + * how {@link DelegatingSecurityContextExecutor} and its mocks are created. + * + * @author Rob Winch + * @since 3.2 + * @see CurrentDelegatingSecurityContextExecutorTests + * @see ExplicitDelegatingSecurityContextExecutorTests + */ +public abstract class AbstractDelegatingSecurityContextExecutorTests extends AbstractDelegatingSecurityContextTestSupport { + @Mock + protected ScheduledExecutorService delegate; + + private DelegatingSecurityContextExecutor executor; + + // --- constructor --- + + @Test(expected = IllegalArgumentException.class) + public void constructorNullDelegate() { + new DelegatingSecurityContextExecutor(null); + } + + // --- execute --- + + @Test + public void execute() { + executor = create(); + executor.execute(runnable); + verify(getExecutor()).execute(wrappedRunnable); + } + + protected Executor getExecutor() { + return delegate; + } + + protected abstract DelegatingSecurityContextExecutor create(); +} diff --git a/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextScheduledExecutorServiceTests.java b/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextScheduledExecutorServiceTests.java new file mode 100644 index 0000000000..9088a50753 --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextScheduledExecutorServiceTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +/** + * Abstract class for testing {@link DelegatingSecurityContextScheduledExecutorService} which allows customization of + * how {@link DelegatingSecurityContextScheduledExecutorService} and its mocks are created. + * + * @author Rob Winch + * @since 3.2 + * @see CurrentDelegatingSecurityContextScheduledExecutorServiceTests + * @see ExplicitDelegatingSecurityContextScheduledExecutorServiceTests + */ +public abstract class AbstractDelegatingSecurityContextScheduledExecutorServiceTests extends + AbstractDelegatingSecurityContextExecutorServiceTests { + @Mock + private ScheduledFuture expectedResult; + + private DelegatingSecurityContextScheduledExecutorService executor; + + @Before + public final void setUpExecutor() { + executor = create(); + } + + @Test + @SuppressWarnings("unchecked") + public void scheduleRunnable() { + when((ScheduledFuture)delegate.schedule(wrappedRunnable, 1, TimeUnit.SECONDS)).thenReturn(expectedResult); + ScheduledFuture result = executor.schedule(runnable, 1, TimeUnit.SECONDS); + assertThat(result).isEqualTo(expectedResult); + verify(delegate).schedule(wrappedRunnable, 1, TimeUnit.SECONDS); + } + + @Test + public void scheduleCallable() { + when((ScheduledFuture)delegate.schedule(wrappedCallable, 1, TimeUnit.SECONDS)).thenReturn(expectedResult); + ScheduledFuture result = executor.schedule(callable, 1, TimeUnit.SECONDS); + assertThat(result).isEqualTo(expectedResult); + verify(delegate).schedule(wrappedCallable, 1, TimeUnit.SECONDS); + } + + @Test + @SuppressWarnings("unchecked") + public void scheduleAtFixedRate() { + when((ScheduledFuture)delegate.scheduleAtFixedRate(wrappedRunnable, 1, 2, TimeUnit.SECONDS)).thenReturn(expectedResult); + ScheduledFuture result = executor.scheduleAtFixedRate(runnable, 1, 2, TimeUnit.SECONDS); + assertThat(result).isEqualTo(expectedResult); + verify(delegate).scheduleAtFixedRate(wrappedRunnable, 1, 2, TimeUnit.SECONDS); + } + + @Test + @SuppressWarnings("unchecked") + public void scheduleWithFixedDelay() { + when((ScheduledFuture)delegate.scheduleWithFixedDelay(wrappedRunnable, 1, 2, TimeUnit.SECONDS)).thenReturn(expectedResult); + ScheduledFuture result = executor.scheduleWithFixedDelay(runnable, 1, 2, TimeUnit.SECONDS); + assertThat(result).isEqualTo(expectedResult); + verify(delegate).scheduleWithFixedDelay(wrappedRunnable, 1, 2, TimeUnit.SECONDS); + } + + @Override + protected abstract DelegatingSecurityContextScheduledExecutorService create(); +} diff --git a/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextTestSupport.java b/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextTestSupport.java new file mode 100644 index 0000000000..f0bc8f597c --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/AbstractDelegatingSecurityContextTestSupport.java @@ -0,0 +1,88 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import static org.mockito.Matchers.eq; +import static org.powermock.api.mockito.PowerMockito.doReturn; +import static org.powermock.api.mockito.PowerMockito.spy; + +import java.util.concurrent.Callable; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * Abstract base class for testing classes that extend {@link AbstractDelegatingSecurityContextSupport} + * + * @author Rob Winch + * @since 3.2 + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ DelegatingSecurityContextRunnable.class, DelegatingSecurityContextCallable.class }) +public abstract class AbstractDelegatingSecurityContextTestSupport { + @Mock + protected SecurityContext securityContext; + + @Mock + protected SecurityContext currentSecurityContext; + + @Captor + protected ArgumentCaptor securityContextCaptor; + + @Mock + protected Callable callable; + + @Mock + protected Callable wrappedCallable; + + @Mock + protected Runnable runnable; + + @Mock + protected Runnable wrappedRunnable; + + public final void explicitSecurityContextPowermockSetup() throws Exception { + spy(DelegatingSecurityContextCallable.class); + doReturn(wrappedCallable).when(DelegatingSecurityContextCallable.class, "create", eq(callable), + securityContextCaptor.capture()); + spy(DelegatingSecurityContextRunnable.class); + doReturn(wrappedRunnable).when(DelegatingSecurityContextRunnable.class, "create", eq(runnable), + securityContextCaptor.capture()); + } + + public final void currentSecurityContextPowermockSetup() throws Exception { + spy(DelegatingSecurityContextCallable.class); + doReturn(wrappedCallable).when(DelegatingSecurityContextCallable.class, "create", callable, null); + spy(DelegatingSecurityContextRunnable.class); + doReturn(wrappedRunnable).when(DelegatingSecurityContextRunnable.class, "create", runnable, null); + } + + @Before + public final void setContext() { + SecurityContextHolder.setContext(currentSecurityContext); + } + + @After + public final void clearContext() { + SecurityContextHolder.clearContext(); + } +} diff --git a/core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorServiceTests.java b/core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorServiceTests.java new file mode 100644 index 0000000000..1d6be8db07 --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorServiceTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import org.junit.Before; + +/** + * Tests using the current {@link SecurityContext} on {@link DelegatingSecurityContextExecutorService} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class CurrentDelegatingSecurityContextExecutorServiceTests extends AbstractDelegatingSecurityContextExecutorServiceTests{ + + @Before + public void setUp() throws Exception { + super.currentSecurityContextPowermockSetup(); + } + + @Override + protected DelegatingSecurityContextExecutorService create() { + return new DelegatingSecurityContextExecutorService(delegate); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorTests.java b/core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorTests.java new file mode 100644 index 0000000000..1d6f5e3045 --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextExecutorTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import org.junit.Before; + +/** + * Tests using the current {@link SecurityContext} on {@link DelegatingSecurityContextExecutor} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class CurrentDelegatingSecurityContextExecutorTests extends + AbstractDelegatingSecurityContextExecutorTests { + + @Before + public void setUp() throws Exception { + super.currentSecurityContextPowermockSetup(); + } + + @Override + protected DelegatingSecurityContextExecutor create() { + return new DelegatingSecurityContextExecutor(getExecutor()); + } +} diff --git a/core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextScheduledExecutorServiceTests.java b/core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextScheduledExecutorServiceTests.java new file mode 100644 index 0000000000..4126f94a8b --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/CurrentDelegatingSecurityContextScheduledExecutorServiceTests.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import org.junit.Before; + +/** + * Tests using the current {@link SecurityContext} on {@link DelegatingSecurityContextScheduledExecutorService} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class CurrentDelegatingSecurityContextScheduledExecutorServiceTests extends + AbstractDelegatingSecurityContextScheduledExecutorServiceTests { + + @Before + public void setUp() throws Exception { + this.currentSecurityContextPowermockSetup(); + } + + @Override + protected DelegatingSecurityContextScheduledExecutorService create() { + return new DelegatingSecurityContextScheduledExecutorService(delegate); + } + +} diff --git a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextCallableTests.java b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextCallableTests.java new file mode 100644 index 0000000000..e6d336c383 --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextCallableTests.java @@ -0,0 +1,134 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.concurrent.Callable; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.internal.stubbing.answers.Returns; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * + * @author Rob Winch + * @since 3.2 + */ +@RunWith(MockitoJUnitRunner.class) +public class DelegatingSecurityContextCallableTests { + @Mock + private Callable delegate; + @Mock + private SecurityContext securityContext; + @Mock + private Object callableResult; + + private Callable callable; + + @Before + @SuppressWarnings("serial") + public void setUp() throws Exception { + when(delegate.call()).thenAnswer(new Returns(callableResult) { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + assertThat(SecurityContextHolder.getContext()).isEqualTo(securityContext); + return super.answer(invocation); + } + }); + } + + @After + public void tearDown() { + SecurityContextHolder.clearContext(); + } + + // --- constructor --- + + @Test(expected = IllegalArgumentException.class) + public void constructorNullDelegate() { + new DelegatingSecurityContextCallable(null); + } + + @Test(expected = IllegalArgumentException.class) + public void constructorNullDelegateNonNullSecurityContext() { + new DelegatingSecurityContextCallable(null, securityContext); + } + + @Test(expected = IllegalArgumentException.class) + public void constructorNullDelegateAndSecurityContext() { + new DelegatingSecurityContextCallable(null, null); + } + + @Test(expected = IllegalArgumentException.class) + public void constructorNullSecurityContext() { + new DelegatingSecurityContextCallable(delegate, null); + } + + // --- call --- + + @Test + public void call() throws Exception { + callable = new DelegatingSecurityContextCallable(delegate, securityContext); + assertWrapped(callable.call()); + } + + @Test + public void callDefaultSecurityContext() throws Exception { + SecurityContextHolder.setContext(securityContext); + callable = new DelegatingSecurityContextCallable(delegate); + SecurityContextHolder.clearContext(); // ensure callable is what sets up the SecurityContextHolder + assertWrapped(callable.call()); + } + + // --- create --- + + @Test(expected = IllegalArgumentException.class) + public void createNullDelegate() { + DelegatingSecurityContextCallable.create(null, securityContext); + } + + @Test(expected = IllegalArgumentException.class) + public void createNullDelegateAndSecurityContext() { + DelegatingSecurityContextRunnable.create(null, null); + } + + @Test + public void createNullSecurityContext() throws Exception { + SecurityContextHolder.setContext(securityContext); + callable = DelegatingSecurityContextCallable.create(delegate, null); + SecurityContextHolder.clearContext(); // ensure callable is what sets up the SecurityContextHolder + assertWrapped(callable.call()); + } + + @Test + public void create() throws Exception { + callable = DelegatingSecurityContextCallable.create(delegate, securityContext); + assertWrapped(callable.call()); + } + + private void assertWrapped(Object actualResult) throws Exception { + assertThat(actualResult).isEqualTo(callableResult); + verify(delegate).call(); + assertThat(SecurityContextHolder.getContext()).isEqualTo(SecurityContextHolder.createEmptyContext()); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnableTests.java b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnableTests.java new file mode 100644 index 0000000000..0eb31c94d4 --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextRunnableTests.java @@ -0,0 +1,134 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * + * @author Rob Winch + * @since 3.2 + */ +@RunWith(MockitoJUnitRunner.class) +public class DelegatingSecurityContextRunnableTests { + @Mock + private Runnable delegate; + @Mock + private SecurityContext securityContext; + @Mock + private Object callableResult; + + private Runnable runnable; + + @Before + public void setUp() throws Exception { + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) throws Throwable { + assertThat(SecurityContextHolder.getContext()).isEqualTo(securityContext); + return null; + } + }) + .when(delegate).run(); + } + + @After + public void tearDown() { + SecurityContextHolder.clearContext(); + } + + // --- constructor --- + + @Test(expected = IllegalArgumentException.class) + public void constructorNullDelegate() { + new DelegatingSecurityContextRunnable(null); + } + + @Test(expected = IllegalArgumentException.class) + public void constructorNullDelegateNonNullSecurityContext() { + new DelegatingSecurityContextRunnable(null, securityContext); + } + + @Test(expected = IllegalArgumentException.class) + public void constructorNullDelegateAndSecurityContext() { + new DelegatingSecurityContextRunnable(null, null); + } + + @Test(expected = IllegalArgumentException.class) + public void constructorNullSecurityContext() { + new DelegatingSecurityContextRunnable(delegate, null); + } + + // --- run --- + + @Test + public void call() throws Exception { + runnable = new DelegatingSecurityContextRunnable(delegate, securityContext); + runnable.run(); + assertWrapped(); + } + + @Test + public void callDefaultSecurityContext() throws Exception { + SecurityContextHolder.setContext(securityContext); + runnable = new DelegatingSecurityContextRunnable(delegate); + SecurityContextHolder.clearContext(); // ensure runnable is what sets up the SecurityContextHolder + runnable.run(); + assertWrapped(); + } + + // --- create --- + + @Test(expected = IllegalArgumentException.class) + public void createNullDelegate() { + DelegatingSecurityContextRunnable.create(null, securityContext); + } + + @Test(expected = IllegalArgumentException.class) + public void createNullDelegateAndSecurityContext() { + DelegatingSecurityContextRunnable.create(null, null); + } + + @Test + public void createNullSecurityContext() { + SecurityContextHolder.setContext(securityContext); + runnable = DelegatingSecurityContextRunnable.create(delegate, null); + SecurityContextHolder.clearContext(); // ensure runnable is what sets up the SecurityContextHolder + runnable.run(); + assertWrapped(); + } + + @Test + public void create() { + runnable = DelegatingSecurityContextRunnable.create(delegate, securityContext); + runnable.run(); + assertWrapped(); + } + + private void assertWrapped() { + verify(delegate).run(); + assertThat(SecurityContextHolder.getContext()).isEqualTo(SecurityContextHolder.createEmptyContext()); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextSupportTests.java b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextSupportTests.java new file mode 100644 index 0000000000..a36ce7a8a3 --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextSupportTests.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import static org.fest.assertions.Assertions.assertThat; + +import org.junit.Test; +import org.springframework.security.core.context.SecurityContext; + +/** + * + * @author Rob Winch + * @since 3.2 + * + */ +public class DelegatingSecurityContextSupportTests extends AbstractDelegatingSecurityContextTestSupport { + private AbstractDelegatingSecurityContextSupport support; + + @Test + public void wrapCallable() throws Exception { + explicitSecurityContextPowermockSetup(); + support = new ConcreteDelegatingSecurityContextSupport(securityContext); + assertThat(support.wrap(callable)).isSameAs(wrappedCallable); + assertThat(securityContextCaptor.getValue()).isSameAs(securityContext); + } + + @Test + public void wrapCallableNullSecurityContext() throws Exception { + currentSecurityContextPowermockSetup(); + support = new ConcreteDelegatingSecurityContextSupport(null); + assertThat(support.wrap(callable)).isSameAs(wrappedCallable); + } + + @Test + public void wrapRunnable() throws Exception { + explicitSecurityContextPowermockSetup(); + support = new ConcreteDelegatingSecurityContextSupport(securityContext); + assertThat(support.wrap(runnable)).isSameAs(wrappedRunnable); + assertThat(securityContextCaptor.getValue()).isSameAs(securityContext); + } + + @Test + public void wrapRunnableNullSecurityContext() throws Exception { + currentSecurityContextPowermockSetup(); + support = new ConcreteDelegatingSecurityContextSupport(null); + assertThat(support.wrap(runnable)).isSameAs(wrappedRunnable); + } + + private static class ConcreteDelegatingSecurityContextSupport extends AbstractDelegatingSecurityContextSupport { + public ConcreteDelegatingSecurityContextSupport(SecurityContext securityContext) { + super(securityContext); + } + } +} \ No newline at end of file diff --git a/core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorServiceTests.java b/core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorServiceTests.java new file mode 100644 index 0000000000..1e536e63f8 --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorServiceTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import org.junit.Before; + +/** + * Tests Explicitly specifying the {@link SecurityContext} on {@link DelegatingSecurityContextExecutorService} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class ExplicitDelegatingSecurityContextExecutorServiceTests extends AbstractDelegatingSecurityContextExecutorServiceTests{ + + @Before + public void setUp() throws Exception { + super.explicitSecurityContextPowermockSetup(); + } + + @Override + protected DelegatingSecurityContextExecutorService create() { + return new DelegatingSecurityContextExecutorService(delegate,securityContext); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorTests.java b/core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorTests.java new file mode 100644 index 0000000000..ba3a6891a9 --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextExecutorTests.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import org.junit.Before; +import org.springframework.security.core.context.SecurityContext; + +/** + * Tests Explicitly specifying the {@link SecurityContext} on {@link DelegatingSecurityContextExecutor} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class ExplicitDelegatingSecurityContextExecutorTests extends + AbstractDelegatingSecurityContextExecutorTests { + + @Before + public void setUp() throws Exception { + super.explicitSecurityContextPowermockSetup(); + } + + @Override + protected DelegatingSecurityContextExecutor create() { + return new DelegatingSecurityContextExecutor(getExecutor(), securityContext); + } +} diff --git a/core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextScheduledExecutorServiceTests.java b/core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextScheduledExecutorServiceTests.java new file mode 100644 index 0000000000..c4cfff8edf --- /dev/null +++ b/core/src/test/java/org/springframework/security/concurrent/ExplicitDelegatingSecurityContextScheduledExecutorServiceTests.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.concurrent; + +import org.junit.Before; +import org.springframework.security.core.context.SecurityContext; + +/** + * Tests Explicitly specifying the {@link SecurityContext} on {@link DelegatingSecurityContextScheduledExecutorService} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class ExplicitDelegatingSecurityContextScheduledExecutorServiceTests extends + AbstractDelegatingSecurityContextScheduledExecutorServiceTests { + + @Before + public void setUp() throws Exception { + this.explicitSecurityContextPowermockSetup(); + } + + @Override + protected DelegatingSecurityContextScheduledExecutorService create() { + return new DelegatingSecurityContextScheduledExecutorService(delegate, securityContext); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/springframework/security/scheduling/AbstractSecurityContextSchedulingTaskExecutorTests.java b/core/src/test/java/org/springframework/security/scheduling/AbstractSecurityContextSchedulingTaskExecutorTests.java new file mode 100644 index 0000000000..5e232ca586 --- /dev/null +++ b/core/src/test/java/org/springframework/security/scheduling/AbstractSecurityContextSchedulingTaskExecutorTests.java @@ -0,0 +1,39 @@ +package org.springframework.security.scheduling; + +import static org.mockito.Mockito.verify; + +import org.junit.Test; +import org.mockito.Mock; +import org.springframework.scheduling.SchedulingTaskExecutor; +import org.springframework.security.task.AbstractDelegatingSecurityContextAsyncTaskExecutorTests; + +/** + * Abstract class for testing {@link DelegatingSecurityContextSchedulingTaskExecutor} which allows customization of + * how {@link DelegatingSecurityContextSchedulingTaskExecutor} and its mocks are created. + * + * @author Rob Winch + * @since 3.2 + * @see CurrentSecurityContextSchedulingTaskExecutorTests + * @see ExplicitSecurityContextSchedulingTaskExecutorTests + */ +public abstract class AbstractSecurityContextSchedulingTaskExecutorTests extends + AbstractDelegatingSecurityContextAsyncTaskExecutorTests { + + @Mock + protected SchedulingTaskExecutor taskExecutorDelegate; + + private DelegatingSecurityContextSchedulingTaskExecutor executor; + + @Test + public void prefersShortLivedTasks() { + executor = create(); + executor.prefersShortLivedTasks(); + verify(taskExecutorDelegate).prefersShortLivedTasks(); + } + + protected SchedulingTaskExecutor getExecutor() { + return taskExecutorDelegate; + } + + protected abstract DelegatingSecurityContextSchedulingTaskExecutor create(); +} diff --git a/core/src/test/java/org/springframework/security/scheduling/CurrentSecurityContextSchedulingTaskExecutorTests.java b/core/src/test/java/org/springframework/security/scheduling/CurrentSecurityContextSchedulingTaskExecutorTests.java new file mode 100644 index 0000000000..5a908464b0 --- /dev/null +++ b/core/src/test/java/org/springframework/security/scheduling/CurrentSecurityContextSchedulingTaskExecutorTests.java @@ -0,0 +1,23 @@ +package org.springframework.security.scheduling; + +import org.junit.Before; +import org.springframework.security.core.context.SecurityContext; + +/** + * Tests using the current {@link SecurityContext} on {@link DelegatingSecurityContextSchedulingTaskExecutor} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class CurrentSecurityContextSchedulingTaskExecutorTests extends AbstractSecurityContextSchedulingTaskExecutorTests { + + @Before + public void setUp() throws Exception { + currentSecurityContextPowermockSetup(); + } + + protected DelegatingSecurityContextSchedulingTaskExecutor create() { + return new DelegatingSecurityContextSchedulingTaskExecutor(taskExecutorDelegate); + } +} diff --git a/core/src/test/java/org/springframework/security/scheduling/ExplicitSecurityContextSchedulingTaskExecutorTests.java b/core/src/test/java/org/springframework/security/scheduling/ExplicitSecurityContextSchedulingTaskExecutorTests.java new file mode 100644 index 0000000000..a9b3b5ca15 --- /dev/null +++ b/core/src/test/java/org/springframework/security/scheduling/ExplicitSecurityContextSchedulingTaskExecutorTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.scheduling; + +import org.junit.Before; +import org.springframework.security.core.context.SecurityContext; + +/** + * Tests Explicitly specifying the {@link SecurityContext} on {@link DelegatingSecurityContextSchedulingTaskExecutor} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class ExplicitSecurityContextSchedulingTaskExecutorTests extends AbstractSecurityContextSchedulingTaskExecutorTests { + + @Before + public void setUp() throws Exception { + explicitSecurityContextPowermockSetup(); + } + + protected DelegatingSecurityContextSchedulingTaskExecutor create() { + return new DelegatingSecurityContextSchedulingTaskExecutor(taskExecutorDelegate, securityContext); + } +} diff --git a/core/src/test/java/org/springframework/security/task/AbstractDelegatingSecurityContextAsyncTaskExecutorTests.java b/core/src/test/java/org/springframework/security/task/AbstractDelegatingSecurityContextAsyncTaskExecutorTests.java new file mode 100644 index 0000000000..a5c2cca828 --- /dev/null +++ b/core/src/test/java/org/springframework/security/task/AbstractDelegatingSecurityContextAsyncTaskExecutorTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.task; + +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.security.concurrent.AbstractDelegatingSecurityContextExecutorTests; + +/** + * Abstract class for testing {@link DelegatingSecurityContextAsyncTaskExecutor} which allows customization of + * how {@link DelegatingSecurityContextAsyncTaskExecutor} and its mocks are created. + * + * @author Rob Winch + * @since 3.2 + * @see CurrentDelegatingSecurityContextAsyncTaskExecutorTests + * @see ExplicitDelegatingSecurityContextAsyncTaskExecutorTests + */ +public abstract class AbstractDelegatingSecurityContextAsyncTaskExecutorTests extends AbstractDelegatingSecurityContextExecutorTests { + @Mock + protected AsyncTaskExecutor taskExecutorDelegate; + + private DelegatingSecurityContextAsyncTaskExecutor executor; + + @Before + public final void setUpExecutor() { + executor = create(); + } + + @Test + public void executeStartTimeout() { + executor.execute(runnable, 1); + verify(getExecutor()).execute(wrappedRunnable, 1); + } + + @Test + public void submit() { + executor.submit(runnable); + verify(getExecutor()).submit(wrappedRunnable); + } + + @Test + public void submitCallable() { + executor.submit(callable); + verify(getExecutor()).submit(wrappedCallable); + } + + protected AsyncTaskExecutor getExecutor() { + return taskExecutorDelegate; + } + + protected abstract DelegatingSecurityContextAsyncTaskExecutor create(); +} diff --git a/core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextAsyncTaskExecutorTests.java b/core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextAsyncTaskExecutorTests.java new file mode 100644 index 0000000000..c98192f8c4 --- /dev/null +++ b/core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextAsyncTaskExecutorTests.java @@ -0,0 +1,26 @@ +package org.springframework.security.task; + +import org.junit.Before; + + +/** + * Tests using the current {@link SecurityContext} on {@link DelegatingSecurityContextAsyncTaskExecutor} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class CurrentDelegatingSecurityContextAsyncTaskExecutorTests extends + AbstractDelegatingSecurityContextAsyncTaskExecutorTests { + + @Before + public void setUp() throws Exception { + currentSecurityContextPowermockSetup(); + } + + @Override + protected DelegatingSecurityContextAsyncTaskExecutor create() { + return new DelegatingSecurityContextAsyncTaskExecutor(taskExecutorDelegate); + } + +} diff --git a/core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextTaskExecutorTests.java b/core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextTaskExecutorTests.java new file mode 100644 index 0000000000..4683f79226 --- /dev/null +++ b/core/src/test/java/org/springframework/security/task/CurrentDelegatingSecurityContextTaskExecutorTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.task; + +import java.util.concurrent.Executor; + +import org.junit.Before; +import org.mockito.Mock; +import org.springframework.core.task.TaskExecutor; +import org.springframework.security.concurrent.DelegatingSecurityContextExecutor; +import org.springframework.security.concurrent.AbstractDelegatingSecurityContextExecutorTests; + + +/** + * Tests using the current {@link SecurityContext} on {@link DelegatingSecurityContextExecutor} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class CurrentDelegatingSecurityContextTaskExecutorTests extends AbstractDelegatingSecurityContextExecutorTests { + @Mock + private TaskExecutor taskExecutorDelegate; + + @Before + public void setUp() throws Exception { + currentSecurityContextPowermockSetup(); + } + + protected Executor getExecutor() { + return taskExecutorDelegate; + } + + protected DelegatingSecurityContextExecutor create() { + return new DelegatingSecurityContextTaskExecutor(taskExecutorDelegate); + } +} diff --git a/core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextAsyncTaskExecutorTests.java b/core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextAsyncTaskExecutorTests.java new file mode 100644 index 0000000000..47fbd88396 --- /dev/null +++ b/core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextAsyncTaskExecutorTests.java @@ -0,0 +1,26 @@ +package org.springframework.security.task; + +import org.junit.Before; + + +/** + * Tests using an explicit {@link SecurityContext} on {@link DelegatingSecurityContextAsyncTaskExecutor} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class ExplicitDelegatingSecurityContextAsyncTaskExecutorTests extends + AbstractDelegatingSecurityContextAsyncTaskExecutorTests { + + @Before + public void setUp() throws Exception { + explicitSecurityContextPowermockSetup(); + } + + @Override + protected DelegatingSecurityContextAsyncTaskExecutor create() { + return new DelegatingSecurityContextAsyncTaskExecutor(taskExecutorDelegate, securityContext); + } + +} diff --git a/core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextTaskExecutorTests.java b/core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextTaskExecutorTests.java new file mode 100644 index 0000000000..d60347c3c4 --- /dev/null +++ b/core/src/test/java/org/springframework/security/task/ExplicitDelegatingSecurityContextTaskExecutorTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * 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 org.springframework.security.task; + +import java.util.concurrent.Executor; + +import org.junit.Before; +import org.mockito.Mock; +import org.springframework.core.task.TaskExecutor; +import org.springframework.security.concurrent.DelegatingSecurityContextExecutor; +import org.springframework.security.concurrent.AbstractDelegatingSecurityContextExecutorTests; + + +/** + * Tests using the an explicit {@link SecurityContext} on {@link DelegatingSecurityContextExecutor} + * + * @author Rob Winch + * @since 3.2 + * + */ +public class ExplicitDelegatingSecurityContextTaskExecutorTests extends AbstractDelegatingSecurityContextExecutorTests { + @Mock + private TaskExecutor taskExecutorDelegate; + + + @Before + public void setUp() throws Exception { + explicitSecurityContextPowermockSetup(); + } + + protected Executor getExecutor() { + return taskExecutorDelegate; + } + + protected DelegatingSecurityContextExecutor create() { + return new DelegatingSecurityContextTaskExecutor(taskExecutorDelegate, securityContext); + } +}