Skip to content

Commit d9f4a97

Browse files
committed
Merge pull request #18287 from veronicavasq
* pr/18287: Polish 'Add @LocalRSocketServerPort support' Add @LocalRSocketServerPort support Closes gh-18287
2 parents 74f988a + ffef3cc commit d9f4a97

File tree

6 files changed

+209
-3
lines changed

6 files changed

+209
-3
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
@@ -19,6 +19,7 @@
1919
import org.junit.jupiter.api.Test;
2020

2121
import org.springframework.boot.autoconfigure.AutoConfigurations;
22+
import org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer;
2223
import org.springframework.boot.rsocket.server.RSocketServerBootstrap;
2324
import org.springframework.boot.rsocket.server.RSocketServerFactory;
2425
import org.springframework.boot.rsocket.server.ServerRSocketFactoryCustomizer;
@@ -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,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.rsocket.context;
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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.rsocket.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.server.RSocketServer;
24+
import org.springframework.context.ApplicationContext;
25+
import org.springframework.context.ApplicationContextInitializer;
26+
import org.springframework.context.ApplicationListener;
27+
import org.springframework.context.ConfigurableApplicationContext;
28+
import org.springframework.core.env.ConfigurableEnvironment;
29+
import org.springframework.core.env.Environment;
30+
import org.springframework.core.env.MapPropertySource;
31+
import org.springframework.core.env.MutablePropertySources;
32+
import org.springframework.core.env.PropertySource;
33+
34+
/**
35+
* {@link ApplicationContextInitializer} that sets {@link Environment} properties for the
36+
* ports that {@link RSocketServer} servers are actually listening on. The property
37+
* {@literal "local.rsocket.server.port"} can be injected directly into tests using
38+
* {@link Value @Value} or obtained via the {@link Environment}.
39+
* <p>
40+
* Properties are automatically propagated up to any parent context.
41+
*
42+
* @author Verónica Vásquez
43+
* @author Eddú Meléndez
44+
* @since 2.2.0
45+
*/
46+
public class RSocketPortInfoApplicationContextInitializer
47+
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
48+
49+
@Override
50+
public void initialize(ConfigurableApplicationContext applicationContext) {
51+
applicationContext.addApplicationListener(new Listener(applicationContext));
52+
}
53+
54+
private static class Listener implements ApplicationListener<RSocketServerInitializedEvent> {
55+
56+
private static final String PROPERTY_NAME = "local.rsocket.server.port";
57+
58+
private ConfigurableApplicationContext applicationContext;
59+
60+
Listener(ConfigurableApplicationContext applicationContext) {
61+
this.applicationContext = applicationContext;
62+
}
63+
64+
@Override
65+
public void onApplicationEvent(RSocketServerInitializedEvent event) {
66+
setPortProperty(this.applicationContext, event.getrSocketServer().address().getPort());
67+
}
68+
69+
private void setPortProperty(ApplicationContext context, int port) {
70+
if (context instanceof ConfigurableApplicationContext) {
71+
setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(), port);
72+
}
73+
if (context.getParent() != null) {
74+
setPortProperty(context.getParent(), port);
75+
}
76+
}
77+
78+
private void setPortProperty(ConfigurableEnvironment environment, int port) {
79+
MutablePropertySources sources = environment.getPropertySources();
80+
PropertySource<?> source = sources.get("server.ports");
81+
if (source == null) {
82+
source = new MapPropertySource("server.ports", new HashMap<>());
83+
sources.addFirst(source);
84+
}
85+
setPortProperty(port, source);
86+
}
87+
88+
@SuppressWarnings("unchecked")
89+
private void setPortProperty(int port, PropertySource<?> source) {
90+
((Map<String, Object>) source.getSource()).put(PROPERTY_NAME, port);
91+
}
92+
93+
}
94+
95+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ 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.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
1920
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
2021

2122
# Application Listeners

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.rsocket.context;
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)