• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

raphw / byte-buddy / #801

27 Oct 2025 09:37AM UTC coverage: 84.715% (-0.4%) from 85.118%
#801

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

46.6
/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/ResettableClassFileTransformer.java
1
/*
2
 * Copyright 2014 - Present Rafael Winterhalter
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.bytebuddy.agent.builder;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.ByteBuddy;
20
import net.bytebuddy.build.AccessControllerPlugin;
21
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
22
import net.bytebuddy.description.type.TypeDescription;
23
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
24
import net.bytebuddy.dynamic.scaffold.TypeValidation;
25
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
26
import net.bytebuddy.implementation.MethodCall;
27
import net.bytebuddy.implementation.bytecode.assign.Assigner;
28
import net.bytebuddy.utility.JavaModule;
29
import net.bytebuddy.utility.JavaType;
30
import net.bytebuddy.utility.nullability.MaybeNull;
31

32
import java.lang.instrument.ClassFileTransformer;
33
import java.lang.instrument.IllegalClassFormatException;
34
import java.lang.instrument.Instrumentation;
35
import java.lang.reflect.Constructor;
36
import java.lang.reflect.InvocationTargetException;
37
import java.security.PrivilegedAction;
38
import java.security.ProtectionDomain;
39
import java.util.Iterator;
40

41
import static net.bytebuddy.matcher.ElementMatchers.named;
42
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
43
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
44

45
/**
46
 * A class file transformer that can reset its transformation.
47
 */
48
public interface ResettableClassFileTransformer extends ClassFileTransformer {
49

50
    /**
51
     * Creates an iterator over the transformers that are applied for a given type.
52
     *
53
     * @param typeDescription     A description of a type.
54
     * @param classLoader         The type's class loader or {@code null} if the boot loader.
55
     * @param module              The type's module or {@code null} if the module system is not supported by the current VM.
56
     * @param classBeingRedefined The class being redefined or {@code null} if the type is not yet loaded.
57
     * @param protectionDomain    The type's protection domain or {@code null} if not available.
58
     * @return An iterator over the transformers that are applied by this class file transformer if the given type is discovered.
59
     */
60
    Iterator<AgentBuilder.Transformer> iterator(TypeDescription typeDescription,
61
                                                @MaybeNull ClassLoader classLoader,
62
                                                @MaybeNull JavaModule module,
63
                                                @MaybeNull Class<?> classBeingRedefined,
64
                                                @MaybeNull ProtectionDomain protectionDomain);
65

66
    /**
67
     * <p>
68
     * Deregisters this class file transformer and redefines any transformed class to its state without this
69
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
70
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
71
     * resetting without undoing any code changes.
72
     * </p>
73
     * <p>
74
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
75
     * which built the transformer should be asked to install a new transformer.
76
     * </p>
77
     * <p>
78
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
79
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
80
     * {@link AgentBuilder#disableClassFormatChanges()}.
81
     * </p>
82
     *
83
     * @param instrumentation      The instrumentation instance from which to deregister the transformer.
84
     * @param redefinitionStrategy The redefinition to apply.
85
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
86
     */
87
    boolean reset(Instrumentation instrumentation, AgentBuilder.RedefinitionStrategy redefinitionStrategy);
88

89
    /**
90
     * <p>
91
     * Deregisters this class file transformer and redefines any transformed class to its state without this
92
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
93
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
94
     * resetting without undoing any code changes.
95
     * </p>
96
     * <p>
97
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
98
     * which built the transformer should be asked to install a new transformer.
99
     * </p>
100
     * <p>
101
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
102
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
103
     * {@link AgentBuilder#disableClassFormatChanges()}.
104
     * </p>
105
     *
106
     * @param instrumentation            The instrumentation instance from which to deregister the transformer.
107
     * @param redefinitionStrategy       The redefinition to apply.
108
     * @param redefinitionBatchAllocator The batch allocator to use.
109
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
110
     */
111
    @SuppressWarnings("overloads")
112
    boolean reset(Instrumentation instrumentation,
113
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
114
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator);
115

116
    /**
117
     * <p>
118
     * Deregisters this class file transformer and redefines any transformed class to its state without this
119
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
120
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
121
     * resetting without undoing any code changes.
122
     * </p>
123
     * <p>
124
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
125
     * which built the transformer should be asked to install a new transformer.
126
     * </p>
127
     * <p>
128
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
129
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
130
     * {@link AgentBuilder#disableClassFormatChanges()}.
131
     * </p>
132
     *
133
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
134
     * @param redefinitionStrategy          The redefinition to apply.
135
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
136
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
137
     */
138
    @SuppressWarnings("overloads")
139
    boolean reset(Instrumentation instrumentation,
140
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
141
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy);
142

143
    /**
144
     * <p>
145
     * Deregisters this class file transformer and redefines any transformed class to its state without this
146
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
147
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
148
     * resetting without undoing any code changes.
149
     * </p>
150
     * <p>
151
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
152
     * which built the transformer should be asked to install a new transformer.
153
     * </p>
154
     * <p>
155
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
156
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
157
     * {@link AgentBuilder#disableClassFormatChanges()}.
158
     * </p>
159
     *
160
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
161
     * @param redefinitionStrategy          The redefinition to apply.
162
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
163
     * @param redefinitionBatchAllocator    The batch allocator to use.
164
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
165
     */
166
    boolean reset(Instrumentation instrumentation,
167
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
168
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
169
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy);
170

171
    /**
172
     * <p>
173
     * Deregisters this class file transformer and redefines any transformed class to its state without this
174
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
175
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
176
     * resetting without undoing any code changes.
177
     * </p>
178
     * <p>
179
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
180
     * which built the transformer should be asked to install a new transformer.
181
     * </p>
182
     * <p>
183
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
184
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
185
     * {@link AgentBuilder#disableClassFormatChanges()}.
186
     * </p>
187
     *
188
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
189
     * @param redefinitionStrategy          The redefinition to apply.
190
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
191
     * @param redefinitionListener          The redefinition listener to apply.
192
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
193
     */
194
    @SuppressWarnings("overloads")
195
    boolean reset(Instrumentation instrumentation,
196
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
197
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
198
                  AgentBuilder.RedefinitionStrategy.Listener redefinitionListener);
199

200
    /**
201
     * <p>
202
     * Deregisters this class file transformer and redefines any transformed class to its state without this
203
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
204
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
205
     * resetting without undoing any code changes.
206
     * </p>
207
     * <p>
208
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
209
     * which built the transformer should be asked to install a new transformer.
210
     * </p>
211
     * <p>
212
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
213
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
214
     * {@link AgentBuilder#disableClassFormatChanges()}.
215
     * </p>
216
     *
217
     * @param instrumentation            The instrumentation instance from which to deregister the transformer.
218
     * @param redefinitionStrategy       The redefinition to apply.
219
     * @param redefinitionBatchAllocator The batch allocator to use.
220
     * @param redefinitionListener       The redefinition listener to apply.
221
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
222
     */
223
    @SuppressWarnings("overloads")
224
    boolean reset(Instrumentation instrumentation,
225
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
226
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
227
                  AgentBuilder.RedefinitionStrategy.Listener redefinitionListener);
228

229
    /**
230
     * <p>
231
     * Deregisters this class file transformer and redefines any transformed class to its state without this
232
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
233
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
234
     * resetting without undoing any code changes.
235
     * </p>
236
     * <p>
237
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
238
     * which built the transformer should be asked to install a new transformer.
239
     * </p>
240
     * <p>
241
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
242
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
243
     * {@link AgentBuilder#disableClassFormatChanges()}.
244
     * </p>
245
     *
246
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
247
     * @param redefinitionStrategy          The redefinition to apply.
248
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
249
     * @param redefinitionBatchAllocator    The batch allocator to use.
250
     * @param redefinitionListener          The redefinition listener to apply.
251
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
252
     */
253
    boolean reset(Instrumentation instrumentation,
254
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
255
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
256
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
257
                  AgentBuilder.RedefinitionStrategy.Listener redefinitionListener);
258

259

260
    /**
261
     * <p>
262
     * Deregisters this class file transformer and redefines any transformed class to its state without this
263
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
264
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
265
     * resetting without undoing any code changes.
266
     * </p>
267
     * <p>
268
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
269
     * which built the transformer should be asked to install a new transformer.
270
     * </p>
271
     * <p>
272
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
273
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
274
     * {@link AgentBuilder#disableClassFormatChanges()}.
275
     * </p>
276
     *
277
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
278
     * @param classFileTransformer          The actual class file transformer to deregister which might be {@code this} instance or any wrapper.
279
     * @param redefinitionStrategy          The redefinition to apply.
280
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
281
     * @param redefinitionBatchAllocator    The batch allocator to use.
282
     * @param redefinitionListener          The redefinition listener to apply.
283
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
284
     */
285
    boolean reset(Instrumentation instrumentation,
286
                  ResettableClassFileTransformer classFileTransformer,
287
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
288
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
289
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
290
                  AgentBuilder.RedefinitionStrategy.Listener redefinitionListener);
291

292
    /**
293
     * A {@link ResettableClassFileTransformer} which allows for substitution the actual class file transformer without
294
     * changes in the order of the transformer chain.
295
     */
296
    interface Substitutable extends ResettableClassFileTransformer {
297

298
        /**
299
         * Substitutes the current class file transformer.
300
         *
301
         * @param classFileTransformer The class file transformer to use.
302
         */
303
        void substitute(ResettableClassFileTransformer classFileTransformer);
304

305
        /**
306
         * Returns the underlying non-substitutable class file transformer.
307
         *
308
         * @return The underlying non-substitutable class file transformer.
309
         */
310
        ResettableClassFileTransformer unwrap();
311
    }
312

313
    /**
314
     * An abstract base implementation of a {@link ResettableClassFileTransformer}.
315
     */
316
    abstract class AbstractBase implements ResettableClassFileTransformer {
1✔
317

318
        /**
319
         * {@inheritDoc}
320
         */
321
        public boolean reset(Instrumentation instrumentation, AgentBuilder.RedefinitionStrategy redefinitionStrategy) {
322
            return reset(instrumentation, redefinitionStrategy, AgentBuilder.RedefinitionStrategy.BatchAllocator.ForTotal.INSTANCE);
1✔
323
        }
324

325
        /**
326
         * {@inheritDoc}
327
         */
328
        public boolean reset(Instrumentation instrumentation,
329
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
330
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator) {
331
            return reset(instrumentation, redefinitionStrategy, redefinitionBatchAllocator, AgentBuilder.RedefinitionStrategy.Listener.NoOp.INSTANCE);
1✔
332
        }
333

334
        /**
335
         * {@inheritDoc}
336
         */
337
        public boolean reset(Instrumentation instrumentation,
338
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
339
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy) {
340
            return reset(instrumentation, redefinitionStrategy, redefinitionDiscoveryStrategy, AgentBuilder.RedefinitionStrategy.Listener.NoOp.INSTANCE);
×
341
        }
342

343
        /**
344
         * {@inheritDoc}
345
         */
346
        public boolean reset(Instrumentation instrumentation,
347
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
348
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
349
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy) {
350
            return reset(instrumentation, redefinitionStrategy, redefinitionDiscoveryStrategy, redefinitionBatchAllocator, AgentBuilder.RedefinitionStrategy.Listener.NoOp.INSTANCE);
×
351
        }
352

353
        /**
354
         * {@inheritDoc}
355
         */
356
        public boolean reset(Instrumentation instrumentation,
357
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
358
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
359
                             AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
360
            return reset(instrumentation, redefinitionStrategy, redefinitionDiscoveryStrategy, AgentBuilder.RedefinitionStrategy.BatchAllocator.ForTotal.INSTANCE, redefinitionListener);
×
361
        }
362

363
        /**
364
         * {@inheritDoc}
365
         */
366
        public boolean reset(Instrumentation instrumentation,
367
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
368
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
369
                             AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
370
            return reset(instrumentation, redefinitionStrategy, AgentBuilder.RedefinitionStrategy.DiscoveryStrategy.SinglePass.INSTANCE, redefinitionBatchAllocator, redefinitionListener);
1✔
371
        }
372

373
        /**
374
         * {@inheritDoc}
375
         */
376
        public boolean reset(Instrumentation instrumentation,
377
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
378
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
379
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
380
                             AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
381
            return reset(instrumentation, this, redefinitionStrategy, redefinitionDiscoveryStrategy, redefinitionBatchAllocator, redefinitionListener);
1✔
382
        }
383
    }
384

385
    /**
386
     * Implements a resettable class file transformer that allows for the delegation of a transformation. Typically implemented
387
     * when using a {@link net.bytebuddy.agent.builder.AgentBuilder.TransformerDecorator}.
388
     */
389
    @HashCodeAndEqualsPlugin.Enhance
390
    abstract class WithDelegation extends AbstractBase {
391

392
        /**
393
         * The class file transformer to delegate to.
394
         */
395
        protected final ResettableClassFileTransformer classFileTransformer;
396

397
        /**
398
         * Creates a new delegating resettable class file transformer.
399
         *
400
         * @param classFileTransformer The class file transformer to delegate to.
401
         */
402
        protected WithDelegation(ResettableClassFileTransformer classFileTransformer) {
1✔
403
            this.classFileTransformer = classFileTransformer;
1✔
404
        }
1✔
405

406
        /**
407
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
408
         *
409
         * @param action The action to execute from a privileged context.
410
         * @param <T>    The type of the action's resolved value.
411
         * @return The action's resolved value.
412
         */
413
        @AccessControllerPlugin.Enhance
414
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
415
            return action.run();
×
416
        }
417

418
        /**
419
         * Creates a resettable class file transformer that wraps another transformer and adds a callback to the
420
         * beginning and end of each transformation. If the module system is supported on the current JVM, the supplied
421
         * module is retained.
422
         *
423
         * @param classFileTransformer The class file transformer to delegate to.
424
         * @param callback             The callback to invoke.
425
         * @return A resettable class file transformer that delegates while also invoking the callback.
426
         */
427
        public static ResettableClassFileTransformer of(ResettableClassFileTransformer classFileTransformer, Callback<?> callback) {
428
            return WithCallback.DISPATCHER.make(classFileTransformer, callback);
1✔
429
        }
430

431
        /**
432
         * {@inheritDoc}
433
         */
434
        public Iterator<AgentBuilder.Transformer> iterator(TypeDescription typeDescription,
435
                                                           @MaybeNull ClassLoader classLoader,
436
                                                           @MaybeNull JavaModule module,
437
                                                           @MaybeNull Class<?> classBeingRedefined,
438
                                                           @MaybeNull ProtectionDomain protectionDomain) {
439
            return classFileTransformer.iterator(typeDescription, classLoader, module, classBeingRedefined, protectionDomain);
×
440
        }
441

442
        /**
443
         * {@inheritDoc}
444
         */
445
        public boolean reset(Instrumentation instrumentation,
446
                             ResettableClassFileTransformer classFileTransformer,
447
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
448
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
449
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
450
                             AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
451
            return this.classFileTransformer.reset(instrumentation,
1✔
452
                    classFileTransformer,
453
                    redefinitionStrategy,
454
                    redefinitionDiscoveryStrategy,
455
                    redefinitionBatchAllocator,
456
                    redefinitionListener);
457
        }
458

459
        /**
460
         * A standard implementation of a substitutable {@link ResettableClassFileTransformer}.
461
         */
462
        @HashCodeAndEqualsPlugin.Enhance
463
        protected static class Substitutable extends AbstractBase implements ResettableClassFileTransformer.Substitutable {
464

465
            /**
466
             * A dispatcher for invoking the correct transformer method.
467
             */
468
            private static final Factory DISPATCHER = doPrivileged(Factory.CreationAction.INSTANCE);
1✔
469

470
            /**
471
             * The class file transformer to delegate to.
472
             */
473
            protected volatile ResettableClassFileTransformer classFileTransformer;
474

475
            /**
476
             * Creates a new delegating resettable class file transformer.
477
             *
478
             * @param classFileTransformer The class file transformer to delegate to.
479
             */
480
            protected Substitutable(ResettableClassFileTransformer classFileTransformer) {
1✔
481
                this.classFileTransformer = classFileTransformer;
1✔
482
            }
1✔
483

484
            /**
485
             * Creates a new substitutable class file transformer of another class file transformer.
486
             *
487
             * @param classFileTransformer The class file transformer to wrap.
488
             * @return A substitutable version of the supplied class file transformer.
489
             */
490
            public static Substitutable of(ResettableClassFileTransformer classFileTransformer) {
491
                return DISPATCHER.make(classFileTransformer);
1✔
492
            }
493

494
            /**
495
             * {@inheritDoc}
496
             */
497
            public void substitute(ResettableClassFileTransformer classFileTransformer) {
498
                while (classFileTransformer instanceof Substitutable) {
1✔
499
                    classFileTransformer = ((Substitutable) classFileTransformer).unwrap();
×
500
                }
501
                this.classFileTransformer = classFileTransformer;
1✔
502
            }
1✔
503

504
            /**
505
             * {@inheritDoc}
506
             */
507
            public ResettableClassFileTransformer unwrap() {
508
                return classFileTransformer;
×
509
            }
510

511
            /**
512
             * {@inheritDoc}
513
             */
514
            public Iterator<AgentBuilder.Transformer> iterator(TypeDescription typeDescription,
515
                                                               @MaybeNull ClassLoader classLoader,
516
                                                               @MaybeNull JavaModule module,
517
                                                               @MaybeNull Class<?> classBeingRedefined,
518
                                                               @MaybeNull ProtectionDomain protectionDomain) {
519
                return classFileTransformer.iterator(typeDescription, classLoader, module, classBeingRedefined, protectionDomain);
×
520
            }
521

522
            /**
523
             * {@inheritDoc}
524
             */
525
            public boolean reset(Instrumentation instrumentation,
526
                                 ResettableClassFileTransformer classFileTransformer,
527
                                 AgentBuilder.RedefinitionStrategy redefinitionStrategy,
528
                                 AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
529
                                 AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
530
                                 AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
531
                return this.classFileTransformer.reset(instrumentation,
×
532
                        classFileTransformer,
533
                        redefinitionStrategy,
534
                        redefinitionDiscoveryStrategy,
535
                        redefinitionBatchAllocator,
536
                        redefinitionListener);
537
            }
538

539
            /**
540
             * {@inheritDoc}
541
             */
542
            public byte[] transform(@MaybeNull ClassLoader classLoader,
543
                                    @MaybeNull String internalName,
544
                                    @MaybeNull Class<?> classBeingRedefined,
545
                                    @MaybeNull ProtectionDomain protectionDomain,
546
                                    byte[] binaryRepresentation) throws IllegalClassFormatException {
547
                return classFileTransformer.transform(classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
1✔
548
            }
549

550
            /**
551
             * A factory for creating a subclass of {@link WithDelegation.Substitutable} that supports the module system, if available.
552
             */
553
            interface Factory {
554

555
                /**
556
                 * Creates a new substitutable class file transformer.
557
                 *
558
                 * @param classFileTransformer The class file transformer to wrap.
559
                 * @return The wrapping class file transformer.
560
                 */
561
                Substitutable make(ResettableClassFileTransformer classFileTransformer);
562

563
                /**
564
                 * An action to create a suitable factory.
565
                 */
566
                enum CreationAction implements PrivilegedAction<Factory> {
1✔
567

568
                    /**
569
                     * The singleton instance.
570
                     */
571
                    INSTANCE;
1✔
572

573
                    /**
574
                     * {@inheritDoc}
575
                     */
576
                    @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
577
                    public Factory run() {
578
                        try {
579
                            return new ForJava9CapableVm(new ByteBuddy()
1✔
580
                                    .with(TypeValidation.DISABLED)
1✔
581
                                    .subclass(WithDelegation.Substitutable.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
1✔
582
                                    .name(WithDelegation.Substitutable.class.getName() + "$ByteBuddy$ModuleSupport")
1✔
583
                                    .method(named("transform").and(takesArgument(0, JavaType.MODULE.load())))
×
584
                                    .intercept(MethodCall.invoke(ClassFileTransformer.class.getMethod("transform",
×
585
                                            JavaType.MODULE.load(),
×
586
                                            ClassLoader.class,
587
                                            String.class,
588
                                            Class.class,
589
                                            ProtectionDomain.class,
590
                                            byte[].class)).onField("classFileTransformer").withAllArguments())
×
591
                                    .make()
×
592
                                    .load(WithDelegation.Substitutable.class.getClassLoader(),
×
593
                                            ClassLoadingStrategy.Default.WRAPPER_PERSISTENT.with(WithDelegation.Substitutable.class.getProtectionDomain()))
×
594
                                    .getLoaded()
×
595
                                    .getConstructor(ResettableClassFileTransformer.class));
×
596
                        } catch (Exception ignored) {
1✔
597
                            return ForLegacyVm.INSTANCE;
1✔
598
                        }
599
                    }
600
                }
601

602
                /**
603
                 * A factory for creating a substitutable class file transformer when the module system is supported.
604
                 */
605
                @HashCodeAndEqualsPlugin.Enhance
606
                class ForJava9CapableVm implements Factory {
607

608
                    /**
609
                     * The constructor to invoke.
610
                     */
611
                    private final Constructor<? extends Substitutable> substitutable;
612

613
                    /**
614
                     * Creates a new Java 9 capable factory.
615
                     *
616
                     * @param substitutable The constructor to invoke.
617
                     */
618
                    protected ForJava9CapableVm(Constructor<? extends Substitutable> substitutable) {
×
619
                        this.substitutable = substitutable;
×
620
                    }
×
621

622
                    /**
623
                     * {@inheritDoc}
624
                     */
625
                    public Substitutable make(ResettableClassFileTransformer classFileTransformer) {
626
                        try {
627
                            return substitutable.newInstance(classFileTransformer);
×
628
                        } catch (IllegalAccessException exception) {
×
629
                            throw new IllegalStateException("Cannot access " + substitutable, exception);
×
630
                        } catch (InstantiationException exception) {
×
631
                            throw new IllegalStateException("Cannot instantiate " + substitutable.getDeclaringClass(), exception);
×
632
                        } catch (InvocationTargetException exception) {
×
633
                            throw new IllegalStateException("Cannot invoke " + substitutable, exception.getTargetException());
×
634
                        }
635
                    }
636
                }
637

638
                /**
639
                 * A factory for a substitutable class file transformer when the module system is not supported.
640
                 */
641
                enum ForLegacyVm implements Factory {
1✔
642

643
                    /**
644
                     * The singleton instance.
645
                     */
646
                    INSTANCE;
1✔
647

648
                    /**
649
                     * {@inheritDoc}
650
                     */
651
                    public Substitutable make(ResettableClassFileTransformer classFileTransformer) {
652
                        return new WithDelegation.Substitutable(classFileTransformer);
1✔
653
                    }
654
                }
655
            }
656
        }
657

658
        /**
659
         * A class file transformer with a callback.
660
         *
661
         * @param <T> The type of the value that is passed between the callback methods.
662
         */
663
        @HashCodeAndEqualsPlugin.Enhance
664
        protected static class WithCallback<T> extends WithDelegation {
665

666
            /**
667
             * The dispatcher that creates the wrapper instance.
668
             */
669
            private static final Factory DISPATCHER = doPrivileged(Factory.CreationAction.INSTANCE);
1✔
670

671
            /**
672
             * The callback to invoke.
673
             */
674
            private final Callback<T> callback;
675

676
            /**
677
             * Creates a delegating class file transformer with callback.
678
             *
679
             * @param classFileTransformer The class file transformer to delegate to.
680
             * @param callback             The callback to invoke.
681
             */
682
            protected WithCallback(ResettableClassFileTransformer classFileTransformer, Callback<T> callback) {
683
                super(classFileTransformer);
1✔
684
                this.callback = callback;
1✔
685
            }
1✔
686

687
            /**
688
             * {@inheritDoc}
689
             */
690
            @MaybeNull
691
            public byte[] transform(@MaybeNull ClassLoader classLoader,
692
                                    @MaybeNull String internalName,
693
                                    @MaybeNull Class<?> classBeingRedefined,
694
                                    @MaybeNull ProtectionDomain protectionDomain,
695
                                    byte[] binaryRepresentation) throws IllegalClassFormatException {
696
                T value = callback.onBeforeTransform(null, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
1✔
697
                try {
698
                    return classFileTransformer.transform(classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
1✔
699
                } finally {
700
                    callback.onAfterTransform(value, null, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
1✔
701
                }
702
            }
703

704
            /**
705
             * A method to delegate to when the module system is supported on the current JVM.
706
             *
707
             * @param rawModule            The instrumented class's {@code java.lang.Module}.
708
             * @param classLoader          The type's class loader or {@code null} if the type is loaded by the bootstrap loader.
709
             * @param internalName         The internal name of the instrumented class.
710
             * @param classBeingRedefined  The loaded {@link Class} being redefined or {@code null} if no such class exists.
711
             * @param protectionDomain     The instrumented type's protection domain or {@code null} if not available.
712
             * @param binaryRepresentation The class file of the instrumented class in its current state.
713
             * @return The binary representation of the transformed class file or {@code null} if no transformation should be applied.
714
             * @throws IllegalClassFormatException If the class file was found invalid.
715
             */
716
            @MaybeNull
717
            protected byte[] transform(Object rawModule,
718
                                       @MaybeNull ClassLoader classLoader,
719
                                       @MaybeNull String internalName,
720
                                       @MaybeNull Class<?> classBeingRedefined,
721
                                       @MaybeNull ProtectionDomain protectionDomain,
722
                                       byte[] binaryRepresentation) throws IllegalClassFormatException {
723
                JavaModule module = JavaModule.of(rawModule);
×
724
                T value = callback.onBeforeTransform(module, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
×
725
                try {
726
                    return doTransform(rawModule, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
×
727
                } finally {
728
                    callback.onAfterTransform(value, module, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
×
729
                }
730
            }
731

732
            /**
733
             * Callback between the transformation callbacks.
734
             *
735
             * @param rawModule            The instrumented class's {@code java.lang.Module}.
736
             * @param classLoader          The type's class loader or {@code null} if the type is loaded by the bootstrap loader.
737
             * @param internalName         The internal name of the instrumented class.
738
             * @param classBeingRedefined  The loaded {@link Class} being redefined or {@code null} if no such class exists.
739
             * @param protectionDomain     The instrumented type's protection domain or {@code null} if not available.
740
             * @param binaryRepresentation The class file of the instrumented class in its current state.
741
             * @return The binary representation of the transformed class file or {@code null} if no transformation should be applied.
742
             * @throws IllegalClassFormatException If the class file was found invalid.
743
             */
744
            @MaybeNull
745
            protected byte[] doTransform(Object rawModule,
746
                                         @MaybeNull ClassLoader classLoader,
747
                                         @MaybeNull String internalName,
748
                                         @MaybeNull Class<?> classBeingRedefined,
749
                                         @MaybeNull ProtectionDomain protectionDomain,
750
                                         byte[] binaryRepresentation) throws IllegalClassFormatException {
751
                throw new UnsupportedOperationException();
×
752
            }
753

754
            /**
755
             * A factory that creates a resettable class file transformer depending on the availability of the module system.
756
             */
757
            interface Factory {
758

759
                /**
760
                 * Creates a new substitutable class file transformer.
761
                 *
762
                 * @param classFileTransformer The class file transformer to wrap.
763
                 * @param callback             The callback to invoke.
764
                 * @return The wrapping class file transformer.
765
                 */
766
                ResettableClassFileTransformer make(ResettableClassFileTransformer classFileTransformer, Callback<?> callback);
767

768
                /**
769
                 * An action to create a suitable factory.
770
                 */
771
                enum CreationAction implements PrivilegedAction<Factory> {
1✔
772

773
                    /**
774
                     * The singleton instance.
775
                     */
776
                    INSTANCE;
1✔
777

778
                    /**
779
                     * {@inheritDoc}
780
                     */
781
                    @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
782
                    public Factory run() {
783
                        try {
784
                            return new Factory.ForJava9CapableVm(new ByteBuddy()
1✔
785
                                    .with(TypeValidation.DISABLED)
1✔
786
                                    .subclass(WithCallback.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
1✔
787
                                    .name(WithCallback.class.getName() + "$ByteBuddy$ModuleSupport")
1✔
788
                                    .method(named("transform").and(takesArguments(JavaType.MODULE.load(),
×
789
                                            ClassLoader.class,
790
                                            String.class,
791
                                            Class.class,
792
                                            ProtectionDomain.class,
793
                                            byte[].class)))
794
                                    .intercept(MethodCall.invoke(WithCallback.class.getDeclaredMethod("transform",
×
795
                                            Object.class,
796
                                            ClassLoader.class,
797
                                            String.class,
798
                                            Class.class,
799
                                            ProtectionDomain.class,
800
                                            byte[].class)).withAllArguments())
×
801
                                    .method(named("doTransform").and(takesArguments(Object.class,
×
802
                                            ClassLoader.class,
803
                                            String.class,
804
                                            Class.class,
805
                                            ProtectionDomain.class,
806
                                            byte[].class)))
807
                                    .intercept(MethodCall.invoke(ClassFileTransformer.class.getMethod("transform",
×
808
                                            JavaType.MODULE.load(),
×
809
                                            ClassLoader.class,
810
                                            String.class,
811
                                            Class.class,
812
                                            ProtectionDomain.class,
813
                                            byte[].class)).onField("classFileTransformer").withAllArguments().withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC))
×
814
                                    .make()
×
815
                                    .load(WithCallback.class.getClassLoader(),
×
816
                                            ClassLoadingStrategy.Default.WRAPPER_PERSISTENT.with(WithCallback.class.getProtectionDomain()))
×
817
                                    .getLoaded()
×
818
                                    .getConstructor(ResettableClassFileTransformer.class, Callback.class));
×
819
                        } catch (Exception ignored) {
1✔
820
                            return Factory.ForLegacyVm.INSTANCE;
1✔
821
                        }
822
                    }
823
                }
824

825
                /**
826
                 * A factory for creating a substitutable class file transformer when the module system is supported.
827
                 */
828
                @HashCodeAndEqualsPlugin.Enhance
829
                class ForJava9CapableVm implements Factory {
830

831
                    /**
832
                     * The constructor to invoke.
833
                     */
834
                    private final Constructor<? extends ResettableClassFileTransformer> withCallback;
835

836
                    /**
837
                     * Creates a new Java 9 capable factory.
838
                     *
839
                     * @param withCallback The constructor to invoke.
840
                     */
841
                    protected ForJava9CapableVm(Constructor<? extends ResettableClassFileTransformer> withCallback) {
×
842
                        this.withCallback = withCallback;
×
843
                    }
×
844

845
                    /**
846
                     * {@inheritDoc}
847
                     */
848
                    public ResettableClassFileTransformer make(ResettableClassFileTransformer classFileTransformer, Callback<?> callback) {
849
                        try {
850
                            return withCallback.newInstance(classFileTransformer, callback);
×
851
                        } catch (IllegalAccessException exception) {
×
852
                            throw new IllegalStateException("Cannot access " + withCallback, exception);
×
853
                        } catch (InstantiationException exception) {
×
854
                            throw new IllegalStateException("Cannot instantiate " + withCallback.getDeclaringClass(), exception);
×
855
                        } catch (InvocationTargetException exception) {
×
856
                            throw new IllegalStateException("Cannot invoke " + withCallback, exception.getTargetException());
×
857
                        }
858
                    }
859
                }
860

861
                /**
862
                 * A factory for a substitutable class file transformer when the module system is not supported.
863
                 */
864
                enum ForLegacyVm implements Factory {
1✔
865

866
                    /**
867
                     * The singleton instance.
868
                     */
869
                    INSTANCE;
1✔
870

871
                    /**
872
                     * {@inheritDoc}
873
                     */
874
                    @SuppressWarnings({"unchecked", "rawtypes"})
875
                    public ResettableClassFileTransformer make(ResettableClassFileTransformer classFileTransformer,
876
                                                               Callback<?> callback) {
877
                        return new WithCallback(classFileTransformer, callback);
1✔
878
                    }
879
                }
880
            }
881
        }
882

883
        /**
884
         * A callback that is invoked upon class file transformation.
885
         *
886
         * @param <T> The type of the value that is passed between the callback methods.
887
         */
888
        public interface Callback<T> {
889

890
            /**
891
             * Invoked before the transformation is applied.
892
             *
893
             * @param module               The instrumented class's Java module or {@code null} if the module system is not supported.
894
             * @param classLoader          The type's class loader or {@code null} if the type is loaded by the bootstrap loader.
895
             * @param internalName         The internal name of the instrumented class.
896
             * @param classBeingRedefined  The loaded {@link Class} being redefined or {@code null} if no such class exists.
897
             * @param protectionDomain     The instrumented type's protection domain or {@code null} if not available.
898
             * @param binaryRepresentation The class file of the instrumented class in its current state.
899
             * @return A value to pass to the method that is invoked after transformation or {@code null}.
900
             * @throws IllegalClassFormatException If the class file was found invalid.
901
             */
902
            @MaybeNull
903
            T onBeforeTransform(@MaybeNull JavaModule module,
904
                                @MaybeNull ClassLoader classLoader,
905
                                @MaybeNull String internalName,
906
                                @MaybeNull Class<?> classBeingRedefined,
907
                                @MaybeNull ProtectionDomain protectionDomain,
908
                                byte[] binaryRepresentation) throws IllegalClassFormatException;
909

910
            /**
911
             * Invoked after the transformation is applied.
912
             *
913
             * @param value                The value that was returned before transformation.
914
             * @param module               The instrumented class's Java module or {@code null} if the module system is not supported.
915
             * @param classLoader          The type's class loader or {@code null} if the type is loaded by the bootstrap loader.
916
             * @param internalName         The internal name of the instrumented class.
917
             * @param classBeingRedefined  The loaded {@link Class} being redefined or {@code null} if no such class exists.
918
             * @param protectionDomain     The instrumented type's protection domain or {@code null} if not available.
919
             * @param binaryRepresentation The class file of the instrumented class in its current state.
920
             * @throws IllegalClassFormatException If the class file was found invalid.
921
             */
922
            void onAfterTransform(@MaybeNull T value,
923
                                  @MaybeNull JavaModule module,
924
                                  @MaybeNull ClassLoader classLoader,
925
                                  @MaybeNull String internalName,
926
                                  @MaybeNull Class<?> classBeingRedefined,
927
                                  @MaybeNull ProtectionDomain protectionDomain,
928
                                  byte[] binaryRepresentation) throws IllegalClassFormatException;
929
        }
930
    }
931
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc