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 new file mode 100644 index 00000000..99b105f7 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/DataCenterAwareMarshallingStrategy.java @@ -0,0 +1,92 @@ +/* + * Copyright 2013-2014 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 + * + * http://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.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.thoughtworks.xstream.MarshallingStrategy; +import com.thoughtworks.xstream.converters.ConverterLookup; +import com.thoughtworks.xstream.converters.DataHolder; +import com.thoughtworks.xstream.core.TreeMarshallingStrategy; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import com.thoughtworks.xstream.mapper.Mapper; + +/** + * A special purpose wrapper for an XStream TreeMarshallingStrategy that is aware of the + * {@link InstanceInfo} type and can create a more useful {@link DataCenterInfo} for it + * after unmarshalling. If the InstanceInfo has a metadataMap containing an + * instanceId, and the DataCenterInfo is not already an AmazonInfo, then the + * instanceId is used to create an identifier, but appending it to the existing one. This + * is useful when not running Eureka in bare EC2 VMs, so the EC2 metadata is not available + * for uniquely identifying the InstanceInfo (the default is to just use the hostname, but + * that isn't very useful when sitting behind a proxy). + * + * @author Dave Syer + * + */ +public class DataCenterAwareMarshallingStrategy implements MarshallingStrategy { + + private TreeMarshallingStrategy delegate = new TreeMarshallingStrategy(); + + @Override + public Object unmarshal(Object root, HierarchicalStreamReader reader, + DataHolder dataHolder, ConverterLookup converterLookup, Mapper mapper) { + Object obj = delegate + .unmarshal(root, reader, dataHolder, converterLookup, mapper); + if (obj instanceof InstanceInfo) { + InstanceInfo info = (InstanceInfo) obj; + String instanceId = info.getMetadata().get("instanceId"); + DataCenterInfo dataCenter = info.getDataCenterInfo(); + if (instanceId != null && Name.Amazon != dataCenter.getName()) { + String old = info.getId(); + info = new InstanceInfo.Builder(info).setDataCenterInfo( + new InstanceIdDataCenterInfo(old + ":" + instanceId)).build(); + obj = info; + } + } + return obj; + } + + @Override + public void marshal(HierarchicalStreamWriter writer, Object obj, + ConverterLookup converterLookup, Mapper mapper, DataHolder dataHolder) { + delegate.marshal(writer, obj, converterLookup, mapper, dataHolder); + } + + public static class InstanceIdDataCenterInfo implements DataCenterInfo, UniqueIdentifier { + + private String instanceId; + + public InstanceIdDataCenterInfo(String instanceId) { + this.instanceId = instanceId; + } + + @Override + public Name getName() { + return Name.MyOwn; + } + + @Override + public String getId() { + return instanceId; + } + + } + +} 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 58c44951..3f50285b 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 @@ -15,6 +15,8 @@ */ package org.springframework.cloud.netflix.eureka; +import javax.annotation.PostConstruct; + import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -24,6 +26,8 @@ import org.springframework.context.annotation.Configuration; import com.netflix.appinfo.EurekaInstanceConfig; import com.netflix.discovery.EurekaClientConfig; +import com.netflix.discovery.converters.JsonXStream; +import com.netflix.discovery.converters.XmlXStream; /** * @author Dave Syer @@ -34,6 +38,12 @@ import com.netflix.discovery.EurekaClientConfig; @ConditionalOnClass(EurekaClientConfig.class) @ConditionalOnExpression("${eureka.client.enabled:true}") public class EurekaClientAutoConfiguration { + + @PostConstruct + public void init() { + XmlXStream.getInstance().setMarshallingStrategy(new DataCenterAwareMarshallingStrategy()); + JsonXStream.getInstance().setMarshallingStrategy(new DataCenterAwareMarshallingStrategy()); + } @Bean @ConditionalOnMissingBean(EurekaClientConfig.class) 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 9644e232..5dda2ed4 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 @@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.netflix.eureka.DataCenterAwareMarshallingStrategy; import org.springframework.cloud.netflix.eureka.DiscoveryManagerInitializer; import org.springframework.cloud.netflix.eureka.EurekaServerConfigBean; import org.springframework.cloud.netflix.eureka.server.advice.LeaseManagerLite; @@ -47,6 +48,8 @@ import org.springframework.util.ReflectionUtils; import org.springframework.web.context.ServletContextAware; import com.netflix.blitz4j.LoggingConfiguration; +import com.netflix.discovery.converters.JsonXStream; +import com.netflix.discovery.converters.XmlXStream; import com.netflix.eureka.EurekaBootStrap; import com.netflix.eureka.EurekaServerConfig; import com.netflix.eureka.EurekaServerConfigurationManager; @@ -100,6 +103,8 @@ public class EurekaServerInitializerConfiguration implements ServletContextAware LoggingConfiguration.getInstance().configure(); EurekaServerConfigurationManager.getInstance() .setConfiguration(eurekaServerConfig); + XmlXStream.getInstance().setMarshallingStrategy(new DataCenterAwareMarshallingStrategy()); + JsonXStream.getInstance().setMarshallingStrategy(new DataCenterAwareMarshallingStrategy()); // PeerAwareInstanceRegistry.getInstance(); applicationContext .publishEvent(new EurekaRegistryAvailableEvent(