From 1c4db22c28f16d51cf7e98cac0da36f877df2bfa Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Fri, 1 Apr 2016 13:02:43 -0600 Subject: [PATCH] Angel metadata.instanceId works with Brixton Fixes gh-642 --- .../eureka/InstanceIdDataCenterInfo.java | 44 ------ .../eureka/server/CloudJacksonJson.java | 128 ++++++++++++++++++ .../server/EurekaServerConfiguration.java | 25 +++- .../eureka/server/ApplicationTests.java | 45 ++++-- 4 files changed, 188 insertions(+), 54 deletions(-) delete mode 100644 spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/InstanceIdDataCenterInfo.java create mode 100644 spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/CloudJacksonJson.java diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/InstanceIdDataCenterInfo.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/InstanceIdDataCenterInfo.java deleted file mode 100644 index b5003e5f..00000000 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/InstanceIdDataCenterInfo.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2013-2015 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; - -/** - * @author Spencer Gibb - */ -public 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 this.instanceId; - } - -} diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/CloudJacksonJson.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/CloudJacksonJson.java new file mode 100644 index 00000000..419f8f4a --- /dev/null +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/CloudJacksonJson.java @@ -0,0 +1,128 @@ +package org.springframework.cloud.netflix.eureka.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.util.HashMap; + +import org.springframework.util.ReflectionUtils; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.netflix.appinfo.DataCenterInfo; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.appinfo.LeaseInfo; +import com.netflix.discovery.converters.EurekaJacksonCodec; +import com.netflix.discovery.converters.EurekaJacksonCodec.InstanceInfoDeserializer; +import com.netflix.discovery.converters.EurekaJacksonCodec.InstanceInfoSerializer; +import com.netflix.discovery.converters.wrappers.CodecWrappers.LegacyJacksonJson; +import com.netflix.discovery.shared.Application; +import com.netflix.discovery.shared.Applications; + +import static com.netflix.discovery.converters.wrappers.CodecWrappers.getCodecName; + +/** + * @author Spencer Gibb + */ +public class CloudJacksonJson extends LegacyJacksonJson { + + protected final CloudJacksonCodec codec = new CloudJacksonCodec(); + + @Override + public String codecName() { + return getCodecName(this.getClass()); + } + + @Override + public String encode(T object) throws IOException { + return codec.writeToString(object); + } + + @Override + public void encode(T object, OutputStream outputStream) throws IOException { + codec.writeTo(object, outputStream); + } + + @Override + public T decode(String textValue, Class type) throws IOException { + return codec.readValue(type, textValue); + } + + @Override + public T decode(InputStream inputStream, Class type) throws IOException { + return codec.readValue(type, inputStream); + } + + static class CloudJacksonCodec extends EurekaJacksonCodec { + private static final Version VERSION = new Version(1, 1, 0, null); + + @SuppressWarnings("deprecation") + public CloudJacksonCodec() { + super(); + + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + SimpleModule module = new SimpleModule("eureka1.x", VERSION); + module.addSerializer(DataCenterInfo.class, new DataCenterInfoSerializer()); + module.addSerializer(InstanceInfo.class, new CloudInstanceInfoSerializer()); + module.addSerializer(Application.class, new ApplicationSerializer()); + module.addSerializer(Applications.class, new ApplicationsSerializer(this.getVersionDeltaKey(), this.getAppHashCodeKey())); + + module.addDeserializer(DataCenterInfo.class, new DataCenterInfoDeserializer()); + module.addDeserializer(LeaseInfo.class, new LeaseInfoDeserializer()); + module.addDeserializer(InstanceInfo.class, new CloudInstanceInfoDeserializer(mapper)); + module.addDeserializer(Application.class, new ApplicationDeserializer(mapper)); + module.addDeserializer(Applications.class, new ApplicationsDeserializer(mapper, this.getVersionDeltaKey(), this.getAppHashCodeKey())); + + mapper.registerModule(module); + + HashMap, ObjectReader> readers = new HashMap<>(); + readers.put(InstanceInfo.class, mapper.reader().withType(InstanceInfo.class).withRootName("instance")); + readers.put(Application.class, mapper.reader().withType(Application.class).withRootName("application")); + readers.put(Applications.class, mapper.reader().withType(Applications.class).withRootName("applications")); + setField("objectReaderByClass", readers); + + HashMap, ObjectWriter> writers = new HashMap<>(); + writers.put(InstanceInfo.class, mapper.writer().withType(InstanceInfo.class).withRootName("instance")); + writers.put(Application.class, mapper.writer().withType(Application.class).withRootName("application")); + writers.put(Applications.class, mapper.writer().withType(Applications.class).withRootName("applications")); + setField("objectWriterByClass", writers); + + setField("mapper", mapper); + } + + void setField(String name, Object value) { + Field field = ReflectionUtils.findField(EurekaJacksonCodec.class, name); + ReflectionUtils.makeAccessible(field); + ReflectionUtils.setField(field, this, value); + } + } + + static class CloudInstanceInfoSerializer extends InstanceInfoSerializer { + @Override + public void serialize(InstanceInfo info, JsonGenerator jgen, SerializerProvider provider) throws IOException { + + if (info.getInstanceId() == null && info.getMetadata() != null) { + String instanceId = info.getMetadata().get("instanceId"); + info = new InstanceInfo.Builder(info).setInstanceId(instanceId).build(); + } + + super.serialize(info, jgen, provider); + } + } + + static class CloudInstanceInfoDeserializer extends InstanceInfoDeserializer { + + protected CloudInstanceInfoDeserializer(ObjectMapper mapper) { + super(mapper); + } + } +} diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java index 92f7c4cc..2295be25 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java @@ -52,6 +52,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClientConfig; +import com.netflix.discovery.converters.wrappers.CodecWrapper; +import com.netflix.discovery.converters.wrappers.CodecWrappers; import com.netflix.eureka.DefaultEurekaServerContext; import com.netflix.eureka.EurekaServerConfig; import com.netflix.eureka.EurekaServerContext; @@ -135,7 +137,28 @@ public class EurekaServerConfiguration extends WebMvcConfigurerAdapter { @Bean public ServerCodecs serverCodecs() { - return new DefaultServerCodecs(this.eurekaServerConfig); + CodecWrappers.registerWrapper(new CloudJacksonJson()); + return new CloudServerCodecs(this.eurekaServerConfig); + } + + private static CodecWrapper getFullJson(EurekaServerConfig serverConfig) { + CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getJsonCodecName()); + return codec == null ? CodecWrappers.getCodec(CloudJacksonJson.class) : codec; + } + + private static CodecWrapper getFullXml(EurekaServerConfig serverConfig) { + CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getXmlCodecName()); + return codec == null ? CodecWrappers.getCodec(CodecWrappers.XStreamXml.class) : codec; + } + + class CloudServerCodecs extends DefaultServerCodecs { + + public CloudServerCodecs(EurekaServerConfig serverConfig) { + super(getFullJson(serverConfig), + CodecWrappers.getCodec(CodecWrappers.JacksonJsonMini.class), + getFullXml(serverConfig), + CodecWrappers.getCodec(CodecWrappers.JacksonXmlMini.class)); + } } @Bean diff --git a/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/ApplicationTests.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/ApplicationTests.java index d613913b..a3e3a2b4 100644 --- a/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/ApplicationTests.java +++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/ApplicationTests.java @@ -21,6 +21,7 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -38,9 +39,16 @@ import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.converters.wrappers.CodecWrapper; +import com.netflix.eureka.resources.ServerCodecs; + +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @RunWith(SpringJUnit4ClassRunner.class) @@ -52,15 +60,8 @@ public class ApplicationTests { @Value("${local.server.port}") private int port = 0; - @Configuration - @EnableAutoConfiguration - @EnableEurekaServer - protected static class Application { - public static void main(String[] args) { - new SpringApplicationBuilder(Application.class).properties( - "spring.application.name=eureka").run(args); - } - } + @Autowired + private ServerCodecs serverCodecs; @Test public void catalogLoads() { @@ -103,4 +104,30 @@ public class ApplicationTests { assertNotNull(body); assertTrue("css wasn't preprocessed", body.contains("spring-logo")); } + + @Test + public void customCodecWorks() throws Exception { + assertThat("serverCodecs is wrong type", this.serverCodecs, is(instanceOf(EurekaServerConfiguration.CloudServerCodecs.class))); + CodecWrapper codec = this.serverCodecs.getFullJsonCodec(); + assertThat("codec is wrong type", codec, is(instanceOf(CloudJacksonJson.class))); + + InstanceInfo instanceInfo = InstanceInfo.Builder.newBuilder() + .setAppName("fooapp") + .add("instanceId", "foo") + .build(); + String encoded = codec.encode(instanceInfo); + InstanceInfo decoded = codec.decode(encoded, InstanceInfo.class); + assertThat("instanceId was wrong", decoded.getInstanceId(), is("foo")); + } + + @Configuration + @EnableAutoConfiguration + @EnableEurekaServer + protected static class Application { + public static void main(String[] args) { + new SpringApplicationBuilder(Application.class).properties( + "spring.application.name=eureka").run(args); + } + } + }