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