From 9ecc5ebd02a9657d87ae2d4fe0a4acd8ddd05314 Mon Sep 17 00:00:00 2001 From: heowc Date: Sat, 4 Apr 2020 13:23:20 +0900 Subject: [PATCH 1/9] Support `@LocalArmeriaPort`, `@LocalArmeriaPorts` --- .../spring/ArmeriaServerInitializedEvent.java | 41 ++++++ ...PortInfoApplicationContextInitializer.java | 120 ++++++++++++++++++ .../spring/ArmeriaAutoConfiguration.java | 7 +- .../armeria/spring/LocalArmeriaPort.java | 34 +++++ .../armeria/spring/LocalArmeriaPorts.java | 34 +++++ .../main/resources/META-INF/spring.factories | 3 + .../armeria/spring/LocalArmeriaPortTest.java | 78 ++++++++++++ .../armeria/spring/LocalArmeriaPortsTest.java | 91 +++++++++++++ .../boot-webflux-autoconfigure/build.gradle | 4 + .../armeria/spring/web/ArmeriaWebServer.java | 10 +- .../ArmeriaReactiveWebServerFactory.java | 18 +-- ...tiveWebServerFactoryAutoConfiguration.java | 6 +- .../main/resources/META-INF/spring.factories | 3 + .../ArmeriaReactiveWebServerFactoryTest.java | 24 ++-- .../web/reactive/LocalArmeriaPortTest.java | 48 +++++++ 15 files changed, 498 insertions(+), 23 deletions(-) create mode 100644 spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerInitializedEvent.java create mode 100644 spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java create mode 100644 spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPort.java create mode 100644 spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPorts.java create mode 100644 spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortTest.java create mode 100644 spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortsTest.java create mode 100644 spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/LocalArmeriaPortTest.java diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerInitializedEvent.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerInitializedEvent.java new file mode 100644 index 00000000000..3940fb56ed1 --- /dev/null +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerInitializedEvent.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.internal.spring; + +import org.springframework.context.ApplicationEvent; + +import com.linecorp.armeria.server.Server; + +/** + * Event to be published after the application context is refreshed and the {@link Server} is ready. + * Useful for obtaining the local port of a running server. + */ +public class ArmeriaServerInitializedEvent extends ApplicationEvent { + + public ArmeriaServerInitializedEvent(Server server) { + super(server); + } + + public Server getServer() { + return getSource(); + } + + @Override + public Server getSource() { + return (Server) super.getSource(); + } +} diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java new file mode 100644 index 00000000000..edcfca9a750 --- /dev/null +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java @@ -0,0 +1,120 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.internal.spring; + +import static java.util.stream.Collectors.toList; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; + +import com.linecorp.armeria.server.Server; + +/** + * {@link ApplicationContextInitializer} that sets {@link Environment} properties for the + * ports that {@link Server} servers are actually listening on. The properties + * {@literal "local.armeria.port"}, {@literal "local.armeria.ports"} can be injected directly into tests using + * {@link Value @Value} or obtained via the {@link Environment}. + * Properties are automatically propagated up to any parent context. + */ +public class ArmeriaServerPortInfoApplicationContextInitializer + implements ApplicationContextInitializer { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + applicationContext.addApplicationListener(new Listener(applicationContext)); + } + + private static class Listener implements ApplicationListener { + + private static final String LOCAL_ARMERIA_PORT = "local.armeria.port"; + private static final String LOCAL_ARMERIA_PORTS = "local.armeria.ports"; + + private final ConfigurableApplicationContext applicationContext; + + Listener(ConfigurableApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public void onApplicationEvent(ArmeriaServerInitializedEvent event) { + setPortProperty(applicationContext, event.getServer().activeLocalPort()); + setPortProperty(applicationContext, event.getServer().activePorts().values().stream() + .map(p -> p.localAddress().getPort()) + .collect(toList())); + } + + private void setPortProperty(ApplicationContext context, int port) { + if (context instanceof ConfigurableApplicationContext) { + setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(), port); + } + if (context.getParent() != null) { + setPortProperty(context.getParent(), port); + } + } + + private void setPortProperty(ConfigurableEnvironment environment, int port) { + final MutablePropertySources sources = environment.getPropertySources(); + PropertySource source = sources.get("server.ports"); + if (source == null) { + source = new MapPropertySource("server.ports", new HashMap<>()); + sources.addFirst(source); + } + setPortProperty(port, source); + } + + @SuppressWarnings("unchecked") + private void setPortProperty(int port, PropertySource source) { + ((Map) source.getSource()).put(LOCAL_ARMERIA_PORT, port); + } + + private void setPortProperty(ApplicationContext context, List ports) { + if (context instanceof ConfigurableApplicationContext) { + setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(), ports); + } + if (context.getParent() != null) { + setPortProperty(context.getParent(), ports); + } + } + + private void setPortProperty(ConfigurableEnvironment environment, List ports) { + final MutablePropertySources sources = environment.getPropertySources(); + PropertySource source = sources.get("server.ports"); + if (source == null) { + source = new MapPropertySource("server.ports", new HashMap<>()); + sources.addFirst(source); + } + setPortProperty(ports, source); + } + + @SuppressWarnings("unchecked") + private void setPortProperty(List ports, PropertySource source) { + ((Map) source.getSource()).put(LOCAL_ARMERIA_PORTS, ports); + } + } +} diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java index 97cdcea51e1..b01f47d16e4 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java @@ -34,12 +34,14 @@ import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.google.common.base.Strings; import com.linecorp.armeria.common.SessionProtocol; +import com.linecorp.armeria.internal.spring.ArmeriaServerInitializedEvent; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.ServerBuilder; import com.linecorp.armeria.server.ServerPort; @@ -79,7 +81,8 @@ public Server armeriaServer( Optional> grpcServiceRegistrationBeans, Optional> httpServiceRegistrationBeans, Optional> annotatedServiceRegistrationBeans, - Optional> docServiceConfigurators) + Optional> docServiceConfigurators, + ApplicationEventPublisher eventPublisher) throws InterruptedException { if (!armeriaServerConfigurators.isPresent() && @@ -157,6 +160,8 @@ public Server armeriaServer( } return result; }).join(); + + eventPublisher.publishEvent(new ArmeriaServerInitializedEvent(server)); logger.info("Armeria server started at ports: {}", server.activePorts()); return server; } diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPort.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPort.java new file mode 100644 index 00000000000..98fab7865ca --- /dev/null +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPort.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.spring; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.beans.factory.annotation.Value; + +/** + * Specifies an active local port of an Armeria server. + */ +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Value("${local.armeria.port}") +public @interface LocalArmeriaPort { +} diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPorts.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPorts.java new file mode 100644 index 00000000000..d5fd98e5a1c --- /dev/null +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPorts.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.spring; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.beans.factory.annotation.Value; + +/** + * Specifies active ports of an Armeria server. + */ +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Value("#{'${local.armeria.ports}'.split(',')}") +public @interface LocalArmeriaPorts { +} diff --git a/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories index c8807cde7e2..d5d40a656d8 100644 --- a/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -1,2 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.linecorp.armeria.spring.ArmeriaAutoConfiguration + +org.springframework.context.ApplicationContextInitializer=\ + com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer \ No newline at end of file diff --git a/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortTest.java b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortTest.java new file mode 100644 index 00000000000..d50bfbde3cd --- /dev/null +++ b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.spring; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.inject.Inject; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.AggregatedHttpResponse; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.spring.LocalArmeriaPortTest.TestConfiguration; + +/** + * Tests for {@link LocalArmeriaPort @LocalArmeriaPort}. + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = TestConfiguration.class) +@ActiveProfiles({ "local", "autoConfTest" }) +@DirtiesContext +public class LocalArmeriaPortTest { + + @SpringBootApplication + @Import(ArmeriaOkServiceConfiguration.class) + static class TestConfiguration { + } + + @Inject + private Server server; + + @LocalArmeriaPort + private Integer port; + + private String newUrl(String scheme) { + return scheme + "://127.0.0.1:" + port; + } + + @Test + public void testPortConfiguration() throws Exception { + final Integer actualPort = server.activeLocalPort(); + assertThat(actualPort).isEqualTo(port); + } + + @Test + public void testHttpServiceRegistrationBean() throws Exception { + final WebClient client = WebClient.of(newUrl("h1c")); + + final HttpResponse response = client.get("/ok"); + + final AggregatedHttpResponse res = response.aggregate().get(); + assertThat(res.status()).isEqualTo(HttpStatus.OK); + assertThat(res.contentUtf8()).isEqualTo("ok"); + } +} diff --git a/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortsTest.java b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortsTest.java new file mode 100644 index 00000000000..99a1e2f10a9 --- /dev/null +++ b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortsTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.spring; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collection; +import java.util.List; + +import javax.inject.Inject; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.AggregatedHttpResponse; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.ServerPort; +import com.linecorp.armeria.spring.LocalArmeriaPortTest.TestConfiguration; + +/** + * Tests for {@link LocalArmeriaPorts @LocalArmeriaPorts}. + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = TestConfiguration.class) +@ActiveProfiles({ "local", "autoConfTest" }) +@DirtiesContext +public class LocalArmeriaPortsTest { + + @SpringBootApplication + @Import(ArmeriaOkServiceConfiguration.class) + static class TestConfiguration { + } + + @LocalArmeriaPorts + private List ports; + + @Inject + private Server server; + + private String newUrl(String scheme, Integer port) { + return scheme + "://127.0.0.1:" + port; + } + + @Test + public void testPortConfiguration() throws Exception { + final Collection serverPorts = server.activePorts().values(); + assertThat(serverPorts).size().isEqualTo(ports.size()); + serverPorts.stream() + .map(sp -> sp.localAddress().getPort()) + .forEach(port -> assertThat(ports).contains(port)); + } + + @Test + public void testHttpServiceRegistrationBean() { + ports.forEach(port -> { + try { + final WebClient client = WebClient.of(newUrl("h1c", port)); + + final HttpResponse response = client.get("/ok"); + + final AggregatedHttpResponse res = response.aggregate().get(); + assertThat(res.status()).isEqualTo(HttpStatus.OK); + assertThat(res.contentUtf8()).isEqualTo("ok"); + } catch (Exception e) { + // Ignored + } + }); + } +} diff --git a/spring/boot-webflux-autoconfigure/build.gradle b/spring/boot-webflux-autoconfigure/build.gradle index 05e379c5e93..5ab68c083aa 100644 --- a/spring/boot-webflux-autoconfigure/build.gradle +++ b/spring/boot-webflux-autoconfigure/build.gradle @@ -33,9 +33,13 @@ task copyFiles(type: Copy) { include '**/*RegistrationBean.java' include '**/ArmeriaConfigurationUtil.java' include '**/ArmeriaServerConfigurator.java' + include '**/ArmeriaServerInitializedEvent.java' + include '**/ArmeriaServerPortInfoApplicationContextInitializer.java' include '**/ArmeriaSettings.java' include '**/CustomAlias*KeyManager*.java' include '**/DropwizardSupport.java' + include '**/LocalArmeriaPort.java' + include '**/LocalArmeriaPorts.java' include '**/*MeterIdPrefixFunctionFactory.java' include '**/PrometheusSupport.java' include '**/Ssl.java' diff --git a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java index 08695464a1c..51553c1298f 100644 --- a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java +++ b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java @@ -26,9 +26,11 @@ import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.server.WebServerException; +import org.springframework.context.ApplicationEventPublisher; import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.common.util.Exceptions; +import com.linecorp.armeria.internal.spring.ArmeriaServerInitializedEvent; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.ServerPort; import com.linecorp.armeria.spring.web.reactive.ArmeriaReactiveWebServerFactory; @@ -45,6 +47,7 @@ public final class ArmeriaWebServer implements WebServer { @Nullable private final InetAddress address; private int port; + private final ApplicationEventPublisher publisher; private boolean isRunning; @@ -55,11 +58,15 @@ public final class ArmeriaWebServer implements WebServer { * @param protocol the session protocol which is used for the primary port * @param address the primary local address that the server will be bound to * @param port the primary local port that the server will be bound to + * @param publisher the application event publisher will deliver {@link ArmeriaServerInitializedEvent} to + * the application context. */ - public ArmeriaWebServer(Server server, SessionProtocol protocol, @Nullable InetAddress address, int port) { + public ArmeriaWebServer(Server server, SessionProtocol protocol, @Nullable InetAddress address, int port, + ApplicationEventPublisher publisher) { this.server = requireNonNull(server, "server"); this.protocol = requireNonNull(protocol, "protocol"); this.address = address; + this.publisher = requireNonNull(publisher, "publisher"); checkArgument(port >= 0 && port <= 65535, "port: %s (expected: 0...65535)", port); this.port = port; } @@ -82,6 +89,7 @@ public synchronized void start() { assert port.isPresent() : "the primary port doest not exist."; this.port = port.get().localAddress().getPort(); } + publisher.publishEvent(new ArmeriaServerInitializedEvent(server)); isRunning = true; } } catch (Exception cause) { diff --git a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactory.java b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactory.java index cd2f97f3fd4..2ccd605bc72 100644 --- a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactory.java +++ b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactory.java @@ -43,7 +43,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; import org.springframework.boot.web.server.Compression; @@ -51,6 +50,7 @@ import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.SslStoreProvider; import org.springframework.boot.web.server.WebServer; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.server.reactive.HttpHandler; import com.google.common.base.Strings; @@ -86,13 +86,13 @@ public class ArmeriaReactiveWebServerFactory extends AbstractReactiveWebServerFactory { private static final Logger logger = LoggerFactory.getLogger(ArmeriaReactiveWebServerFactory.class); - private final ConfigurableListableBeanFactory beanFactory; + private final ConfigurableApplicationContext applicationContext; /** - * Creates a new factory instance with the specified {@link ConfigurableListableBeanFactory}. + * Creates a new factory instance with the specified {@link ConfigurableApplicationContext}. */ - public ArmeriaReactiveWebServerFactory(ConfigurableListableBeanFactory beanFactory) { - this.beanFactory = requireNonNull(beanFactory, "beanFactory"); + public ArmeriaReactiveWebServerFactory(ConfigurableApplicationContext applicationContext) { + this.applicationContext = requireNonNull(applicationContext, "applicationContext"); } private static com.linecorp.armeria.spring.Ssl toArmeriaSslConfiguration(Ssl ssl) { @@ -203,7 +203,7 @@ public WebServer getWebServer(HttpHandler httpHandler) { firstNonNull(findBean(DataBufferFactoryWrapper.class), DataBufferFactoryWrapper.DEFAULT); final Server server = configureService(sb, httpHandler, factoryWrapper, getServerHeader()).build(); - return new ArmeriaWebServer(server, protocol, address, port); + return new ArmeriaWebServer(server, protocol, address, port, applicationContext); } private static ServerBuilder configureService(ServerBuilder sb, HttpHandler httpHandler, @@ -277,7 +277,7 @@ private void configureArmeriaService(ServerBuilder sb, ArmeriaSettings settings) @Nullable private T findBean(Class clazz) { try { - return beanFactory.getBean(clazz); + return applicationContext.getBean(clazz); } catch (NoUniqueBeanDefinitionException e) { throw new IllegalStateException("Too many " + clazz.getSimpleName() + " beans: (expected: 1)", e); } catch (NoSuchBeanDefinitionException e) { @@ -286,12 +286,12 @@ private T findBean(Class clazz) { } private List findBeans(Class clazz) { - final String[] names = beanFactory.getBeanNamesForType(clazz); + final String[] names = applicationContext.getBeanNamesForType(clazz); if (names.length == 0) { return ImmutableList.of(); } return Arrays.stream(names) - .map(name -> beanFactory.getBean(name, clazz)) + .map(name -> applicationContext.getBean(name, clazz)) .collect(toImmutableList()); } diff --git a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryAutoConfiguration.java b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryAutoConfiguration.java index 597f4f8c504..470f9a9954a 100644 --- a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryAutoConfiguration.java +++ b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryAutoConfiguration.java @@ -15,13 +15,13 @@ */ package com.linecorp.armeria.spring.web.reactive; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -46,7 +46,7 @@ public class ArmeriaReactiveWebServerFactoryAutoConfiguration { */ @Bean public ArmeriaReactiveWebServerFactory armeriaReactiveWebServerFactory( - ConfigurableListableBeanFactory beanFactory) { - return new ArmeriaReactiveWebServerFactory(beanFactory); + ConfigurableApplicationContext applicationContext) { + return new ArmeriaReactiveWebServerFactory(applicationContext); } } diff --git a/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories b/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories index 9a78afee7b7..7d69451e944 100644 --- a/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories @@ -1,3 +1,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.linecorp.armeria.spring.web.reactive.ArmeriaReactiveWebServerFactoryAutoConfiguration,\ com.linecorp.armeria.spring.web.reactive.ArmeriaClientAutoConfiguration + +org.springframework.context.ApplicationContextInitializer=\ + com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer \ No newline at end of file diff --git a/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryTest.java b/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryTest.java index a69001c1ac1..9bb6f6a1f6f 100644 --- a/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryTest.java +++ b/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/ArmeriaReactiveWebServerFactoryTest.java @@ -21,13 +21,14 @@ import java.util.function.Consumer; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; import org.springframework.boot.web.server.Compression; import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.WebServer; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -55,7 +56,7 @@ class ArmeriaReactiveWebServerFactoryTest { static final String POST_BODY = "Hello, world!"; - private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + private final GenericApplicationContext context = new GenericApplicationContext(); private final ClientFactory clientFactory = ClientFactory.builder() .tlsNoVerify() @@ -63,7 +64,7 @@ class ArmeriaReactiveWebServerFactoryTest { .build(); private ArmeriaReactiveWebServerFactory factory() { - return new ArmeriaReactiveWebServerFactory(beanFactory); + return new ArmeriaReactiveWebServerFactory(context); } private WebClient httpsClient(WebServer server) { @@ -78,6 +79,11 @@ private WebClient httpClient(WebServer server) { .build(); } + @BeforeEach + void before() { + context.refresh(); + } + @Test void shouldRunOnSpecifiedPort() { final ArmeriaReactiveWebServerFactory factory = factory(); @@ -232,13 +238,13 @@ void testMultipleBeansRegistered_TooManyMeterRegistryBeans() { final ArmeriaReactiveWebServerFactory factory = factory(); RootBeanDefinition rbd = new RootBeanDefinition(ArmeriaSettings.class); - beanFactory.registerBeanDefinition("armeriaSettings", rbd); + context.registerBeanDefinition("armeriaSettings", rbd); rbd = new RootBeanDefinition(MeterRegistry.class, PrometheusMeterRegistries::newRegistry); - beanFactory.registerBeanDefinition("meterRegistry1", rbd); + context.registerBeanDefinition("meterRegistry1", rbd); rbd = new RootBeanDefinition(MeterRegistry.class, PrometheusMeterRegistries::newRegistry); - beanFactory.registerBeanDefinition("meterRegistry2", rbd); + context.registerBeanDefinition("meterRegistry2", rbd); assertThatThrownBy(() -> runEchoServer(factory, server -> fail("Should never reach here"))) .isInstanceOf(IllegalStateException.class); @@ -249,14 +255,14 @@ void testMultipleBeansRegistered_shouldUsePrimaryBean() { final ArmeriaReactiveWebServerFactory factory = factory(); RootBeanDefinition rbd = new RootBeanDefinition(ArmeriaSettings.class); - beanFactory.registerBeanDefinition("armeriaSettings", rbd); + context.registerBeanDefinition("armeriaSettings", rbd); rbd = new RootBeanDefinition(MeterRegistry.class, PrometheusMeterRegistries::newRegistry); - beanFactory.registerBeanDefinition("meterRegistry1", rbd); + context.registerBeanDefinition("meterRegistry1", rbd); rbd = new RootBeanDefinition(MeterRegistry.class, PrometheusMeterRegistries::newRegistry); rbd.setPrimary(true); - beanFactory.registerBeanDefinition("meterRegistry2", rbd); + context.registerBeanDefinition("meterRegistry2", rbd); runEchoServer(factory, server -> { final WebClient client = httpClient(server); diff --git a/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/LocalArmeriaPortTest.java b/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/LocalArmeriaPortTest.java new file mode 100644 index 00000000000..09ad4d18a69 --- /dev/null +++ b/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/LocalArmeriaPortTest.java @@ -0,0 +1,48 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.spring.web.reactive; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; + +import com.linecorp.armeria.spring.LocalArmeriaPort; + +/** + * Tests for {@link LocalArmeriaPort @LocalArmeriaPort}. + */ +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +class LocalArmeriaPortTest { + + @SpringBootApplication + static class TestConfiguration { + } + + @LocalServerPort + int port; + + @LocalArmeriaPort + int armeriaPort; + + @Test + void testSamePort() { + assertThat(port).isEqualTo(armeriaPort); + } +} From 73601a57d2e73390e83015e3f526384f556e2129 Mon Sep 17 00:00:00 2001 From: heowc Date: Sun, 5 Apr 2020 01:05:51 +0900 Subject: [PATCH 2/9] Move package / Clean --- ...rmeriaServerPortInfoApplicationContextInitializer.java | 1 + .../linecorp/armeria/spring/ArmeriaAutoConfiguration.java | 8 ++++---- .../spring/ArmeriaServerInitializedEvent.java | 8 +++++++- .../com/linecorp/armeria/spring/web/ArmeriaWebServer.java | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) rename spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/{internal => }/spring/ArmeriaServerInitializedEvent.java (90%) diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java index edcfca9a750..d5488a8e7b0 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java @@ -34,6 +34,7 @@ import org.springframework.core.env.PropertySource; import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.spring.ArmeriaServerInitializedEvent; /** * {@link ApplicationContextInitializer} that sets {@link Environment} properties for the diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java index b01f47d16e4..ef4d8f1b4fd 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java @@ -41,7 +41,6 @@ import com.google.common.base.Strings; import com.linecorp.armeria.common.SessionProtocol; -import com.linecorp.armeria.internal.spring.ArmeriaServerInitializedEvent; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.ServerBuilder; import com.linecorp.armeria.server.ServerPort; @@ -82,7 +81,7 @@ public Server armeriaServer( Optional> httpServiceRegistrationBeans, Optional> annotatedServiceRegistrationBeans, Optional> docServiceConfigurators, - ApplicationEventPublisher eventPublisher) + Optional eventPublisher) throws InterruptedException { if (!armeriaServerConfigurators.isPresent() && @@ -90,7 +89,8 @@ public Server armeriaServer( !thriftServiceRegistrationBeans.isPresent() && !grpcServiceRegistrationBeans.isPresent() && !httpServiceRegistrationBeans.isPresent() && - !annotatedServiceRegistrationBeans.isPresent()) { + !annotatedServiceRegistrationBeans.isPresent() && + !eventPublisher.isPresent()) { // No services to register, no need to start up armeria server. return null; } @@ -161,7 +161,7 @@ public Server armeriaServer( return result; }).join(); - eventPublisher.publishEvent(new ArmeriaServerInitializedEvent(server)); + eventPublisher.get().publishEvent(new ArmeriaServerInitializedEvent(server)); logger.info("Armeria server started at ports: {}", server.activePorts()); return server; } diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerInitializedEvent.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerInitializedEvent.java similarity index 90% rename from spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerInitializedEvent.java rename to spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerInitializedEvent.java index 3940fb56ed1..0e6c9d74237 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerInitializedEvent.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerInitializedEvent.java @@ -14,7 +14,7 @@ * under the License. */ -package com.linecorp.armeria.internal.spring; +package com.linecorp.armeria.spring; import org.springframework.context.ApplicationEvent; @@ -26,10 +26,16 @@ */ public class ArmeriaServerInitializedEvent extends ApplicationEvent { + /** + * Creates a new instance. + */ public ArmeriaServerInitializedEvent(Server server) { super(server); } + /** + * Returns the {@link Server}. + */ public Server getServer() { return getSource(); } diff --git a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java index 51553c1298f..1a188dde6e2 100644 --- a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java +++ b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java @@ -30,7 +30,7 @@ import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.common.util.Exceptions; -import com.linecorp.armeria.internal.spring.ArmeriaServerInitializedEvent; +import com.linecorp.armeria.spring.ArmeriaServerInitializedEvent; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.ServerPort; import com.linecorp.armeria.spring.web.reactive.ArmeriaReactiveWebServerFactory; From 383a9b5a3e647f90ec58fda3da8d788fda3ba212 Mon Sep 17 00:00:00 2001 From: heowc Date: Sun, 5 Apr 2020 12:42:09 +0900 Subject: [PATCH 3/9] Checkstyle --- .../java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java index 1a188dde6e2..d17d39c5637 100644 --- a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java +++ b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java @@ -30,9 +30,9 @@ import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.common.util.Exceptions; -import com.linecorp.armeria.spring.ArmeriaServerInitializedEvent; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.ServerPort; +import com.linecorp.armeria.spring.ArmeriaServerInitializedEvent; import com.linecorp.armeria.spring.web.reactive.ArmeriaReactiveWebServerFactory; /** From 4912d7ab5f1a38ca2df80213949ff1837314877e Mon Sep 17 00:00:00 2001 From: heowc Date: Mon, 6 Apr 2020 18:41:01 +0900 Subject: [PATCH 4/9] Address comments by @trusin --- ...erPortInfoApplicationContextInitializer.java | 14 +++++++------- .../spring/ArmeriaAutoConfiguration.java | 7 +++---- ...vent.java => ArmeriaServerStartedEvent.java} | 17 +++++++++++------ .../main/resources/META-INF/spring.factories | 2 +- spring/boot-webflux-autoconfigure/build.gradle | 2 +- .../armeria/spring/web/ArmeriaWebServer.java | 6 +++--- .../main/resources/META-INF/spring.factories | 2 +- 7 files changed, 27 insertions(+), 23 deletions(-) rename spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/{ArmeriaServerInitializedEvent.java => ArmeriaServerStartedEvent.java} (77%) diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java index d5488a8e7b0..46db322deb8 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java @@ -34,12 +34,12 @@ import org.springframework.core.env.PropertySource; import com.linecorp.armeria.server.Server; -import com.linecorp.armeria.spring.ArmeriaServerInitializedEvent; +import com.linecorp.armeria.spring.ArmeriaServerStartedEvent; /** * {@link ApplicationContextInitializer} that sets {@link Environment} properties for the - * ports that {@link Server} servers are actually listening on. The properties - * {@literal "local.armeria.port"}, {@literal "local.armeria.ports"} can be injected directly into tests using + * ports that Armeria {@link Server}s are actually listening on. The properties + * {@code "local.armeria.port"}, {@code "local.armeria.ports"} can be injected directly into tests using * {@link Value @Value} or obtained via the {@link Environment}. * Properties are automatically propagated up to any parent context. */ @@ -51,7 +51,7 @@ public void initialize(ConfigurableApplicationContext applicationContext) { applicationContext.addApplicationListener(new Listener(applicationContext)); } - private static class Listener implements ApplicationListener { + private static class Listener implements ApplicationListener { private static final String LOCAL_ARMERIA_PORT = "local.armeria.port"; private static final String LOCAL_ARMERIA_PORTS = "local.armeria.ports"; @@ -63,9 +63,9 @@ private static class Listener implements ApplicationListener p.localAddress().getPort()) .collect(toList())); } diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java index ef4d8f1b4fd..b3d7e7d376c 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaAutoConfiguration.java @@ -81,7 +81,7 @@ public Server armeriaServer( Optional> httpServiceRegistrationBeans, Optional> annotatedServiceRegistrationBeans, Optional> docServiceConfigurators, - Optional eventPublisher) + ApplicationEventPublisher eventPublisher) throws InterruptedException { if (!armeriaServerConfigurators.isPresent() && @@ -89,8 +89,7 @@ public Server armeriaServer( !thriftServiceRegistrationBeans.isPresent() && !grpcServiceRegistrationBeans.isPresent() && !httpServiceRegistrationBeans.isPresent() && - !annotatedServiceRegistrationBeans.isPresent() && - !eventPublisher.isPresent()) { + !annotatedServiceRegistrationBeans.isPresent()) { // No services to register, no need to start up armeria server. return null; } @@ -161,7 +160,7 @@ public Server armeriaServer( return result; }).join(); - eventPublisher.get().publishEvent(new ArmeriaServerInitializedEvent(server)); + eventPublisher.publishEvent(new ArmeriaServerStartedEvent(server)); logger.info("Armeria server started at ports: {}", server.activePorts()); return server; } diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerInitializedEvent.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java similarity index 77% rename from spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerInitializedEvent.java rename to spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java index 0e6c9d74237..1c08d0ea299 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerInitializedEvent.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java @@ -18,30 +18,35 @@ import org.springframework.context.ApplicationEvent; +import com.google.common.base.MoreObjects; + import com.linecorp.armeria.server.Server; /** * Event to be published after the application context is refreshed and the {@link Server} is ready. * Useful for obtaining the local port of a running server. */ -public class ArmeriaServerInitializedEvent extends ApplicationEvent { +public class ArmeriaServerStartedEvent extends ApplicationEvent { /** * Creates a new instance. */ - public ArmeriaServerInitializedEvent(Server server) { + public ArmeriaServerStartedEvent(Server server) { super(server); } /** * Returns the {@link Server}. */ - public Server getServer() { - return getSource(); - } - @Override public Server getSource() { return (Server) super.getSource(); } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("source", getSource()) + .toString(); + } } diff --git a/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories index d5d40a656d8..a737a972467 100644 --- a/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -2,4 +2,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.linecorp.armeria.spring.ArmeriaAutoConfiguration org.springframework.context.ApplicationContextInitializer=\ - com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer \ No newline at end of file + com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer diff --git a/spring/boot-webflux-autoconfigure/build.gradle b/spring/boot-webflux-autoconfigure/build.gradle index 5ab68c083aa..7395388888b 100644 --- a/spring/boot-webflux-autoconfigure/build.gradle +++ b/spring/boot-webflux-autoconfigure/build.gradle @@ -33,8 +33,8 @@ task copyFiles(type: Copy) { include '**/*RegistrationBean.java' include '**/ArmeriaConfigurationUtil.java' include '**/ArmeriaServerConfigurator.java' - include '**/ArmeriaServerInitializedEvent.java' include '**/ArmeriaServerPortInfoApplicationContextInitializer.java' + include '**/ArmeriaServerStartedEvent.java' include '**/ArmeriaSettings.java' include '**/CustomAlias*KeyManager*.java' include '**/DropwizardSupport.java' diff --git a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java index d17d39c5637..4fe9ed5066c 100644 --- a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java +++ b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java @@ -32,7 +32,7 @@ import com.linecorp.armeria.common.util.Exceptions; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.ServerPort; -import com.linecorp.armeria.spring.ArmeriaServerInitializedEvent; +import com.linecorp.armeria.spring.ArmeriaServerStartedEvent; import com.linecorp.armeria.spring.web.reactive.ArmeriaReactiveWebServerFactory; /** @@ -58,7 +58,7 @@ public final class ArmeriaWebServer implements WebServer { * @param protocol the session protocol which is used for the primary port * @param address the primary local address that the server will be bound to * @param port the primary local port that the server will be bound to - * @param publisher the application event publisher will deliver {@link ArmeriaServerInitializedEvent} to + * @param publisher the application event publisher will deliver {@link ArmeriaServerStartedEvent} to * the application context. */ public ArmeriaWebServer(Server server, SessionProtocol protocol, @Nullable InetAddress address, int port, @@ -89,7 +89,7 @@ public synchronized void start() { assert port.isPresent() : "the primary port doest not exist."; this.port = port.get().localAddress().getPort(); } - publisher.publishEvent(new ArmeriaServerInitializedEvent(server)); + publisher.publishEvent(new ArmeriaServerStartedEvent(server)); isRunning = true; } } catch (Exception cause) { diff --git a/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories b/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories index 7d69451e944..053e33ab43f 100644 --- a/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories @@ -3,4 +3,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.linecorp.armeria.spring.web.reactive.ArmeriaClientAutoConfiguration org.springframework.context.ApplicationContextInitializer=\ - com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer \ No newline at end of file + com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer From b575815b2ae905dbca5c7f05b82bfc64d20dc817 Mon Sep 17 00:00:00 2001 From: heowc Date: Tue, 7 Apr 2020 11:34:22 +0900 Subject: [PATCH 5/9] Address comments by @ikhoon --- ...rverPortInfoApplicationContextInitializer.java | 15 ++++++++------- .../armeria/spring/ArmeriaServerStartedEvent.java | 2 +- .../armeria/spring/web/ArmeriaWebServer.java | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java index 46db322deb8..c5800658b55 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java @@ -16,7 +16,7 @@ package com.linecorp.armeria.internal.spring; -import static java.util.stream.Collectors.toList; +import static com.google.common.collect.ImmutableList.toImmutableList; import java.util.HashMap; import java.util.List; @@ -64,10 +64,11 @@ private static class Listener implements ApplicationListener p.localAddress().getPort()) - .collect(toList())); + final Server source = event.getSource(); + setPortProperty(applicationContext, source.activeLocalPort()); + setPortProperty(applicationContext, source.activePorts().values().stream() + .map(p -> p.localAddress().getPort()) + .collect(toImmutableList())); } private void setPortProperty(ApplicationContext context, int port) { @@ -90,7 +91,7 @@ private void setPortProperty(ConfigurableEnvironment environment, int port) { } @SuppressWarnings("unchecked") - private void setPortProperty(int port, PropertySource source) { + private static void setPortProperty(int port, PropertySource source) { ((Map) source.getSource()).put(LOCAL_ARMERIA_PORT, port); } @@ -114,7 +115,7 @@ private void setPortProperty(ConfigurableEnvironment environment, List } @SuppressWarnings("unchecked") - private void setPortProperty(List ports, PropertySource source) { + private static void setPortProperty(List ports, PropertySource source) { ((Map) source.getSource()).put(LOCAL_ARMERIA_PORTS, ports); } } diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java index 1c08d0ea299..68035b2afd3 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java @@ -26,7 +26,7 @@ * Event to be published after the application context is refreshed and the {@link Server} is ready. * Useful for obtaining the local port of a running server. */ -public class ArmeriaServerStartedEvent extends ApplicationEvent { +public final class ArmeriaServerStartedEvent extends ApplicationEvent { /** * Creates a new instance. diff --git a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java index 4fe9ed5066c..b74e281d4b5 100644 --- a/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java +++ b/spring/boot-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java @@ -58,7 +58,7 @@ public final class ArmeriaWebServer implements WebServer { * @param protocol the session protocol which is used for the primary port * @param address the primary local address that the server will be bound to * @param port the primary local port that the server will be bound to - * @param publisher the application event publisher will deliver {@link ArmeriaServerStartedEvent} to + * @param publisher the application event publisher that delivers {@link ArmeriaServerStartedEvent} to * the application context. */ public ArmeriaWebServer(Server server, SessionProtocol protocol, @Nullable InetAddress address, int port, From deeabdc7a8f395cbd5a574c4c2ef406e6ebd1d93 Mon Sep 17 00:00:00 2001 From: heowc Date: Wed, 8 Apr 2020 22:58:15 +0900 Subject: [PATCH 6/9] Add comment --- .../ArmeriaServerPortInfoApplicationContextInitializer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java index c5800658b55..d95946c87e8 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java @@ -53,6 +53,8 @@ public void initialize(ConfigurableApplicationContext applicationContext) { private static class Listener implements ApplicationListener { + // The following naming conventions are referenced here. + // https://github.com/spring-projects/spring-boot/blob/2.2.x/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/context/ServerPortInfoApplicationContextInitializer.java. private static final String LOCAL_ARMERIA_PORT = "local.armeria.port"; private static final String LOCAL_ARMERIA_PORTS = "local.armeria.ports"; From ac047adddf0d9e7aecb48f94c1e02752caed2e64 Mon Sep 17 00:00:00 2001 From: heowc Date: Mon, 13 Apr 2020 20:19:23 +0900 Subject: [PATCH 7/9] Address comments by @trusin --- .../ArmeriaServerPortInfoApplicationContextInitializer.java | 6 +++--- .../linecorp/armeria/spring/ArmeriaServerStartedEvent.java | 5 +++-- .../src/main/resources/META-INF/spring.factories | 2 +- .../com/linecorp/armeria/spring/LocalArmeriaPortTest.java | 2 +- .../com/linecorp/armeria/spring/LocalArmeriaPortsTest.java | 2 +- .../src/main/resources/META-INF/spring.factories | 2 +- .../armeria/spring/web/reactive/LocalArmeriaPortTest.java | 2 +- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java index d95946c87e8..4225e4cc462 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/ArmeriaServerPortInfoApplicationContextInitializer.java @@ -66,9 +66,9 @@ private static class Listener implements ApplicationListener p.localAddress().getPort()) .collect(toImmutableList())); } diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java index 68035b2afd3..39bdbc4f206 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/ArmeriaServerStartedEvent.java @@ -16,6 +16,7 @@ package com.linecorp.armeria.spring; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import com.google.common.base.MoreObjects; @@ -23,8 +24,8 @@ import com.linecorp.armeria.server.Server; /** - * Event to be published after the application context is refreshed and the {@link Server} is ready. - * Useful for obtaining the local port of a running server. + * An {@link ApplicationEvent} that is published after an {@link ApplicationContext} is refreshed + * and the {@link Server} is ready. Useful for obtaining the local port of a running server. */ public final class ArmeriaServerStartedEvent extends ApplicationEvent { diff --git a/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories index a737a972467..d5d40a656d8 100644 --- a/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -2,4 +2,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.linecorp.armeria.spring.ArmeriaAutoConfiguration org.springframework.context.ApplicationContextInitializer=\ - com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer + com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer \ No newline at end of file diff --git a/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortTest.java b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortTest.java index d50bfbde3cd..ba0f80b5c28 100644 --- a/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortTest.java +++ b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortTest.java @@ -36,7 +36,7 @@ import com.linecorp.armeria.spring.LocalArmeriaPortTest.TestConfiguration; /** - * Tests for {@link LocalArmeriaPort @LocalArmeriaPort}. + * Tests for {@link LocalArmeriaPort}. */ @RunWith(SpringRunner.class) @SpringBootTest(classes = TestConfiguration.class) diff --git a/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortsTest.java b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortsTest.java index 99a1e2f10a9..05ab59dbf40 100644 --- a/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortsTest.java +++ b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/spring/LocalArmeriaPortsTest.java @@ -40,7 +40,7 @@ import com.linecorp.armeria.spring.LocalArmeriaPortTest.TestConfiguration; /** - * Tests for {@link LocalArmeriaPorts @LocalArmeriaPorts}. + * Tests for {@link LocalArmeriaPorts}. */ @RunWith(SpringRunner.class) @SpringBootTest(classes = TestConfiguration.class) diff --git a/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories b/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories index 053e33ab43f..7d69451e944 100644 --- a/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories @@ -3,4 +3,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.linecorp.armeria.spring.web.reactive.ArmeriaClientAutoConfiguration org.springframework.context.ApplicationContextInitializer=\ - com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer + com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer \ No newline at end of file diff --git a/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/LocalArmeriaPortTest.java b/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/LocalArmeriaPortTest.java index 09ad4d18a69..76fc35c0cc3 100644 --- a/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/LocalArmeriaPortTest.java +++ b/spring/boot-webflux-autoconfigure/src/test/java/com/linecorp/armeria/spring/web/reactive/LocalArmeriaPortTest.java @@ -26,7 +26,7 @@ import com.linecorp.armeria.spring.LocalArmeriaPort; /** - * Tests for {@link LocalArmeriaPort @LocalArmeriaPort}. + * Tests for {@link LocalArmeriaPort}. */ @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) class LocalArmeriaPortTest { From e2a45c5353d163f1fb7af55f65d0813fd3cb0bde Mon Sep 17 00:00:00 2001 From: heowc Date: Tue, 14 Apr 2020 13:29:28 +0900 Subject: [PATCH 8/9] Missing EOF / Remove empty line. --- .../src/main/resources/META-INF/spring.factories | 3 +-- .../src/main/resources/META-INF/spring.factories | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories index d5d40a656d8..af60c8cb343 100644 --- a/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring/boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -1,5 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.linecorp.armeria.spring.ArmeriaAutoConfiguration - org.springframework.context.ApplicationContextInitializer=\ - com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer \ No newline at end of file + com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer diff --git a/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories b/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories index 7d69451e944..d78e3d5c7e0 100644 --- a/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring/boot-webflux-autoconfigure/src/main/resources/META-INF/spring.factories @@ -1,6 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.linecorp.armeria.spring.web.reactive.ArmeriaReactiveWebServerFactoryAutoConfiguration,\ com.linecorp.armeria.spring.web.reactive.ArmeriaClientAutoConfiguration - org.springframework.context.ApplicationContextInitializer=\ - com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer \ No newline at end of file + com.linecorp.armeria.internal.spring.ArmeriaServerPortInfoApplicationContextInitializer From 3717e2757da3b0e45ece98a3bb5d337763b50a8a Mon Sep 17 00:00:00 2001 From: heowc Date: Thu, 23 Apr 2020 18:12:07 +0900 Subject: [PATCH 9/9] WIP --- .../armeria/common/SessionProtocol.java | 6 +- ...rtAnnotationAutowireCandidateResolver.java | 97 ++++++++++++++++++ ...rmeriaPortAnnotationBeanPostProcessor.java | 43 ++++++++ .../armeria/spring/LocalArmeriaPort.java | 8 +- ...iaPortAnnotationBeanPostProcessorTest.java | 99 +++++++++++++++++++ 5 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationAutowireCandidateResolver.java create mode 100644 spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationBeanPostProcessor.java create mode 100644 spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationBeanPostProcessorTest.java diff --git a/core/src/main/java/com/linecorp/armeria/common/SessionProtocol.java b/core/src/main/java/com/linecorp/armeria/common/SessionProtocol.java index 607b33a8e15..8f05c8888d9 100644 --- a/core/src/main/java/com/linecorp/armeria/common/SessionProtocol.java +++ b/core/src/main/java/com/linecorp/armeria/common/SessionProtocol.java @@ -59,7 +59,11 @@ public enum SessionProtocol { /** * PROXY protocol - v1 or v2. */ - PROXY("proxy", false, false, 0); + PROXY("proxy", false, false, 0), + /** + * None. + */ + NONE("none", false, false, -1); private static final Set HTTP_VALUES = Sets.immutableEnumSet(HTTP, H1C, H2C); diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationAutowireCandidateResolver.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationAutowireCandidateResolver.java new file mode 100644 index 00000000000..60abbb1e5f6 --- /dev/null +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationAutowireCandidateResolver.java @@ -0,0 +1,97 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.internal.spring; + +import java.lang.annotation.Annotation; + +import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.beans.factory.support.SimpleAutowireCandidateResolver; +import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.lang.Nullable; + +import com.linecorp.armeria.common.SessionProtocol; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.spring.LocalArmeriaPort; + +public class LocalArmeriaPortAnnotationAutowireCandidateResolver extends SimpleAutowireCandidateResolver { + + private final Class localArmeriaPortAnnotationType = LocalArmeriaPort.class; + + @Nullable + private Server server; + + public void setServer(@Nullable Server server) { + this.server = server; + } + + /** + * Determine whether the given dependency declares a value annotation. + * @see LocalArmeriaPort + */ + @Override + @Nullable + public Object getSuggestedValue(DependencyDescriptor descriptor) { + Object value = findValue(descriptor.getAnnotations()); + if (value == null) { + final MethodParameter methodParam = descriptor.getMethodParameter(); + if (methodParam != null) { + value = findValue(methodParam.getMethodAnnotations()); + } + } + return value; + } + + /** + * Determine a suggested value from any of the given candidate annotations. + */ + @Nullable + protected Object findValue(Annotation[] annotationsToSearch) { + if (annotationsToSearch.length > 0) { // qualifier annotations have to be local + final AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( + AnnotatedElementUtils.forAnnotations(annotationsToSearch), localArmeriaPortAnnotationType); + if (attr != null) { + return extractValue(attr); + } + } + return null; + } + + /** + * Extract the value attribute from the given annotation. + * @since 4.3 + */ + @Nullable + protected Object extractValue(AnnotationAttributes attr) { + assert server != null; + final SessionProtocol value = (SessionProtocol) attr.get(AnnotationUtils.VALUE); + if (value == null) { + throw new IllegalStateException("LocalArmeriaPort annotation must have a value attribute"); + } + if (value == SessionProtocol.NONE) { + return server.activeLocalPort(); + } + + try { + return server.activeLocalPort(value); + } catch (IllegalStateException e) { + // ignored + return null; + } + } +} diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationBeanPostProcessor.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationBeanPostProcessor.java new file mode 100644 index 00000000000..7165e554594 --- /dev/null +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationBeanPostProcessor.java @@ -0,0 +1,43 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.internal.spring; + +import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; +import org.springframework.core.Ordered; + +import com.linecorp.armeria.spring.LocalArmeriaPort; + +/** + * TBD. + */ +public class LocalArmeriaPortAnnotationBeanPostProcessor extends AutowiredAnnotationBeanPostProcessor { + + private int order = Ordered.LOWEST_PRECEDENCE - 2; + + public LocalArmeriaPortAnnotationBeanPostProcessor() { + setAutowiredAnnotationType(LocalArmeriaPort.class); + } + + @Override + public void setOrder(int order) { + this.order = order; + } + + @Override + public int getOrder() { + return order; + } +} diff --git a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPort.java b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPort.java index 98fab7865ca..43de564a437 100644 --- a/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPort.java +++ b/spring/boot-autoconfigure/src/main/java/com/linecorp/armeria/spring/LocalArmeriaPort.java @@ -21,7 +21,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.beans.factory.annotation.Value; +import com.linecorp.armeria.common.SessionProtocol; /** * Specifies an active local port of an Armeria server. @@ -29,6 +29,10 @@ @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented -@Value("${local.armeria.port}") public @interface LocalArmeriaPort { + + /** + * TBD. + */ + SessionProtocol value() default SessionProtocol.NONE; } diff --git a/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationBeanPostProcessorTest.java b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationBeanPostProcessorTest.java new file mode 100644 index 00000000000..f7f67830684 --- /dev/null +++ b/spring/boot-autoconfigure/src/test/java/com/linecorp/armeria/internal/spring/LocalArmeriaPortAnnotationBeanPostProcessorTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2020 LINE Corporation + * + * LINE Corporation licenses this file to you 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 com.linecorp.armeria.internal.spring; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; + +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.SessionProtocol; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.spring.LocalArmeriaPort; + +class LocalArmeriaPortAnnotationBeanPostProcessorTest { + + private DefaultListableBeanFactory bf; + + private LocalArmeriaPortAnnotationBeanPostProcessor bpp; + + @BeforeEach + void setup() { + bf = new DefaultListableBeanFactory(); + bf.registerResolvableDependency(BeanFactory.class, bf); + bpp = new LocalArmeriaPortAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); + } + + @AfterEach + public void close() { + bf.destroySingletons(); + } + + @Test + public void testCustomAnnotationRequiredFieldResourceInjection() { + final ServerBuilder builder = Server.builder(); + builder.service("/hello", (ctx, req) -> HttpResponse.of("hello")); + + final Server server = builder.build(); + server.start().handle((result, t) -> { + if (t != null) { + throw new IllegalStateException("Armeria server failed to start", t); + } + return result; + }).join(); + + bf.registerBeanDefinition("customBean", new RootBeanDefinition( + CustomAnnotationFieldResourceInjectionBean.class)); + bf.registerSingleton("server", server); + + final LocalArmeriaPortAnnotationAutowireCandidateResolver resolver = + new LocalArmeriaPortAnnotationAutowireCandidateResolver(); + resolver.setServer(server); + bf.setAutowireCandidateResolver(resolver); + + final CustomAnnotationFieldResourceInjectionBean bean = + (CustomAnnotationFieldResourceInjectionBean) bf.getBean("customBean"); + assertThat(bean.defaultPort()).isEqualTo(server.activeLocalPort()); + assertThat(bean.httpPort()).isEqualTo(server.activeLocalPort(SessionProtocol.HTTP)); + } + + static class CustomAnnotationFieldResourceInjectionBean { + + @LocalArmeriaPort + private Integer defaultPort; + + @LocalArmeriaPort(SessionProtocol.HTTP) + private Integer httpPort; + + public Integer defaultPort() { + return defaultPort; + } + + public Integer httpPort() { + return httpPort; + } + } +}