From aa273f8af31514e9ec5888b6b43e5049ab128c36 Mon Sep 17 00:00:00 2001 From: Zen Huifer Date: Tue, 14 Jun 2022 18:28:42 +0800 Subject: [PATCH] Adds: RegistrationLifecycle (#1044) --- .../AbstractAutoServiceRegistration.java | 70 ++++- .../RegistrationLifecycle.java | 66 +++++ .../RegistrationManagementLifecycle.java | 55 ++++ ...egistrationRegistrationLifecycleTests.java | 258 ++++++++++++++++++ 4 files changed, 443 insertions(+), 6 deletions(-) create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/RegistrationLifecycle.java create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/RegistrationManagementLifecycle.java create mode 100644 spring-cloud-commons/src/test/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistrationRegistrationLifecycleTests.java diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistration.java index 55244360..75ff107f 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistration.java @@ -16,6 +16,8 @@ package org.springframework.cloud.client.serviceregistry; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -42,6 +44,7 @@ import org.springframework.core.env.Environment; * * @param Registration type passed to the {@link ServiceRegistry}. * @author Spencer Gibb + * @author Zen Huifer */ public abstract class AbstractAutoServiceRegistration implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener { @@ -50,20 +53,27 @@ public abstract class AbstractAutoServiceRegistration private final ServiceRegistry serviceRegistry; - private boolean autoStartup = true; + private final boolean autoStartup = true; - private AtomicBoolean running = new AtomicBoolean(false); + private final AtomicBoolean running = new AtomicBoolean(false); - private int order = 0; + private final int order = 0; + + private final AtomicInteger port = new AtomicInteger(0); private ApplicationContext context; private Environment environment; - private AtomicInteger port = new AtomicInteger(0); - private AutoServiceRegistrationProperties properties; + private List> registrationManagementLifecycles = new ArrayList<>(); + + private List> registrationLifecycles = new ArrayList<>(); + + /** + * @deprecated This function is deprecated and can be replaced by another constructor + */ @Deprecated protected AbstractAutoServiceRegistration(ServiceRegistry serviceRegistry) { this.serviceRegistry = serviceRegistry; @@ -75,6 +85,32 @@ public abstract class AbstractAutoServiceRegistration this.properties = properties; } + protected AbstractAutoServiceRegistration(ServiceRegistry serviceRegistry, + AutoServiceRegistrationProperties properties, + List> registrationManagementLifecycles, + List> registrationLifecycles) { + this.serviceRegistry = serviceRegistry; + this.properties = properties; + this.registrationManagementLifecycles = registrationManagementLifecycles; + this.registrationLifecycles = registrationLifecycles; + } + + protected AbstractAutoServiceRegistration(ServiceRegistry serviceRegistry, + AutoServiceRegistrationProperties properties, List> registrationLifecycles) { + this.serviceRegistry = serviceRegistry; + this.properties = properties; + this.registrationLifecycles = registrationLifecycles; + } + + public void addRegistrationManagementLifecycle( + RegistrationManagementLifecycle registrationManagementLifecycle) { + this.registrationManagementLifecycles.add(registrationManagementLifecycle); + } + + public void addRegistrationLifecycle(RegistrationLifecycle registrationLifecycle) { + this.registrationLifecycles.add(registrationLifecycle); + } + protected ApplicationContext getContext() { return this.context; } @@ -129,9 +165,20 @@ public abstract class AbstractAutoServiceRegistration // because of containerPortInitializer below if (!this.running.get()) { this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration())); + registrationLifecycles.forEach( + registrationLifecycle -> registrationLifecycle.postProcessBeforeStartRegister(getRegistration())); register(); + this.registrationLifecycles.forEach( + registrationLifecycle -> registrationLifecycle.postProcessAfterStartRegister(getRegistration())); if (shouldRegisterManagement()) { - registerManagement(); + this.registrationManagementLifecycles + .forEach(registrationManagementLifecycle -> registrationManagementLifecycle + .postProcessBeforeStartRegisterManagement(getManagementRegistration())); + this.registerManagement(); + registrationManagementLifecycles + .forEach(registrationManagementLifecycle -> registrationManagementLifecycle + .postProcessAfterStartRegisterManagement(getManagementRegistration())); + } this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration())); this.running.compareAndSet(false, true); @@ -260,9 +307,20 @@ public abstract class AbstractAutoServiceRegistration public void stop() { if (this.getRunning().compareAndSet(true, false) && isEnabled()) { + + this.registrationLifecycles.forEach( + registrationLifecycle -> registrationLifecycle.postProcessBeforeStopRegister(getRegistration())); deregister(); + this.registrationLifecycles.forEach( + registrationLifecycle -> registrationLifecycle.postProcessAfterStopRegister(getRegistration())); if (shouldRegisterManagement()) { + this.registrationManagementLifecycles + .forEach(registrationManagementLifecycle -> registrationManagementLifecycle + .postProcessBeforeStopRegisterManagement(getManagementRegistration())); deregisterManagement(); + this.registrationManagementLifecycles + .forEach(registrationManagementLifecycle -> registrationManagementLifecycle + .postProcessAfterStopRegisterManagement(getManagementRegistration())); } this.serviceRegistry.close(); } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/RegistrationLifecycle.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/RegistrationLifecycle.java new file mode 100644 index 00000000..9a25d527 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/RegistrationLifecycle.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed 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 org.springframework.cloud.client.serviceregistry; + +import org.springframework.core.Ordered; + +/** + * Service registration life cycle. This life cycle is only related to + * {@link Registration}. + * + * @author Zen Huifer + */ +public interface RegistrationLifecycle extends Ordered { + + /** + * default order. + */ + int DEFAULT_ORDER = 0; + + /** + * A method executed before registering the local service with the + * {@link ServiceRegistry}. + * @param registration registration + */ + void postProcessBeforeStartRegister(R registration); + + /** + * A method executed after registering the local service with the + * {@link ServiceRegistry}. + * @param registration registration + */ + void postProcessAfterStartRegister(R registration); + + /** + * A method executed before de-registering the local service with the + * {@link ServiceRegistry}. + * @param registration registration + */ + void postProcessBeforeStopRegister(R registration); + + /** + * A method executed after de-registering the local service with the + * {@link ServiceRegistry}. + * @param registration registration + */ + void postProcessAfterStopRegister(R registration); + + default int getOrder() { + return DEFAULT_ORDER; + } + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/RegistrationManagementLifecycle.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/RegistrationManagementLifecycle.java new file mode 100644 index 00000000..a41a57ed --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/RegistrationManagementLifecycle.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed 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 org.springframework.cloud.client.serviceregistry; + +/** + * Service registration life cycle. This life cycle is only related to + * {@link org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration#getManagementRegistration()}. + * + * @author Zen Huifer + */ +public interface RegistrationManagementLifecycle extends RegistrationLifecycle { + + /** + * A method executed before registering the local management service with the + * {@link ServiceRegistry}. + * @param registrationManagement registrationManagement + */ + void postProcessBeforeStartRegisterManagement(R registrationManagement); + + /** + * A method executed after registering the local management service with the + * {@link ServiceRegistry}. + * @param registrationManagement registrationManagement + */ + void postProcessAfterStartRegisterManagement(R registrationManagement); + + /** + * A method executed before de-registering the management local service with the + * {@link ServiceRegistry}. + * @param registrationManagement registrationManagement + */ + void postProcessBeforeStopRegisterManagement(R registrationManagement); + + /** + * A method executed after de-registering the management local service with the + * {@link ServiceRegistry}. + * @param registrationManagement registrationManagement + */ + void postProcessAfterStopRegisterManagement(R registrationManagement); + +} diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistrationRegistrationLifecycleTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistrationRegistrationLifecycleTests.java new file mode 100644 index 00000000..ed8f9826 --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistrationRegistrationLifecycleTests.java @@ -0,0 +1,258 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed 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 org.springframework.cloud.client.serviceregistry; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Zen Huifer + */ +@SpringBootTest(classes = AbstractAutoServiceRegistrationRegistrationLifecycleTests.Config.class) +class AbstractAutoServiceRegistrationRegistrationLifecycleTests { + + @Autowired + @Qualifier("testAutoServiceRegistration") + private TestAutoServiceRegistrationLifecycle autoRegistration; + + @Test + void startTest() { + autoRegistration.start(); + } + + @Test + void stopTest() { + autoRegistration.stop(); + } + + static class RegistrationLifecycleImpl implements RegistrationLifecycle { + + @Override + public void postProcessBeforeStartRegister(TestRegistrationLifecycleRegistration registration) { + registration.getMetadata().putIfAbsent("test_data", "test_data"); + registration.getMetadata().putIfAbsent("is_start", "false"); + } + + @Override + public void postProcessAfterStartRegister(TestRegistrationLifecycleRegistration registration) { + assertThat(registration.getMetadata()).containsKey("test_data"); + assertThat(registration.getMetadata().get("test_data")).isEqualTo("test_data"); + assertThat(registration.getMetadata()).containsKey("is_start"); + registration.getMetadata().putIfAbsent("is_start", "true"); + } + + @Override + public void postProcessBeforeStopRegister(TestRegistrationLifecycleRegistration registration) { + assertThat(registration.getMetadata().get("is_start")).isEqualTo("true"); + Map metadata = registration.getMetadata(); + metadata.putIfAbsent("is_stop", "false"); + metadata.putIfAbsent("is_start", "false"); + } + + @Override + public void postProcessAfterStopRegister(TestRegistrationLifecycleRegistration registration) { + + assertThat(registration.getMetadata().get("test_data")).isEqualTo("test_data"); + assertThat(registration.getMetadata().get("is_stop")).isEqualTo("false"); + assertThat(registration.getMetadata().get("is_start")).isEqualTo("false"); + registration.getMetadata().putIfAbsent("is_stop", "true"); + } + + } + + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + public TestAutoServiceRegistrationLifecycle testAutoServiceRegistration( + AutoServiceRegistrationProperties properties) { + List> registrationLifecycles = new ArrayList<>(); + registrationLifecycles.add(new RegistrationLifecycleImpl()); + return new TestAutoServiceRegistrationLifecycle(properties, registrationLifecycles); + } + + } + + static class TestAutoServiceRegistrationLifecycle + extends AbstractAutoServiceRegistration { + + private final int port = 0; + + TestRegistrationLifecycleRegistration testRegistrationLifecycleRegistration = new TestRegistrationLifecycleRegistration(); + + protected TestAutoServiceRegistrationLifecycle() { + super(new TestRegistrationLifecycleServiceRegistration()); + } + + TestAutoServiceRegistrationLifecycle(AutoServiceRegistrationProperties properties) { + super(new TestRegistrationLifecycleServiceRegistration(), properties); + } + + TestAutoServiceRegistrationLifecycle(AutoServiceRegistrationProperties properties, + List> registrationManagementLifecycles, + List> registrationLifecycles) { + super(new TestRegistrationLifecycleServiceRegistration(), properties, registrationManagementLifecycles, + registrationLifecycles); + } + + TestAutoServiceRegistrationLifecycle(AutoServiceRegistrationProperties properties, + List> registrationLifecycles) { + super(new TestRegistrationLifecycleServiceRegistration(), properties, registrationLifecycles); + } + + @Override + protected AtomicInteger getPort() { + return super.getPort(); + } + + @Override + protected String getAppName() { + return super.getAppName(); + } + + @Override + protected TestRegistrationLifecycleRegistration getRegistration() { + return testRegistrationLifecycleRegistration; + } + + @Override + protected TestRegistrationLifecycleRegistration getManagementRegistration() { + return null; + } + + @Override + protected Object getConfiguration() { + return null; + } + + @Override + protected boolean isEnabled() { + return true; + } + + } + + static class TestRegistrationLifecycleServiceRegistration + implements ServiceRegistry { + + private boolean registered = false; + + private boolean deregistered = false; + + @Override public void register( + TestRegistrationLifecycleRegistration registration) { + if (registration == null) { + throw new NullPointerException(); + } + if (!(registration instanceof TestRegistrationLifecycleRegistration)) { + this.registered = true; + } + } + + @Override + public void deregister(TestRegistrationLifecycleRegistration registration) { + if (registration == null) { + throw new NullPointerException(); + } + if (!(registration instanceof TestRegistrationLifecycleRegistration)) { + this.deregistered = true; + } + } + + @Override + public void close() { + + } + + @Override + public void setStatus(TestRegistrationLifecycleRegistration registration, String status) { + + } + + @Override + public T getStatus(TestRegistrationLifecycleRegistration registration) { + return null; + } + + boolean isRegistered() { + return registered; + } + + boolean isDeregistered() { + return deregistered; + } + + } + + static class TestRegistrationLifecycleRegistration implements Registration { + + private final Map metadata = new HashMap<>(8); + + @Override + public String getInstanceId() { + return "TestRegistrationLifecycleRegistration"; + } + + @Override + public String getServiceId() { + return "test"; + } + + @Override + public String getHost() { + return null; + } + + @Override + public int getPort() { + return 0; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public URI getUri() { + return null; + } + + @Override + public Map getMetadata() { + return this.metadata; + } + + } + +}