diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareMarshallingStrategy.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareMarshallingStrategy.java index 81f1f75a..372adc69 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareMarshallingStrategy.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareMarshallingStrategy.java @@ -16,20 +16,24 @@ package org.springframework.cloud.netflix.eureka; import com.netflix.appinfo.DataCenterInfo; -import com.netflix.appinfo.UniqueIdentifier; import com.netflix.appinfo.DataCenterInfo.Name; import com.netflix.appinfo.InstanceInfo; +import com.netflix.appinfo.UniqueIdentifier; +import com.netflix.discovery.converters.Converters.ApplicationsConverter; import com.netflix.discovery.converters.Converters.InstanceInfoConverter; +import com.netflix.discovery.shared.Applications; import com.thoughtworks.xstream.MarshallingStrategy; -import com.thoughtworks.xstream.converters.Converter; -import com.thoughtworks.xstream.converters.ConverterLookup; -import com.thoughtworks.xstream.converters.DataHolder; -import com.thoughtworks.xstream.converters.MarshallingContext; -import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.converters.*; import com.thoughtworks.xstream.core.TreeMarshallingStrategy; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.mapper.Mapper; +import lombok.extern.slf4j.Slf4j; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.cloud.client.discovery.DiscoveryHeartbeatEvent; +import org.springframework.context.ApplicationContext; /** * A special purpose wrapper for an XStream TreeMarshallingStrategy that is aware of the @@ -47,18 +51,23 @@ import com.thoughtworks.xstream.mapper.Mapper; public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy { private TreeMarshallingStrategy delegate = new TreeMarshallingStrategy(); + private ApplicationContext context; + + public DataCenterAwareMarshallingStrategy(ApplicationContext context) { + this.context = context; + } @Override public Object unmarshal(Object root, HierarchicalStreamReader reader, DataHolder dataHolder, ConverterLookup converterLookup, Mapper mapper) { - ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup); + ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup, context); return delegate.unmarshal(root, reader, dataHolder, wrapped, mapper); } @Override public void marshal(HierarchicalStreamWriter writer, Object obj, ConverterLookup converterLookup, Mapper mapper, DataHolder dataHolder) { - ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup); + ConverterLookup wrapped = new DataCenterAwareConverterLookup(converterLookup, context); delegate.marshal(writer, obj, wrapped, mapper, dataHolder); } @@ -86,9 +95,11 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy { private static class DataCenterAwareConverterLookup implements ConverterLookup { private ConverterLookup delegate; + private ApplicationContext context; - public DataCenterAwareConverterLookup(ConverterLookup delegate) { + public DataCenterAwareConverterLookup(ConverterLookup delegate, ApplicationContext context) { this.delegate = delegate; + this.context = context; } @Override @@ -96,12 +107,52 @@ public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy { Converter converter = delegate.lookupConverterForType(type); if (InstanceInfo.class == type) { return new DataCenterAwareConverter(); + } else if (Applications.class == type) { + return new PublishingApplicationsConverter(context); } return converter; } } + private static class PublishingApplicationsConverter extends ApplicationsConverter { + + private ApplicationContext context; + + public PublishingApplicationsConverter(ApplicationContext context) { + this.context = context; + } + + @Override + public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext unmarshallingContext) { + Object obj = super.unmarshal(reader, unmarshallingContext); + + ProxyFactory factory = new ProxyFactory(obj); + factory.addAdvice(new SetVersionInterceptor(this.context)); + return factory.getProxy(); + } + } + + @Slf4j + private static class SetVersionInterceptor implements MethodInterceptor { + private ApplicationContext context; + + public SetVersionInterceptor(ApplicationContext context) { + this.context = context; + } + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Object ret = invocation.proceed(); + if ("setVersion".equals(invocation.getMethod().getName())) { + Long version = Long.class.cast(invocation.getArguments()[0]); + log.debug("Applications.setVersion() called with version: " + version); + context.publishEvent(new DiscoveryHeartbeatEvent(invocation.getThis(), version)); + } + return ret; + } + } + private static class DataCenterAwareConverter extends InstanceInfoConverter { @Override diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java index 278d3eba..a3a3696d 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java @@ -17,10 +17,12 @@ package org.springframework.cloud.netflix.eureka; import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -39,12 +41,15 @@ import com.netflix.discovery.converters.XmlXStream; @ConditionalOnExpression("${eureka.client.enabled:true}") public class EurekaClientAutoConfiguration { + @Autowired + ApplicationContext context; + @PostConstruct public void init() { XmlXStream.getInstance().setMarshallingStrategy( - new DataCenterAwareMarshallingStrategy()); + new DataCenterAwareMarshallingStrategy(context)); JsonXStream.getInstance().setMarshallingStrategy( - new DataCenterAwareMarshallingStrategy()); + new DataCenterAwareMarshallingStrategy(context)); } @Bean diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClient.java index 0356ef7c..91c79588 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClient.java @@ -1,6 +1,7 @@ package org.springframework.cloud.netflix.eureka; import com.google.common.base.Function; +import com.google.common.base.Predicates; import com.google.common.collect.Lists; import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.shared.Application; @@ -95,13 +96,16 @@ public class EurekaDiscoveryClient implements DiscoveryClient { if (applications == null) { return Collections.emptyList(); } - return Lists.newArrayList(transform(applications.getRegisteredApplications(), new Function() { + return Lists.newArrayList(filter(transform(applications.getRegisteredApplications(), new Function() { @Nullable @Override public String apply(@Nullable Application app) { + if (app.getInstances().isEmpty()) { + return null; + } return app.getName().toLowerCase(); } - })); + }), Predicates.notNull())); } @Override diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulHandlerMapping.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulHandlerMapping.java index 382336c3..8425fd1e 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulHandlerMapping.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulHandlerMapping.java @@ -1,6 +1,7 @@ package org.springframework.cloud.netflix.zuul; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.discovery.DiscoveryHeartbeatEvent; import org.springframework.cloud.client.discovery.InstanceRegisteredEvent; import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; import org.springframework.context.ApplicationEvent; @@ -12,6 +13,7 @@ import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping; import java.util.Collection; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; /** * MVC HandlerMapping that maps incoming request paths to remote services. @@ -27,6 +29,8 @@ public class ZuulHandlerMapping extends AbstractUrlHandlerMapping implements private ZuulController zuul; + private AtomicReference latestHeartbeat = new AtomicReference<>(); + @Autowired public ZuulHandlerMapping(ProxyRouteLocator routeLocator, ZuulController zuul) { this.routeLocator = routeLocator; @@ -39,6 +43,12 @@ public class ZuulHandlerMapping extends AbstractUrlHandlerMapping implements if (event instanceof InstanceRegisteredEvent || event instanceof RefreshScopeRefreshedEvent) { reset(); + } else if (event instanceof DiscoveryHeartbeatEvent) { + DiscoveryHeartbeatEvent e = (DiscoveryHeartbeatEvent) event; + if (latestHeartbeat.get() == null || !latestHeartbeat.get().equals(e.getValue())) { + latestHeartbeat.set(e.getValue()); + reset(); + } } } diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java index c5b661e7..fd549342 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerInitializerConfiguration.java @@ -120,9 +120,9 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware EurekaServerConfigurationManager.getInstance() .setConfiguration(eurekaServerConfig); XmlXStream.getInstance().setMarshallingStrategy( - new DataCenterAwareMarshallingStrategy()); + new DataCenterAwareMarshallingStrategy(applicationContext)); JsonXStream.getInstance().setMarshallingStrategy( - new DataCenterAwareMarshallingStrategy()); + new DataCenterAwareMarshallingStrategy(applicationContext)); // PeerAwareInstanceRegistry.getInstance(); applicationContext .publishEvent(new EurekaRegistryAvailableEvent(