WebSession creation does not block

Closes gh-24027
This commit is contained in:
Rossen Stoyanchev 2019-11-26 10:44:09 +00:00
parent ddb38eefee
commit 70a3dbff24
3 changed files with 31 additions and 11 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.IdGenerator; import org.springframework.util.IdGenerator;
@ -111,9 +112,14 @@ public class InMemoryWebSessionStore implements WebSessionStore {
@Override @Override
public Mono<WebSession> createWebSession() { public Mono<WebSession> createWebSession() {
// Opportunity to clean expired sessions
Instant now = this.clock.instant(); Instant now = this.clock.instant();
this.expiredSessionChecker.checkIfNecessary(now); this.expiredSessionChecker.checkIfNecessary(now);
return Mono.fromSupplier(() -> new InMemoryWebSession(now));
return Mono.fromSupplier(() -> new InMemoryWebSession(now))
.subscribeOn(Schedulers.boundedElastic())
.cast(WebSession.class);
} }
@Override @Override

View File

@ -22,7 +22,10 @@ import java.time.Instant;
import java.util.Map; import java.util.Map;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.DirectFieldAccessor;
import org.springframework.web.server.WebSession; import org.springframework.web.server.WebSession;
@ -56,6 +59,14 @@ public class InMemoryWebSessionStoreTests {
assertThat(session.isStarted()).isTrue(); assertThat(session.isStarted()).isTrue();
} }
@Disabled // TODO: remove if/when Blockhound is enabled
@Test // gh-24027
public void createSessionDoesNotBlock() {
Mono.defer(() -> this.store.createWebSession())
.subscribeOn(Schedulers.parallel())
.block();
}
@Test @Test
public void retrieveExpiredSession() { public void retrieveExpiredSession() {
WebSession session = this.store.createWebSession().block(); WebSession session = this.store.createWebSession().block();

View File

@ -64,6 +64,9 @@ import static org.mockito.Mockito.mock;
*/ */
public class ModelInitializerTests { public class ModelInitializerTests {
private static final Duration TIMEOUT = Duration.ofMillis(5000);
private ModelInitializer modelInitializer; private ModelInitializer modelInitializer;
private final ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/path")); private final ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/path"));
@ -93,7 +96,7 @@ public class ModelInitializerTests {
Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod(); Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod();
HandlerMethod handlerMethod = new HandlerMethod(controller, method); HandlerMethod handlerMethod = new HandlerMethod(controller, method);
this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(Duration.ofMillis(5000)); this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(TIMEOUT);
WebExchangeDataBinder binder = context.createDataBinder(this.exchange, "name"); WebExchangeDataBinder binder = context.createDataBinder(this.exchange, "name");
assertThat(binder.getValidators()).isEqualTo(Collections.singletonList(validator)); assertThat(binder.getValidators()).isEqualTo(Collections.singletonList(validator));
@ -107,7 +110,7 @@ public class ModelInitializerTests {
Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod(); Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod();
HandlerMethod handlerMethod = new HandlerMethod(controller, method); HandlerMethod handlerMethod = new HandlerMethod(controller, method);
this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(Duration.ofMillis(5000)); this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(TIMEOUT);
Map<String, Object> model = context.getModel().asMap(); Map<String, Object> model = context.getModel().asMap();
assertThat(model.size()).isEqualTo(5); assertThat(model.size()).isEqualTo(5);
@ -116,7 +119,7 @@ public class ModelInitializerTests {
assertThat(((TestBean) value).getName()).isEqualTo("Bean"); assertThat(((TestBean) value).getName()).isEqualTo("Bean");
value = model.get("monoBean"); value = model.get("monoBean");
assertThat(((Mono<TestBean>) value).block(Duration.ofMillis(5000)).getName()).isEqualTo("Mono Bean"); assertThat(((Mono<TestBean>) value).block(TIMEOUT).getName()).isEqualTo("Mono Bean");
value = model.get("singleBean"); value = model.get("singleBean");
assertThat(((Single<TestBean>) value).toBlocking().value().getName()).isEqualTo("Single Bean"); assertThat(((Single<TestBean>) value).toBlocking().value().getName()).isEqualTo("Single Bean");
@ -135,7 +138,7 @@ public class ModelInitializerTests {
Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod(); Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod();
HandlerMethod handlerMethod = new HandlerMethod(controller, method); HandlerMethod handlerMethod = new HandlerMethod(controller, method);
this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(Duration.ofMillis(5000)); this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(TIMEOUT);
WebSession session = this.exchange.getSession().block(Duration.ZERO); WebSession session = this.exchange.getSession().block(Duration.ZERO);
assertThat(session).isNotNull(); assertThat(session).isNotNull();
@ -148,7 +151,7 @@ public class ModelInitializerTests {
@Test @Test
public void retrieveModelAttributeFromSession() { public void retrieveModelAttributeFromSession() {
WebSession session = this.exchange.getSession().block(Duration.ZERO); WebSession session = this.exchange.getSession().block(TIMEOUT);
assertThat(session).isNotNull(); assertThat(session).isNotNull();
TestBean testBean = new TestBean("Session Bean"); TestBean testBean = new TestBean("Session Bean");
@ -159,7 +162,7 @@ public class ModelInitializerTests {
Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod(); Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod();
HandlerMethod handlerMethod = new HandlerMethod(controller, method); HandlerMethod handlerMethod = new HandlerMethod(controller, method);
this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(Duration.ofMillis(5000)); this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(TIMEOUT);
context.saveModel(); context.saveModel();
assertThat(session.getAttributes().size()).isEqualTo(1); assertThat(session.getAttributes().size()).isEqualTo(1);
@ -174,13 +177,13 @@ public class ModelInitializerTests {
Method method = ResolvableMethod.on(TestController.class).annotPresent(PostMapping.class).resolveMethod(); Method method = ResolvableMethod.on(TestController.class).annotPresent(PostMapping.class).resolveMethod();
HandlerMethod handlerMethod = new HandlerMethod(controller, method); HandlerMethod handlerMethod = new HandlerMethod(controller, method);
assertThatIllegalArgumentException().isThrownBy(() -> assertThatIllegalArgumentException().isThrownBy(() ->
this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(Duration.ofMillis(5000))) this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(TIMEOUT))
.withMessage("Required attribute 'missing-bean' is missing."); .withMessage("Required attribute 'missing-bean' is missing.");
} }
@Test @Test
public void clearModelAttributeFromSession() { public void clearModelAttributeFromSession() {
WebSession session = this.exchange.getSession().block(Duration.ZERO); WebSession session = this.exchange.getSession().block(TIMEOUT);
assertThat(session).isNotNull(); assertThat(session).isNotNull();
TestBean testBean = new TestBean("Session Bean"); TestBean testBean = new TestBean("Session Bean");
@ -191,7 +194,7 @@ public class ModelInitializerTests {
Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod(); Method method = ResolvableMethod.on(TestController.class).annotPresent(GetMapping.class).resolveMethod();
HandlerMethod handlerMethod = new HandlerMethod(controller, method); HandlerMethod handlerMethod = new HandlerMethod(controller, method);
this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(Duration.ofMillis(5000)); this.modelInitializer.initModel(handlerMethod, context, this.exchange).block(TIMEOUT);
context.getSessionStatus().setComplete(); context.getSessionStatus().setComplete();
context.saveModel(); context.saveModel();