1 /* 2 * Copyright 2018, OpenCensus Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.opencensus.common; 18 19 import java.nio.ByteBuffer; 20 import java.nio.ByteOrder; 21 22 /** 23 * A service class to encode/decode {@link ServerStats} as defined by the spec. 24 * 25 * <p>See <a 26 * href="https://github.com/census-instrumentation/opencensus-specs/blob/master/encodings/CensusServerStatsEncoding.md">opencensus-server-stats-specs</a> 27 * for encoding {@code ServerStats} 28 * 29 * <p>Use {@code ServerStatsEncoding.toBytes(ServerStats stats)} to encode. 30 * 31 * <p>Use {@code ServerStatsEncoding.parseBytes(byte[] serialized)} to decode. 32 * 33 * @since 0.16 34 */ 35 public final class ServerStatsEncoding { 36 ServerStatsEncoding()37 private ServerStatsEncoding() {} 38 39 /** 40 * The current encoding version. The value is {@value #CURRENT_VERSION} 41 * 42 * @since 0.16 43 */ 44 public static final byte CURRENT_VERSION = (byte) 0; 45 46 /** 47 * Encodes the {@link ServerStats} as per the Opencensus Summary Span specification. 48 * 49 * @param stats {@code ServerStats} to encode. 50 * @return encoded byte array. 51 * @since 0.16 52 */ toBytes(ServerStats stats)53 public static byte[] toBytes(ServerStats stats) { 54 // Should this be optimized to not include invalid values? 55 56 ByteBuffer bb = ByteBuffer.allocate(ServerStatsFieldEnums.getTotalSize() + 1); 57 bb.order(ByteOrder.LITTLE_ENDIAN); 58 59 // put version 60 bb.put(CURRENT_VERSION); 61 62 bb.put((byte) ServerStatsFieldEnums.Id.SERVER_STATS_LB_LATENCY_ID.value()); 63 bb.putLong(stats.getLbLatencyNs()); 64 65 bb.put((byte) ServerStatsFieldEnums.Id.SERVER_STATS_SERVICE_LATENCY_ID.value()); 66 bb.putLong(stats.getServiceLatencyNs()); 67 68 bb.put((byte) ServerStatsFieldEnums.Id.SERVER_STATS_TRACE_OPTION_ID.value()); 69 bb.put(stats.getTraceOption()); 70 return bb.array(); 71 } 72 73 /** 74 * Decodes serialized byte array to create {@link ServerStats} as per Opencensus Summary Span 75 * specification. 76 * 77 * @param serialized encoded {@code ServerStats} in byte array. 78 * @return decoded {@code ServerStats}. null if decoding fails. 79 * @since 0.16 80 */ parseBytes(byte[] serialized)81 public static ServerStats parseBytes(byte[] serialized) 82 throws ServerStatsDeserializationException { 83 final ByteBuffer bb = ByteBuffer.wrap(serialized); 84 bb.order(ByteOrder.LITTLE_ENDIAN); 85 long serviceLatencyNs = 0L; 86 long lbLatencyNs = 0L; 87 byte traceOption = (byte) 0; 88 89 // Check the version first. 90 if (!bb.hasRemaining()) { 91 throw new ServerStatsDeserializationException("Serialized ServerStats buffer is empty"); 92 } 93 byte version = bb.get(); 94 95 if (version > CURRENT_VERSION || version < 0) { 96 throw new ServerStatsDeserializationException("Invalid ServerStats version: " + version); 97 } 98 99 while (bb.hasRemaining()) { 100 ServerStatsFieldEnums.Id id = ServerStatsFieldEnums.Id.valueOf((int) bb.get() & 0xFF); 101 if (id == null) { 102 // Skip remaining; 103 bb.position(bb.limit()); 104 } else { 105 switch (id) { 106 case SERVER_STATS_LB_LATENCY_ID: 107 lbLatencyNs = bb.getLong(); 108 break; 109 case SERVER_STATS_SERVICE_LATENCY_ID: 110 serviceLatencyNs = bb.getLong(); 111 break; 112 case SERVER_STATS_TRACE_OPTION_ID: 113 traceOption = bb.get(); 114 break; 115 } 116 } 117 } 118 try { 119 return ServerStats.create(lbLatencyNs, serviceLatencyNs, traceOption); 120 } catch (IllegalArgumentException e) { 121 throw new ServerStatsDeserializationException( 122 "Serialized ServiceStats contains invalid values: " + e.getMessage()); 123 } 124 } 125 } 126