/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import jdk.jfr.internal.EventClassBuilder;
import jdk.jfr.internal.JVMSupport;
import jdk.jfr.internal.MetadataRepository;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.Utils;
Class for defining an event at runtime.
It's highly recommended that the event is defined at compile time, if the field layout is known, so the Java Virtual Machine (JVM) can optimize the code, possibly remove all instrumentation if Flight Recorder is inactive or if the enabled setting for this event is set to false
.
To define an event at compile time, see Event
.
The following example shows how to implement a dynamic Event
class.
List<ValueDescriptor> fields = new ArrayList<>();
List<AnnotationElement> messageAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Message"));
fields.add(new ValueDescriptor(String.class, "message", messageAnnotations));
List<AnnotationElement> numberAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Number"));
fields.add(new ValueDescriptor(int.class, "number", numberAnnotations));
String[] category = { "Example", "Getting Started" };
List<AnnotationElement> eventAnnotations = new ArrayList<>();
eventAnnotations.add(new AnnotationElement(Name.class, "com.example.HelloWorld"));
eventAnnotations.add(new AnnotationElement(Label.class, "Hello World"));
eventAnnotations.add(new AnnotationElement(Description.class, "Helps programmer getting started"));
eventAnnotations.add(new AnnotationElement(Category.class, category));
EventFactory f = EventFactory.create(eventAnnotations, fields);
Event event = f.newEvent();
event.set(0, "hello, world!");
event.set(1, 4711);
event.commit();
Since: 9
/**
* Class for defining an event at runtime.
* <p>
* It's highly recommended that the event is defined at compile time, if the
* field layout is known, so the Java Virtual Machine (JVM) can optimize the
* code, possibly remove all instrumentation if Flight Recorder is inactive or
* if the enabled setting for this event is set to {@code false}.
* <p>
* To define an event at compile time, see {@link Event}.
* <p>
* The following example shows how to implement a dynamic {@code Event} class.
*
* <pre>
* {@code
* List<ValueDescriptor> fields = new ArrayList<>();
* List<AnnotationElement> messageAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Message"));
* fields.add(new ValueDescriptor(String.class, "message", messageAnnotations));
* List<AnnotationElement> numberAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Number"));
* fields.add(new ValueDescriptor(int.class, "number", numberAnnotations));
*
* String[] category = { "Example", "Getting Started" };
* List<AnnotationElement> eventAnnotations = new ArrayList<>();
* eventAnnotations.add(new AnnotationElement(Name.class, "com.example.HelloWorld"));
* eventAnnotations.add(new AnnotationElement(Label.class, "Hello World"));
* eventAnnotations.add(new AnnotationElement(Description.class, "Helps programmer getting started"));
* eventAnnotations.add(new AnnotationElement(Category.class, category));
*
* EventFactory f = EventFactory.create(eventAnnotations, fields);
*
* Event event = f.newEvent();
* event.set(0, "hello, world!");
* event.set(1, 4711);
* event.commit();
* }
* </pre>
*
* @since 9
*/
public final class EventFactory {
private static final long REGISTERED_ID = Type.getTypeId(Registered.class);
private final Class<? extends Event> eventClass;
private final MethodHandle constructorHandle;
private final List<AnnotationElement> sanitizedAnnotation;
private final List<ValueDescriptor> sanitizedFields;
private EventFactory(Class<? extends Event> eventClass, List<AnnotationElement> sanitizedAnnotation, List<ValueDescriptor> sanitizedFields) throws IllegalAccessException, NoSuchMethodException, SecurityException {
this.constructorHandle = MethodHandles.lookup().unreflectConstructor(eventClass.getConstructor());
this.eventClass = eventClass;
this.sanitizedAnnotation = sanitizedAnnotation;
this.sanitizedFields = sanitizedFields;
}
Creates an EventFactory
object.
The order of the value descriptors specifies the index to use when setting
event values.
Params: - annotationElements – list of annotation elements that describes the annotations on the event, not
null
- fields – list of descriptors that describes the fields of the event, not
null
Throws: - IllegalArgumentException – if the input is not valid. For example,
input might not be valid if the field type or name is not valid in
the Java language or an annotation element references a type that
can't be found.
- SecurityException – if a security manager exists and the caller does not have
FlightRecorderPermission("registerEvent")
See Also: Returns: event factory, not null
/**
* Creates an {@code EventFactory} object.
* <p>
* The order of the value descriptors specifies the index to use when setting
* event values.
*
* @param annotationElements list of annotation elements that describes the
* annotations on the event, not {@code null}
*
* @param fields list of descriptors that describes the fields of the event, not
* {@code null}
*
* @return event factory, not {@code null}
*
* @throws IllegalArgumentException if the input is not valid. For example,
* input might not be valid if the field type or name is not valid in
* the Java language or an annotation element references a type that
* can't be found.
*
* @throws SecurityException if a security manager exists and the caller does
* not have {@code FlightRecorderPermission("registerEvent")}
*
* @see Event#set(int, Object)
*/
public static EventFactory create(List<AnnotationElement> annotationElements, List<ValueDescriptor> fields) {
Objects.requireNonNull(fields);
Objects.requireNonNull(annotationElements);
JVMSupport.ensureWithInternalError();
Utils.checkRegisterPermission();
List<AnnotationElement> sanitizedAnnotation = Utils.sanitizeNullFreeList(annotationElements, AnnotationElement.class);
List<ValueDescriptor> sanitizedFields = Utils.sanitizeNullFreeList(fields, ValueDescriptor.class);
Set<String> nameSet = new HashSet<>();
for (ValueDescriptor v : sanitizedFields) {
String name = v.getName();
if (v.isArray()) {
throw new IllegalArgumentException("Array types are not allowed for fields");
}
if (!Type.isValidJavaFieldType(v.getTypeName())) {
throw new IllegalArgumentException(v.getTypeName() + " is not a valid type for an event field");
}
if (!Type.isValidJavaIdentifier(v.getName())) {
throw new IllegalArgumentException(name + " is not a valid name for an event field");
}
if (nameSet.contains(name)) {
throw new IllegalArgumentException("Name of fields must be unique. Found two instances of " + name);
}
nameSet.add(name);
}
// Prevent event from being registered in <clinit>
// and only use annotations that can be resolved (those in boot class loader)
boolean needRegister = true;
List<AnnotationElement> bootAnnotations = new ArrayList<>();
for (AnnotationElement ae : sanitizedAnnotation) {
long id = ae.getTypeId();
if (ae.isInBoot()) {
if (id == REGISTERED_ID) {
if (Boolean.FALSE.equals(ae.getValue("value"))) {
needRegister = false;
}
} else {
bootAnnotations.add(ae);
}
}
}
bootAnnotations.add(new AnnotationElement(Registered.class, false));
EventClassBuilder ecb = new EventClassBuilder(bootAnnotations, sanitizedFields);
Class<? extends Event> eventClass = ecb.build();
if (needRegister) {
MetadataRepository.getInstance().register(eventClass, sanitizedAnnotation, sanitizedFields);
}
try {
return new EventFactory(eventClass, sanitizedAnnotation, sanitizedFields);
} catch (IllegalAccessException e) {
throw new IllegalAccessError("Could not accees constructor of generated event handler, " + e.getMessage());
} catch (NoSuchMethodException e) {
throw new InternalError("Could not find constructor in generated event handler, " + e.getMessage());
}
}
Instantiates an event, so it can be populated with data and written to the
Flight Recorder system.
Use the Event.set(int, Object)
method to set a value.
Returns: an event instance, not null
/**
* Instantiates an event, so it can be populated with data and written to the
* Flight Recorder system.
* <p>
* Use the {@link Event#set(int, Object)} method to set a value.
*
* @return an event instance, not {@code null}
*/
public Event newEvent() {
try {
return (Event) constructorHandle.invoke();
} catch (Throwable e) {
throw new InstantiationError("Could not instantaite dynamically generated event class " + eventClass.getName() + ". " + e.getMessage());
}
}
Returns the event type that is associated with this event factory.
Throws: - IllegalStateException – if the event factory is created with the
Registered(false)
annotation and the event class is not manually registered before the invocation of this method
Returns: event type that is associated with this event factory, not null
/**
* Returns the event type that is associated with this event factory.
*
* @return event type that is associated with this event factory, not
* {@code null}
*
* @throws java.lang.IllegalStateException if the event factory is created with
* the {@code Registered(false)} annotation and the event class is not
* manually registered before the invocation of this method
*/
public EventType getEventType() {
return EventType.getEventType(eventClass);
}
Registers an unregistered event.
By default, the event class associated with this event factory is registered when the event factory is created, unless the event has the Registered
annotation.
A registered event class can write data to Flight Recorder and event metadata can be obtained by invoking FlightRecorder.getEventTypes()
.
If the event class associated with this event factory is already registered,
the call to this method is ignored.
Throws: - SecurityException – if a security manager exists and the caller does not have
FlightRecorderPermission("registerEvent")
See Also:
/**
* Registers an unregistered event.
* <p>
* By default, the event class associated with this event factory is registered
* when the event factory is created, unless the event has the
* {@link Registered} annotation.
* <p>
* A registered event class can write data to Flight Recorder and event metadata
* can be obtained by invoking {@link FlightRecorder#getEventTypes()}.
* <p>
* If the event class associated with this event factory is already registered,
* the call to this method is ignored.
*
* @throws SecurityException if a security manager exists and the caller
* does not have {@code FlightRecorderPermission("registerEvent")}
* @see Registered
* @see FlightRecorder#register(Class)
*/
public void register() {
MetadataRepository.getInstance().register(eventClass, sanitizedAnnotation, sanitizedFields);
}
Unregisters the event that is associated with this event factory.
A unregistered event class can't write data to Flight Recorder and event metadata can't be obtained by invoking FlightRecorder.getEventTypes()
.
If the event class associated with this event factory is not already
registered, the call to this method is ignored.
Throws: - SecurityException – if a security manager exists and the caller does not have
FlightRecorderPermission("registerEvent")
See Also:
/**
* Unregisters the event that is associated with this event factory.
* <p>
* A unregistered event class can't write data to Flight Recorder and event
* metadata can't be obtained by invoking
* {@link FlightRecorder#getEventTypes()}.
* <p>
* If the event class associated with this event factory is not already
* registered, the call to this method is ignored.
*
* @throws SecurityException if a security manager exists and the caller does
* not have {@code FlightRecorderPermission("registerEvent")}
* @see Registered
* @see FlightRecorder#unregister(Class)
*/
public void unregister() {
MetadataRepository.getInstance().unregister(eventClass);
}
}