Merge branch '2.1.x'
Closes gh-18021
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.security.servlet;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -43,9 +44,7 @@ public abstract class ApplicationContextRequestMatcher<C> implements RequestMatc
|
||||
|
||||
private final Class<? extends C> contextClass;
|
||||
|
||||
private volatile Supplier<C> context;
|
||||
|
||||
private final Object contextLock = new Object();
|
||||
private final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
|
||||
public ApplicationContextRequestMatcher(Class<? extends C> contextClass) {
|
||||
Assert.notNull(contextClass, "Context class must not be null");
|
||||
@@ -54,7 +53,48 @@ public abstract class ApplicationContextRequestMatcher<C> implements RequestMatc
|
||||
|
||||
@Override
|
||||
public final boolean matches(HttpServletRequest request) {
|
||||
return matches(request, getContext(request));
|
||||
WebApplicationContext webApplicationContext = WebApplicationContextUtils
|
||||
.getRequiredWebApplicationContext(request.getServletContext());
|
||||
if (ignoreApplicationContext(webApplicationContext)) {
|
||||
return false;
|
||||
}
|
||||
Supplier<C> context = () -> getContext(webApplicationContext);
|
||||
if (this.initialized.compareAndSet(false, true)) {
|
||||
initialized(context);
|
||||
}
|
||||
return matches(request, context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private C getContext(WebApplicationContext webApplicationContext) {
|
||||
if (this.contextClass.isInstance(webApplicationContext)) {
|
||||
return (C) webApplicationContext;
|
||||
}
|
||||
return webApplicationContext.getBean(this.contextClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the {@link WebApplicationContext} should be ignored and not used for
|
||||
* matching. If this method returns {@code true} then the context will not be used and
|
||||
* the {@link #matches(HttpServletRequest) matches} method will return {@code false}.
|
||||
* @param webApplicationContext the candidate web application context
|
||||
* @return if the application context should be ignored
|
||||
* @since 2.1.8
|
||||
*/
|
||||
protected boolean ignoreApplicationContext(WebApplicationContext webApplicationContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that can be implemented by subclasses that wish to initialize items the
|
||||
* first time that the matcher is called. This method will be called only once and
|
||||
* only if {@link #ignoreApplicationContext(WebApplicationContext)} returns
|
||||
* {@code true}. Note that the supplied context will be based on the
|
||||
* <strong>first</strong> request sent to the matcher.
|
||||
* @param context a supplier for the initialized context (may throw an exception)
|
||||
* @see #ignoreApplicationContext(WebApplicationContext)
|
||||
*/
|
||||
protected void initialized(Supplier<C> context) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,34 +105,4 @@ public abstract class ApplicationContextRequestMatcher<C> implements RequestMatc
|
||||
*/
|
||||
protected abstract boolean matches(HttpServletRequest request, Supplier<C> context);
|
||||
|
||||
private Supplier<C> getContext(HttpServletRequest request) {
|
||||
if (this.context == null) {
|
||||
synchronized (this.contextLock) {
|
||||
if (this.context == null) {
|
||||
Supplier<C> createdContext = createContext(request);
|
||||
initialized(createdContext);
|
||||
this.context = createdContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once the context has been initialized.
|
||||
* @param context a supplier for the initialized context (may throw an exception)
|
||||
*/
|
||||
protected void initialized(Supplier<C> context) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Supplier<C> createContext(HttpServletRequest request) {
|
||||
WebApplicationContext context = WebApplicationContextUtils
|
||||
.getRequiredWebApplicationContext(request.getServletContext());
|
||||
if (this.contextClass.isInstance(context)) {
|
||||
return () -> (C) context;
|
||||
}
|
||||
return () -> context.getBean(this.contextClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.boot.web.context;
|
||||
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by {@link ApplicationContext application contexts} that
|
||||
@@ -44,4 +45,17 @@ public interface WebServerApplicationContext extends ApplicationContext {
|
||||
*/
|
||||
String getServerNamespace();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the specified context is a
|
||||
* {@link WebServerApplicationContext} with a matching server namespace.
|
||||
* @param context the context to check
|
||||
* @param serverNamespace the server namespace to match against
|
||||
* @return {@code true} if the server namespace of the context matches
|
||||
* @since 2.1.8
|
||||
*/
|
||||
static boolean hasServerNamespace(ApplicationContext context, String serverNamespace) {
|
||||
return (context instanceof WebServerApplicationContext) && ObjectUtils
|
||||
.nullSafeEquals(((WebServerApplicationContext) context).getServerNamespace(), serverNamespace);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,6 +69,42 @@ class ApplicationContextRequestMatcherTests {
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(supplier::get);
|
||||
}
|
||||
|
||||
@Test // gh-18012
|
||||
void machesWhenCalledWithDifferentApplicationContextDoesNotCache() {
|
||||
StaticWebApplicationContext context1 = createWebApplicationContext();
|
||||
StaticWebApplicationContext context2 = createWebApplicationContext();
|
||||
TestApplicationContextRequestMatcher<ApplicationContext> matcher = new TestApplicationContextRequestMatcher<>(
|
||||
ApplicationContext.class);
|
||||
assertThat(matcher.callMatchesAndReturnProvidedContext(context1).get()).isEqualTo(context1);
|
||||
assertThat(matcher.callMatchesAndReturnProvidedContext(context2).get()).isEqualTo(context2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void initializeAndMatchesAreNotCalledIfContextIsIgnored() {
|
||||
StaticWebApplicationContext context = createWebApplicationContext();
|
||||
TestApplicationContextRequestMatcher<ApplicationContext> matcher = new TestApplicationContextRequestMatcher<ApplicationContext>(
|
||||
ApplicationContext.class) {
|
||||
|
||||
@Override
|
||||
protected boolean ignoreApplicationContext(WebApplicationContext webApplicationContext) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialized(Supplier<ApplicationContext> context) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request, Supplier<ApplicationContext> context) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
};
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(context.getServletContext());
|
||||
assertThat(matcher.matches(request)).isFalse();
|
||||
}
|
||||
|
||||
private StaticWebApplicationContext createWebApplicationContext() {
|
||||
StaticWebApplicationContext context = new StaticWebApplicationContext();
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.boot.web.context;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebServerApplicationContext}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class WebServerApplicationContextTests {
|
||||
|
||||
@Test
|
||||
public void hasServerNamespaceWhenContextIsNotWebServerApplicationContextReturnsFalse() {
|
||||
ApplicationContext context = mock(ApplicationContext.class);
|
||||
assertThat(WebServerApplicationContext.hasServerNamespace(context, "test")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasServerNamespaceWhenContextIsWebServerApplicationContextAndNamespaceDoesNotMatchReturnsFalse() {
|
||||
ApplicationContext context = mock(WebServerApplicationContext.class);
|
||||
assertThat(WebServerApplicationContext.hasServerNamespace(context, "test")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasServerNamespaceWhenContextIsWebServerApplicationContextAndNamespaceMatchesReturnsTrue() {
|
||||
WebServerApplicationContext context = mock(WebServerApplicationContext.class);
|
||||
given(context.getServerNamespace()).willReturn("test");
|
||||
assertThat(WebServerApplicationContext.hasServerNamespace(context, "test")).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user