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

raphw / byte-buddy / #810

03 Nov 2025 07:17AM UTC coverage: 84.023% (-0.01%) from 84.035%
#810

push

raphw
Fix javadoc.

29813 of 35482 relevant lines covered (84.02%)

0.84 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

136
    /**
137
     * The persistence handler of this class loader.
138
     */
139
    protected final PersistenceHandler persistenceHandler;
140

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

147
    /**
148
     * The package definer to be queried for package definitions.
149
     */
150
    protected final PackageDefinitionStrategy packageDefinitionStrategy;
151

152
    /**
153
     * The class file transformer to apply on loaded classes.
154
     */
155
    protected final ClassFilePostProcessor classFilePostProcessor;
156

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

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

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

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

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

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

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

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

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

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

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

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

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

340
    /**
341
     * Loads a given set of class descriptions and their binary representations.
342
     *
343
     * @param classLoader               The parent class loader.
344
     * @param types                     The unloaded types to be loaded.
345
     * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
346
     * @param persistenceHandler        The persistence handler of the created class loader.
347
     * @param packageDefinitionStrategy The package definer to be queried for package definitions.
348
     * @param forbidExisting            {@code true} if the class loading should throw an exception if a class was already loaded by a parent class loader.
349
     * @param sealed                    {@code true} if the class loader should be sealed.
350
     * @return A map of the given type descriptions pointing to their loaded representations.
351
     */
352
    public static Map<TypeDescription, Class<?>> load(@MaybeNull ClassLoader classLoader,
353
                                                      Map<TypeDescription, byte[]> types,
354
                                                      @MaybeNull ProtectionDomain protectionDomain,
355
                                                      PersistenceHandler persistenceHandler,
356
                                                      PackageDefinitionStrategy packageDefinitionStrategy,
357
                                                      boolean forbidExisting,
358
                                                      boolean sealed) {
359
        return load(classLoader,
1✔
360
                types,
361
                protectionDomain,
362
                persistenceHandler,
363
                packageDefinitionStrategy,
364
                ModuleLayerResolver.Disabled.INSTANCE,
365
                forbidExisting,
366
                sealed);
367
    }
368

369
    /**
370
     * Loads a given set of class descriptions and their binary representations.
371
     *
372
     * @param classLoader               The parent class loader.
373
     * @param types                     The unloaded types to be loaded.
374
     * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
375
     * @param persistenceHandler        The persistence handler of the created class loader.
376
     * @param packageDefinitionStrategy The package definer to be queried for package definitions.
377
     * @param forbidExisting            {@code true} if the class loading should throw an exception if a class was already loaded by a parent class loader.
378
     * @param moduleLayerResolver       The module layer resolver to use.
379
     * @param sealed                    {@code true} if the class loader should be sealed.
380
     * @return A map of the given type descriptions pointing to their loaded representations.
381
     */
382
    @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
383
    public static Map<TypeDescription, Class<?>> load(@MaybeNull ClassLoader classLoader,
384
                                                      Map<TypeDescription, byte[]> types,
385
                                                      @MaybeNull ProtectionDomain protectionDomain,
386
                                                      PersistenceHandler persistenceHandler,
387
                                                      PackageDefinitionStrategy packageDefinitionStrategy,
388
                                                      ModuleLayerResolver moduleLayerResolver,
389
                                                      boolean forbidExisting,
390
                                                      boolean sealed) {
391
        Map<String, byte[]> typesByName = new HashMap<String, byte[]>();
1✔
392
        ModuleDescription moduleDescription = null;
1✔
393
        for (Map.Entry<TypeDescription, byte[]> entry : types.entrySet()) {
1✔
394
            if (entry.getKey().getName().equals(ModuleDescription.MODULE_CLASS_NAME)) {
1✔
395
                moduleDescription = entry.getKey().toModuleDescription();
×
396
            }
397
            typesByName.put(entry.getKey().getName(), entry.getValue());
1✔
398
        }
1✔
399
        classLoader = new ByteArrayClassLoader(classLoader,
1✔
400
                sealed,
401
                typesByName,
402
                protectionDomain,
403
                persistenceHandler,
404
                packageDefinitionStrategy,
405
                ClassFilePostProcessor.NoOp.INSTANCE);
406
        ClassLoader moduleClassLoader;
407
        Set<String> modulePackages;
408
        if (moduleDescription != null) {
1✔
409
            moduleClassLoader = moduleLayerResolver.resolve(classLoader, typesByName);
×
410
            modulePackages = moduleDescription.getPackages();
×
411
        } else {
412
            moduleClassLoader = classLoader;
1✔
413
            modulePackages = Collections.<String>emptySet();
1✔
414
        }
415
        Map<TypeDescription, Class<?>> result = new LinkedHashMap<TypeDescription, Class<?>>();
1✔
416
        for (TypeDescription typeDescription : types.keySet()) {
1✔
417
            if (typeDescription.isModuleType()) {
1✔
418
                continue;
×
419
            }
420
            try {
421
                ClassLoader typeClassLoader;
422
                if (moduleDescription != null) {
1✔
423
                    PackageDescription packageDescription = typeDescription.getPackage();
×
424
                    typeClassLoader = packageDescription != null && modulePackages.contains(packageDescription.getName())
×
425
                            ? moduleClassLoader
426
                            : classLoader;
427
                } else {
×
428
                    typeClassLoader = classLoader;
1✔
429
                }
430
                Class<?> type = Class.forName(typeDescription.getName(), false, typeClassLoader);
1✔
431
                if (!GraalImageCode.getCurrent().isNativeImageExecution() && forbidExisting && type.getClassLoader() != typeClassLoader) {
1✔
432
                    throw new IllegalStateException("Class already loaded: " + type);
1✔
433
                }
434
                result.put(typeDescription, type);
1✔
435
            } catch (ClassNotFoundException exception) {
×
436
                throw new IllegalStateException("Cannot load class " + typeDescription, exception);
×
437
            }
1✔
438
        }
1✔
439
        return result;
1✔
440
    }
441

442
    @Override
443
    protected Map<String, Class<?>> doDefineClasses(Map<String, byte[]> typeDefinitions) throws ClassNotFoundException {
444
        Map<String, byte[]> previous = new HashMap<String, byte[]>();
1✔
445
        for (Map.Entry<String, byte[]> entry : typeDefinitions.entrySet()) {
1✔
446
            previous.put(entry.getKey(), this.typeDefinitions.putIfAbsent(entry.getKey(), entry.getValue()));
1✔
447
        }
1✔
448
        try {
449
            Map<String, Class<?>> types = new LinkedHashMap<String, Class<?>>();
1✔
450
            for (String name : typeDefinitions.keySet()) {
1✔
451
                synchronized (SYNCHRONIZATION_STRATEGY.initialize().getClassLoadingLock(this, name)) {
1✔
452
                    types.put(name, loadClass(name));
1✔
453
                }
1✔
454
            }
1✔
455
            return types;
1✔
456
        } finally {
457
            for (Map.Entry<String, byte[]> entry : previous.entrySet()) {
1✔
458
                if (entry.getValue() == null) {
1✔
459
                    persistenceHandler.release(entry.getKey(), this.typeDefinitions);
1✔
460
                } else {
461
                    this.typeDefinitions.put(entry.getKey(), entry.getValue());
1✔
462
                }
463
            }
1✔
464
        }
465
    }
466

467
    /**
468
     * {@inheritDoc}
469
     */
470
    protected Class<?> findClass(String name) throws ClassNotFoundException {
471
        byte[] binaryRepresentation = persistenceHandler.lookup(name, typeDefinitions);
1✔
472
        if (binaryRepresentation == null) {
1✔
473
            throw new ClassNotFoundException(name);
1✔
474
        } else {
475
            return doPrivileged(new ClassDefinitionAction(name, classFilePostProcessor.transform(this,
1✔
476
                    name,
477
                    protectionDomain,
478
                    binaryRepresentation)), accessControlContext);
479
        }
480
    }
481

482
    /**
483
     * {@inheritDoc}
484
     */
485
    @MaybeNull
486
    protected URL findResource(String name) {
487
        return persistenceHandler.url(name, typeDefinitions);
1✔
488
    }
489

490
    /**
491
     * {@inheritDoc}
492
     */
493
    protected Enumeration<URL> findResources(String name) {
494
        URL url = persistenceHandler.url(name, typeDefinitions);
1✔
495
        return url == null
1✔
496
                ? EmptyEnumeration.INSTANCE
497
                : new SingletonEnumeration(url);
498
    }
499

500
    /**
501
     * Returns the package for a given name.
502
     *
503
     * @param name The name of the package.
504
     * @return A suitable package or {@code null} if no such package exists.
505
     */
506
    @MaybeNull
507
    @SuppressWarnings("deprecation")
508
    private Package doGetPackage(String name) {
509
        return getPackage(name);
1✔
510
    }
511

512
    /**
513
     * An engine for receiving a <i>class loading lock</i> when loading a class.
514
     */
515
    protected interface SynchronizationStrategy {
516

517
        /**
518
         * Receives the class loading lock.
519
         *
520
         * @param name        The name of the class being loaded.
521
         * @param classLoader The class loader loading the class.
522
         * @return The corresponding class loading lock.
523
         */
524
        Object getClassLoadingLock(ByteArrayClassLoader classLoader, String name);
525

526
        /**
527
         * An uninitialized synchronization strategy.
528
         */
529
        interface Initializable {
530

531
            /**
532
             * Initializes this synchronization strategy.
533
             *
534
             * @return The synchronization strategy to use.
535
             */
536
            SynchronizationStrategy initialize();
537
        }
538

539
        /**
540
         * A creation action for a synchronization strategy.
541
         */
542
        enum CreationAction implements PrivilegedAction<Initializable> {
1✔
543

544
            /**
545
             * The singleton instance.
546
             */
547
            INSTANCE;
1✔
548

549
            /**
550
             * {@inheritDoc}
551
             */
552
            @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
553
            public Initializable run() {
554
                try {
555
                    try {
556
                        Class<?> methodType = Class.forName("java.lang.invoke.MethodType"), methodHandle = Class.forName("java.lang.invoke.MethodHandle");
1✔
557
                        return new ForJava8CapableVm(Class.forName("java.lang.invoke.MethodHandles$Lookup")
1✔
558
                                .getMethod("findVirtual", Class.class, String.class, methodType)
1✔
559
                                .invoke(ByteArrayClassLoader.methodHandle(), ClassLoader.class, "getClassLoadingLock", methodType.getMethod("methodType",
1✔
560
                                        Class.class,
561
                                        Class[].class).invoke(null, Object.class, new Class<?>[]{String.class})),
1✔
562
                                methodHandle.getMethod("bindTo", Object.class),
1✔
563
                                methodHandle.getMethod("invokeWithArguments", Object[].class));
1✔
564
                    } catch (Exception ignored) {
×
565
                        // On the bootstrap class loader, a lookup instance cannot be located reflectively. To avoid issuing a warning for accessing
566
                        // a protected method from outside of a class that is caused if the module system does not offer accessing the method.
567
                        return ClassFileVersion.ofThisVm(ClassFileVersion.JAVA_V5).isAtLeast(ClassFileVersion.JAVA_V9) && ByteArrayClassLoader.class.getClassLoader() == null
×
568
                                ? SynchronizationStrategy.ForLegacyVm.INSTANCE
569
                                : new ForJava7CapableVm(ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class));
×
570
                    }
571
                } catch (Exception ignored) {
×
572
                    return SynchronizationStrategy.ForLegacyVm.INSTANCE;
×
573
                }
574
            }
575
        }
576

577
        /**
578
         * A synchronization engine for a VM that is not aware of parallel-capable class loaders.
579
         */
580
        enum ForLegacyVm implements SynchronizationStrategy, Initializable {
1✔
581

582
            /**
583
             * The singleton instance.
584
             */
585
            INSTANCE;
1✔
586

587
            /**
588
             * {@inheritDoc}
589
             */
590
            public Object getClassLoadingLock(ByteArrayClassLoader classLoader, String name) {
591
                return classLoader;
1✔
592
            }
593

594
            /**
595
             * {@inheritDoc}
596
             */
597
            public SynchronizationStrategy initialize() {
598
                return this;
1✔
599
            }
600
        }
601

602
        /**
603
         * A synchronization engine for a VM that is aware of parallel-capable class loaders.
604
         */
605
        @HashCodeAndEqualsPlugin.Enhance
606
        class ForJava7CapableVm implements SynchronizationStrategy, Initializable {
607

608
            /**
609
             * The {@code ClassLoader#getClassLoadingLock(String)} method.
610
             */
611
            private final Method method;
612

613
            /**
614
             * Creates a new synchronization strategy.
615
             *
616
             * @param method The {@code ClassLoader#getClassLoadingLock(String)} method.
617
             */
618
            protected ForJava7CapableVm(Method method) {
×
619
                this.method = method;
×
620
            }
×
621

622
            /**
623
             * {@inheritDoc}
624
             */
625
            public Object getClassLoadingLock(ByteArrayClassLoader classLoader, String name) {
626
                try {
627
                    return method.invoke(classLoader, name);
×
628
                } catch (IllegalAccessException exception) {
×
629
                    throw new IllegalStateException(exception);
×
630
                } catch (InvocationTargetException exception) {
×
631
                    throw new IllegalStateException(exception.getTargetException());
×
632
                }
633
            }
634

635
            /**
636
             * {@inheritDoc}
637
             */
638
            @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
639
            public SynchronizationStrategy initialize() {
640
                try {
641
                    method.setAccessible(true);
×
642
                    return this;
×
643
                } catch (Exception ignored) {
×
644
                    return ForLegacyVm.INSTANCE;
×
645
                }
646
            }
647
        }
648

649
        /**
650
         * A synchronization engine for a VM that is aware of parallel-capable class loaders using method handles to respect module boundaries.
651
         */
652
        @HashCodeAndEqualsPlugin.Enhance
653
        class ForJava8CapableVm implements SynchronizationStrategy, Initializable {
654

655
            /**
656
             * The {@code java.lang.invoke.MethodHandle} to use.
657
             */
658
            private final Object methodHandle;
659

660
            /**
661
             * The {@code java.lang.invoke.MethodHandle#bindTo(Object)} method.
662
             */
663
            private final Method bindTo;
664

665
            /**
666
             * The {@code java.lang.invoke.MethodHandle#invokeWithArguments(Object[])} method.
667
             */
668
            private final Method invokeWithArguments;
669

670
            /**
671
             * Creates a new synchronization strategy.
672
             *
673
             * @param methodHandle        The {@code java.lang.invoke.MethodHandle} to use.
674
             * @param bindTo              The {@code java.lang.invoke.MethodHandle#bindTo(Object)} method.
675
             * @param invokeWithArguments The {@code java.lang.invoke.MethodHandle#invokeWithArguments(Object[])} method.
676
             */
677
            protected ForJava8CapableVm(Object methodHandle, Method bindTo, Method invokeWithArguments) {
1✔
678
                this.methodHandle = methodHandle;
1✔
679
                this.bindTo = bindTo;
1✔
680
                this.invokeWithArguments = invokeWithArguments;
1✔
681
            }
1✔
682

683
            /**
684
             * {@inheritDoc}
685
             */
686
            public SynchronizationStrategy initialize() {
687
                return this;
1✔
688
            }
689

690
            /**
691
             * {@inheritDoc}
692
             */
693
            public Object getClassLoadingLock(ByteArrayClassLoader classLoader, String name) {
694
                try {
695
                    return invokeWithArguments.invoke(bindTo.invoke(methodHandle, classLoader), (Object) new Object[]{name});
1✔
696
                } catch (IllegalAccessException exception) {
×
697
                    throw new IllegalStateException(exception);
×
698
                } catch (InvocationTargetException exception) {
×
699
                    throw new IllegalStateException(exception.getTargetException());
×
700
                }
701
            }
702
        }
703
    }
704

705
    /**
706
     * An action for defining a located class that is not yet loaded.
707
     */
708
    @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
709
    protected class ClassDefinitionAction implements PrivilegedAction<Class<?>> {
710

711
        /**
712
         * The binary name of the class to define.
713
         */
714
        private final String name;
715

716
        /**
717
         * The binary representation of the class to be loaded.
718
         */
719
        private final byte[] binaryRepresentation;
720

721
        /**
722
         * Creates a new class definition action.
723
         *
724
         * @param name                 The binary name of the class to define.
725
         * @param binaryRepresentation The binary representation of the class to be loaded.
726
         */
727
        protected ClassDefinitionAction(String name, byte[] binaryRepresentation) {
1✔
728
            this.name = name;
1✔
729
            this.binaryRepresentation = binaryRepresentation;
1✔
730
        }
1✔
731

732
        /**
733
         * {@inheritDoc}
734
         */
735
        public Class<?> run() {
736
            int packageIndex = name.lastIndexOf('.');
1✔
737
            if (packageIndex != -1) {
1✔
738
                String packageName = name.substring(0, packageIndex);
1✔
739
                PackageDefinitionStrategy.Definition definition = packageDefinitionStrategy.define(ByteArrayClassLoader.this, packageName, name);
1✔
740
                if (definition.isDefined()) {
1✔
741
                    Package definedPackage = PACKAGE_LOOKUP_STRATEGY.apply(ByteArrayClassLoader.this, packageName);
1✔
742
                    if (definedPackage == null) {
1✔
743
                        definePackage(packageName,
1✔
744
                                definition.getSpecificationTitle(),
1✔
745
                                definition.getSpecificationVersion(),
1✔
746
                                definition.getSpecificationVendor(),
1✔
747
                                definition.getImplementationTitle(),
1✔
748
                                definition.getImplementationVersion(),
1✔
749
                                definition.getImplementationVendor(),
1✔
750
                                definition.getSealBase());
1✔
751
                    } else if (!definition.isCompatibleTo(definedPackage)) {
1✔
752
                        throw new SecurityException("Sealing violation for package " + packageName);
×
753
                    }
754
                }
755
            }
756
            return defineClass(name, binaryRepresentation, FROM_BEGINNING, binaryRepresentation.length, protectionDomain);
1✔
757
        }
758
    }
759

760
    /**
761
     * A package lookup strategy for locating a package by name.
762
     */
763
    protected interface PackageLookupStrategy {
764

765
        /**
766
         * Returns a package for a given byte array class loader and a name.
767
         *
768
         * @param classLoader The class loader to locate a package for.
769
         * @param name        The name of the package.
770
         * @return A suitable package or {@code null} if no such package exists.
771
         */
772
        @MaybeNull
773
        Package apply(ByteArrayClassLoader classLoader, String name);
774

775
        /**
776
         * A creation action for a package lookup strategy.
777
         */
778
        enum CreationAction implements PrivilegedAction<PackageLookupStrategy> {
1✔
779

780
            /**
781
             * The singleton instance.
782
             */
783
            INSTANCE;
1✔
784

785
            /**
786
             * {@inheritDoc}
787
             */
788
            @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
789
            public PackageLookupStrategy run() {
790
                if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
1✔
791
                    try {
792
                        return new PackageLookupStrategy.ForJava9CapableVm(ClassLoader.class.getMethod("getDefinedPackage", String.class));
×
793
                    } catch (Exception ignored) {
×
794
                        return PackageLookupStrategy.ForLegacyVm.INSTANCE;
×
795
                    }
796
                } else {
797
                    return PackageLookupStrategy.ForLegacyVm.INSTANCE;
1✔
798
                }
799
            }
800
        }
801

802
        /**
803
         * A package lookup strategy for a VM prior to Java 9.
804
         */
805
        enum ForLegacyVm implements PackageLookupStrategy {
1✔
806

807
            /**
808
             * The singleton instance.
809
             */
810
            INSTANCE;
1✔
811

812
            /**
813
             * {@inheritDoc}
814
             */
815
            @MaybeNull
816
            public Package apply(ByteArrayClassLoader classLoader, String name) {
817
                return classLoader.doGetPackage(name);
1✔
818
            }
819
        }
820

821
        /**
822
         * A package lookup strategy for Java 9 or newer.
823
         */
824
        @HashCodeAndEqualsPlugin.Enhance
825
        class ForJava9CapableVm implements PackageLookupStrategy {
826

827
            /**
828
             * The {@code java.lang.ClassLoader#getDefinedPackage(String)} method.
829
             */
830
            private final Method getDefinedPackage;
831

832
            /**
833
             * Creates a new package lookup strategy for a modern VM.
834
             *
835
             * @param getDefinedPackage The {@code java.lang.ClassLoader#getDefinedPackage(String)} method.
836
             */
837
            protected ForJava9CapableVm(Method getDefinedPackage) {
×
838
                this.getDefinedPackage = getDefinedPackage;
×
839
            }
×
840

841
            /**
842
             * {@inheritDoc}
843
             */
844
            @MaybeNull
845
            public Package apply(ByteArrayClassLoader classLoader, String name) {
846
                try {
847
                    return (Package) getDefinedPackage.invoke(classLoader, name);
×
848
                } catch (IllegalAccessException exception) {
×
849
                    throw new IllegalStateException(exception);
×
850
                } catch (InvocationTargetException exception) {
×
851
                    throw new IllegalStateException(exception.getTargetException());
×
852
                }
853
            }
854
        }
855
    }
856

857
    /**
858
     * A persistence handler decides on whether the byte array that represents a loaded class is exposed by
859
     * the {@link java.lang.ClassLoader#getResourceAsStream(String)} method.
860
     */
861
    public enum PersistenceHandler {
1✔
862

863
        /**
864
         * The manifest persistence handler retains all class file representations and makes them accessible.
865
         */
866
        MANIFEST(true) {
1✔
867
            @Override
868
            protected byte[] lookup(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
869
                return typeDefinitions.get(name);
1✔
870
            }
871

872
            @Override
873
            protected URL url(String resourceName, ConcurrentMap<String, byte[]> typeDefinitions) {
874
                if (!resourceName.endsWith(CLASS_FILE_SUFFIX)) {
1✔
875
                    return NO_URL;
×
876
                } else if (resourceName.startsWith("/")) {
1✔
877
                    resourceName = resourceName.substring(1);
1✔
878
                }
879
                String typeName = resourceName.replace('/', '.').substring(FROM_BEGINNING, resourceName.length() - CLASS_FILE_SUFFIX.length());
1✔
880
                byte[] binaryRepresentation = typeDefinitions.get(typeName);
1✔
881
                return binaryRepresentation == null
1✔
882
                        ? NO_URL
1✔
883
                        : doPrivileged(new UrlDefinitionAction(resourceName, binaryRepresentation));
1✔
884
            }
885

886
            @Override
887
            protected void release(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
888
                /* do nothing */
889
            }
1✔
890
        },
891

892
        /**
893
         * The latent persistence handler hides all class file representations and does not make them accessible
894
         * even before they are loaded.
895
         */
896
        LATENT(false) {
1✔
897
            @Override
898
            protected byte[] lookup(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
899
                return typeDefinitions.remove(name);
1✔
900
            }
901

902
            @Override
903
            protected URL url(String resourceName, ConcurrentMap<String, byte[]> typeDefinitions) {
904
                return NO_URL;
1✔
905
            }
906

907
            @Override
908
            protected void release(String name, ConcurrentMap<String, byte[]> typeDefinitions) {
909
                typeDefinitions.remove(name);
1✔
910
            }
1✔
911
        };
912

913
        /**
914
         * The suffix of files in the Java class file format.
915
         */
916
        private static final String CLASS_FILE_SUFFIX = ".class";
917

918
        /**
919
         * {@code true} if this persistence handler represents manifest class file storage.
920
         */
921
        private final boolean manifest;
922

923
        /**
924
         * Creates a new persistence handler.
925
         *
926
         * @param manifest {@code true} if this persistence handler represents manifest class file storage.
927
         */
928
        PersistenceHandler(boolean manifest) {
1✔
929
            this.manifest = manifest;
1✔
930
        }
1✔
931

932
        /**
933
         * Checks if this persistence handler represents manifest class file storage.
934
         *
935
         * @return {@code true} if this persistence handler represents manifest class file storage.
936
         */
937
        public boolean isManifest() {
938
            return manifest;
1✔
939
        }
940

941
        /**
942
         * Performs a lookup of a class file by its name.
943
         *
944
         * @param name            The name of the class to be loaded.
945
         * @param typeDefinitions A map of fully qualified class names pointing to their binary representations.
946
         * @return The byte array representing the requested class or {@code null} if no such class is known.
947
         */
948
        @MaybeNull
949
        protected abstract byte[] lookup(String name, ConcurrentMap<String, byte[]> typeDefinitions);
950

951
        /**
952
         * Returns a URL representing a class file.
953
         *
954
         * @param resourceName    The name of the requested resource.
955
         * @param typeDefinitions A mapping of byte arrays by their type names.
956
         * @return A URL representing the type definition or {@code null} if the requested resource does not represent a class file.
957
         */
958
        @MaybeNull
959
        protected abstract URL url(String resourceName, ConcurrentMap<String, byte[]> typeDefinitions);
960

961
        /**
962
         * Removes the binary representation of the supplied type if this class loader is latent.
963
         *
964
         * @param name            The name of the type.
965
         * @param typeDefinitions A mapping of byte arrays by their type names.
966
         */
967
        protected abstract void release(String name, ConcurrentMap<String, byte[]> typeDefinitions);
968

969
        /**
970
         * An action to define a URL that represents a class file.
971
         */
972
        @HashCodeAndEqualsPlugin.Enhance
973
        protected static class UrlDefinitionAction implements PrivilegedAction<URL> {
974

975
            /**
976
             * A dispatcher for creating URLs.
977
             */
978
            private static final Dispatcher DISPATCHER = doPrivileged(JavaDispatcher.of(Dispatcher.class));
1✔
979

980
            /**
981
             * The URL's encoding character set.
982
             */
983
            private static final String ENCODING = "UTF-8";
984

985
            /**
986
             * A value to define a standard port as Byte Buddy's URLs do not represent a port.
987
             */
988
            private static final int NO_PORT = -1;
989

990
            /**
991
             * Indicates that Byte Buddy's URLs do not have a file segment.
992
             */
993
            private static final String NO_FILE = "";
994

995
            /**
996
             * The name of the type that this URL represents.
997
             */
998
            private final String typeName;
999

1000
            /**
1001
             * The binary representation of the type's class file.
1002
             */
1003
            private final byte[] binaryRepresentation;
1004

1005
            /**
1006
             * Creates a new URL definition action.
1007
             *
1008
             * @param typeName             The name of the type that this URL represents.
1009
             * @param binaryRepresentation The binary representation of the type's class file.
1010
             */
1011
            protected UrlDefinitionAction(String typeName, byte[] binaryRepresentation) {
1✔
1012
                this.typeName = typeName;
1✔
1013
                this.binaryRepresentation = binaryRepresentation;
1✔
1014
            }
1✔
1015

1016
            /**
1017
             * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
1018
             *
1019
             * @param action The action to execute from a privileged context.
1020
             * @param <T>    The type of the action's resolved value.
1021
             * @return The action's resolved value.
1022
             */
1023
            @AccessControllerPlugin.Enhance
1024
            private static <T> T doPrivileged(PrivilegedAction<T> action) {
1025
                return action.run();
×
1026
            }
1027

1028
            /**
1029
             * {@inheritDoc}
1030
             */
1031
            public URL run() {
1032
                try {
1033
                    String path = URLEncoder.encode(typeName.replace('.', '/'), ENCODING);
1✔
1034
                    URLStreamHandler handler = new ByteArrayUrlStreamHandler(binaryRepresentation);
1✔
1035
                    URL url = DISPATCHER.of(URI.create(URL_SCHEMA + "://" + path), handler);
1✔
1036
                    return url == null
1✔
1037
                            ? DISPATCHER.make(URL_SCHEMA, path, NO_PORT, NO_FILE, handler)
1✔
1038
                            : url;
1039
                } catch (MalformedURLException exception) {
×
1040
                    throw new IllegalStateException("Cannot create URL for " + typeName, exception);
×
1041
                } catch (UnsupportedEncodingException exception) {
×
1042
                    throw new IllegalStateException("Could not find encoding: " + ENCODING, exception);
×
1043
                }
1044
            }
1045

1046
            /**
1047
             * A stream handler that returns the given binary representation.
1048
             */
1049
            @HashCodeAndEqualsPlugin.Enhance
1050
            protected static class ByteArrayUrlStreamHandler extends URLStreamHandler {
1051

1052
                /**
1053
                 * The binary representation of a type's class file.
1054
                 */
1055
                private final byte[] binaryRepresentation;
1056

1057
                /**
1058
                 * Creates a new byte array URL stream handler.
1059
                 *
1060
                 * @param binaryRepresentation The binary representation of a type's class file.
1061
                 */
1062
                protected ByteArrayUrlStreamHandler(byte[] binaryRepresentation) {
1✔
1063
                    this.binaryRepresentation = binaryRepresentation;
1✔
1064
                }
1✔
1065

1066
                /**
1067
                 * {@inheritDoc}
1068
                 */
1069
                protected URLConnection openConnection(URL url) {
1070
                    return new ByteArrayUrlConnection(url, new ByteArrayInputStream(binaryRepresentation));
1✔
1071
                }
1072

1073
                /**
1074
                 * A URL connection for a given byte array.
1075
                 */
1076
                protected static class ByteArrayUrlConnection extends URLConnection {
1077

1078
                    /**
1079
                     * The input stream to return for this connection.
1080
                     */
1081
                    private final InputStream inputStream;
1082

1083
                    /**
1084
                     * Creates a new byte array URL connection.
1085
                     *
1086
                     * @param url         The URL that this connection represents.
1087
                     * @param inputStream The input stream to return from this connection.
1088
                     */
1089
                    protected ByteArrayUrlConnection(URL url, InputStream inputStream) {
1090
                        super(url);
1✔
1091
                        this.inputStream = inputStream;
1✔
1092
                    }
1✔
1093

1094
                    /**
1095
                     * {@inheritDoc}
1096
                     */
1097
                    public void connect() {
1098
                        connected = true;
1✔
1099
                    }
1✔
1100

1101
                    /**
1102
                     * {@inheritDoc}
1103
                     */
1104
                    public InputStream getInputStream() {
1105
                        connect(); // Mimics the semantics of an actual URL connection.
1✔
1106
                        return inputStream;
1✔
1107
                    }
1108
                }
1109
            }
1110

1111
            /**
1112
             * A dispatcher for interacting with {@link URL}.
1113
             */
1114
            @JavaDispatcher.Proxied("java.net.URL")
1115
            protected interface Dispatcher {
1116

1117
                /**
1118
                 * Creates a {@link URL}.
1119
                 *
1120
                 * @param protocol The URL's protocol.
1121
                 * @param host     The host on the URL.
1122
                 * @param port     The port on the URL or a negative value if no port is defined.
1123
                 * @param file     The file on the URL.
1124
                 * @param handler  The stream handler to use.
1125
                 * @return An appropriate URL.
1126
                 * @throws MalformedURLException If the supplied URL is malformed.
1127
                 */
1128
                @JavaDispatcher.IsConstructor
1129
                URL make(String protocol, String host, int port, String file, URLStreamHandler handler) throws MalformedURLException;
1130

1131
                /**
1132
                 * Resolves a URL from an URI, if possible.
1133
                 *
1134
                 * @param uri     The URI to represent.
1135
                 * @param handler The stream handler to attach to that URL.
1136
                 * @return An appropriate URL.
1137
                 * @throws MalformedURLException If the supplied URL is malformed.
1138
                 */
1139
                @MaybeNull
1140
                @JavaDispatcher.IsStatic
1141
                @JavaDispatcher.Defaults
1142
                URL of(URI uri, URLStreamHandler handler) throws MalformedURLException;
1143
            }
1144
        }
1145
    }
1146

1147
    /**
1148
     * <p>
1149
     * A {@link net.bytebuddy.dynamic.loading.ByteArrayClassLoader} which applies child-first semantics for the
1150
     * given type definitions.
1151
     * </p>
1152
     * <p>
1153
     * <b>Important</b>: Package definitions remain their parent-first semantics as loaded package definitions do not expose their class loaders.
1154
     * Also, it is not possible to make this class or its subclass parallel-capable as the loading strategy is overridden.
1155
     * </p>
1156
     */
1157
    public static class ChildFirst extends ByteArrayClassLoader {
1158

1159
        /**
1160
         * The suffix of files in the Java class file format.
1161
         */
1162
        private static final String CLASS_FILE_SUFFIX = ".class";
1163

1164
        /*
1165
         * Register class loader as parallel capable if the current VM supports it.
1166
         */
1167
        static {
1168
            doRegisterAsParallelCapable();
1✔
1169
        }
1✔
1170

1171
        /**
1172
         * Registers class loader as parallel capable if possible.
1173
         */
1174
        @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Must be invoked from targeting class loader type.")
1175
        private static void doRegisterAsParallelCapable() {
1176
            try {
1177
                Method method = ClassLoader.class.getDeclaredMethod("registerAsParallelCapable");
1✔
1178
                method.setAccessible(true);
1✔
1179
                method.invoke(null);
1✔
1180
            } catch (Throwable ignored) {
×
1181
                /* do nothing */
1182
            }
1✔
1183
        }
1✔
1184

1185
        /**
1186
         * Creates a new child-first byte array class loader.
1187
         *
1188
         * @param parent          The {@link java.lang.ClassLoader} that is the parent of this class loader.
1189
         * @param typeDefinitions A map of fully qualified class names pointing to their binary representations.
1190
         */
1191
        public ChildFirst(@MaybeNull ClassLoader parent, Map<String, byte[]> typeDefinitions) {
1192
            super(parent, typeDefinitions);
×
1193
        }
×
1194

1195
        /**
1196
         * Creates a new child-first byte array class loader.
1197
         *
1198
         * @param parent          The {@link java.lang.ClassLoader} that is the parent of this class loader.
1199
         * @param sealed          {@code true} if this class loader is sealed.
1200
         * @param typeDefinitions A map of fully qualified class names pointing to their binary representations.
1201
         */
1202
        public ChildFirst(@MaybeNull ClassLoader parent, boolean sealed, Map<String, byte[]> typeDefinitions) {
1203
            super(parent, sealed, typeDefinitions);
×
1204
        }
×
1205

1206
        /**
1207
         * Creates a new child-first byte array class loader.
1208
         *
1209
         * @param parent             The {@link java.lang.ClassLoader} that is the parent of this class loader.
1210
         * @param typeDefinitions    A map of fully qualified class names pointing to their binary representations.
1211
         * @param persistenceHandler The persistence handler of this class loader.
1212
         */
1213
        public ChildFirst(@MaybeNull ClassLoader parent, Map<String, byte[]> typeDefinitions, PersistenceHandler persistenceHandler) {
1214
            super(parent, typeDefinitions, persistenceHandler);
1✔
1215
        }
1✔
1216

1217
        /**
1218
         * Creates a new child-first byte array class loader.
1219
         *
1220
         * @param parent             The {@link java.lang.ClassLoader} that is the parent of this class loader.
1221
         * @param sealed             {@code true} if this class loader is sealed.
1222
         * @param typeDefinitions    A map of fully qualified class names pointing to their binary representations.
1223
         * @param persistenceHandler The persistence handler of this class loader.
1224
         */
1225
        public ChildFirst(@MaybeNull ClassLoader parent, boolean sealed, Map<String, byte[]> typeDefinitions, PersistenceHandler persistenceHandler) {
1226
            super(parent, sealed, typeDefinitions, persistenceHandler);
×
1227
        }
×
1228

1229
        /**
1230
         * Creates a new child-first byte array class loader.
1231
         *
1232
         * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
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
         */
1238
        public ChildFirst(@MaybeNull ClassLoader parent,
1239
                          Map<String, byte[]> typeDefinitions,
1240
                          @MaybeNull ProtectionDomain protectionDomain,
1241
                          PersistenceHandler persistenceHandler,
1242
                          PackageDefinitionStrategy packageDefinitionStrategy) {
1243
            super(parent, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy);
1✔
1244
        }
1✔
1245

1246
        /**
1247
         * Creates a new child-first byte array class loader.
1248
         *
1249
         * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
1250
         * @param sealed                    {@code true} if this class loader is sealed.
1251
         * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
1252
         * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
1253
         * @param persistenceHandler        The persistence handler of this class loader.
1254
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
1255
         */
1256
        public ChildFirst(@MaybeNull ClassLoader parent,
1257
                          boolean sealed,
1258
                          Map<String, byte[]> typeDefinitions,
1259
                          @MaybeNull ProtectionDomain protectionDomain,
1260
                          PersistenceHandler persistenceHandler,
1261
                          PackageDefinitionStrategy packageDefinitionStrategy) {
1262
            super(parent, sealed, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy);
×
1263
        }
×
1264

1265
        /**
1266
         * Creates a new child-first byte array class loader.
1267
         *
1268
         * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
1269
         * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
1270
         * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
1271
         * @param persistenceHandler        The persistence handler of this class loader.
1272
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
1273
         * @param classFilePostProcessor    A post processor for class files to apply p
1274
         */
1275
        public ChildFirst(@MaybeNull ClassLoader parent,
1276
                          Map<String, byte[]> typeDefinitions,
1277
                          @MaybeNull ProtectionDomain protectionDomain,
1278
                          PersistenceHandler persistenceHandler,
1279
                          PackageDefinitionStrategy packageDefinitionStrategy,
1280
                          ClassFilePostProcessor classFilePostProcessor) {
1281
            super(parent, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy, classFilePostProcessor);
×
1282
        }
×
1283

1284
        /**
1285
         * Creates a new child-first byte array class loader.
1286
         *
1287
         * @param parent                    The {@link java.lang.ClassLoader} that is the parent of this class loader.
1288
         * @param sealed                    {@code true} if this class loader is sealed.
1289
         * @param typeDefinitions           A map of fully qualified class names pointing to their binary representations.
1290
         * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
1291
         * @param persistenceHandler        The persistence handler of this class loader.
1292
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
1293
         * @param classFilePostProcessor    A post processor for class files to apply p
1294
         */
1295
        public ChildFirst(@MaybeNull ClassLoader parent,
1296
                          boolean sealed,
1297
                          Map<String, byte[]> typeDefinitions,
1298
                          @MaybeNull ProtectionDomain protectionDomain,
1299
                          PersistenceHandler persistenceHandler,
1300
                          PackageDefinitionStrategy packageDefinitionStrategy,
1301
                          ClassFilePostProcessor classFilePostProcessor) {
1302
            super(parent, sealed, typeDefinitions, protectionDomain, persistenceHandler, packageDefinitionStrategy, classFilePostProcessor);
1✔
1303
        }
1✔
1304

1305
        /**
1306
         * Loads a given set of class descriptions and their binary representations using a child-first class loader.
1307
         *
1308
         * @param classLoader The parent class loader.
1309
         * @param types       The unloaded types to be loaded.
1310
         * @return A map of the given type descriptions pointing to their loaded representations.
1311
         */
1312
        public static Map<TypeDescription, Class<?>> load(@MaybeNull ClassLoader classLoader, Map<TypeDescription, byte[]> types) {
1313
            return load(classLoader,
×
1314
                    types,
1315
                    ClassLoadingStrategy.NO_PROTECTION_DOMAIN,
1316
                    PersistenceHandler.LATENT,
1317
                    PackageDefinitionStrategy.Trivial.INSTANCE,
1318
                    false,
1319
                    true);
1320
        }
1321

1322
        /**
1323
         * Loads a given set of class descriptions and their binary representations using a child-first class loader.
1324
         *
1325
         * @param classLoader               The parent class loader.
1326
         * @param types                     The unloaded types to be loaded.
1327
         * @param protectionDomain          The protection domain to apply where {@code null} references an implicit protection domain.
1328
         * @param persistenceHandler        The persistence handler of the created class loader.
1329
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
1330
         * @param forbidExisting            {@code true} if the class loading should throw an exception if a class was already loaded by a parent class loader.
1331
         * @param sealed                    {@code true} if the class loader should be sealed.
1332
         * @return A map of the given type descriptions pointing to their loaded representations.
1333
         */
1334
        @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
1335
        public static Map<TypeDescription, Class<?>> load(@MaybeNull ClassLoader classLoader,
1336
                                                          Map<TypeDescription, byte[]> types,
1337
                                                          @MaybeNull ProtectionDomain protectionDomain,
1338
                                                          PersistenceHandler persistenceHandler,
1339
                                                          PackageDefinitionStrategy packageDefinitionStrategy,
1340
                                                          boolean forbidExisting,
1341
                                                          boolean sealed) {
1342
            Map<String, byte[]> typesByName = new HashMap<String, byte[]>();
1✔
1343
            for (Map.Entry<TypeDescription, byte[]> entry : types.entrySet()) {
1✔
1344
                typesByName.put(entry.getKey().getName(), entry.getValue());
1✔
1345
            }
1✔
1346
            classLoader = new ChildFirst(classLoader,
1✔
1347
                    sealed,
1348
                    typesByName,
1349
                    protectionDomain,
1350
                    persistenceHandler,
1351
                    packageDefinitionStrategy,
1352
                    ClassFilePostProcessor.NoOp.INSTANCE);
1353
            Map<TypeDescription, Class<?>> result = new LinkedHashMap<TypeDescription, Class<?>>();
1✔
1354
            for (TypeDescription typeDescription : types.keySet()) {
1✔
1355
                if (typeDescription.getName().equals(ModuleDescription.MODULE_CLASS_NAME)) {
1✔
1356
                    continue;
×
1357
                }
1358
                try {
1359
                    Class<?> type = Class.forName(typeDescription.getName(), false, classLoader);
1✔
1360
                    if (!GraalImageCode.getCurrent().isNativeImageExecution() && forbidExisting && type.getClassLoader() != classLoader) {
1✔
1361
                        throw new IllegalStateException("Class already loaded: " + type);
×
1362
                    }
1363
                    result.put(typeDescription, type);
1✔
1364
                } catch (ClassNotFoundException exception) {
×
1365
                    throw new IllegalStateException("Cannot load class " + typeDescription, exception);
×
1366
                }
1✔
1367
            }
1✔
1368
            return result;
1✔
1369
        }
1370

1371
        /**
1372
         * {@inheritDoc}
1373
         */
1374
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
1375
            synchronized (SYNCHRONIZATION_STRATEGY.initialize().getClassLoadingLock(this, name)) {
1✔
1376
                Class<?> type = findLoadedClass(name);
1✔
1377
                if (type != null) {
1✔
1378
                    return type;
1✔
1379
                }
1380
                try {
1381
                    type = findClass(name);
1✔
1382
                    if (resolve) {
1✔
1383
                        resolveClass(type);
×
1384
                    }
1385
                    return type;
1✔
1386
                } catch (ClassNotFoundException exception) {
1✔
1387
                    // If an unknown class is loaded, this implementation causes the findClass method of this instance
1388
                    // to be triggered twice. This is however of minor importance because this would result in a
1389
                    // ClassNotFoundException what does not alter the outcome.
1390
                    return super.loadClass(name, resolve);
1✔
1391
                }
1392
            }
1393
        }
1394

1395
        /**
1396
         * {@inheritDoc}
1397
         */
1398
        public URL getResource(String name) {
1399
            URL url = persistenceHandler.url(name, typeDefinitions);
1✔
1400
            // If a class resource is defined by this class loader but it is not defined in a manifest manner,
1401
            // the resource of the parent class loader should be shadowed by 'null'. Note that the delegation
1402
            // model causes a redundant query to the persistent handler but renders a correct result.
1403
            return url != null || isShadowed(name)
1✔
1404
                    ? url
1405
                    : super.getResource(name);
1✔
1406
        }
1407

1408
        /**
1409
         * {@inheritDoc}
1410
         */
1411
        public Enumeration<URL> getResources(String name) throws IOException {
1412
            URL url = persistenceHandler.url(name, typeDefinitions);
1✔
1413
            return url == null
1✔
1414
                    ? super.getResources(name)
1✔
1415
                    : new PrependingEnumeration(url, super.getResources(name));
1✔
1416
        }
1417

1418
        /**
1419
         * Checks if a resource name represents a class file of a class that was loaded by this class loader.
1420
         *
1421
         * @param resource The resource name of the class to be exposed as its class file.
1422
         * @return {@code true} if this class represents a class that is being loaded by this class loader.
1423
         */
1424
        private boolean isShadowed(String resource) {
1425
            if (persistenceHandler.isManifest() || !resource.endsWith(CLASS_FILE_SUFFIX)) {
1✔
1426
                return false;
1✔
1427
            }
1428
            // This synchronization is required to avoid a racing condition to the actual class loading.
1429
            String name = resource.replace('/', '.').substring(0, resource.length() - CLASS_FILE_SUFFIX.length());
1✔
1430
            synchronized (SYNCHRONIZATION_STRATEGY.initialize().getClassLoadingLock(this, name)) {
1✔
1431
                if (typeDefinitions.containsKey(name)) {
1✔
1432
                    return true;
1✔
1433
                }
1434
                Class<?> loadedClass = findLoadedClass(name);
1✔
1435
                return loadedClass != null && loadedClass.getClassLoader() == this;
1✔
1436
            }
1437
        }
1438

1439
        /**
1440
         * An enumeration that prepends an element to another enumeration and skips the last element of the provided enumeration.
1441
         */
1442
        protected static class PrependingEnumeration implements Enumeration<URL> {
1443

1444
            /**
1445
             * The next element to return from this enumeration or {@code null} if such an element does not exist.
1446
             */
1447
            @MaybeNull
1448
            private URL nextElement;
1449

1450
            /**
1451
             * The enumeration from which the next elements should be pulled.
1452
             */
1453
            private final Enumeration<URL> enumeration;
1454

1455
            /**
1456
             * Creates a new prepending enumeration.
1457
             *
1458
             * @param url         The first element of the enumeration.
1459
             * @param enumeration An enumeration that is used for pulling subsequent urls.
1460
             */
1461
            protected PrependingEnumeration(URL url, Enumeration<URL> enumeration) {
1✔
1462
                nextElement = url;
1✔
1463
                this.enumeration = enumeration;
1✔
1464
            }
1✔
1465

1466
            /**
1467
             * {@inheritDoc}
1468
             */
1469
            public boolean hasMoreElements() {
1470
                return nextElement != null && enumeration.hasMoreElements();
1✔
1471
            }
1472

1473
            /**
1474
             * {@inheritDoc}
1475
             */
1476
            public URL nextElement() {
1477
                if (nextElement != null && enumeration.hasMoreElements()) {
1✔
1478
                    try {
1479
                        return nextElement;
1✔
1480
                    } finally {
1481
                        nextElement = enumeration.nextElement();
1✔
1482
                    }
1483
                } else {
1484
                    throw new NoSuchElementException();
1✔
1485
                }
1486
            }
1487
        }
1488
    }
1489

1490
    /**
1491
     * An enumeration without any elements.
1492
     */
1493
    protected enum EmptyEnumeration implements Enumeration<URL> {
1✔
1494

1495
        /**
1496
         * The singleton instance.
1497
         */
1498
        INSTANCE;
1✔
1499

1500
        /**
1501
         * {@inheritDoc}
1502
         */
1503
        public boolean hasMoreElements() {
1504
            return false;
1✔
1505
        }
1506

1507
        /**
1508
         * {@inheritDoc}
1509
         */
1510
        public URL nextElement() {
1511
            throw new NoSuchElementException();
1✔
1512
        }
1513
    }
1514

1515
    /**
1516
     * An enumeration that contains a single element.
1517
     */
1518
    protected static class SingletonEnumeration implements Enumeration<URL> {
1519

1520
        /**
1521
         * The current element or {@code null} if this enumeration does not contain further elements.
1522
         */
1523
        @MaybeNull
1524
        private URL element;
1525

1526
        /**
1527
         * Creates a new singleton enumeration.
1528
         *
1529
         * @param element The only element.
1530
         */
1531
        protected SingletonEnumeration(URL element) {
1✔
1532
            this.element = element;
1✔
1533
        }
1✔
1534

1535
        /**
1536
         * {@inheritDoc}
1537
         */
1538
        public boolean hasMoreElements() {
1539
            return element != null;
1✔
1540
        }
1541

1542
        /**
1543
         * {@inheritDoc}
1544
         */
1545
        public URL nextElement() {
1546
            if (element == null) {
1✔
1547
                throw new NoSuchElementException();
1✔
1548
            } else {
1549
                try {
1550
                    return element;
1✔
1551
                } finally {
1552
                    element = null;
1✔
1553
                }
1554
            }
1555
        }
1556
    }
1557
}
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