1 /* 2 * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jfr.consumer; 27 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Objects; 32 33 import jdk.jfr.EventType; 34 import jdk.jfr.ValueDescriptor; 35 import jdk.jfr.internal.MetadataDescriptor; 36 import jdk.jfr.internal.PrivateAccess; 37 import jdk.jfr.internal.Type; 38 import jdk.jfr.internal.consumer.RecordingInput; 39 40 /** 41 * Class that create parsers suitable for reading events and constant pools 42 */ 43 final class ParserFactory { 44 private final LongMap<Parser> parsers = new LongMap<>(); 45 private final TimeConverter timeConverter; 46 private final LongMap<Type> types = new LongMap<>(); 47 private final LongMap<ConstantMap> constantPools; 48 49 public ParserFactory(MetadataDescriptor metadata, TimeConverter timeConverter) throws IOException { 50 this.constantPools = new LongMap<>(); 51 this.timeConverter = timeConverter; 52 for (Type t : metadata.getTypes()) { 53 types.put(t.getId(), t); 54 } 55 for (Type t : types) { 56 if (!t.getFields().isEmpty()) { // Avoid primitives 57 CompositeParser cp = createCompositeParser(t); 58 if (t.isSimpleType()) { // Reduce to nested parser 59 parsers.put(t.getId(), cp.parsers[0]); 60 } 61 62 } 63 } 64 // Override event types with event parsers 65 for (EventType t : metadata.getEventTypes()) { 66 parsers.put(t.getId(), createEventParser(t)); 67 } 68 } 69 70 public LongMap<Parser> getParsers() { 71 return parsers; 72 } 73 74 public LongMap<ConstantMap> getConstantPools() { 75 return constantPools; 76 } 77 78 public LongMap<Type> getTypeMap() { 79 return types; 80 } 81 82 private EventParser createEventParser(EventType eventType) throws IOException { 83 List<Parser> parsers = new ArrayList<Parser>(); 84 for (ValueDescriptor f : eventType.getFields()) { 85 parsers.add(createParser(f)); 86 } 87 return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0])); 88 } 89 90 private Parser createParser(ValueDescriptor v) throws IOException { 91 boolean constantPool = PrivateAccess.getInstance().isConstantPool(v); 92 if (v.isArray()) { 93 Type valueType = PrivateAccess.getInstance().getType(v); 94 ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null); 95 return new ArrayParser(createParser(element)); 96 } 97 long id = v.getTypeId(); 98 Type type = types.get(id); 99 if (type == null) { 100 throw new IOException("Type '" + v.getTypeName() + "' is not defined"); 101 } 102 if (constantPool) { 103 ConstantMap pool = constantPools.get(id); 104 if (pool == null) { 105 pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); 106 constantPools.put(id, pool); 107 } 108 return new ConstantMapValueParser(pool); 109 } 110 Parser parser = parsers.get(id); 111 if (parser == null) { 112 if (!v.getFields().isEmpty()) { 113 return createCompositeParser(type); 114 } else { 115 return registerParserType(type, createPrimitiveParser(type)); 116 } 117 } 118 return parser; 119 } 120 121 private Parser createPrimitiveParser(Type type) throws IOException { 122 switch (type.getName()) { 123 case "int": 124 return new IntegerParser(); 125 case "long": 126 return new LongParser(); 127 case "float": 128 return new FloatParser(); 129 case "double": 130 return new DoubleParser(); 131 case "char": 132 return new CharacterParser(); 133 case "boolean": 134 return new BooleanParser(); 135 case "short": 136 return new ShortParser(); 137 case "byte": 138 return new ByteParser(); 139 case "java.lang.String": 140 ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); 141 constantPools.put(type.getId(), pool); 142 return new StringParser(pool); 143 default: 144 throw new IOException("Unknown primitive type " + type.getName()); 145 } 146 } 147 148 private Parser registerParserType(Type t, Parser parser) { 149 Parser p = parsers.get(t.getId()); 150 // check if parser exists (known type) 151 if (p != null) { 152 return p; 153 } 154 parsers.put(t.getId(), parser); 155 return parser; 156 } 157 158 private CompositeParser createCompositeParser(Type type) throws IOException { 159 List<ValueDescriptor> vds = type.getFields(); 160 Parser[] parsers = new Parser[vds.size()]; 161 CompositeParser composite = new CompositeParser(parsers); 162 // need to pre-register so recursive types can be handled 163 registerParserType(type, composite); 164 165 int index = 0; 166 for (ValueDescriptor vd : vds) { 167 parsers[index++] = createParser(vd); 168 } 169 return composite; 170 } 171 172 private static final class BooleanParser extends Parser { 173 @Override 174 public Object parse(RecordingInput input) throws IOException { 175 return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE; 176 } 177 } 178 179 private static final class ByteParser extends Parser { 180 @Override 181 public Object parse(RecordingInput input) throws IOException { 182 return Byte.valueOf(input.readByte()); 183 } 184 } 185 186 private static final class LongParser extends Parser { 187 @Override 188 public Object parse(RecordingInput input) throws IOException { 189 return Long.valueOf(input.readLong()); 190 } 191 } 192 193 private static final class IntegerParser extends Parser { 194 @Override 195 public Object parse(RecordingInput input) throws IOException { 196 return Integer.valueOf(input.readInt()); 197 } 198 } 199 200 private static final class ShortParser extends Parser { 201 @Override 202 public Object parse(RecordingInput input) throws IOException { 203 return Short.valueOf(input.readShort()); 204 } 205 } 206 207 private static final class CharacterParser extends Parser { 208 @Override 209 public Object parse(RecordingInput input) throws IOException { 210 return Character.valueOf(input.readChar()); 211 } 212 } 213 214 private static final class FloatParser extends Parser { 215 @Override 216 public Object parse(RecordingInput input) throws IOException { 217 return Float.valueOf(input.readFloat()); 218 } 219 } 220 221 private static final class DoubleParser extends Parser { 222 @Override 223 public Object parse(RecordingInput input) throws IOException { 224 return Double.valueOf(input.readDouble()); 225 } 226 } 227 228 private static final class StringParser extends Parser { 229 private final ConstantMap stringConstantMap; 230 private String last; 231 232 StringParser(ConstantMap stringConstantMap) { 233 this.stringConstantMap = stringConstantMap; 234 } 235 236 @Override 237 public Object parse(RecordingInput input) throws IOException { 238 String s = parseEncodedString(input); 239 if (!Objects.equals(s, last)) { 240 last = s; 241 } 242 return last; 243 } 244 245 private String parseEncodedString(RecordingInput input) throws IOException { 246 byte encoding = input.readByte(); 247 if (encoding == RecordingInput.STRING_ENCODING_CONSTANT_POOL) { 248 long id = input.readLong(); 249 return (String) stringConstantMap.get(id); 250 } else { 251 return input.readEncodedString(encoding); 252 } 253 } 254 } 255 256 private final static class ArrayParser extends Parser { 257 private final Parser elementParser; 258 259 public ArrayParser(Parser elementParser) { 260 this.elementParser = elementParser; 261 } 262 263 @Override 264 public Object parse(RecordingInput input) throws IOException { 265 final int size = input.readInt(); 266 final Object[] array = new Object[size]; 267 for (int i = 0; i < size; i++) { 268 array[i] = elementParser.parse(input); 269 } 270 return array; 271 } 272 } 273 274 private final static class CompositeParser extends Parser { 275 private final Parser[] parsers; 276 277 public CompositeParser(Parser[] valueParsers) { 278 this.parsers = valueParsers; 279 } 280 281 @Override 282 public Object parse(RecordingInput input) throws IOException { 283 final Object[] values = new Object[parsers.length]; 284 for (int i = 0; i < values.length; i++) { 285 values[i] = parsers[i].parse(input); 286 } 287 return values; 288 } 289 } 290 291 private static final class ConstantMapValueParser extends Parser { 292 private final ConstantMap pool; 293 294 ConstantMapValueParser(ConstantMap pool) { 295 this.pool = pool; 296 } 297 298 @Override 299 public Object parse(RecordingInput input) throws IOException { 300 return pool.get(input.readLong()); 301 } 302 } 303 }