• 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

78.23
/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ByteArrayClassLoader.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.dynamic.loading;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.ClassFileVersion;
20
import net.bytebuddy.build.AccessControllerPlugin;
21
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
22
import net.bytebuddy.description.type.TypeDescription;
23
import net.bytebuddy.utility.GraalImageCode;
24
import net.bytebuddy.utility.JavaModule;
25
import net.bytebuddy.utility.dispatcher.JavaDispatcher;
26
import net.bytebuddy.utility.nullability.AlwaysNull;
27
import net.bytebuddy.utility.nullability.MaybeNull;
28

29
import java.io.ByteArrayInputStream;
30
import java.io.IOException;
31
import java.io.InputStream;
32
import java.io.UnsupportedEncodingException;
33
import java.lang.reflect.InvocationTargetException;
34
import java.lang.reflect.Method;
35
import java.net.MalformedURLException;
36
import java.net.URI;
37
import java.net.URL;
38
import java.net.URLConnection;
39
import java.net.URLEncoder;
40
import java.net.URLStreamHandler;
41
import java.security.PrivilegedAction;
42
import java.security.ProtectionDomain;
43
import java.util.Enumeration;
44
import java.util.HashMap;
45
import java.util.LinkedHashMap;
46
import java.util.Map;
47
import java.util.NoSuchElementException;
48
import java.util.concurrent.ConcurrentHashMap;
49
import java.util.concurrent.ConcurrentMap;
50

51
/**
52
 * <p>
53
 * A {@link java.lang.ClassLoader} that is capable of loading explicitly defined classes. The class loader will free
54
 * any binary resources once a class that is defined by its binary data is loaded. This class loader is thread safe since
55
 * the class loading mechanics are only called from synchronized context.
56
 * </p>
57
 * <p>
58
 * <b>Note</b>: Instances of this class loader return URLs for their represented class loaders with the <i>bytebuddy</i> schema.
59
 * These URLs do not represent URIs as two classes with the same name yield identical URLs but might represents different byte
60
 * arrays.
61
 * </p>
62
 * <p>
63
 * <b>Note</b>: Any class and package definition is performed using the creator's {@code java.security.AccessControlContext}.
64
 * </p>
65
 */
66
public class ByteArrayClassLoader extends InjectionClassLoader {
67

68
    /**
69
     * The schema for URLs that represent a class file of byte array class loaders.
70
     */
71
    public static final String URL_SCHEMA = "bytebuddy";
72

73
    /**
74
     * Indicates that an array should be included from its first index. Improves the source code readability.
75
     */
76
    private static final int FROM_BEGINNING = 0;
77

78
    /**
79
     * Indicates that a URL does not exist to improve code readability.
80
     */
81
    @AlwaysNull
82
    private static final URL NO_URL = null;
1✔
83

84
    /**
85
     * A strategy for locating a package by name.
86
     */
87
    private static final PackageLookupStrategy PACKAGE_LOOKUP_STRATEGY = doPrivileged(PackageLookupStrategy.CreationAction.INSTANCE);
1✔
88

89
    /**
90
     * The synchronization engine for the executing JVM.
91
     */
92
    protected static final SynchronizationStrategy.Initializable SYNCHRONIZATION_STRATEGY = doPrivileged(SynchronizationStrategy.CreationAction.INSTANCE);
1✔
93

94
    /*
95
     * Register class loader as parallel capable if the current VM supports it.
96
     */
97
    static {
98
        doRegisterAsParallelCapable();
1✔
99
    }
1✔
100

101
    /**
102
     * Registers class loader as parallel capable if possible.
103
     */
104
    @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Must be invoked from targeting class loader type.")
105
    private static void doRegisterAsParallelCapable() {
106
        try {
107
            Method method = ClassLoader.class.getDeclaredMethod("registerAsParallelCapable");
1✔
108
            method.setAccessible(true);
1✔
109
            method.invoke(null);
1✔
110
        } catch (Throwable ignored) {
×
111
            /* do nothing */
112
        }
1✔
113
    }
1✔
114

115
    /**
116
     * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
117
     *
118
     * @param action The action to execute from a privileged context.
119
     * @param <T>    The type of the action's resolved value.
120
     * @return The action's resolved value.
121
     */
122
    @AccessControllerPlugin.Enhance
123
    private static <T> T doPrivileged(PrivilegedAction<T> action) {
124
        return action.run();
×
125
    }
126

127
    /**
128
     * A mutable map of type names mapped to their binary representation.
129
     */
130
    protected final ConcurrentMap<String, byte[]> typeDefinitions;
131

132
    /**
133
     * The persistence handler of this class loader.
134
     */
135
    protected final PersistenceHandler persistenceHandler;
136

137
    /**
138
     * The protection domain to apply. Might be {@code null} when referencing the default protection domain.
139
     */
140
    @MaybeNull
141
    protected final ProtectionDomain protectionDomain;
142

143
    /**
144
     * The package definer to be queried for package definitions.
145
     */
146
    protected final PackageDefinitionStrategy packageDefinitionStrategy;
147

148
    /**
149
     * The class file transformer to apply on loaded classes.
150
     */
151
    protected final ClassFilePostProcessor classFilePostProcessor;
152

153
    /**
154
     * The access control context to use for loading classes or {@code null} if this is not supported on the current VM.
155
     */
156
    @MaybeNull
157
    protected final Object accessControlContext;
158

159
    /**
160
     * Creates a new class loader for a given definition of classes.
161
     *
162
     * @param parent          The {@link java.lang.ClassLoader} that is the parent of this class loader.
163
     * @param typeDefinitions A map of fully qualified class names pointing to their binary representations.
164
     */
165
    public ByteArrayClassLoader(@MaybeNull ClassLoader parent, Map<String, byte[]> typeDefinitions) {
166
        this(parent, true, typeDefinitions);
1✔
167
    }
1✔
168

169
    /**
170
     * Creates a new class loader for a given definition of classes.
171
     *
172
     * @param parent          The {@link java.lang.ClassLoader} that is the parent of this class loader.
173
     * @param sealed          {@code true} if this class loader is sealed.
174
     * @param typeDefinitions A map of fully qualified class names pointing to their binary representations.
175
     */
176
    public ByteArrayClassLoader(@MaybeNull ClassLoader parent, boolean sealed, Map<String, byte[]> typeDefinitions) {
177
        this(parent, sealed, typeDefinitions, PersistenceHandler.LATENT);
1✔
178
    }
1✔
179

180
    /**
181
     * Creates a new class loader for a given definition of classes.
182
     *
183
     * @param parent             The {@link java.lang.ClassLoader} that is the parent of this class loader.
184
     * @param typeDefinitions    A map of fully qualified class names pointing to their binary representations.
185
     * @param persistenceHandler The persistence handler of this class loader.
186
     */
187
    public ByteArrayClassLoader(@MaybeNull ClassLoader parent, Map<String, byte[]> typeDefinitions, PersistenceHandler persistenceHandler) {
188
        this(parent, true, typeDefinitions, persistenceHandler);
1✔
189
    }
1✔
190

191
    /**
192
     * Creates a new class loader for a given definition of classes.
193
     *
194
     * @param parent             The {@link java.lang.ClassLoader} that is the parent of this class loader.
195
     * @param sealed             {@code true} if this class loader is sealed.
196
     * @param typeDefinitions    A map of fully qualified class names pointing to their binary representations.
197
     * @param persistenceHandler The persistence handler of this class loader.
198
     */
199
    public ByteArrayClassLoader(@MaybeNull ClassLoader parent, boolean sealed, Map<String, byte[]> typeDefinitions, PersistenceHandler persistenceHandler) {
200
        this(parent, sealed, typeDefinitions, ClassLoadingStrategy.NO_PROTECTION_DOMAIN, persistenceHandler, PackageDefinitionStrategy.Trivial.INSTANCE);
1✔
201
    }
1✔
202

203
    /**
204
     * Creates a new class loader for a given definition of classes.
205
     *
206
     * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
207
     * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
208
     * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
209
     * @param packageDefinitionStrategy The package definer to be queried for package definitions.
210
     * @param persistenceHandler        The persistence handler of this class loader.
211
     */
212
    public ByteArrayClassLoader(@MaybeNull ClassLoader parent,
213
                                Map<String, byte[]> typeDefinitions,
214
                                @MaybeNull ProtectionDomain protectionDomain,
215
                                PersistenceHandler persistenceHandler,
216
                                PackageDefinitionStrategy packageDefinitionStrategy) {
217
        this(parent, true, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy);
1✔
218
    }
1✔
219

220
    /**
221
     * Creates a new class loader for a given definition of classes.
222
     *
223
     * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
224
     * @param sealed                    {@code true} if this class loader is sealed.
225
     * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
226
     * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
227
     * @param packageDefinitionStrategy The package definer to be queried for package definitions.
228
     * @param persistenceHandler        The persistence handler of this class loader.
229
     */
230
    public ByteArrayClassLoader(@MaybeNull ClassLoader parent,
231
                                boolean sealed,
232
                                Map<String, byte[]> typeDefinitions,
233
                                @MaybeNull ProtectionDomain protectionDomain,
234
                                PersistenceHandler persistenceHandler,
235
                                PackageDefinitionStrategy packageDefinitionStrategy) {
236
        this(parent, sealed, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy, ClassFilePostProcessor.NoOp.INSTANCE);
1✔
237
    }
1✔
238

239
    /**
240
     * Creates a new class loader for a given definition of classes.
241
     *
242
     * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
243
     * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
244
     * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
245
     * @param packageDefinitionStrategy The package definer to be queried for package definitions.
246
     * @param persistenceHandler        The persistence handler of this class loader.
247
     * @param classFilePostProcessor    A post processor for class files to apply p
248
     */
249
    public ByteArrayClassLoader(@MaybeNull ClassLoader parent,
250
                                Map<String, byte[]> typeDefinitions,
251
                                @MaybeNull ProtectionDomain protectionDomain,
252
                                PersistenceHandler persistenceHandler,
253
                                PackageDefinitionStrategy packageDefinitionStrategy,
254
                                ClassFilePostProcessor classFilePostProcessor) {
255
        this(parent, true, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy, classFilePostProcessor);
×
256
    }
×
257

258
    /**
259
     * Creates a new class loader for a given definition of classes.
260
     *
261
     * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
262
     * @param sealed                    {@code true} if this class loader is sealed.
263
     * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
264
     * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
265
     * @param packageDefinitionStrategy The package definer to be queried for package definitions.
266
     * @param persistenceHandler        The persistence handler of this class loader.
267
     * @param classFilePostProcessor    A post processor for class files to apply p
268
     */
269
    public ByteArrayClassLoader(@MaybeNull ClassLoader parent,
270
                                boolean sealed,
271
                                Map<String, byte[]> typeDefinitions,
272
                                @MaybeNull ProtectionDomain protectionDomain,
273
                                PersistenceHandler persistenceHandler,
274
                                PackageDefinitionStrategy packageDefinitionStrategy,
275
                                ClassFilePostProcessor classFilePostProcessor) {
276
        super(parent, sealed);
1✔
277
        this.typeDefinitions = new ConcurrentHashMap<String, byte[]>(typeDefinitions);
1✔
278
        this.protectionDomain = protectionDomain;
1✔
279
        this.persistenceHandler = persistenceHandler;
1✔
280
        this.packageDefinitionStrategy = packageDefinitionStrategy;
1✔
281
        this.classFilePostProcessor = classFilePostProcessor;
1✔
282
        accessControlContext = getContext();
1✔
283
    }
1✔
284

285
    /**
286
     * A proxy for {@code java.security.AccessController#getContext} that is activated if available.
287
     *
288
     * @return The current access control context or {@code null} if the current VM does not support it.
289
     */
290
    @MaybeNull
291
    @AccessControllerPlugin.Enhance
292
    private static Object getContext() {
293
        return null;
×
294
    }
295

296
    /**
297
     * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
298
     *
299
     * @param action  The action to execute from a privileged context.
300
     * @param context The access control context or {@code null} if the current VM does not support it.
301
     * @param <T>     The type of the action's resolved value.
302
     * @return The action's resolved value.
303
     */
304
    @AccessControllerPlugin.Enhance
305
    private static <T> T doPrivileged(PrivilegedAction<T> action, @MaybeNull @SuppressWarnings("unused") Object context) {
306
        return action.run();
×
307
    }
308

309
    /**
310
     * Resolves a method handle in the scope of the {@link ByteArrayClassLoader} class.
311
     *
312
     * @return A method handle for this class.
313
     * @throws Exception If the method handle facility is not supported by the current virtual machine.
314
     */
315
    private static Object methodHandle() throws Exception {
316
        return Class.forName("java.lang.invoke.MethodHandles").getMethod("lookup").invoke(null);
1✔
317
    }
318

319
    /**
320
     * Loads a given set of class descriptions and their binary representations.
321
     *
322
     * @param classLoader The parent class loader.
323
     * @param types       The unloaded types to be loaded.
324
     * @return A map of the given type descriptions pointing to their loaded representations.
325
     */
326
    public static Map<TypeDescription, Class<?>> load(@MaybeNull ClassLoader classLoader, Map<TypeDescription, byte[]> types) {
327
        return load(classLoader,
×
328
                types,
329
                ClassLoadingStrategy.NO_PROTECTION_DOMAIN,
330
                PersistenceHandler.LATENT,
331
                PackageDefinitionStrategy.Trivial.INSTANCE,
332
                false,
333
                true);
334
    }
335

336
    /**
337
     * Loads a given set of class descriptions and their binary representations.
338
     *
339
     * @param classLoader               The parent class loader.
340
     * @param types                     The unloaded types to be loaded.
341
     * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
342
     * @param persistenceHandler        The persistence handler of the created class loader.
343
     * @param packageDefinitionStrategy The package definer to be queried for package definitions.
344
     * @param forbidExisting            {@code true} if the class loading should throw an exception if a class was already loaded by a parent class loader.
345
     * @param sealed                    {@code true} if the class loader should be sealed.
346
     * @return A map of the given type descriptions pointing to their loaded representations.
347
     */
348
    @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
349
    public static Map<TypeDescription, Class<?>> load(@MaybeNull ClassLoader classLoader,
350
                                                      Map<TypeDescription, byte[]> types,
351
                                                      @MaybeNull ProtectionDomain protectionDomain,
352
                                                      PersistenceHandler persistenceHandler,
353
                                                      PackageDefinitionStrategy packageDefinitionStrategy,
354
                                                      boolean forbidExisting,
355
                                                      boolean sealed) {
356
        Map<String, byte[]> typesByName = new HashMap<String, byte[]>();
1✔
357
        for (Map.Entry<TypeDescription, byte[]> entry : types.entrySet()) {
1✔
358
            typesByName.put(entry.getKey().getName(), entry.getValue());
1✔
359
        }
1✔
360
        classLoader = new ByteArrayClassLoader(classLoader,
1✔
361
                sealed,
362
                typesByName,
363
                protectionDomain,
364
                persistenceHandler,
365
                packageDefinitionStrategy,
366
                ClassFilePostProcessor.NoOp.INSTANCE);
367
        Map<TypeDescription, Class<?>> result = new LinkedHashMap<TypeDescription, Class<?>>();
1✔
368
        for (TypeDescription typeDescription : types.keySet()) {
1✔
369
            try {
370
                Class<?> type = Class.forName(typeDescription.getName(), false, classLoader);
1✔
371
                if (!GraalImageCode.getCurrent().isNativeImageExecution() && forbidExisting && type.getClassLoader() != classLoader) {
1✔
372
                    throw new IllegalStateException("Class already loaded: " + type);
1✔
373
                }
374
                result.put(typeDescription, type);
1✔
375
            } catch (ClassNotFoundException exception) {
×
376
                throw new IllegalStateException("Cannot load class " + typeDescription, exception);
×
377
            }
1✔
378
        }
1✔
379
        return result;
1✔
380
    }
381

382
    @Override
383
    protected Map<String, Class<?>> doDefineClasses(Map<String, byte[]> typeDefinitions) throws ClassNotFoundException {
384
        Map<String, byte[]> previous = new HashMap<String, byte[]>();
1✔
385
        for (Map.Entry<String, byte[]> entry : typeDefinitions.entrySet()) {
1✔
386
            previous.put(entry.getKey(), this.typeDefinitions.putIfAbsent(entry.getKey(), entry.getValue()));
1✔
387
        }
1✔
388
        try {
389
            Map<String, Class<?>> types = new LinkedHashMap<String, Class<?>>();
1✔
390
            for (String name : typeDefinitions.keySet()) {
1✔
391
                synchronized (SYNCHRONIZATION_STRATEGY.initialize().getClassLoadingLock(this, name)) {
1✔
392
                    types.put(name, loadClass(name));
1✔
393
                }
1✔
394
            }
1✔
395
            return types;
1✔
396
        } finally {
397
            for (Map.Entry<String, byte[]> entry : previous.entrySet()) {
1✔
398
                if (entry.getValue() == null) {
1✔
399
                    persistenceHandler.release(entry.getKey(), this.typeDefinitions);
1✔
400
                } else {
401
                    this.typeDefinitions.put(entry.getKey(), entry.getValue());
1✔
402
                }
403
            }
1✔
404
        }
405
    }
406

407
    /**
408
     * {@inheritDoc}
409
     */
410
    protected Class<?> findClass(String name) throws ClassNotFoundException {
411
        byte[] binaryRepresentation = persistenceHandler.lookup(name, typeDefinitions);
1✔
412
        if (binaryRepresentation == null) {
1✔
413
            throw new ClassNotFoundException(name);
1✔
414
        } else {
415
            return doPrivileged(new ClassDefinitionAction(name, classFilePostProcessor.transform(this,
1✔
416
                    name,
417
                    protectionDomain,
418
                    binaryRepresentation)), accessControlContext);
419
        }
420
    }
421

422
    /**
423
     * {@inheritDoc}
424
     */
425
    @MaybeNull
426
    protected URL findResource(String name) {
427
        return persistenceHandler.url(name, typeDefinitions);
1✔
428
    }
429

430
    /**
431
     * {@inheritDoc}
432
     */
433
    protected Enumeration<URL> findResources(String name) {
434
        URL url = persistenceHandler.url(name, typeDefinitions);
1✔
435
        return url == null
1✔
436
                ? EmptyEnumeration.INSTANCE
437
                : new SingletonEnumeration(url);
438
    }
439

440
    /**
441
     * Returns the package for a given name.
442
     *
443
     * @param name The name of the package.
444
     * @return A suitable package or {@code null} if no such package exists.
445
     */
446
    @MaybeNull
447
    @SuppressWarnings("deprecation")
448
    private Package doGetPackage(String name) {
449
        return getPackage(name);
1✔
450
    }
451

452
    /**
453
     * An engine for receiving a <i>class loading lock</i> when loading a class.
454
     */
455
    protected interface SynchronizationStrategy {
456

457
        /**
458
         * Receives the class loading lock.
459
         *
460
         * @param name        The name of the class being loaded.
461
         * @param classLoader The class loader loading the class.
462
         * @return The corresponding class loading lock.
463
         */
464
        Object getClassLoadingLock(ByteArrayClassLoader classLoader, String name);
465

466
        /**
467
         * An uninitialized synchronization strategy.
468
         */
469
        interface Initializable {
470

471
            /**
472
             * Initializes this synchronization strategy.
473
             *
474
             * @return The synchronization strategy to use.
475
             */
476
            SynchronizationStrategy initialize();
477
        }
478

479
        /**
480
         * A creation action for a synchronization strategy.
481
         */
482
        enum CreationAction implements PrivilegedAction<Initializable> {
1✔
483

484
            /**
485
             * The singleton instance.
486
             */
487
            INSTANCE;
1✔
488

489
            /**
490
             * {@inheritDoc}
491
             */
492
            @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
493
            public Initializable run() {
494
                try {
495
                    try {
496
                        Class<?> methodType = Class.forName("java.lang.invoke.MethodType"), methodHandle = Class.forName("java.lang.invoke.MethodHandle");
1✔
497
                        return new ForJava8CapableVm(Class.forName("java.lang.invoke.MethodHandles$Lookup")
1✔
498
                                .getMethod("findVirtual", Class.class, String.class, methodType)
1✔
499
                                .invoke(ByteArrayClassLoader.methodHandle(), ClassLoader.class, "getClassLoadingLock", methodType.getMethod("methodType",
1✔
500
                                        Class.class,
501
                                        Class[].class).invoke(null, Object.class, new Class<?>[]{String.class})),
1✔
502
                                methodHandle.getMethod("bindTo", Object.class),
1✔
503
                                methodHandle.getMethod("invokeWithArguments", Object[].class));
1✔
504
                    } catch (Exception ignored) {
×
505
                        // On the bootstrap class loader, a lookup instance cannot be located reflectively. To avoid issuing a warning for accessing
506
                        // a protected method from outside of a class that is caused if the module system does not offer accessing the method.
507
                        return ClassFileVersion.ofThisVm(ClassFileVersion.JAVA_V5).isAtLeast(ClassFileVersion.JAVA_V9) && ByteArrayClassLoader.class.getClassLoader() == null
×
508
                                ? SynchronizationStrategy.ForLegacyVm.INSTANCE
509
                                : new ForJava7CapableVm(ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class));
×
510
                    }
511
                } catch (Exception ignored) {
×
512
                    return SynchronizationStrategy.ForLegacyVm.INSTANCE;
×
513
                }
514
            }
515
        }
516

517
        /**
518
         * A synchronization engine for a VM that is not aware of parallel-capable class loaders.
519
         */
520
        enum ForLegacyVm implements SynchronizationStrategy, Initializable {
1✔
521

522
            /**
523
             * The singleton instance.
524
             */
525
            INSTANCE;
1✔
526

527
            /**
528
             * {@inheritDoc}
529
             */
530
            public Object getClassLoadingLock(ByteArrayClassLoader classLoader, String name) {
531
                return classLoader;
1✔
532
            }
533

534
            /**
535
             * {@inheritDoc}
536
             */
537
            public SynchronizationStrategy initialize() {
538
                return this;
1✔
539
            }
540
        }
541

542
        /**
543
         * A synchronization engine for a VM that is aware of parallel-capable class loaders.
544
         */
545
        @HashCodeAndEqualsPlugin.Enhance
546
        class ForJava7CapableVm implements SynchronizationStrategy, Initializable {
547

548
            /**
549
             * The {@code ClassLoader#getClassLoadingLock(String)} method.
550
             */
551
            private final Method method;
552

553
            /**
554
             * Creates a new synchronization strategy.
555
             *
556
             * @param method The {@code ClassLoader#getClassLoadingLock(String)} method.
557
             */
558
            protected ForJava7CapableVm(Method method) {
×
559
                this.method = method;
×
560
            }
×
561

562
            /**
563
             * {@inheritDoc}
564
             */
565
            public Object getClassLoadingLock(ByteArrayClassLoader classLoader, String name) {
566
                try {
567
                    return method.invoke(classLoader, name);
×
568
                } catch (IllegalAccessException exception) {
×
569
                    throw new IllegalStateException(exception);
×
570
                } catch (InvocationTargetException exception) {
×
571
                    throw new IllegalStateException(exception.getTargetException());
×
572
                }
573
            }
574

575
            /**
576
             * {@inheritDoc}
577
             */
578
            @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
579
            public SynchronizationStrategy initialize() {
580
                try {
581
                    method.setAccessible(true);
×
582
                    return this;
×
583
                } catch (Exception ignored) {
×
584
                    return ForLegacyVm.INSTANCE;
×
585
                }
586
            }
587
        }
588

589
        /**
590
         * A synchronization engine for a VM that is aware of parallel-capable class loaders using method handles to respect module boundaries.
591
         */
592
        @HashCodeAndEqualsPlugin.Enhance
593
        class ForJava8CapableVm implements SynchronizationStrategy, Initializable {
594

595
            /**
596
             * The {@code java.lang.invoke.MethodHandle} to use.
597
             */
598
            private final Object methodHandle;
599

600
            /**
601
             * The {@code java.lang.invoke.MethodHandle#bindTo(Object)} method.
602
             */
603
            private final Method bindTo;
604

605
            /**
606
             * The {@code java.lang.invoke.MethodHandle#invokeWithArguments(Object[])} method.
607
             */
608
            private final Method invokeWithArguments;
609

610
            /**
611
             * Creates a new synchronization strategy.
612
             *
613
             * @param methodHandle        The {@code java.lang.invoke.MethodHandle} to use.
614
             * @param bindTo              The {@code java.lang.invoke.MethodHandle#bindTo(Object)} method.
615
             * @param invokeWithArguments The {@code java.lang.invoke.MethodHandle#invokeWithArguments(Object[])} method.
616
             */
617
            protected ForJava8CapableVm(Object methodHandle, Method bindTo, Method invokeWithArguments) {
1✔
618
                this.methodHandle = methodHandle;
1✔
619
                this.bindTo = bindTo;
1✔
620
                this.invokeWithArguments = invokeWithArguments;
1✔
621
            }
1✔
622

623
            /**
624
             * {@inheritDoc}
625
             */
626
            public SynchronizationStrategy initialize() {
627
                return this;
1✔
628
            }
629

630
            /**
631
             * {@inheritDoc}
632
             */
633
            public Object getClassLoadingLock(ByteArrayClassLoader classLoader, String name) {
634
                try {
635
                    return invokeWithArguments.invoke(bindTo.invoke(methodHandle, classLoader), (Object) new Object[]{name});
1✔
636
                } catch (IllegalAccessException exception) {
×
637
                    throw new IllegalStateException(exception);
×
638
                } catch (InvocationTargetException exception) {
×
639
                    throw new IllegalStateException(exception.getTargetException());
×
640
                }
641
            }
642
        }
643
    }
644

645
    /**
646
     * An action for defining a located class that is not yet loaded.
647
     */
648
    @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
649
    protected class ClassDefinitionAction implements PrivilegedAction<Class<?>> {
650

651
        /**
652
         * The binary name of the class to define.
653
         */
654
        private final String name;
655

656
        /**
657
         * The binary representation of the class to be loaded.
658
         */
659
        private final byte[] binaryRepresentation;
660

661
        /**
662
         * Creates a new class definition action.
663
         *
664
         * @param name                 The binary name of the class to define.
665
         * @param binaryRepresentation The binary representation of the class to be loaded.
666
         */
667
        protected ClassDefinitionAction(String name, byte[] binaryRepresentation) {
1✔
668
            this.name = name;
1✔
669
            this.binaryRepresentation = binaryRepresentation;
1✔
670
        }
1✔
671

672
        /**
673
         * {@inheritDoc}
674
         */
675
        public Class<?> run() {
676
            int packageIndex = name.lastIndexOf('.');
1✔
677
            if (packageIndex != -1) {
1✔
678
                String packageName = name.substring(0, packageIndex);
1✔
679
                PackageDefinitionStrategy.Definition definition = packageDefinitionStrategy.define(ByteArrayClassLoader.this, packageName, name);
1✔
680
                if (definition.isDefined()) {
1✔
681
                    Package definedPackage = PACKAGE_LOOKUP_STRATEGY.apply(ByteArrayClassLoader.this, packageName);
1✔
682
                    if (definedPackage == null) {
1✔
683
                        definePackage(packageName,
1✔
684
                                definition.getSpecificationTitle(),
1✔
685
                                definition.getSpecificationVersion(),
1✔
686
                                definition.getSpecificationVendor(),
1✔
687
                                definition.getImplementationTitle(),
1✔
688
                                definition.getImplementationVersion(),
1✔
689
                                definition.getImplementationVendor(),
1✔
690
                                definition.getSealBase());
1✔
691
                    } else if (!definition.isCompatibleTo(definedPackage)) {
1✔
692
                        throw new SecurityException("Sealing violation for package " + packageName);
×
693
                    }
694
                }
695
            }
696
            return defineClass(name, binaryRepresentation, FROM_BEGINNING, binaryRepresentation.length, protectionDomain);
1✔
697
        }
698
    }
699

700
    /**
701
     * A package lookup strategy for locating a package by name.
702
     */
703
    protected interface PackageLookupStrategy {
704

705
        /**
706
         * Returns a package for a given byte array class loader and a name.
707
         *
708
         * @param classLoader The class loader to locate a package for.
709
         * @param name        The name of the package.
710
         * @return A suitable package or {@code null} if no such package exists.
711
         */
712
        @MaybeNull
713
        Package apply(ByteArrayClassLoader classLoader, String name);
714

715
        /**
716
         * A creation action for a package lookup strategy.
717
         */
718
        enum CreationAction implements PrivilegedAction<PackageLookupStrategy> {
1✔
719

720
            /**
721
             * The singleton instance.
722
             */
723
            INSTANCE;
1✔
724

725
            /**
726
             * {@inheritDoc}
727
             */
728
            @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
729
            public PackageLookupStrategy run() {
730
                if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
1✔
731
                    try {
732
                        return new PackageLookupStrategy.ForJava9CapableVm(ClassLoader.class.getMethod("getDefinedPackage", String.class));
×
733
                    } catch (Exception ignored) {
×
734
                        return PackageLookupStrategy.ForLegacyVm.INSTANCE;
×
735
                    }
736
                } else {
737
                    return PackageLookupStrategy.ForLegacyVm.INSTANCE;
1✔
738
                }
739
            }
740
        }
741

742
        /**
743
         * A package lookup strategy for a VM prior to Java 9.
744
         */
745
        enum ForLegacyVm implements PackageLookupStrategy {
1✔
746

747
            /**
748
             * The singleton instance.
749
             */
750
            INSTANCE;
1✔
751

752
            /**
753
             * {@inheritDoc}
754
             */
755
            @MaybeNull
756
            public Package apply(ByteArrayClassLoader classLoader, String name) {
757
                return classLoader.doGetPackage(name);
1✔
758
            }
759
        }
760

761
        /**
762
         * A package lookup strategy for Java 9 or newer.
763
         */
764
        @HashCodeAndEqualsPlugin.Enhance
765
        class ForJava9CapableVm implements PackageLookupStrategy {
766

767
            /**
768
             * The {@code java.lang.ClassLoader#getDefinedPackage(String)} method.
769
             */
770
            private final Method getDefinedPackage;
771

772
            /**
773
             * Creates a new package lookup strategy for a modern VM.
774
             *
775
             * @param getDefinedPackage The {@code java.lang.ClassLoader#getDefinedPackage(String)} method.
776
             */
777
            protected ForJava9CapableVm(Method getDefinedPackage) {
×
778
                this.getDefinedPackage = getDefinedPackage;
×
779
            }
×
780

781
            /**
782
             * {@inheritDoc}
783
             */
784
            @MaybeNull
785
            public Package apply(ByteArrayClassLoader classLoader, String name) {
786
                try {
787
                    return (Package) getDefinedPackage.invoke(classLoader, name);
×
788
                } catch (IllegalAccessException exception) {
×
789
                    throw new IllegalStateException(exception);
×
790
                } catch (InvocationTargetException exception) {
×
791
                    throw new IllegalStateException(exception.getTargetException());
×
792
                }
793
            }
794
        }
795
    }
796

797
    /**
798
     * A persistence handler decides on whether the byte array that represents a loaded class is exposed by
799
     * the {@link java.lang.ClassLoader#getResourceAsStream(String)} method.
800
     */
801
    public enum PersistenceHandler {
1✔
802

803
        /**
804
         * The manifest persistence handler retains all class file representations and makes them accessible.
805
         */
806
        MANIFEST(true) {
1✔
807
            @Override
808
            protected byte[] lookup(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
809
                return typeDefinitions.get(name);
1✔
810
            }
811

812
            @Override
813
            protected URL url(String resourceName, ConcurrentMap<String, byte[]> typeDefinitions) {
814
                if (!resourceName.endsWith(CLASS_FILE_SUFFIX)) {
1✔
815
                    return NO_URL;
×
816
                } else if (resourceName.startsWith("/")) {
1✔
817
                    resourceName = resourceName.substring(1);
1✔
818
                }
819
                String typeName = resourceName.replace('/', '.').substring(FROM_BEGINNING, resourceName.length() - CLASS_FILE_SUFFIX.length());
1✔
820
                byte[] binaryRepresentation = typeDefinitions.get(typeName);
1✔
821
                return binaryRepresentation == null
1✔
822
                        ? NO_URL
1✔
823
                        : doPrivileged(new UrlDefinitionAction(resourceName, binaryRepresentation));
1✔
824
            }
825

826
            @Override
827
            protected void release(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
828
                /* do nothing */
829
            }
1✔
830
        },
831

832
        /**
833
         * The latent persistence handler hides all class file representations and does not make them accessible
834
         * even before they are loaded.
835
         */
836
        LATENT(false) {
1✔
837
            @Override
838
            protected byte[] lookup(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
839
                return typeDefinitions.remove(name);
1✔
840
            }
841

842
            @Override
843
            protected URL url(String resourceName, ConcurrentMap<String, byte[]> typeDefinitions) {
844
                return NO_URL;
1✔
845
            }
846

847
            @Override
848
            protected void release(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
849
                typeDefinitions.remove(name);
1✔
850
            }
1✔
851
        };
852

853
        /**
854
         * The suffix of files in the Java class file format.
855
         */
856
        private static final String CLASS_FILE_SUFFIX = ".class";
857

858
        /**
859
         * {@code true} if this persistence handler represents manifest class file storage.
860
         */
861
        private final boolean manifest;
862

863
        /**
864
         * Creates a new persistence handler.
865
         *
866
         * @param manifest {@code true} if this persistence handler represents manifest class file storage.
867
         */
868
        PersistenceHandler(boolean manifest) {
1✔
869
            this.manifest = manifest;
1✔
870
        }
1✔
871

872
        /**
873
         * Checks if this persistence handler represents manifest class file storage.
874
         *
875
         * @return {@code true} if this persistence handler represents manifest class file storage.
876
         */
877
        public boolean isManifest() {
878
            return manifest;
1✔
879
        }
880

881
        /**
882
         * Performs a lookup of a class file by its name.
883
         *
884
         * @param name            The name of the class to be loaded.
885
         * @param typeDefinitions A map of fully qualified class names pointing to their binary representations.
886
         * @return The byte array representing the requested class or {@code null} if no such class is known.
887
         */
888
        @MaybeNull
889
        protected abstract byte[] lookup(String name, ConcurrentMap<String, byte[]> typeDefinitions);
890

891
        /**
892
         * Returns a URL representing a class file.
893
         *
894
         * @param resourceName    The name of the requested resource.
895
         * @param typeDefinitions A mapping of byte arrays by their type names.
896
         * @return A URL representing the type definition or {@code null} if the requested resource does not represent a class file.
897
         */
898
        @MaybeNull
899
        protected abstract URL url(String resourceName, ConcurrentMap<String, byte[]> typeDefinitions);
900

901
        /**
902
         * Removes the binary representation of the supplied type if this class loader is latent.
903
         *
904
         * @param name            The name of the type.
905
         * @param typeDefinitions A mapping of byte arrays by their type names.
906
         */
907
        protected abstract void release(String name, ConcurrentMap<String, byte[]> typeDefinitions);
908

909
        /**
910
         * An action to define a URL that represents a class file.
911
         */
912
        @HashCodeAndEqualsPlugin.Enhance
913
        protected static class UrlDefinitionAction implements PrivilegedAction<URL> {
914

915
            /**
916
             * A dispatcher for creating URLs.
917
             */
918
            private static final Dispatcher DISPATCHER = doPrivileged(JavaDispatcher.of(Dispatcher.class));
1✔
919

920
            /**
921
             * The URL's encoding character set.
922
             */
923
            private static final String ENCODING = "UTF-8";
924

925
            /**
926
             * A value to define a standard port as Byte Buddy's URLs do not represent a port.
927
             */
928
            private static final int NO_PORT = -1;
929

930
            /**
931
             * Indicates that Byte Buddy's URLs do not have a file segment.
932
             */
933
            private static final String NO_FILE = "";
934

935
            /**
936
             * The name of the type that this URL represents.
937
             */
938
            private final String typeName;
939

940
            /**
941
             * The binary representation of the type's class file.
942
             */
943
            private final byte[] binaryRepresentation;
944

945
            /**
946
             * Creates a new URL definition action.
947
             *
948
             * @param typeName             The name of the type that this URL represents.
949
             * @param binaryRepresentation The binary representation of the type's class file.
950
             */
951
            protected UrlDefinitionAction(String typeName, byte[] binaryRepresentation) {
1✔
952
                this.typeName = typeName;
1✔
953
                this.binaryRepresentation = binaryRepresentation;
1✔
954
            }
1✔
955

956
            /**
957
             * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
958
             *
959
             * @param action The action to execute from a privileged context.
960
             * @param <T>    The type of the action's resolved value.
961
             * @return The action's resolved value.
962
             */
963
            @AccessControllerPlugin.Enhance
964
            private static <T> T doPrivileged(PrivilegedAction<T> action) {
965
                return action.run();
×
966
            }
967

968
            /**
969
             * {@inheritDoc}
970
             */
971
            public URL run() {
972
                try {
973
                    String path = URLEncoder.encode(typeName.replace('.', '/'), ENCODING);
1✔
974
                    URLStreamHandler handler = new ByteArrayUrlStreamHandler(binaryRepresentation);
1✔
975
                    URL url = DISPATCHER.of(URI.create(URL_SCHEMA + "://" + path), handler);
1✔
976
                    return url == null
1✔
977
                            ? DISPATCHER.make(URL_SCHEMA, path, NO_PORT, NO_FILE, handler)
1✔
978
                            : url;
979
                } catch (MalformedURLException exception) {
×
980
                    throw new IllegalStateException("Cannot create URL for " + typeName, exception);
×
981
                } catch (UnsupportedEncodingException exception) {
×
982
                    throw new IllegalStateException("Could not find encoding: " + ENCODING, exception);
×
983
                }
984
            }
985

986
            /**
987
             * A stream handler that returns the given binary representation.
988
             */
989
            @HashCodeAndEqualsPlugin.Enhance
990
            protected static class ByteArrayUrlStreamHandler extends URLStreamHandler {
991

992
                /**
993
                 * The binary representation of a type's class file.
994
                 */
995
                private final byte[] binaryRepresentation;
996

997
                /**
998
                 * Creates a new byte array URL stream handler.
999
                 *
1000
                 * @param binaryRepresentation The binary representation of a type's class file.
1001
                 */
1002
                protected ByteArrayUrlStreamHandler(byte[] binaryRepresentation) {
1✔
1003
                    this.binaryRepresentation = binaryRepresentation;
1✔
1004
                }
1✔
1005

1006
                /**
1007
                 * {@inheritDoc}
1008
                 */
1009
                protected URLConnection openConnection(URL url) {
1010
                    return new ByteArrayUrlConnection(url, new ByteArrayInputStream(binaryRepresentation));
1✔
1011
                }
1012

1013
                /**
1014
                 * A URL connection for a given byte array.
1015
                 */
1016
                protected static class ByteArrayUrlConnection extends URLConnection {
1017

1018
                    /**
1019
                     * The input stream to return for this connection.
1020
                     */
1021
                    private final InputStream inputStream;
1022

1023
                    /**
1024
                     * Creates a new byte array URL connection.
1025
                     *
1026
                     * @param url         The URL that this connection represents.
1027
                     * @param inputStream The input stream to return from this connection.
1028
                     */
1029
                    protected ByteArrayUrlConnection(URL url, InputStream inputStream) {
1030
                        super(url);
1✔
1031
                        this.inputStream = inputStream;
1✔
1032
                    }
1✔
1033

1034
                    /**
1035
                     * {@inheritDoc}
1036
                     */
1037
                    public void connect() {
1038
                        connected = true;
1✔
1039
                    }
1✔
1040

1041
                    /**
1042
                     * {@inheritDoc}
1043
                     */
1044
                    public InputStream getInputStream() {
1045
                        connect(); // Mimics the semantics of an actual URL connection.
1✔
1046
                        return inputStream;
1✔
1047
                    }
1048
                }
1049
            }
1050

1051
            /**
1052
             * A dispatcher for interacting with {@link URL}.
1053
             */
1054
            @JavaDispatcher.Proxied("java.net.URL")
1055
            protected interface Dispatcher {
1056

1057
                /**
1058
                 * Creates a {@link URL}.
1059
                 *
1060
                 * @param protocol The URL's protocol.
1061
                 * @param host     The host on the URL.
1062
                 * @param port     The port on the URL or a negative value if no port is defined.
1063
                 * @param file     The file on the URL.
1064
                 * @param handler  The stream handler to use.
1065
                 * @return An appropriate URL.
1066
                 * @throws MalformedURLException If the supplied URL is malformed.
1067
                 */
1068
                @JavaDispatcher.IsConstructor
1069
                URL make(String protocol, String host, int port, String file, URLStreamHandler handler) throws MalformedURLException;
1070

1071
                /**
1072
                 * Resolves a URL from an URI, if possible.
1073
                 *
1074
                 * @param uri     The URI to represent.
1075
                 * @param handler The stream handler to attach to that URL.
1076
                 * @return An appropriate URL.
1077
                 * @throws MalformedURLException If the supplied URL is malformed.
1078
                 */
1079
                @MaybeNull
1080
                @JavaDispatcher.IsStatic
1081
                @JavaDispatcher.Defaults
1082
                URL of(URI uri, URLStreamHandler handler) throws MalformedURLException;
1083
            }
1084
        }
1085
    }
1086

1087
    /**
1088
     * <p>
1089
     * A {@link net.bytebuddy.dynamic.loading.ByteArrayClassLoader} which applies child-first semantics for the
1090
     * given type definitions.
1091
     * </p>
1092
     * <p>
1093
     * <b>Important</b>: Package definitions remain their parent-first semantics as loaded package definitions do not expose their class loaders.
1094
     * Also, it is not possible to make this class or its subclass parallel-capable as the loading strategy is overridden.
1095
     * </p>
1096
     */
1097
    public static class ChildFirst extends ByteArrayClassLoader {
1098

1099
        /**
1100
         * The suffix of files in the Java class file format.
1101
         */
1102
        private static final String CLASS_FILE_SUFFIX = ".class";
1103

1104
        /*
1105
         * Register class loader as parallel capable if the current VM supports it.
1106
         */
1107
        static {
1108
            doRegisterAsParallelCapable();
1✔
1109
        }
1✔
1110

1111
        /**
1112
         * Registers class loader as parallel capable if possible.
1113
         */
1114
        @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Must be invoked from targeting class loader type.")
1115
        private static void doRegisterAsParallelCapable() {
1116
            try {
1117
                Method method = ClassLoader.class.getDeclaredMethod("registerAsParallelCapable");
1✔
1118
                method.setAccessible(true);
1✔
1119
                method.invoke(null);
1✔
1120
            } catch (Throwable ignored) {
×
1121
                /* do nothing */
1122
            }
1✔
1123
        }
1✔
1124

1125
        /**
1126
         * Creates a new child-first byte array class loader.
1127
         *
1128
         * @param parent          The {@link java.lang.ClassLoader} that is the parent of this class loader.
1129
         * @param typeDefinitions A map of fully qualified class names pointing to their binary representations.
1130
         */
1131
        public ChildFirst(@MaybeNull ClassLoader parent, Map<String, byte[]> typeDefinitions) {
1132
            super(parent, typeDefinitions);
×
1133
        }
×
1134

1135
        /**
1136
         * Creates a new child-first byte array class loader.
1137
         *
1138
         * @param parent          The {@link java.lang.ClassLoader} that is the parent of this class loader.
1139
         * @param sealed          {@code true} if this class loader is sealed.
1140
         * @param typeDefinitions A map of fully qualified class names pointing to their binary representations.
1141
         */
1142
        public ChildFirst(@MaybeNull ClassLoader parent, boolean sealed, Map<String, byte[]> typeDefinitions) {
1143
            super(parent, sealed, typeDefinitions);
×
1144
        }
×
1145

1146
        /**
1147
         * Creates a new child-first byte array class loader.
1148
         *
1149
         * @param parent             The {@link java.lang.ClassLoader} that is the parent of this class loader.
1150
         * @param typeDefinitions    A map of fully qualified class names pointing to their binary representations.
1151
         * @param persistenceHandler The persistence handler of this class loader.
1152
         */
1153
        public ChildFirst(@MaybeNull ClassLoader parent, Map<String, byte[]> typeDefinitions, PersistenceHandler persistenceHandler) {
1154
            super(parent, typeDefinitions, persistenceHandler);
1✔
1155
        }
1✔
1156

1157
        /**
1158
         * Creates a new child-first byte array class loader.
1159
         *
1160
         * @param parent             The {@link java.lang.ClassLoader} that is the parent of this class loader.
1161
         * @param sealed             {@code true} if this class loader is sealed.
1162
         * @param typeDefinitions    A map of fully qualified class names pointing to their binary representations.
1163
         * @param persistenceHandler The persistence handler of this class loader.
1164
         */
1165
        public ChildFirst(@MaybeNull ClassLoader parent, boolean sealed, Map<String, byte[]> typeDefinitions, PersistenceHandler persistenceHandler) {
1166
            super(parent, sealed, typeDefinitions, persistenceHandler);
×
1167
        }
×
1168

1169
        /**
1170
         * Creates a new child-first byte array class loader.
1171
         *
1172
         * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
1173
         * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
1174
         * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
1175
         * @param persistenceHandler        The persistence handler of this class loader.
1176
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
1177
         */
1178
        public ChildFirst(@MaybeNull ClassLoader parent,
1179
                          Map<String, byte[]> typeDefinitions,
1180
                          @MaybeNull ProtectionDomain protectionDomain,
1181
                          PersistenceHandler persistenceHandler,
1182
                          PackageDefinitionStrategy packageDefinitionStrategy) {
1183
            super(parent, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy);
1✔
1184
        }
1✔
1185

1186
        /**
1187
         * Creates a new child-first byte array class loader.
1188
         *
1189
         * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
1190
         * @param sealed                    {@code true} if this class loader is sealed.
1191
         * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
1192
         * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
1193
         * @param persistenceHandler        The persistence handler of this class loader.
1194
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
1195
         */
1196
        public ChildFirst(@MaybeNull ClassLoader parent,
1197
                          boolean sealed,
1198
                          Map<String, byte[]> typeDefinitions,
1199
                          @MaybeNull ProtectionDomain protectionDomain,
1200
                          PersistenceHandler persistenceHandler,
1201
                          PackageDefinitionStrategy packageDefinitionStrategy) {
1202
            super(parent, sealed, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy);
×
1203
        }
×
1204

1205
        /**
1206
         * Creates a new child-first byte array class loader.
1207
         *
1208
         * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
1209
         * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
1210
         * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
1211
         * @param persistenceHandler        The persistence handler of this class loader.
1212
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
1213
         * @param classFilePostProcessor    A post processor for class files to apply p
1214
         */
1215
        public ChildFirst(@MaybeNull ClassLoader parent,
1216
                          Map<String, byte[]> typeDefinitions,
1217
                          @MaybeNull ProtectionDomain protectionDomain,
1218
                          PersistenceHandler persistenceHandler,
1219
                          PackageDefinitionStrategy packageDefinitionStrategy,
1220
                          ClassFilePostProcessor classFilePostProcessor) {
1221
            super(parent, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy, classFilePostProcessor);
×
1222
        }
×
1223

1224
        /**
1225
         * Creates a new child-first byte array class loader.
1226
         *
1227
         * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
1228
         * @param sealed                    {@code true} if this class loader is sealed.
1229
         * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
1230
         * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
1231
         * @param persistenceHandler        The persistence handler of this class loader.
1232
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
1233
         * @param classFilePostProcessor    A post processor for class files to apply p
1234
         */
1235
        public ChildFirst(@MaybeNull ClassLoader parent,
1236
                          boolean sealed,
1237
                          Map<String, byte[]> typeDefinitions,
1238
                          @MaybeNull ProtectionDomain protectionDomain,
1239
                          PersistenceHandler persistenceHandler,
1240
                          PackageDefinitionStrategy packageDefinitionStrategy,
1241
                          ClassFilePostProcessor classFilePostProcessor) {
1242
            super(parent, sealed, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy, classFilePostProcessor);
1✔
1243
        }
1✔
1244

1245
        /**
1246
         * Loads a given set of class descriptions and their binary representations using a child-first class loader.
1247
         *
1248
         * @param classLoader The parent class loader.
1249
         * @param types       The unloaded types to be loaded.
1250
         * @return A map of the given type descriptions pointing to their loaded representations.
1251
         */
1252
        public static Map<TypeDescription, Class<?>> load(@MaybeNull ClassLoader classLoader, Map<TypeDescription, byte[]> types) {
1253
            return load(classLoader,
×
1254
                    types,
1255
                    ClassLoadingStrategy.NO_PROTECTION_DOMAIN,
1256
                    PersistenceHandler.LATENT,
1257
                    PackageDefinitionStrategy.Trivial.INSTANCE,
1258
                    false,
1259
                    true);
1260
        }
1261

1262
        /**
1263
         * Loads a given set of class descriptions and their binary representations using a child-first class loader.
1264
         *
1265
         * @param classLoader               The parent class loader.
1266
         * @param types                     The unloaded types to be loaded.
1267
         * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
1268
         * @param persistenceHandler        The persistence handler of the created class loader.
1269
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
1270
         * @param forbidExisting            {@code true} if the class loading should throw an exception if a class was already loaded by a parent class loader.
1271
         * @param sealed                    {@code true} if the class loader should be sealed.
1272
         * @return A map of the given type descriptions pointing to their loaded representations.
1273
         */
1274
        @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
1275
        public static Map<TypeDescription, Class<?>> load(@MaybeNull ClassLoader classLoader,
1276
                                                          Map<TypeDescription, byte[]> types,
1277
                                                          @MaybeNull ProtectionDomain protectionDomain,
1278
                                                          PersistenceHandler persistenceHandler,
1279
                                                          PackageDefinitionStrategy packageDefinitionStrategy,
1280
                                                          boolean forbidExisting,
1281
                                                          boolean sealed) {
1282
            Map<String, byte[]> typesByName = new HashMap<String, byte[]>();
1✔
1283
            for (Map.Entry<TypeDescription, byte[]> entry : types.entrySet()) {
1✔
1284
                typesByName.put(entry.getKey().getName(), entry.getValue());
1✔
1285
            }
1✔
1286
            classLoader = new ChildFirst(classLoader,
1✔
1287
                    sealed,
1288
                    typesByName,
1289
                    protectionDomain,
1290
                    persistenceHandler,
1291
                    packageDefinitionStrategy,
1292
                    ClassFilePostProcessor.NoOp.INSTANCE);
1293
            Map<TypeDescription, Class<?>> result = new LinkedHashMap<TypeDescription, Class<?>>();
1✔
1294
            for (TypeDescription typeDescription : types.keySet()) {
1✔
1295
                try {
1296
                    Class<?> type = Class.forName(typeDescription.getName(), false, classLoader);
1✔
1297
                    if (!GraalImageCode.getCurrent().isNativeImageExecution() && forbidExisting && type.getClassLoader() != classLoader) {
1✔
1298
                        throw new IllegalStateException("Class already loaded: " + type);
×
1299
                    }
1300
                    result.put(typeDescription, type);
1✔
1301
                } catch (ClassNotFoundException exception) {
×
1302
                    throw new IllegalStateException("Cannot load class " + typeDescription, exception);
×
1303
                }
1✔
1304
            }
1✔
1305
            return result;
1✔
1306
        }
1307

1308
        /**
1309
         * {@inheritDoc}
1310
         */
1311
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
1312
            synchronized (SYNCHRONIZATION_STRATEGY.initialize().getClassLoadingLock(this, name)) {
1✔
1313
                Class<?> type = findLoadedClass(name);
1✔
1314
                if (type != null) {
1✔
1315
                    return type;
1✔
1316
                }
1317
                try {
1318
                    type = findClass(name);
1✔
1319
                    if (resolve) {
1✔
1320
                        resolveClass(type);
×
1321
                    }
1322
                    return type;
1✔
1323
                } catch (ClassNotFoundException exception) {
1✔
1324
                    // If an unknown class is loaded, this implementation causes the findClass method of this instance
1325
                    // to be triggered twice. This is however of minor importance because this would result in a
1326
                    // ClassNotFoundException what does not alter the outcome.
1327
                    return super.loadClass(name, resolve);
1✔
1328
                }
1329
            }
1330
        }
1331

1332
        /**
1333
         * {@inheritDoc}
1334
         */
1335
        public URL getResource(String name) {
1336
            URL url = persistenceHandler.url(name, typeDefinitions);
1✔
1337
            // If a class resource is defined by this class loader but it is not defined in a manifest manner,
1338
            // the resource of the parent class loader should be shadowed by 'null'. Note that the delegation
1339
            // model causes a redundant query to the persistent handler but renders a correct result.
1340
            return url != null || isShadowed(name)
1✔
1341
                    ? url
1342
                    : super.getResource(name);
1✔
1343
        }
1344

1345
        /**
1346
         * {@inheritDoc}
1347
         */
1348
        public Enumeration<URL> getResources(String name) throws IOException {
1349
            URL url = persistenceHandler.url(name, typeDefinitions);
1✔
1350
            return url == null
1✔
1351
                    ? super.getResources(name)
1✔
1352
                    : new PrependingEnumeration(url, super.getResources(name));
1✔
1353
        }
1354

1355
        /**
1356
         * Checks if a resource name represents a class file of a class that was loaded by this class loader.
1357
         *
1358
         * @param resource The resource name of the class to be exposed as its class file.
1359
         * @return {@code true} if this class represents a class that is being loaded by this class loader.
1360
         */
1361
        private boolean isShadowed(String resource) {
1362
            if (persistenceHandler.isManifest() || !resource.endsWith(CLASS_FILE_SUFFIX)) {
1✔
1363
                return false;
1✔
1364
            }
1365
            // This synchronization is required to avoid a racing condition to the actual class loading.
1366
            String name = resource.replace('/', '.').substring(0, resource.length() - CLASS_FILE_SUFFIX.length());
1✔
1367
            synchronized (SYNCHRONIZATION_STRATEGY.initialize().getClassLoadingLock(this, name)) {
1✔
1368
                if (typeDefinitions.containsKey(name)) {
1✔
1369
                    return true;
1✔
1370
                }
1371
                Class<?> loadedClass = findLoadedClass(name);
1✔
1372
                return loadedClass != null && loadedClass.getClassLoader() == this;
1✔
1373
            }
1374
        }
1375

1376
        /**
1377
         * An enumeration that prepends an element to another enumeration and skips the last element of the provided enumeration.
1378
         */
1379
        protected static class PrependingEnumeration implements Enumeration<URL> {
1380

1381
            /**
1382
             * The next element to return from this enumeration or {@code null} if such an element does not exist.
1383
             */
1384
            @MaybeNull
1385
            private URL nextElement;
1386

1387
            /**
1388
             * The enumeration from which the next elements should be pulled.
1389
             */
1390
            private final Enumeration<URL> enumeration;
1391

1392
            /**
1393
             * Creates a new prepending enumeration.
1394
             *
1395
             * @param url         The first element of the enumeration.
1396
             * @param enumeration An enumeration that is used for pulling subsequent urls.
1397
             */
1398
            protected PrependingEnumeration(URL url, Enumeration<URL> enumeration) {
1✔
1399
                nextElement = url;
1✔
1400
                this.enumeration = enumeration;
1✔
1401
            }
1✔
1402

1403
            /**
1404
             * {@inheritDoc}
1405
             */
1406
            public boolean hasMoreElements() {
1407
                return nextElement != null && enumeration.hasMoreElements();
1✔
1408
            }
1409

1410
            /**
1411
             * {@inheritDoc}
1412
             */
1413
            public URL nextElement() {
1414
                if (nextElement != null && enumeration.hasMoreElements()) {
1✔
1415
                    try {
1416
                        return nextElement;
1✔
1417
                    } finally {
1418
                        nextElement = enumeration.nextElement();
1✔
1419
                    }
1420
                } else {
1421
                    throw new NoSuchElementException();
1✔
1422
                }
1423
            }
1424
        }
1425
    }
1426

1427
    /**
1428
     * An enumeration without any elements.
1429
     */
1430
    protected enum EmptyEnumeration implements Enumeration<URL> {
1✔
1431

1432
        /**
1433
         * The singleton instance.
1434
         */
1435
        INSTANCE;
1✔
1436

1437
        /**
1438
         * {@inheritDoc}
1439
         */
1440
        public boolean hasMoreElements() {
1441
            return false;
1✔
1442
        }
1443

1444
        /**
1445
         * {@inheritDoc}
1446
         */
1447
        public URL nextElement() {
1448
            throw new NoSuchElementException();
1✔
1449
        }
1450
    }
1451

1452
    /**
1453
     * An enumeration that contains a single element.
1454
     */
1455
    protected static class SingletonEnumeration implements Enumeration<URL> {
1456

1457
        /**
1458
         * The current element or {@code null} if this enumeration does not contain further elements.
1459
         */
1460
        @MaybeNull
1461
        private URL element;
1462

1463
        /**
1464
         * Creates a new singleton enumeration.
1465
         *
1466
         * @param element The only element.
1467
         */
1468
        protected SingletonEnumeration(URL element) {
1✔
1469
            this.element = element;
1✔
1470
        }
1✔
1471

1472
        /**
1473
         * {@inheritDoc}
1474
         */
1475
        public boolean hasMoreElements() {
1476
            return element != null;
1✔
1477
        }
1478

1479
        /**
1480
         * {@inheritDoc}
1481
         */
1482
        public URL nextElement() {
1483
            if (element == null) {
1✔
1484
                throw new NoSuchElementException();
1✔
1485
            } else {
1486
                try {
1487
                    return element;
1✔
1488
                } finally {
1489
                    element = null;
1✔
1490
                }
1491
            }
1492
        }
1493
    }
1494
}
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