Skip to content

Commit 725f784

Browse files
veronicavasqeddumelendez
authored andcommitted
Add @LocalRSocketServerPort support
Add an appication context initializer to detect and store the active RSocket port in the Environment under `local.rsocket.server.port`. Additionally add a `@LocalServerPort` that provides a convenient alternative to `@Value`. See gh-18287 Co-authored-by: Eddú Meléndez <[email protected]>
1 parent 74f988a commit 725f784

File tree

6 files changed

+200
-4
lines changed

6 files changed

+200
-4
lines changed

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/rsocket/RSocketServerAutoConfigurationTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.boot.rsocket.server.ServerRSocketFactoryCustomizer;
2525
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2626
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
27+
import org.springframework.boot.web.context.RSocketPortInfoApplicationContextInitializer;
2728
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
2829
import org.springframework.context.annotation.Bean;
2930
import org.springframework.context.annotation.Configuration;
@@ -39,6 +40,7 @@
3940
* Tests for {@link RSocketServerAutoConfiguration}.
4041
*
4142
* @author Brian Clozel
43+
* @author Verónica Vásquez
4244
*/
4345
class RSocketServerAutoConfigurationTests {
4446

@@ -80,6 +82,17 @@ void shouldCreateDefaultBeansForRSocketServerWhenPortIsSet() {
8082
.hasSingleBean(ServerRSocketFactoryCustomizer.class));
8183
}
8284

85+
@Test
86+
void shouldSetLocalServerPortWhenRSocketServerPortIsSet() {
87+
reactiveWebContextRunner().withPropertyValues("spring.rsocket.server.port=0")
88+
.withInitializer(new RSocketPortInfoApplicationContextInitializer()).run((context) -> {
89+
assertThat(context).hasSingleBean(RSocketServerFactory.class)
90+
.hasSingleBean(RSocketServerBootstrap.class)
91+
.hasSingleBean(ServerRSocketFactoryCustomizer.class);
92+
assertThat(context.getEnvironment().getProperty("local.rsocket.server.port")).isNotNull();
93+
});
94+
}
95+
8396
@Test
8497
void shouldUseCustomServerBootstrap() {
8598
contextRunner().withUserConfiguration(CustomServerBootstrapConfig.class).run((context) -> assertThat(context)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2012-2019 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.web.context;
18+
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
22+
import org.springframework.beans.factory.annotation.Value;
23+
import org.springframework.boot.rsocket.context.RSocketServerInitializedEvent;
24+
import org.springframework.boot.rsocket.server.RSocketServer;
25+
import org.springframework.context.ApplicationContext;
26+
import org.springframework.context.ApplicationContextInitializer;
27+
import org.springframework.context.ApplicationListener;
28+
import org.springframework.context.ConfigurableApplicationContext;
29+
import org.springframework.core.env.ConfigurableEnvironment;
30+
import org.springframework.core.env.Environment;
31+
import org.springframework.core.env.MapPropertySource;
32+
import org.springframework.core.env.MutablePropertySources;
33+
import org.springframework.core.env.PropertySource;
34+
35+
/**
36+
* {@link ApplicationContextInitializer} that sets {@link Environment} properties for the
37+
* ports that {@link RSocketServer} servers are actually listening on. The property
38+
* {@literal "local.rsocket.server.port"} can be injected directly into tests using
39+
* {@link Value @Value} or obtained via the {@link Environment}.
40+
* <p>
41+
* Properties are automatically propagated up to any parent context.
42+
*
43+
* @author Verónica Vásquez
44+
* @author Eddú Meléndez
45+
* @since 2.2.0
46+
*/
47+
public class RSocketPortInfoApplicationContextInitializer
48+
implements ApplicationContextInitializer<ConfigurableApplicationContext>,
49+
ApplicationListener<RSocketServerInitializedEvent> {
50+
51+
private ConfigurableApplicationContext applicationContext;
52+
53+
@Override
54+
public void initialize(ConfigurableApplicationContext applicationContext) {
55+
applicationContext.addApplicationListener(this);
56+
this.applicationContext = applicationContext;
57+
}
58+
59+
@Override
60+
public void onApplicationEvent(RSocketServerInitializedEvent event) {
61+
String propertyName = "local.rsocket.server.port";
62+
setPortProperty(this.applicationContext, propertyName, event.getrSocketServer().address().getPort());
63+
}
64+
65+
private void setPortProperty(ApplicationContext context, String propertyName, int port) {
66+
if (context instanceof ConfigurableApplicationContext) {
67+
setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(), propertyName, port);
68+
}
69+
if (context.getParent() != null) {
70+
setPortProperty(context.getParent(), propertyName, port);
71+
}
72+
}
73+
74+
@SuppressWarnings("unchecked")
75+
private void setPortProperty(ConfigurableEnvironment environment, String propertyName, int port) {
76+
MutablePropertySources sources = environment.getPropertySources();
77+
PropertySource<?> source = sources.get("server.ports");
78+
if (source == null) {
79+
source = new MapPropertySource("server.ports", new HashMap<>());
80+
sources.addFirst(source);
81+
}
82+
((Map<String, Object>) source.getSource()).put(propertyName, port);
83+
}
84+
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2012-2019 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.web.server;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import org.springframework.beans.factory.annotation.Value;
26+
27+
/**
28+
* Annotation at the field or method/constructor parameter level that injects the RSocket
29+
* port that got allocated at runtime. Provides a convenient alternative for
30+
* <code>&#064;Value(&quot;${local.rsocket.server.port}&quot;)</code>.
31+
*
32+
* @author Verónica Vásquez
33+
* @author Eddú Meléndez
34+
* @since 2.2.0
35+
*/
36+
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
37+
@Retention(RetentionPolicy.RUNTIME)
38+
@Documented
39+
@Value("${local.rsocket.server.port}")
40+
public @interface LocalRSocketServerPort {
41+
42+
}

spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ org.springframework.context.ApplicationContextInitializer=\
1616
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
1717
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
1818
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
19-
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
19+
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer,\
20+
org.springframework.boot.web.context.RSocketPortInfoApplicationContextInitializer
2021

2122
# Application Listeners
2223
org.springframework.context.ApplicationListener=\

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,15 +240,15 @@ void initializersCreatedOnce() {
240240
SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class)
241241
.web(WebApplicationType.NONE);
242242
this.context = application.run();
243-
assertThat(application.application().getInitializers()).hasSize(4);
243+
assertThat(application.application().getInitializers()).hasSize(5);
244244
}
245245

246246
@Test
247247
void initializersCreatedOnceForChild() {
248248
SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class)
249249
.child(ChildConfig.class).web(WebApplicationType.NONE);
250250
this.context = application.run();
251-
assertThat(application.application().getInitializers()).hasSize(5);
251+
assertThat(application.application().getInitializers()).hasSize(6);
252252
}
253253

254254
@Test
@@ -257,7 +257,7 @@ void initializersIncludeDefaults() {
257257
.web(WebApplicationType.NONE).initializers((ConfigurableApplicationContext applicationContext) -> {
258258
});
259259
this.context = application.run();
260-
assertThat(application.application().getInitializers()).hasSize(5);
260+
assertThat(application.application().getInitializers()).hasSize(6);
261261
}
262262

263263
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2012-2019 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.web.server;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.ExtendWith;
21+
22+
import org.springframework.beans.factory.annotation.Value;
23+
import org.springframework.context.annotation.Configuration;
24+
import org.springframework.test.context.TestPropertySource;
25+
import org.springframework.test.context.junit.jupiter.SpringExtension;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* Tests for {@link LocalRSocketServerPort @LocalRSocketServerPort}.
31+
*
32+
* @author Verónica Vásquez
33+
* @author Eddú Meléndez
34+
*/
35+
@ExtendWith(SpringExtension.class)
36+
@TestPropertySource(properties = "local.rsocket.server.port=8181")
37+
class LocalRSocketServerPortTests {
38+
39+
@Value("${local.rsocket.server.port}")
40+
private String fromValue;
41+
42+
@LocalRSocketServerPort
43+
private String fromAnnotation;
44+
45+
@Test
46+
void testLocalRSocketServerPortAnnotation() {
47+
assertThat(this.fromAnnotation).isNotNull().isEqualTo(this.fromValue);
48+
}
49+
50+
@Configuration(proxyBeanMethods = false)
51+
static class Config {
52+
53+
}
54+
55+
}

0 commit comments

Comments
 (0)