/** * Copyright 2015-2017 The OpenZipkin 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 zipkin.storage.elasticsearch.http; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.JsonDataException; import com.squareup.moshi.JsonReader; import com.squareup.moshi.JsonWriter; import java.io.IOException; import okio.Buffer; import okio.ByteString; import zipkin.Annotation; import zipkin.BinaryAnnotation; import zipkin.DependencyLink; import zipkin.Endpoint; import zipkin.Span; import zipkin.internal.Util; import static zipkin.internal.Util.UTF_8; import static zipkin.internal.Util.lowerHexToUnsignedLong; /** * Read-only json adapters resurrected from before we switched to Java 6 as storage components can * be Java 7+ */ final class JsonAdapters { static final JsonAdapter<Span> SPAN_ADAPTER = new JsonAdapter<Span>() { @Override public Span fromJson(JsonReader reader) throws IOException { Span.Builder result = Span.builder(); reader.beginObject(); while (reader.hasNext()) { String nextName = reader.nextName(); if (reader.peek() == JsonReader.Token.NULL) { reader.skipValue(); continue; } switch (nextName) { case "traceId": String traceId = reader.nextString(); if (traceId.length() == 32) { result.traceIdHigh(lowerHexToUnsignedLong(traceId, 0)); } result.traceId(lowerHexToUnsignedLong(traceId)); break; case "name": result.name(reader.nextString()); break; case "id": result.id(Util.lowerHexToUnsignedLong(reader.nextString())); break; case "parentId": result.parentId(Util.lowerHexToUnsignedLong(reader.nextString())); break; case "timestamp": result.timestamp(reader.nextLong()); break; case "duration": result.duration(reader.nextLong()); break; case "annotations": reader.beginArray(); while (reader.hasNext()) { result.addAnnotation(ANNOTATION_ADAPTER.fromJson(reader)); } reader.endArray(); break; case "binaryAnnotations": reader.beginArray(); while (reader.hasNext()) { result.addBinaryAnnotation(BINARY_ANNOTATION_ADAPTER.fromJson(reader)); } reader.endArray(); break; case "debug": result.debug(reader.nextBoolean()); break; default: reader.skipValue(); } } reader.endObject(); return result.build(); } @Override public void toJson(JsonWriter writer, Span value) throws IOException { throw new UnsupportedOperationException(); } }; static final JsonAdapter<Annotation> ANNOTATION_ADAPTER = new JsonAdapter<Annotation>() { @Override public Annotation fromJson(JsonReader reader) throws IOException { Annotation.Builder result = Annotation.builder(); reader.beginObject(); while (reader.hasNext()) { switch (reader.nextName()) { case "timestamp": result.timestamp(reader.nextLong()); break; case "value": result.value(reader.nextString()); break; case "endpoint": result.endpoint(ENDPOINT_ADAPTER.fromJson(reader)); break; default: reader.skipValue(); } } reader.endObject(); return result.build(); } @Override public void toJson(JsonWriter writer, Annotation value) throws IOException { throw new UnsupportedOperationException(); } }; static final JsonAdapter<Endpoint> ENDPOINT_ADAPTER = new JsonAdapter<Endpoint>() { @Override public Endpoint fromJson(JsonReader reader) throws IOException { Endpoint.Builder result = Endpoint.builder(); reader.beginObject(); while (reader.hasNext()) { switch (reader.nextName()) { case "serviceName": result.serviceName(reader.nextString()); break; case "ipv4": case "ipv6": result.parseIp(reader.nextString()); break; case "port": result.port(reader.nextInt()); break; default: reader.skipValue(); } } reader.endObject(); return result.build(); } @Override public void toJson(JsonWriter writer, Endpoint value) throws IOException { throw new UnsupportedOperationException(); } }.nullSafe(); static final JsonAdapter<BinaryAnnotation> BINARY_ANNOTATION_ADAPTER = new JsonAdapter<BinaryAnnotation>() { @Override public BinaryAnnotation fromJson(JsonReader reader) throws IOException { BinaryAnnotation.Builder result = BinaryAnnotation.builder(); String number = null; String string = null; BinaryAnnotation.Type type = BinaryAnnotation.Type.STRING; reader.beginObject(); while (reader.hasNext()) { switch (reader.nextName()) { case "key": result.key(reader.nextString()); break; case "value": switch (reader.peek()) { case BOOLEAN: type = BinaryAnnotation.Type.BOOL; result.value(reader.nextBoolean() ? new byte[] {1} : new byte[] {0}); break; case STRING: string = reader.nextString(); break; case NUMBER: number = reader.nextString(); break; default: throw new JsonDataException( "Expected value to be a boolean, string or number but was " + reader.peek() + " at path " + reader.getPath()); } break; case "type": type = BinaryAnnotation.Type.valueOf(reader.nextString()); break; case "endpoint": result.endpoint(ENDPOINT_ADAPTER.fromJson(reader)); break; default: reader.skipValue(); } } reader.endObject(); result.type(type); switch (type) { case BOOL: return result.build(); case STRING: return result.value(string.getBytes(UTF_8)).build(); case BYTES: return result.value(ByteString.decodeBase64(string).toByteArray()).build(); default: break; } Buffer buffer = new Buffer(); switch (type) { case I16: buffer.writeShort(Short.parseShort(number)); break; case I32: buffer.writeInt(Integer.parseInt(number)); break; case I64: case DOUBLE: if (number == null) number = string; long v = type == BinaryAnnotation.Type.I64 ? Long.parseLong(number) : Double.doubleToRawLongBits(Double.parseDouble(number)); buffer.writeLong(v); break; default: throw new AssertionError( "BinaryAnnotationType " + type + " was added, but not handled"); } return result.value(buffer.readByteArray()).build(); } @Override public void toJson(JsonWriter writer, BinaryAnnotation value) throws IOException { throw new UnsupportedOperationException(); } }; static final JsonAdapter<DependencyLink> DEPENDENCY_LINK_ADAPTER = new JsonAdapter<DependencyLink>() { @Override public DependencyLink fromJson(JsonReader reader) throws IOException { DependencyLink.Builder result = DependencyLink.builder(); reader.beginObject(); while (reader.hasNext()) { switch (reader.nextName()) { case "parent": result.parent(reader.nextString()); break; case "child": result.child(reader.nextString()); break; case "callCount": result.callCount(reader.nextLong()); break; default: reader.skipValue(); } } reader.endObject(); return result.build(); } @Override public void toJson(JsonWriter writer, DependencyLink value) throws IOException { throw new UnsupportedOperationException(); } }; }