1 /* 2 * Copyright (c) 2016, 2019, 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; 27 28 import java.lang.invoke.MethodHandle; 29 import java.lang.invoke.MethodHandles; 30 import java.util.ArrayList; 31 import java.util.HashSet; 32 import java.util.List; 33 import java.util.Objects; 34 import java.util.Set; 35 36 import jdk.jfr.internal.EventClassBuilder; 37 import jdk.jfr.internal.JVMSupport; 38 import jdk.jfr.internal.MetadataRepository; 39 import jdk.jfr.internal.Type; 40 import jdk.jfr.internal.Utils; 41 42 /** 43 * Class for defining an event at runtime. 44 * <p> 45 * It's highly recommended that the event is defined at compile time, if the 46 * field layout is known, so the Java Virtual Machine (JVM) can optimize the 47 * code, possibly remove all instrumentation if Flight Recorder is inactive or 48 * if the enabled setting for this event is set to {@code false}. 49 * <p> 50 * To define an event at compile time, see {@link Event}. 51 * <p> 52 * The following example shows how to implement a dynamic {@code Event} class. 53 * 54 * <pre> 55 * { 56 * @code 57 * List<ValueDescriptor> fields = new ArrayList<>(); 58 * List<AnnotationElement> messageAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Message")); 59 * fields.add(new ValueDescriptor(String.class, "message", messageAnnotations)); 60 * List<AnnotationElement> numberAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Number")); 61 * fields.add(new ValueDescriptor(int.class, "number", numberAnnotations)); 62 * 63 * String[] category = { "Example", "Getting Started" }; 64 * List<AnnotationElement> eventAnnotations = new ArrayList<>(); 65 * eventAnnotations.add(new AnnotationElement(Name.class, "com.example.HelloWorld")); 66 * eventAnnotations.add(new AnnotationElement(Label.class, "Hello World")); 67 * eventAnnotations.add(new AnnotationElement(Description.class, "Helps programmer getting started")); 68 * eventAnnotations.add(new AnnotationElement(Category.class, category)); 69 * 70 * EventFactory f = EventFactory.create(eventAnnotations, fields); 71 * 72 * Event event = f.newEvent(); 73 * event.set(0, "hello, world!"); 74 * event.set(1, 4711); 75 * event.commit(); 76 * } 77 * </pre> 78 * 79 * @since 9 80 */ 81 public final class EventFactory { 82 83 private static final long REGSITERED_ID = Type.getTypeId(Registered.class); 84 85 private final Class<? extends Event> eventClass; 86 private final MethodHandle constructorHandle; 87 private final List<AnnotationElement> sanitizedAnnotation; 88 private final List<ValueDescriptor> sanitizedFields; 89 90 private EventFactory(Class<? extends Event> eventClass, List<AnnotationElement> sanitizedAnnotation, List<ValueDescriptor> sanitizedFields) throws IllegalAccessException, NoSuchMethodException, SecurityException { 91 this.constructorHandle = MethodHandles.lookup().unreflectConstructor(eventClass.getConstructor()); 92 this.eventClass = eventClass; 93 this.sanitizedAnnotation = sanitizedAnnotation; 94 this.sanitizedFields = sanitizedFields; 95 } 96 97 /** 98 * Creates an {@code EventFactory} object. 99 * <p> 100 * The order of the value descriptors specifies the index to use when setting 101 * event values. 102 * 103 * @param annotationElements list of annotation elements that describes the 104 * annotations on the event, not {@code null} 105 * 106 * @param fields list of descriptors that describes the fields of the event, not 107 * {@code null} 108 * 109 * @return event factory, not {@code null} 110 * 111 * @throws IllegalArgumentException if the input is not valid. For example, 112 * input might not be valid if the field type or name is not valid in 113 * the Java language or an annotation element references a type that 114 * can't be found. 115 * 116 * @throws SecurityException if a security manager exists and the caller does 117 * not have {@code FlightRecorderPermission("registerEvent")} 118 * 119 * @see Event#set(int, Object) 120 */ 121 public static EventFactory create(List<AnnotationElement> annotationElements, List<ValueDescriptor> fields) { 122 Objects.requireNonNull(fields); 123 Objects.requireNonNull(annotationElements); 124 JVMSupport.ensureWithInternalError(); 125 126 Utils.checkRegisterPermission(); 127 128 List<AnnotationElement> sanitizedAnnotation = Utils.sanitizeNullFreeList(annotationElements, AnnotationElement.class); 129 List<ValueDescriptor> sanitizedFields = Utils.sanitizeNullFreeList(fields, ValueDescriptor.class); 130 Set<String> nameSet = new HashSet<>(); 131 for (ValueDescriptor v : sanitizedFields) { 132 String name = v.getName(); 133 if (v.isArray()) { 134 throw new IllegalArgumentException("Array types are not allowed for fields"); 135 } 136 if (!Type.isValidJavaFieldType(v.getTypeName())) { 137 throw new IllegalArgumentException(v.getTypeName() + " is not a valid type for an event field"); 138 } 139 if (!Type.isValidJavaIdentifier(v.getName())) { 140 throw new IllegalArgumentException(name + " is not a valid name for an event field"); 141 } 142 if (nameSet.contains(name)) { 143 throw new IllegalArgumentException("Name of fields must be unique. Found two instances of " + name); 144 } 145 nameSet.add(name); 146 } 147 148 // Prevent event from being registered in <clinit> 149 // and only use annotations that can be resolved (those in boot class loader) 150 boolean needRegister = true; 151 List<AnnotationElement> bootAnnotations = new ArrayList<>(); 152 for (AnnotationElement ae : sanitizedAnnotation) { 153 long id = ae.getTypeId(); 154 if (ae.isInBoot()) { 155 if (id == REGSITERED_ID) { 156 if (Boolean.FALSE.equals(ae.getValue("value"))) { 157 needRegister = false; 158 } 159 } else { 160 bootAnnotations.add(ae); 161 } 162 } 163 } 164 bootAnnotations.add(new AnnotationElement(Registered.class, false)); 165 166 EventClassBuilder ecb = new EventClassBuilder(bootAnnotations, sanitizedFields); 167 Class<? extends Event> eventClass = ecb.build(); 168 169 if (needRegister) { 170 MetadataRepository.getInstance().register(eventClass, sanitizedAnnotation, sanitizedFields); 171 } 172 try { 173 return new EventFactory(eventClass, sanitizedAnnotation, sanitizedFields); 174 } catch (IllegalAccessException e) { 175 throw new IllegalAccessError("Could not accees constructor of generated event handler, " + e.getMessage()); 176 } catch (NoSuchMethodException e) { 177 throw new InternalError("Could not find constructor in generated event handler, " + e.getMessage()); 178 } 179 } 180 181 /** 182 * Instantiates an event, so it can be populated with data and written to the 183 * Flight Recorder system. 184 * <p> 185 * Use the {@link Event#set(int, Object)} method to set a value. 186 * 187 * @return an event instance, not {@code null} 188 */ 189 public Event newEvent() { 190 try { 191 return (Event) constructorHandle.invoke(); 192 } catch (Throwable e) { 193 throw new InstantiationError("Could not instantaite dynamically generated event class " + eventClass.getName() + ". " + e.getMessage()); 194 } 195 } 196 197 /** 198 * Returns the event type that is associated with this event factory. 199 * 200 * @return event type that is associated with this event factory, not 201 * {@code null} 202 * 203 * @throws java.lang.IllegalStateException if the event factory is created with 204 * the {@code Registered(false)} annotation and the event class is not 205 * manually registered before the invocation of this method 206 */ 207 public EventType getEventType() { 208 return EventType.getEventType(eventClass); 209 } 210 211 /** 212 * Registers an unregistered event. 213 * <p> 214 * By default, the event class associated with this event factory is registered 215 * when the event factory is created, unless the event has the 216 * {@link Registered} annotation. 217 * <p> 218 * A registered event class can write data to Flight Recorder and event metadata 219 * can be obtained by invoking {@link FlightRecorder#getEventTypes()}. 220 * <p> 221 * If the event class associated with this event factory is already registered, 222 * the call to this method is ignored. 223 * 224 * @throws SecurityException if a security manager exists and the caller 225 * does not have {@code FlightRecorderPermission("registerEvent")} 226 * @see Registered 227 * @see FlightRecorder#register(Class) 228 */ 229 public void register() { 230 MetadataRepository.getInstance().register(eventClass, sanitizedAnnotation, sanitizedFields); 231 } 232 233 /** 234 * Unregisters the event that is associated with this event factory. 235 * <p> 236 * A unregistered event class can't write data to Flight Recorder and event 237 * metadata can't be obtained by invoking 238 * {@link FlightRecorder#getEventTypes()}. 239 * <p> 240 * If the event class associated with this event factory is not already 241 * registered, the call to this method is ignored. 242 * 243 * @throws SecurityException if a security manager exists and the caller does 244 * not have {@code FlightRecorderPermission("registerEvent")} 245 * @see Registered 246 * @see FlightRecorder#unregister(Class) 247 */ 248 public void unregister() { 249 MetadataRepository.getInstance().unregister(eventClass); 250 } 251 252 }