2121import datadog .trace .bootstrap .debugger .ProbeRateLimiter ;
2222import datadog .trace .relocate .api .RatelimitedLogger ;
2323import datadog .trace .util .TagsHelper ;
24+ import java .lang .annotation .Annotation ;
25+ import java .lang .annotation .ElementType ;
26+ import java .lang .annotation .Target ;
2427import java .lang .instrument .Instrumentation ;
28+ import java .lang .reflect .AnnotatedType ;
29+ import java .lang .reflect .Array ;
2530import java .lang .reflect .Method ;
31+ import java .lang .reflect .Modifier ;
2632import java .lang .reflect .Parameter ;
2733import java .util .ArrayList ;
34+ import java .util .Arrays ;
2835import java .util .Collection ;
2936import java .util .Collections ;
3037import java .util .EnumMap ;
4451 * re-transformation of required classes
4552 */
4653public class ConfigurationUpdater implements DebuggerContext .ProbeResolver , ConfigurationAcceptor {
47-
54+ private static final Logger LOGGER = LoggerFactory .getLogger (ConfigurationUpdater .class );
55+ private static final int MINUTES_BETWEEN_ERROR_LOG = 5 ;
4856 private static final boolean JAVA_AT_LEAST_19 = JavaVirtualMachine .isJavaVersionAtLeast (19 );
57+ private static final boolean JAVA_AT_LEAST_16 = JavaVirtualMachine .isJavaVersionAtLeast (16 );
58+ private static final Method GET_RECORD_COMPONENTS_METHOD ;
59+ private static final Method GET_ANNOTATED_TYPES_METHOD ;
60+
61+ static {
62+ Method getRecordComponentsMethod = null ;
63+ Method getAnnotatedTypesMethod = null ;
64+ if (JAVA_AT_LEAST_16 ) {
65+ try {
66+ Class <?> recordClass = Class .forName ("java.lang.Record" );
67+ getRecordComponentsMethod = recordClass .getClass ().getDeclaredMethod ("getRecordComponents" );
68+ Class <?> recordComponentClass = Class .forName ("java.lang.reflect.RecordComponent" );
69+ getAnnotatedTypesMethod = recordComponentClass .getDeclaredMethod ("getAnnotatedType" );
70+ } catch (Exception e ) {
71+ LOGGER .debug ("Exception initializing reflection constants" , e );
72+ }
73+ }
74+ GET_RECORD_COMPONENTS_METHOD = getRecordComponentsMethod ;
75+ GET_ANNOTATED_TYPES_METHOD = getAnnotatedTypesMethod ;
76+ }
4977
5078 public interface TransformerSupplier {
5179 DebuggerTransformer supply (
@@ -56,9 +84,6 @@ DebuggerTransformer supply(
5684 DebuggerSink debuggerSink );
5785 }
5886
59- private static final Logger LOGGER = LoggerFactory .getLogger (ConfigurationUpdater .class );
60- private static final int MINUTES_BETWEEN_ERROR_LOG = 5 ;
61-
6287 private final Instrumentation instrumentation ;
6388 private final TransformerSupplier transformerSupplier ;
6489 private final Lock configurationLock = new ReentrantLock ();
@@ -185,6 +210,7 @@ private void handleProbesChanges(ConfigurationComparer changes, Configuration ne
185210 List <Class <?>> changedClasses =
186211 finder .getAllLoadedChangedClasses (instrumentation .getAllLoadedClasses (), changes );
187212 changedClasses = detectMethodParameters (changes , changedClasses );
213+ changedClasses = detectRecordWithTypeAnnotation (changes , changedClasses );
188214 retransformClasses (changedClasses );
189215 // ensures that we have at least re-transformed 1 class
190216 if (changedClasses .size () > 0 ) {
@@ -248,6 +274,65 @@ private List<Class<?>> detectMethodParameters(
248274 return result ;
249275 }
250276
277+ private List <Class <?>> detectRecordWithTypeAnnotation (
278+ ConfigurationComparer changes , List <Class <?>> changedClasses ) {
279+ if (!JAVA_AT_LEAST_16 ) {
280+ // records introduced in JDK 16 (final version)
281+ return changedClasses ;
282+ }
283+ List <Class <?>> result = new ArrayList <>();
284+ for (Class <?> changedClass : changedClasses ) {
285+ boolean addClass = true ;
286+ try {
287+ if (changedClass .getSuperclass ().getTypeName ().equals ("java.lang.Record" )
288+ && Modifier .isFinal (changedClass .getModifiers ())) {
289+ if (hasTypeAnnotationOnRecordComponent (changedClass )) {
290+ LOGGER .debug (
291+ "Record with type annotation detected, instrumentation not supported for {}" ,
292+ changedClass .getTypeName ());
293+ reportError (
294+ changes ,
295+ "Record with type annotation detected, instrumentation not supported for "
296+ + changedClass .getTypeName ());
297+ addClass = false ;
298+ }
299+ }
300+ } catch (Exception e ) {
301+ LOGGER .debug ("Exception detecting record with type annotation" , e );
302+ }
303+ if (addClass ) {
304+ result .add (changedClass );
305+ }
306+ }
307+ return result ;
308+ }
309+
310+ private boolean hasTypeAnnotationOnRecordComponent (Class <?> recordClass ) {
311+ if (GET_RECORD_COMPONENTS_METHOD == null || GET_ANNOTATED_TYPES_METHOD == null ) {
312+ return false ;
313+ }
314+ try {
315+ Object recordComponentsArray = GET_RECORD_COMPONENTS_METHOD .invoke (recordClass );
316+ int len = Array .getLength (recordComponentsArray );
317+ for (int i = 0 ; i < len ; i ++) {
318+ Object recordComponent = Array .get (recordComponentsArray , i );
319+ AnnotatedType annotatedType =
320+ (AnnotatedType ) GET_ANNOTATED_TYPES_METHOD .invoke (recordComponent );
321+ for (Annotation annotation : annotatedType .getAnnotations ()) {
322+ Target annotationTarget = annotation .annotationType ().getAnnotation (Target .class );
323+ if (annotationTarget != null
324+ && Arrays .stream (annotationTarget .value ())
325+ .anyMatch (it -> it == ElementType .TYPE_USE )) {
326+ return true ;
327+ }
328+ }
329+ }
330+ return false ;
331+ } catch (Exception ex ) {
332+ return false ;
333+ }
334+ }
335+
251336 private void reportReceived (ConfigurationComparer changes ) {
252337 for (ProbeDefinition def : changes .getAddedDefinitions ()) {
253338 if (def instanceof ExceptionProbe ) {
0 commit comments