Skip to content

Commit 9dd6c96

Browse files
committed
Provide an Actuator endpoint for non-indexed session repositories
WIP Closes gh-10827
1 parent c6872e5 commit 9dd6c96

File tree

10 files changed

+351
-78
lines changed

10 files changed

+351
-78
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/session/SessionsEndpointAutoConfiguration.java

+33-6
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,24 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.session;
1818

19+
import org.springframework.beans.factory.ObjectProvider;
1920
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
21+
import org.springframework.boot.actuate.session.ReactiveSessionsEndpoint;
2022
import org.springframework.boot.actuate.session.SessionsEndpoint;
2123
import org.springframework.boot.autoconfigure.AutoConfiguration;
2224
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2325
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2426
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2527
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
29+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
2630
import org.springframework.boot.autoconfigure.session.SessionAutoConfiguration;
2731
import org.springframework.context.annotation.Bean;
32+
import org.springframework.context.annotation.Configuration;
2833
import org.springframework.session.FindByIndexNameSessionRepository;
34+
import org.springframework.session.ReactiveSessionRepository;
2935
import org.springframework.session.Session;
36+
import org.springframework.session.SessionRepository;
3037

3138
/**
3239
* {@link EnableAutoConfiguration Auto-configuration} for {@link SessionsEndpoint}.
@@ -35,15 +42,35 @@
3542
* @since 2.0.0
3643
*/
3744
@AutoConfiguration(after = SessionAutoConfiguration.class)
38-
@ConditionalOnClass(FindByIndexNameSessionRepository.class)
45+
@ConditionalOnClass(Session.class)
3946
@ConditionalOnAvailableEndpoint(endpoint = SessionsEndpoint.class)
4047
public class SessionsEndpointAutoConfiguration {
4148

42-
@Bean
43-
@ConditionalOnBean(FindByIndexNameSessionRepository.class)
44-
@ConditionalOnMissingBean
45-
public SessionsEndpoint sessionEndpoint(FindByIndexNameSessionRepository<? extends Session> sessionRepository) {
46-
return new SessionsEndpoint(sessionRepository);
49+
@Configuration(proxyBeanMethods = false)
50+
@ConditionalOnWebApplication(type = Type.SERVLET)
51+
@ConditionalOnBean(SessionRepository.class)
52+
static class ServletSessionEndpointConfiguration {
53+
54+
@Bean
55+
@ConditionalOnMissingBean
56+
SessionsEndpoint sessionEndpoint(SessionRepository<? extends Session> sessionRepository,
57+
ObjectProvider<FindByIndexNameSessionRepository<? extends Session>> indexedSessionRepository) {
58+
return new SessionsEndpoint(sessionRepository, indexedSessionRepository.getIfAvailable());
59+
}
60+
61+
}
62+
63+
@Configuration(proxyBeanMethods = false)
64+
@ConditionalOnWebApplication(type = Type.REACTIVE)
65+
@ConditionalOnBean(ReactiveSessionRepository.class)
66+
static class ReactiveSessionEndpointConfiguration {
67+
68+
@Bean
69+
@ConditionalOnMissingBean
70+
ReactiveSessionsEndpoint sessionsEndpoint(ReactiveSessionRepository<? extends Session> sessionRepository) {
71+
return new ReactiveSessionsEndpoint(sessionRepository);
72+
}
73+
4774
}
4875

4976
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/SessionsEndpointDocumentationTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ static class TestConfiguration {
122122

123123
@Bean
124124
SessionsEndpoint endpoint(FindByIndexNameSessionRepository<?> sessionRepository) {
125-
return new SessionsEndpoint(sessionRepository);
125+
return new SessionsEndpoint(sessionRepository, sessionRepository);
126126
}
127127

128128
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/session/SessionsEndpointAutoConfigurationTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
2020

2121
import org.springframework.boot.actuate.session.SessionsEndpoint;
2222
import org.springframework.boot.autoconfigure.AutoConfigurations;
23-
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
23+
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
2424
import org.springframework.context.annotation.Bean;
2525
import org.springframework.context.annotation.Configuration;
2626
import org.springframework.session.FindByIndexNameSessionRepository;
@@ -35,7 +35,7 @@
3535
*/
3636
class SessionsEndpointAutoConfigurationTests {
3737

38-
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
38+
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
3939
.withConfiguration(AutoConfigurations.of(SessionsEndpointAutoConfiguration.class))
4040
.withUserConfiguration(SessionConfiguration.class);
4141

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.session;
18+
19+
import reactor.core.publisher.Mono;
20+
21+
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
22+
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
23+
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
24+
import org.springframework.boot.actuate.endpoint.annotation.Selector;
25+
import org.springframework.session.ReactiveSessionRepository;
26+
import org.springframework.session.Session;
27+
28+
/**
29+
* {@link Endpoint @Endpoint} to expose a user's {@link Session}s.
30+
*
31+
* @author Vedran Pavic
32+
* @since 3.0.0
33+
*/
34+
@Endpoint(id = "sessions")
35+
public class ReactiveSessionsEndpoint {
36+
37+
private final ReactiveSessionRepository<? extends Session> sessionRepository;
38+
39+
/**
40+
* Create a new {@link ReactiveSessionsEndpoint} instance.
41+
* @param sessionRepository the session repository
42+
*/
43+
public ReactiveSessionsEndpoint(ReactiveSessionRepository<? extends Session> sessionRepository) {
44+
this.sessionRepository = sessionRepository;
45+
}
46+
47+
@ReadOperation
48+
public Mono<SessionDescriptor> getSession(@Selector String sessionId) {
49+
return this.sessionRepository.findById(sessionId).map(SessionDescriptor::new);
50+
}
51+
52+
@DeleteOperation
53+
public Mono<Void> deleteSession(@Selector String sessionId) {
54+
return this.sessionRepository.deleteById(sessionId);
55+
}
56+
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.session;
18+
19+
import java.time.Instant;
20+
import java.util.Set;
21+
22+
import org.springframework.session.Session;
23+
24+
/**
25+
* A description of user's {@link Session session}. Primarily intended for serialization
26+
* to JSON.
27+
*
28+
* @author Vedran Pavic
29+
* @since 3.0.0
30+
*/
31+
public final class SessionDescriptor {
32+
33+
private final String id;
34+
35+
private final Set<String> attributeNames;
36+
37+
private final Instant creationTime;
38+
39+
private final Instant lastAccessedTime;
40+
41+
private final long maxInactiveInterval;
42+
43+
private final boolean expired;
44+
45+
public SessionDescriptor(Session session) {
46+
this.id = session.getId();
47+
this.attributeNames = session.getAttributeNames();
48+
this.creationTime = session.getCreationTime();
49+
this.lastAccessedTime = session.getLastAccessedTime();
50+
this.maxInactiveInterval = session.getMaxInactiveInterval().getSeconds();
51+
this.expired = session.isExpired();
52+
}
53+
54+
public String getId() {
55+
return this.id;
56+
}
57+
58+
public Set<String> getAttributeNames() {
59+
return this.attributeNames;
60+
}
61+
62+
public Instant getCreationTime() {
63+
return this.creationTime;
64+
}
65+
66+
public Instant getLastAccessedTime() {
67+
return this.lastAccessedTime;
68+
}
69+
70+
public long getMaxInactiveInterval() {
71+
return this.maxInactiveInterval;
72+
}
73+
74+
public boolean isExpired() {
75+
return this.expired;
76+
}
77+
78+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,10 +16,8 @@
1616

1717
package org.springframework.boot.actuate.session;
1818

19-
import java.time.Instant;
2019
import java.util.List;
2120
import java.util.Map;
22-
import java.util.Set;
2321
import java.util.stream.Collectors;
2422

2523
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
@@ -28,6 +26,7 @@
2826
import org.springframework.boot.actuate.endpoint.annotation.Selector;
2927
import org.springframework.session.FindByIndexNameSessionRepository;
3028
import org.springframework.session.Session;
29+
import org.springframework.session.SessionRepository;
3130

3231
/**
3332
* {@link Endpoint @Endpoint} to expose a user's {@link Session}s.
@@ -38,19 +37,27 @@
3837
@Endpoint(id = "sessions")
3938
public class SessionsEndpoint {
4039

41-
private final FindByIndexNameSessionRepository<? extends Session> sessionRepository;
40+
private final SessionRepository<? extends Session> sessionRepository;
41+
42+
private final FindByIndexNameSessionRepository<? extends Session> indexedSessionRepository;
4243

4344
/**
4445
* Create a new {@link SessionsEndpoint} instance.
4546
* @param sessionRepository the session repository
47+
* @param indexedSessionRepository the indexed session repository
4648
*/
47-
public SessionsEndpoint(FindByIndexNameSessionRepository<? extends Session> sessionRepository) {
49+
public SessionsEndpoint(SessionRepository<? extends Session> sessionRepository,
50+
FindByIndexNameSessionRepository<? extends Session> indexedSessionRepository) {
4851
this.sessionRepository = sessionRepository;
52+
this.indexedSessionRepository = indexedSessionRepository;
4953
}
5054

5155
@ReadOperation
5256
public SessionsReport sessionsForUsername(String username) {
53-
Map<String, ? extends Session> sessions = this.sessionRepository.findByPrincipalName(username);
57+
if (this.indexedSessionRepository == null) {
58+
return null;
59+
}
60+
Map<String, ? extends Session> sessions = this.indexedSessionRepository.findByPrincipalName(username);
5461
return new SessionsReport(sessions);
5562
}
5663

@@ -86,57 +93,4 @@ public List<SessionDescriptor> getSessions() {
8693

8794
}
8895

89-
/**
90-
* A description of user's {@link Session session}. Primarily intended for
91-
* serialization to JSON.
92-
*/
93-
public static final class SessionDescriptor {
94-
95-
private final String id;
96-
97-
private final Set<String> attributeNames;
98-
99-
private final Instant creationTime;
100-
101-
private final Instant lastAccessedTime;
102-
103-
private final long maxInactiveInterval;
104-
105-
private final boolean expired;
106-
107-
public SessionDescriptor(Session session) {
108-
this.id = session.getId();
109-
this.attributeNames = session.getAttributeNames();
110-
this.creationTime = session.getCreationTime();
111-
this.lastAccessedTime = session.getLastAccessedTime();
112-
this.maxInactiveInterval = session.getMaxInactiveInterval().getSeconds();
113-
this.expired = session.isExpired();
114-
}
115-
116-
public String getId() {
117-
return this.id;
118-
}
119-
120-
public Set<String> getAttributeNames() {
121-
return this.attributeNames;
122-
}
123-
124-
public Instant getCreationTime() {
125-
return this.creationTime;
126-
}
127-
128-
public Instant getLastAccessedTime() {
129-
return this.lastAccessedTime;
130-
}
131-
132-
public long getMaxInactiveInterval() {
133-
return this.maxInactiveInterval;
134-
}
135-
136-
public boolean isExpired() {
137-
return this.expired;
138-
}
139-
140-
}
141-
14296
}

0 commit comments

Comments
 (0)