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

raphw / byte-buddy / #809

02 Nov 2025 10:24PM UTC coverage: 84.035% (-0.6%) from 84.614%
#809

push

raphw
Clean up code.

5 of 7 new or added lines in 4 files covered. (71.43%)

958 existing lines in 10 files now uncovered.

29803 of 35465 relevant lines covered (84.03%)

0.84 hits per line

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

77.85
/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.module.ModuleDescription;
23
import net.bytebuddy.description.type.TypeDescription;
24
import net.bytebuddy.utility.GraalImageCode;
25
import net.bytebuddy.utility.JavaModule;
26
import net.bytebuddy.utility.dispatcher.JavaDispatcher;
27
import net.bytebuddy.utility.nullability.AlwaysNull;
28
import net.bytebuddy.utility.nullability.MaybeNull;
29

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

426
    /**
427
     * {@inheritDoc}
428
     */
429
    @MaybeNull
430
    protected URL findResource(String name) {
431
        return persistenceHandler.url(name, typeDefinitions);
1✔
432
    }
433

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

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

456
    /**
457
     * An engine for receiving a <i>class loading lock</i> when loading a class.
458
     */
459
    protected interface SynchronizationStrategy {
460

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

470
        /**
471
         * An uninitialized synchronization strategy.
472
         */
473
        interface Initializable {
474

475
            /**
476
             * Initializes this synchronization strategy.
477
             *
478
             * @return The synchronization strategy to use.
479
             */
480
            SynchronizationStrategy initialize();
481
        }
482

483
        /**
484
         * A creation action for a synchronization strategy.
485
         */
486
        enum CreationAction implements PrivilegedAction<Initializable> {
1✔
487

488
            /**
489
             * The singleton instance.
490
             */
491
            INSTANCE;
1✔
492

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

521
        /**
522
         * A synchronization engine for a VM that is not aware of parallel-capable class loaders.
523
         */
524
        enum ForLegacyVm implements SynchronizationStrategy, Initializable {
1✔
525

526
            /**
527
             * The singleton instance.
528
             */
529
            INSTANCE;
1✔
530

531
            /**
532
             * {@inheritDoc}
533
             */
534
            public Object getClassLoadingLock(ByteArrayClassLoader classLoader, String name) {
535
                return classLoader;
1✔
536
            }
537

538
            /**
539
             * {@inheritDoc}
540
             */
541
            public SynchronizationStrategy initialize() {
542
                return this;
1✔
543
            }
544
        }
545

546
        /**
547
         * A synchronization engine for a VM that is aware of parallel-capable class loaders.
548
         */
549
        @HashCodeAndEqualsPlugin.Enhance
550
        class ForJava7CapableVm implements SynchronizationStrategy, Initializable {
551

552
            /**
553
             * The {@code ClassLoader#getClassLoadingLock(String)} method.
554
             */
555
            private final Method method;
556

557
            /**
558
             * Creates a new synchronization strategy.
559
             *
560
             * @param method The {@code ClassLoader#getClassLoadingLock(String)} method.
561
             */
UNCOV
562
            protected ForJava7CapableVm(Method method) {
×
UNCOV
563
                this.method = method;
×
UNCOV
564
            }
×
565

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

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

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

599
            /**
600
             * The {@code java.lang.invoke.MethodHandle} to use.
601
             */
602
            private final Object methodHandle;
603

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

609
            /**
610
             * The {@code java.lang.invoke.MethodHandle#invokeWithArguments(Object[])} method.
611
             */
612
            private final Method invokeWithArguments;
613

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

627
            /**
628
             * {@inheritDoc}
629
             */
630
            public SynchronizationStrategy initialize() {
631
                return this;
1✔
632
            }
633

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

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

655
        /**
656
         * The binary name of the class to define.
657
         */
658
        private final String name;
659

660
        /**
661
         * The binary representation of the class to be loaded.
662
         */
663
        private final byte[] binaryRepresentation;
664

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

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

704
    /**
705
     * A package lookup strategy for locating a package by name.
706
     */
707
    protected interface PackageLookupStrategy {
708

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

719
        /**
720
         * A creation action for a package lookup strategy.
721
         */
722
        enum CreationAction implements PrivilegedAction<PackageLookupStrategy> {
1✔
723

724
            /**
725
             * The singleton instance.
726
             */
727
            INSTANCE;
1✔
728

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

746
        /**
747
         * A package lookup strategy for a VM prior to Java 9.
748
         */
749
        enum ForLegacyVm implements PackageLookupStrategy {
1✔
750

751
            /**
752
             * The singleton instance.
753
             */
754
            INSTANCE;
1✔
755

756
            /**
757
             * {@inheritDoc}
758
             */
759
            @MaybeNull
760
            public Package apply(ByteArrayClassLoader classLoader, String name) {
761
                return classLoader.doGetPackage(name);
1✔
762
            }
763
        }
764

765
        /**
766
         * A package lookup strategy for Java 9 or newer.
767
         */
768
        @HashCodeAndEqualsPlugin.Enhance
769
        class ForJava9CapableVm implements PackageLookupStrategy {
770

771
            /**
772
             * The {@code java.lang.ClassLoader#getDefinedPackage(String)} method.
773
             */
774
            private final Method getDefinedPackage;
775

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

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

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

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

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

830
            @Override
831
            protected void release(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
832
                /* do nothing */
833
            }
1✔
834
        },
835

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

846
            @Override
847
            protected URL url(String resourceName, ConcurrentMap<String, byte[]> typeDefinitions) {
848
                return NO_URL;
1✔
849
            }
850

851
            @Override
852
            protected void release(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
853
                typeDefinitions.remove(name);
1✔
854
            }
1✔
855
        };
856

857
        /**
858
         * The suffix of files in the Java class file format.
859
         */
860
        private static final String CLASS_FILE_SUFFIX = ".class";
861

862
        /**
863
         * {@code true} if this persistence handler represents manifest class file storage.
864
         */
865
        private final boolean manifest;
866

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

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

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

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

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

913
        /**
914
         * An action to define a URL that represents a class file.
915
         */
916
        @HashCodeAndEqualsPlugin.Enhance
917
        protected static class UrlDefinitionAction implements PrivilegedAction<URL> {
918

919
            /**
920
             * A dispatcher for creating URLs.
921
             */
922
            private static final Dispatcher DISPATCHER = doPrivileged(JavaDispatcher.of(Dispatcher.class));
1✔
923

924
            /**
925
             * The URL's encoding character set.
926
             */
927
            private static final String ENCODING = "UTF-8";
928

929
            /**
930
             * A value to define a standard port as Byte Buddy's URLs do not represent a port.
931
             */
932
            private static final int NO_PORT = -1;
933

934
            /**
935
             * Indicates that Byte Buddy's URLs do not have a file segment.
936
             */
937
            private static final String NO_FILE = "";
938

939
            /**
940
             * The name of the type that this URL represents.
941
             */
942
            private final String typeName;
943

944
            /**
945
             * The binary representation of the type's class file.
946
             */
947
            private final byte[] binaryRepresentation;
948

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

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

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

990
            /**
991
             * A stream handler that returns the given binary representation.
992
             */
993
            @HashCodeAndEqualsPlugin.Enhance
994
            protected static class ByteArrayUrlStreamHandler extends URLStreamHandler {
995

996
                /**
997
                 * The binary representation of a type's class file.
998
                 */
999
                private final byte[] binaryRepresentation;
1000

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

1010
                /**
1011
                 * {@inheritDoc}
1012
                 */
1013
                protected URLConnection openConnection(URL url) {
1014
                    return new ByteArrayUrlConnection(url, new ByteArrayInputStream(binaryRepresentation));
1✔
1015
                }
1016

1017
                /**
1018
                 * A URL connection for a given byte array.
1019
                 */
1020
                protected static class ByteArrayUrlConnection extends URLConnection {
1021

1022
                    /**
1023
                     * The input stream to return for this connection.
1024
                     */
1025
                    private final InputStream inputStream;
1026

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

1038
                    /**
1039
                     * {@inheritDoc}
1040
                     */
1041
                    public void connect() {
1042
                        connected = true;
1✔
1043
                    }
1✔
1044

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

1055
            /**
1056
             * A dispatcher for interacting with {@link URL}.
1057
             */
1058
            @JavaDispatcher.Proxied("java.net.URL")
1059
            protected interface Dispatcher {
1060

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

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

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

1103
        /**
1104
         * The suffix of files in the Java class file format.
1105
         */
1106
        private static final String CLASS_FILE_SUFFIX = ".class";
1107

1108
        /*
1109
         * Register class loader as parallel capable if the current VM supports it.
1110
         */
1111
        static {
1112
            doRegisterAsParallelCapable();
1✔
1113
        }
1✔
1114

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

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

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

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

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

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

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

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

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

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

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

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

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

1352
        /**
1353
         * {@inheritDoc}
1354
         */
1355
        public Enumeration<URL> getResources(String name) throws IOException {
1356
            URL url = persistenceHandler.url(name, typeDefinitions);
1✔
1357
            return url == null
1✔
1358
                    ? super.getResources(name)
1✔
1359
                    : new PrependingEnumeration(url, super.getResources(name));
1✔
1360
        }
1361

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

1383
        /**
1384
         * An enumeration that prepends an element to another enumeration and skips the last element of the provided enumeration.
1385
         */
1386
        protected static class PrependingEnumeration implements Enumeration<URL> {
1387

1388
            /**
1389
             * The next element to return from this enumeration or {@code null} if such an element does not exist.
1390
             */
1391
            @MaybeNull
1392
            private URL nextElement;
1393

1394
            /**
1395
             * The enumeration from which the next elements should be pulled.
1396
             */
1397
            private final Enumeration<URL> enumeration;
1398

1399
            /**
1400
             * Creates a new prepending enumeration.
1401
             *
1402
             * @param url         The first element of the enumeration.
1403
             * @param enumeration An enumeration that is used for pulling subsequent urls.
1404
             */
1405
            protected PrependingEnumeration(URL url, Enumeration<URL> enumeration) {
1✔
1406
                nextElement = url;
1✔
1407
                this.enumeration = enumeration;
1✔
1408
            }
1✔
1409

1410
            /**
1411
             * {@inheritDoc}
1412
             */
1413
            public boolean hasMoreElements() {
1414
                return nextElement != null && enumeration.hasMoreElements();
1✔
1415
            }
1416

1417
            /**
1418
             * {@inheritDoc}
1419
             */
1420
            public URL nextElement() {
1421
                if (nextElement != null && enumeration.hasMoreElements()) {
1✔
1422
                    try {
1423
                        return nextElement;
1✔
1424
                    } finally {
1425
                        nextElement = enumeration.nextElement();
1✔
1426
                    }
1427
                } else {
1428
                    throw new NoSuchElementException();
1✔
1429
                }
1430
            }
1431
        }
1432
    }
1433

1434
    /**
1435
     * An enumeration without any elements.
1436
     */
1437
    protected enum EmptyEnumeration implements Enumeration<URL> {
1✔
1438

1439
        /**
1440
         * The singleton instance.
1441
         */
1442
        INSTANCE;
1✔
1443

1444
        /**
1445
         * {@inheritDoc}
1446
         */
1447
        public boolean hasMoreElements() {
1448
            return false;
1✔
1449
        }
1450

1451
        /**
1452
         * {@inheritDoc}
1453
         */
1454
        public URL nextElement() {
1455
            throw new NoSuchElementException();
1✔
1456
        }
1457
    }
1458

1459
    /**
1460
     * An enumeration that contains a single element.
1461
     */
1462
    protected static class SingletonEnumeration implements Enumeration<URL> {
1463

1464
        /**
1465
         * The current element or {@code null} if this enumeration does not contain further elements.
1466
         */
1467
        @MaybeNull
1468
        private URL element;
1469

1470
        /**
1471
         * Creates a new singleton enumeration.
1472
         *
1473
         * @param element The only element.
1474
         */
1475
        protected SingletonEnumeration(URL element) {
1✔
1476
            this.element = element;
1✔
1477
        }
1✔
1478

1479
        /**
1480
         * {@inheritDoc}
1481
         */
1482
        public boolean hasMoreElements() {
1483
            return element != null;
1✔
1484
        }
1485

1486
        /**
1487
         * {@inheritDoc}
1488
         */
1489
        public URL nextElement() {
1490
            if (element == null) {
1✔
1491
                throw new NoSuchElementException();
1✔
1492
            } else {
1493
                try {
1494
                    return element;
1✔
1495
                } finally {
1496
                    element = null;
1✔
1497
                }
1498
            }
1499
        }
1500
    }
1501
}
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