diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistry.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistry.java index 05c5676f..c1ba8b25 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistry.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistry.java @@ -18,6 +18,7 @@ package org.springframework.cloud.netflix.eureka.server; import java.util.List; +import com.netflix.eureka.lease.Lease; import org.springframework.beans.BeansException; import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent; import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent; @@ -35,6 +36,7 @@ import com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl; import com.netflix.eureka.resources.ServerCodecs; import lombok.extern.apachecommons.CommonsLog; +import org.springframework.context.ApplicationEvent; /** * @author Spencer Gibb @@ -78,37 +80,27 @@ public class InstanceRegistry extends PeerAwareInstanceRegistryImpl @Override public void register(InstanceInfo info, int leaseDuration, boolean isReplication) { - if (log.isDebugEnabled()) { - log.debug("register " + info.getAppName() + ", vip " + info.getVIPAddress() - + ", leaseDuration " + leaseDuration + ", isReplication " - + isReplication); - } - // TODO: what to publish from info (whole object?) - this.ctxt.publishEvent(new EurekaInstanceRegisteredEvent(this, info, - leaseDuration, isReplication)); - + handleRegistration(info, leaseDuration, isReplication); super.register(info, leaseDuration, isReplication); } @Override - public boolean cancel(String appName, String serverId, boolean isReplication) { - if (log.isDebugEnabled()) { - log.debug("cancel " + appName + " serverId " + serverId + ", isReplication {}" - + isReplication); - } - this.ctxt.publishEvent( - new EurekaInstanceCanceledEvent(this, appName, serverId, isReplication)); + public void register(final InstanceInfo info, final boolean isReplication) { + handleRegistration(info, resolveInstanceLeaseDuration(info), isReplication); + super.register(info, isReplication); + } + @Override + public boolean cancel(String appName, String serverId, boolean isReplication) { + handleCancelation(appName, serverId, isReplication); return super.cancel(appName, serverId, isReplication); } @Override public boolean renew(final String appName, final String serverId, boolean isReplication) { - if (log.isDebugEnabled()) { - log.debug("renew " + appName + " serverId " + serverId + ", isReplication {}" - + isReplication); - } + log("renew " + appName + " serverId " + serverId + ", isReplication {}" + + isReplication); List applications = getSortedApplications(); for (Application input : applications) { if (input.getName().equals(appName)) { @@ -119,11 +111,49 @@ public class InstanceRegistry extends PeerAwareInstanceRegistryImpl break; } } - this.ctxt.publishEvent(new EurekaInstanceRenewedEvent(this, appName, - serverId, instance, isReplication)); + publishEvent(new EurekaInstanceRenewedEvent(this, appName, serverId, + instance, isReplication)); break; } } return super.renew(appName, serverId, isReplication); } + + @Override + protected boolean internalCancel(String appName, String id, boolean isReplication) { + handleCancelation(appName, id, isReplication); + return super.internalCancel(appName, id, isReplication); + } + + private void handleCancelation(String appName, String id, boolean isReplication) { + log("cancel " + appName + ", serverId " + id + ", isReplication " + isReplication); + publishEvent(new EurekaInstanceCanceledEvent(this, appName, id, isReplication)); + } + + private void handleRegistration(InstanceInfo info, int leaseDuration, + boolean isReplication) { + log("register " + info.getAppName() + ", vip " + info.getVIPAddress() + + ", leaseDuration " + leaseDuration + ", isReplication " + + isReplication); + publishEvent(new EurekaInstanceRegisteredEvent(this, info, leaseDuration, + isReplication)); + } + + private void log(String message) { + if (log.isDebugEnabled()) { + log.debug(message); + } + } + + private void publishEvent(ApplicationEvent applicationEvent) { + this.ctxt.publishEvent(applicationEvent); + } + + private int resolveInstanceLeaseDuration(final InstanceInfo info) { + int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS; + if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) { + leaseDuration = info.getLeaseInfo().getDurationInSecs(); + } + return leaseDuration; + } } diff --git a/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistryTest.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistryTest.java new file mode 100644 index 00000000..9689e779 --- /dev/null +++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistryTest.java @@ -0,0 +1,186 @@ +package org.springframework.cloud.netflix.eureka.server; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import com.netflix.discovery.shared.Application; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.cloud.netflix.eureka.server.InstanceRegistryTest.TestApplication; +import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent; +import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent; +import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRenewedEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.netflix.appinfo.InstanceInfo; +import com.netflix.appinfo.LeaseInfo; +import com.netflix.eureka.registry.PeerAwareInstanceRegistry; + +/** + * @author Bartlomiej Slota + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = TestApplication.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + value = {"spring.application.name=eureka", "logging.level.org.springframework." + + "cloud.netflix.eureka.server.InstanceRegistry=DEBUG"}) +public class InstanceRegistryTest { + + private final List applicationEvents = new LinkedList<>(); + private static final String APP_NAME = "MY-APP-NAME"; + private static final String HOST_NAME = "my-host-name"; + + @SpyBean(PeerAwareInstanceRegistry.class) + private InstanceRegistry instanceRegistry; + + @MockBean + private ApplicationListener + instanceRegisteredEventListenerMock; + + @MockBean + private ApplicationListener + instanceCanceledEventListenerMock; + + @MockBean + private ApplicationListener instanceRenewedEventListener; + + @Before + public void setup() { + applicationEvents.clear(); + Answer applicationListenerAnswer = prepareListenerMockAnswer(); + doAnswer(applicationListenerAnswer).when(instanceRegisteredEventListenerMock) + .onApplicationEvent(isA(EurekaInstanceRegisteredEvent.class)); + doAnswer(applicationListenerAnswer).when(instanceCanceledEventListenerMock) + .onApplicationEvent(isA(EurekaInstanceCanceledEvent.class)); + doAnswer(applicationListenerAnswer).when(instanceRenewedEventListener) + .onApplicationEvent(isA(EurekaInstanceRenewedEvent.class)); + } + + + @Test + public void testRegister() throws Exception { + // creating instance info + final LeaseInfo leaseInfo = getLeaseInfo(); + final InstanceInfo instanceInfo = getInstanceInfo(leaseInfo); + // calling tested method + instanceRegistry.register(instanceInfo, false); + // event of proper type is registered + assertEquals(1, applicationEvents.size()); + assertTrue(applicationEvents.get(0) instanceof EurekaInstanceRegisteredEvent); + // event details are correct + final EurekaInstanceRegisteredEvent registeredEvent = + (EurekaInstanceRegisteredEvent) (applicationEvents.get(0)); + assertEquals(instanceInfo, registeredEvent.getInstanceInfo()); + assertEquals(leaseInfo.getDurationInSecs(), registeredEvent.getLeaseDuration()); + assertEquals(instanceRegistry, registeredEvent.getSource()); + assertFalse(registeredEvent.isReplication()); + } + + @Test + public void testDefaultLeaseDurationRegisterEvent() throws Exception { + // creating instance info + final InstanceInfo instanceInfo = getInstanceInfo(null); + // calling tested method + instanceRegistry.register(instanceInfo, false); + // instance info duration is set to default + final EurekaInstanceRegisteredEvent registeredEvent = + (EurekaInstanceRegisteredEvent) (applicationEvents.get(0)); + assertEquals(LeaseInfo.DEFAULT_LEASE_DURATION, + registeredEvent.getLeaseDuration()); + } + + @Test + public void testInternalCancel() throws Exception { + // calling tested method + instanceRegistry.internalCancel(APP_NAME, HOST_NAME, false); + // event of proper type is registered + assertEquals(1, applicationEvents.size()); + assertTrue(applicationEvents.get(0) instanceof EurekaInstanceCanceledEvent); + // event details are correct + final EurekaInstanceCanceledEvent registeredEvent = + (EurekaInstanceCanceledEvent) (applicationEvents.get(0)); + assertEquals(APP_NAME, registeredEvent.getAppName()); + assertEquals(HOST_NAME, registeredEvent.getServerId()); + assertEquals(instanceRegistry, registeredEvent.getSource()); + assertFalse(registeredEvent.isReplication()); + } + + @Test + public void testRenew() throws Exception { + // creating application list + final LeaseInfo leaseInfo = getLeaseInfo(); + final InstanceInfo instanceInfo = getInstanceInfo(leaseInfo); + final List instances = new ArrayList<>(); + instances.add(instanceInfo); + final Application application = new Application(APP_NAME, instances); + final List applications = new ArrayList<>(); + applications.add(application); + // stubbing applications list + doReturn(applications).when(instanceRegistry).getSortedApplications(); + // calling tested method + instanceRegistry.renew(APP_NAME, HOST_NAME, false); + // event of proper type is registered + assertEquals(1, applicationEvents.size()); + assertTrue(applicationEvents.get(0) instanceof EurekaInstanceRenewedEvent); + // event details are correct + final EurekaInstanceRenewedEvent registeredEvent = (EurekaInstanceRenewedEvent) + (applicationEvents.get(0)); + assertEquals(APP_NAME, registeredEvent.getAppName()); + assertEquals(HOST_NAME, registeredEvent.getServerId()); + assertEquals(instanceRegistry, registeredEvent.getSource()); + assertEquals(instanceInfo, registeredEvent.getInstanceInfo()); + assertFalse(registeredEvent.isReplication()); + } + + @Configuration + @EnableAutoConfiguration + @EnableEurekaServer + protected static class TestApplication { + public static void main(String[] args) { + new SpringApplicationBuilder(TestApplication.class).run(args); + } + } + + private LeaseInfo getLeaseInfo() { + LeaseInfo.Builder leaseBuilder = LeaseInfo.Builder.newBuilder(); + leaseBuilder.setRenewalIntervalInSecs(10); + leaseBuilder.setDurationInSecs(15); + return leaseBuilder.build(); + } + + private InstanceInfo getInstanceInfo(LeaseInfo leaseInfo) { + InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder(); + builder.setAppName(APP_NAME); + builder.setHostName(HOST_NAME); + builder.setPort(8008); + builder.setLeaseInfo(leaseInfo); + return builder.build(); + } + + private Answer prepareListenerMockAnswer() { + return new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return applicationEvents + .add((ApplicationEvent) invocation.getArguments()[0]); + } + }; + } +} \ No newline at end of file