package com.netflix.discovery.converters.wrappers;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.netflix.appinfo.EurekaAccept;
import com.netflix.discovery.converters.EurekaJacksonCodec;
import com.netflix.discovery.converters.JsonXStream;
import com.netflix.discovery.converters.KeyFormatter;
import com.netflix.discovery.converters.XmlXStream;
import com.netflix.discovery.converters.jackson.EurekaJsonJacksonCodec;
import com.netflix.discovery.converters.jackson.EurekaXmlJacksonCodec;
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClientImpl;
/**
* This is just a helper class during transition when multiple codecs are supported. One day this should all go away
* when there is only 1 type of json and xml codecs each.
*
* For adding custom codecs to Discovery, prefer creating a custom EurekaJerseyClient to added to DiscoveryClient
* either completely independently or via
* {@link EurekaJerseyClientImpl.EurekaJerseyClientBuilder#withDecoderWrapper(DecoderWrapper)}
* and
* {@link EurekaJerseyClientImpl.EurekaJerseyClientBuilder#withEncoderWrapper(EncoderWrapper)}
*
* @author David Liu
*/
public final class CodecWrappers {
private static final Map<String, CodecWrapper> CODECS = new ConcurrentHashMap<>();
/**
* For transition use: register a new codec wrapper.
*/
public static void registerWrapper(CodecWrapper wrapper) {
CODECS.put(wrapper.codecName(), wrapper);
}
public static <T extends CodecWrapperBase> String getCodecName(Class<T> clazz) {
return clazz.getSimpleName();
}
public static <T extends CodecWrapper> CodecWrapper getCodec(Class<T> clazz) {
return getCodec(getCodecName(clazz));
}
public static synchronized CodecWrapper getCodec(String name) {
if (name == null) {
return null;
}
if (!CODECS.containsKey(name)) {
CodecWrapper wrapper = create(name);
if (wrapper != null) {
CODECS.put(wrapper.codecName(), wrapper);
}
}
return CODECS.get(name);
}
public static <T extends EncoderWrapper> EncoderWrapper getEncoder(Class<T> clazz) {
return getEncoder(getCodecName(clazz));
}
public static synchronized EncoderWrapper getEncoder(String name) {
if (name == null) {
return null;
}
if (!CODECS.containsKey(name)) {
CodecWrapper wrapper = create(name);
if (wrapper != null) {
CODECS.put(wrapper.codecName(), wrapper);
}
}
return CODECS.get(name);
}
public static <T extends DecoderWrapper> DecoderWrapper getDecoder(Class<T> clazz) {
return getDecoder(getCodecName(clazz));
}
/**
* Resolve the decoder to use based on the specified decoder name, as well as the specified eurekaAccept.
* The eurekAccept trumps the decoder name if the decoder specified is one that is not valid for use for the
* specified eurekaAccept.
*/
public static synchronized DecoderWrapper resolveDecoder(String name, String eurekaAccept) {
EurekaAccept accept = EurekaAccept.fromString(eurekaAccept);
switch (accept) {
case compact:
return getDecoder(JacksonJsonMini.class);
case full:
default:
return getDecoder(name);
}
}
public static synchronized DecoderWrapper getDecoder(String name) {
if (name == null) {
return null;
}
if (!CODECS.containsKey(name)) {
CodecWrapper wrapper = create(name);
if (wrapper != null) {
CODECS.put(wrapper.codecName(), wrapper);
}
}
return CODECS.get(name);
}
private static CodecWrapper create(String name) {
if (getCodecName(JacksonJson.class).equals(name)) {
return new JacksonJson();
} else if (getCodecName(JacksonJsonMini.class).equals(name)) {
return new JacksonJsonMini();
} else if (getCodecName(LegacyJacksonJson.class).equals(name)) {
return new LegacyJacksonJson();
} else if (getCodecName(XStreamJson.class).equals(name)) {
return new XStreamJson();
} else if (getCodecName(JacksonXml.class).equals(name)) {
return new JacksonXml();
} else if (getCodecName(JacksonXmlMini.class).equals(name)) {
return new JacksonXmlMini();
} else if (getCodecName(XStreamXml.class).equals(name)) {
return new XStreamXml();
} else {
return null;
}
}
// ========================
// wrapper definitions
// ========================
public static class JacksonJson implements CodecWrapper {
protected final EurekaJsonJacksonCodec codec = new EurekaJsonJacksonCodec();
@Override
public String codecName() {
return getCodecName(this.getClass());
}
@Override
public boolean support(MediaType mediaType) {
return mediaType.equals(MediaType.APPLICATION_JSON_TYPE);
}
@Override
public <T> String encode(T object) throws IOException {
return codec.getObjectMapper(object.getClass()).writeValueAsString(object);
}
@Override
public <T> void encode(T object, OutputStream outputStream) throws IOException {
codec.writeTo(object, outputStream);
}
@Override
public <T> T decode(String textValue, Class<T> type) throws IOException {
return codec.getObjectMapper(type).readValue(textValue, type);
}
@Override
public <T> T decode(InputStream inputStream, Class<T> type) throws IOException {
return codec.getObjectMapper(type).readValue(inputStream, type);
}
}
public static class JacksonJsonMini implements CodecWrapper {
protected final EurekaJsonJacksonCodec codec = new EurekaJsonJacksonCodec(KeyFormatter.defaultKeyFormatter(), true);
@Override
public String codecName() {
return getCodecName(this.getClass());
}
@Override
public boolean support(MediaType mediaType) {
return mediaType.equals(MediaType.APPLICATION_JSON_TYPE);
}
@Override
public <T> String encode(T object) throws IOException {
return codec.getObjectMapper(object.getClass()).writeValueAsString(object);
}
@Override
public <T> void encode(T object, OutputStream outputStream) throws IOException {
codec.writeTo(object, outputStream);
}
@Override
public <T> T decode(String textValue, Class<T> type) throws IOException {
return codec.getObjectMapper(type).readValue(textValue, type);
}
@Override
public <T> T decode(InputStream inputStream, Class<T> type) throws IOException {
return codec.getObjectMapper(type).readValue(inputStream, type);
}
}
public static class JacksonXml implements CodecWrapper {
protected final EurekaXmlJacksonCodec codec = new EurekaXmlJacksonCodec();
@Override
public String codecName() {
return getCodecName(this.getClass());
}
@Override
public boolean support(MediaType mediaType) {
return mediaType.equals(MediaType.APPLICATION_XML_TYPE);
}
@Override
public <T> String encode(T object) throws IOException {
return codec.getObjectMapper(object.getClass()).writeValueAsString(object);
}
@Override
public <T> void encode(T object, OutputStream outputStream) throws IOException {
codec.writeTo(object, outputStream);
}
@Override
public <T> T decode(String textValue, Class<T> type) throws IOException {
return codec.getObjectMapper(type).readValue(textValue, type);
}
@Override
public <T> T decode(InputStream inputStream, Class<T> type) throws IOException {
return codec.getObjectMapper(type).readValue(inputStream, type);
}
}
public static class JacksonXmlMini implements CodecWrapper {
protected final EurekaXmlJacksonCodec codec = new EurekaXmlJacksonCodec(KeyFormatter.defaultKeyFormatter(), true);
@Override
public String codecName() {
return getCodecName(this.getClass());
}
@Override
public boolean support(MediaType mediaType) {
return mediaType.equals(MediaType.APPLICATION_XML_TYPE);
}
@Override
public <T> String encode(T object) throws IOException {
return codec.getObjectMapper(object.getClass()).writeValueAsString(object);
}
@Override
public <T> void encode(T object, OutputStream outputStream) throws IOException {
codec.writeTo(object, outputStream);
}
@Override
public <T> T decode(String textValue, Class<T> type) throws IOException {
return codec.getObjectMapper(type).readValue(textValue, type);
}
@Override
public <T> T decode(InputStream inputStream, Class<T> type) throws IOException {
return codec.getObjectMapper(type).readValue(inputStream, type);
}
}
public static class LegacyJacksonJson implements CodecWrapper {
protected final EurekaJacksonCodec codec = new EurekaJacksonCodec();
@Override
public String codecName() {
return getCodecName(this.getClass());
}
@Override
public boolean support(MediaType mediaType) {
return mediaType.equals(MediaType.APPLICATION_JSON_TYPE);
}
@Override
public <T> String encode(T object) throws IOException {
return codec.writeToString(object);
}
@Override
public <T> void encode(T object, OutputStream outputStream) throws IOException {
codec.writeTo(object, outputStream);
}
@Override
public <T> T decode(String textValue, Class<T> type) throws IOException {
return codec.readValue(type, textValue);
}
@Override
public <T> T decode(InputStream inputStream, Class<T> type) throws IOException {
return codec.readValue(type, inputStream);
}
}
public static class XStreamJson implements CodecWrapper {
protected final JsonXStream codec = JsonXStream.getInstance();
@Override
public String codecName() {
return getCodecName(this.getClass());
}
@Override
public boolean support(MediaType mediaType) {
return mediaType.equals(MediaType.APPLICATION_JSON_TYPE);
}
@Override
public <T> String encode(T object) throws IOException {
return codec.toXML(object);
}
@Override
public <T> void encode(T object, OutputStream outputStream) throws IOException {
codec.toXML(object, outputStream);
}
@Override
public <T> T decode(String textValue, Class<T> type) throws IOException {
return (T) codec.fromXML(textValue, type);
}
@Override
public <T> T decode(InputStream inputStream, Class<T> type) throws IOException {
return (T) codec.fromXML(inputStream, type);
}
}
/**
* @author David Liu
*/
public static class XStreamXml implements CodecWrapper {
protected final XmlXStream codec = XmlXStream.getInstance();
@Override
public String codecName() {
return CodecWrappers.getCodecName(this.getClass());
}
@Override
public boolean support(MediaType mediaType) {
return mediaType.equals(MediaType.APPLICATION_XML_TYPE);
}
@Override
public <T> String encode(T object) throws IOException {
return codec.toXML(object);
}
@Override
public <T> void encode(T object, OutputStream outputStream) throws IOException {
codec.toXML(object, outputStream);
}
@Override
public <T> T decode(String textValue, Class<T> type) throws IOException {
return (T) codec.fromXML(textValue, type);
}
@Override
public <T> T decode(InputStream inputStream, Class<T> type) throws IOException {
return (T) codec.fromXML(inputStream, type);
}
}
}