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

raphw / byte-buddy / #801

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

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

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

84.35
/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/MultipleParentClassLoader.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.build.HashCodeAndEqualsPlugin;
20
import net.bytebuddy.matcher.ElementMatcher;
21
import net.bytebuddy.utility.nullability.MaybeNull;
22
import net.bytebuddy.utility.nullability.UnknownNull;
23

24
import java.io.IOException;
25
import java.lang.reflect.Method;
26
import java.net.URL;
27
import java.util.ArrayList;
28
import java.util.Arrays;
29
import java.util.Collection;
30
import java.util.Collections;
31
import java.util.Enumeration;
32
import java.util.HashMap;
33
import java.util.HashSet;
34
import java.util.Iterator;
35
import java.util.List;
36
import java.util.Map;
37
import java.util.NoSuchElementException;
38
import java.util.Set;
39

40
import static net.bytebuddy.matcher.ElementMatchers.is;
41
import static net.bytebuddy.matcher.ElementMatchers.not;
42

43
/**
44
 * <p>
45
 * This {@link java.lang.ClassLoader} is capable of loading classes from multiple parents. This class loader
46
 * implicitly defines the bootstrap class loader to be its direct parent as it is required for all class loaders.
47
 * This can be useful when creating a type that inherits a super type and interfaces that are defined by different,
48
 * non-compatible class loaders.
49
 * </p>
50
 * <p>
51
 * <b>Note</b>: Instances of this class loader can have the same class loader as its parent multiple times,
52
 * either directly or indirectly by multiple parents sharing a common parent class loader. By definition,
53
 * this implies that the bootstrap class loader is {@code #(direct parents) + 1} times a parent of this class loader.
54
 * For the {@link java.lang.ClassLoader#getResources(java.lang.String)} method, this means that this class loader
55
 * might return the same url multiple times by representing the same class loader multiple times.
56
 * </p>
57
 * <p>
58
 * <b>Important</b>: This class loader does not support the location of packages from its multiple parents. This breaks
59
 * package equality when loading classes by either loading them directly via this class loader (e.g. by subclassing) or
60
 * by loading classes with child class loaders of this class loader.
61
 * </p>
62
 */
63
public class MultipleParentClassLoader extends InjectionClassLoader {
64

65
    /*
66
     * Register class loader as parallel capable if the current VM supports it.
67
     */
68
    static {
69
        doRegisterAsParallelCapable();
1✔
70
    }
1✔
71

72
    /**
73
     * Registers class loader as parallel capable if possible.
74
     */
75
    @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Must be invoked from targeting class loader type.")
76
    private static void doRegisterAsParallelCapable() {
77
        try {
78
            Method method = ClassLoader.class.getDeclaredMethod("registerAsParallelCapable");
1✔
79
            method.setAccessible(true);
1✔
80
            method.invoke(null);
1✔
81
        } catch (Throwable ignored) {
×
82
            /* do nothing */
83
        }
1✔
84
    }
1✔
85

86
    /**
87
     * The parents of this class loader in their application order.
88
     */
89
    private final List<? extends ClassLoader> parents;
90

91
    /**
92
     * Creates a new class loader with multiple parents.
93
     *
94
     * @param parents The parents of this class loader in their application order. This list must not contain {@code null},
95
     *                i.e. the bootstrap class loader which is an implicit parent of any class loader.
96
     */
97
    public MultipleParentClassLoader(List<? extends ClassLoader> parents) {
98
        this(ClassLoadingStrategy.BOOTSTRAP_LOADER, parents);
×
99
    }
×
100

101
    /**
102
     * Creates a new class loader with multiple parents.
103
     *
104
     * @param parent  An explicit parent in compliance with the class loader API. This explicit parent should only be set if
105
     *                the current platform does not allow creating a class loader that extends the bootstrap loader.
106
     * @param parents The parents of this class loader in their application order. This list must not contain {@code null},
107
     *                i.e. the bootstrap class loader which is an implicit parent of any class loader.
108
     */
109
    public MultipleParentClassLoader(@MaybeNull ClassLoader parent, List<? extends ClassLoader> parents) {
110
        this(parent, parents, true);
×
111
    }
×
112

113
    /**
114
     * Creates a new class loader with multiple parents.
115
     *
116
     * @param parent  An explicit parent in compliance with the class loader API. This explicit parent should only be set if
117
     *                the current platform does not allow creating a class loader that extends the bootstrap loader.
118
     * @param parents The parents of this class loader in their application order. This list must not contain {@code null},
119
     *                i.e. the bootstrap class loader which is an implicit parent of any class loader.
120
     * @param sealed  {@code true} if the class loader is sealed for injection of additional classes.
121
     */
122
    public MultipleParentClassLoader(@MaybeNull ClassLoader parent, List<? extends ClassLoader> parents, boolean sealed) {
123
        super(parent, sealed);
1✔
124
        this.parents = parents;
1✔
125
    }
1✔
126

127
    /**
128
     * {@inheritDoc}
129
     */
130
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
131
        for (ClassLoader parent : parents) {
1✔
132
            try {
133
                Class<?> type = parent.loadClass(name);
1✔
134
                if (resolve) {
1✔
135
                    resolveClass(type);
×
136
                }
137
                return type;
1✔
138
            } catch (ClassNotFoundException ignored) {
1✔
139
                /* try next class loader */
140
            }
141
        }
1✔
142
        return super.loadClass(name, resolve);
×
143
    }
144

145
    /**
146
     * {@inheritDoc}
147
     */
148
    public URL getResource(String name) {
149
        for (ClassLoader parent : parents) {
1✔
150
            URL url = parent.getResource(name);
1✔
151
            if (url != null) {
1✔
152
                return url;
1✔
153
            }
154
        }
1✔
155
        return super.getResource(name);
1✔
156
    }
157

158
    /**
159
     * {@inheritDoc}
160
     */
161
    public Enumeration<URL> getResources(String name) throws IOException {
162
        List<Enumeration<URL>> enumerations = new ArrayList<Enumeration<URL>>(parents.size() + 1);
1✔
163
        for (ClassLoader parent : parents) {
1✔
164
            enumerations.add(parent.getResources(name));
1✔
165
        }
1✔
166
        enumerations.add(super.getResources(name));
1✔
167
        return new CompoundEnumeration(enumerations);
1✔
168
    }
169

170
    @Override
171
    protected Map<String, Class<?>> doDefineClasses(Map<String, byte[]> typeDefinitions) {
172
        Map<String, Class<?>> types = new HashMap<String, Class<?>>();
×
173
        for (Map.Entry<String, byte[]> entry : typeDefinitions.entrySet()) {
×
174
            types.put(entry.getKey(), defineClass(entry.getKey(), entry.getValue(), 0, entry.getValue().length));
×
175
        }
×
176
        return types;
×
177
    }
178

179
    /**
180
     * A compound URL enumeration.
181
     */
182
    protected static class CompoundEnumeration implements Enumeration<URL> {
183

184
        /**
185
         * Indicates the first index of a list.
186
         */
187
        private static final int FIRST = 0;
188

189
        /**
190
         * The remaining lists of enumerations.
191
         */
192
        private final List<Enumeration<URL>> enumerations;
193

194
        /**
195
         * The currently represented enumeration or {@code null} if no such enumeration is currently selected.
196
         */
197
        @UnknownNull
198
        private Enumeration<URL> current;
199

200
        /**
201
         * Creates a compound enumeration.
202
         *
203
         * @param enumerations The enumerations to represent.
204
         */
205
        protected CompoundEnumeration(List<Enumeration<URL>> enumerations) {
1✔
206
            this.enumerations = enumerations;
1✔
207
        }
1✔
208

209
        /**
210
         * {@inheritDoc}
211
         */
212
        public boolean hasMoreElements() {
213
            if (current != null && current.hasMoreElements()) {
1✔
214
                return true;
1✔
215
            } else if (!enumerations.isEmpty()) {
1✔
216
                current = enumerations.remove(FIRST);
1✔
217
                return hasMoreElements();
1✔
218
            } else {
219
                return false;
1✔
220
            }
221
        }
222

223
        /**
224
         * {@inheritDoc}
225
         */
226
        @SuppressFBWarnings(value = "UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", justification = "Null reference is avoided by element check.")
227
        public URL nextElement() {
228
            if (hasMoreElements()) {
1✔
229
                return current.nextElement();
1✔
230
            } else {
231
                throw new NoSuchElementException();
1✔
232
            }
233
        }
234
    }
235

236
    /**
237
     * A builder to collect class loader and that creates a
238
     * {@link net.bytebuddy.dynamic.loading.MultipleParentClassLoader} only if multiple or no
239
     * {@link java.lang.ClassLoader}s are found in the process. If exactly a single class loader is found,
240
     * this class loader is returned. All class loaders are applied in their collection order with the exception
241
     * of the bootstrap class loader which is represented by {@code null} and which is an implicit parent of any
242
     * class loader.
243
     */
244
    @HashCodeAndEqualsPlugin.Enhance
245
    public static class Builder {
246

247
        /**
248
         * {@code true} if the created class loader is sealed.
249
         */
250
        private final boolean sealed;
251

252
        /**
253
         * The class loaders that were collected.
254
         */
255
        private final List<? extends ClassLoader> classLoaders;
256

257
        /**
258
         * Creates a new builder without any class loaders.
259
         */
260
        public Builder() {
261
            this(true);
1✔
262
        }
1✔
263

264
        /**
265
         * Creates a new builder without any class loaders.
266
         *
267
         * @param sealed {@code true} if the created class loader is sealed.
268
         */
269
        public Builder(boolean sealed) {
270
            this(Collections.<ClassLoader>emptyList(), sealed);
1✔
271
        }
1✔
272

273
        /**
274
         * Creates a new builder.
275
         *
276
         * @param classLoaders The class loaders that were collected until now.
277
         * @param sealed       {@code true} if the created class loader is sealed.
278
         */
279
        private Builder(List<? extends ClassLoader> classLoaders, boolean sealed) {
1✔
280
            this.classLoaders = classLoaders;
1✔
281
            this.sealed = sealed;
1✔
282
        }
1✔
283

284
        /**
285
         * Appends the class loaders of the given types. The bootstrap class loader is implicitly skipped as
286
         * it is an implicit parent of any class loader.
287
         *
288
         * @param type The types of which to collect the class loaders.
289
         * @return A new builder instance with the additional class loaders of the provided types if they were not
290
         * yet collected.
291
         */
292
        public Builder append(Class<?>... type) {
293
            return append(Arrays.asList(type));
×
294
        }
295

296
        /**
297
         * Appends the class loaders of the given types if those class loaders were not yet collected. The bootstrap class
298
         * loader is implicitly skipped as it is an implicit parent of any class loader.
299
         *
300
         * @param types The types of which to collect the class loaders.
301
         * @return A new builder instance with the additional class loaders.
302
         */
303
        public Builder append(Collection<? extends Class<?>> types) {
304
            List<ClassLoader> classLoaders = new ArrayList<ClassLoader>(types.size());
×
305
            for (Class<?> type : types) {
×
306
                classLoaders.add(type.getClassLoader());
×
307
            }
×
308
            return append(classLoaders);
×
309
        }
310

311
        /**
312
         * Appends the given class loaders if they were not yet collected. The bootstrap class loader is implicitly
313
         * skipped as it is an implicit parent of any class loader.
314
         *
315
         * @param classLoader The class loaders to be collected.
316
         * @return A new builder instance with the additional class loaders.
317
         */
318
        public Builder append(ClassLoader... classLoader) {
319
            return append(Arrays.asList(classLoader));
1✔
320
        }
321

322
        /**
323
         * Appends the given class loaders if they were not yet appended. The bootstrap class loader is never appended as
324
         * it is an implicit parent of any class loader.
325
         *
326
         * @param classLoaders The class loaders to collected.
327
         * @return A new builder instance with the additional class loaders.
328
         */
329
        public Builder append(List<? extends ClassLoader> classLoaders) {
330
            List<ClassLoader> filtered = new ArrayList<ClassLoader>(this.classLoaders.size() + classLoaders.size());
1✔
331
            filtered.addAll(this.classLoaders);
1✔
332
            Set<ClassLoader> registered = new HashSet<ClassLoader>(this.classLoaders);
1✔
333
            for (ClassLoader classLoader : classLoaders) {
1✔
334
                if (classLoader != null && registered.add(classLoader)) {
1✔
335
                    filtered.add(classLoader);
1✔
336
                }
337
            }
1✔
338
            return new Builder(filtered, sealed);
1✔
339
        }
340

341
        /**
342
         * Appends the class loaders of the given types but filters any duplicates within the hierarchy of class loaders.
343
         * The bootstrap class loader is implicitly skipped as it is an implicit parent of any class loader. Class loaders
344
         * are prepended to the list of class loaders.
345
         *
346
         * @param type The types of which to collect the class loaders.
347
         * @return A new builder instance with the additional class loaders of the provided types if they were not
348
         * yet collected.
349
         */
350
        public Builder appendMostSpecific(Class<?>... type) {
351
            return appendMostSpecific(Arrays.asList(type));
1✔
352
        }
353

354
        /**
355
         * Appends the class loaders of the given types but filters any duplicates within the hierarchy of class loaders.
356
         * The bootstrap class loader is implicitly skipped as it is an implicit parent of any class loader. Class loaders
357
         * are prepended to the list of class loaders.
358
         *
359
         * @param types The types of which to collect the class loaders.
360
         * @return A new builder instance with the additional class loaders.
361
         */
362
        public Builder appendMostSpecific(Collection<? extends Class<?>> types) {
363
            List<ClassLoader> classLoaders = new ArrayList<ClassLoader>(types.size());
1✔
364
            for (Class<?> type : types) {
1✔
365
                classLoaders.add(type.getClassLoader());
1✔
366
            }
1✔
367
            return appendMostSpecific(classLoaders);
1✔
368
        }
369

370
        /**
371
         * Appends the given class loaders but removes any class loaders that are the parent of any previously registered class loader.
372
         * The bootstrap class loader is implicitly skipped as it is an implicit parent of any class loader.
373
         *
374
         * @param classLoader The class loaders to be collected.
375
         * @return A new builder instance with the additional class loaders.
376
         */
377
        public Builder appendMostSpecific(ClassLoader... classLoader) {
378
            return appendMostSpecific(Arrays.asList(classLoader));
1✔
379
        }
380

381
        /**
382
         * Appends the given class loaders but removes any class loaders that are the parent of any previously registered class loader.
383
         * The bootstrap class loader is implicitly skipped as it is an implicit parent of any class loader.
384
         *
385
         * @param classLoaders The class loaders to collected.
386
         * @return A new builder instance with the additional class loaders.
387
         */
388
        public Builder appendMostSpecific(List<? extends ClassLoader> classLoaders) {
389
            List<ClassLoader> filtered = new ArrayList<ClassLoader>(this.classLoaders.size() + classLoaders.size());
1✔
390
            filtered.addAll(this.classLoaders);
1✔
391
            consideration:
392
            for (ClassLoader classLoader : classLoaders) {
1✔
393
                if (classLoader == null) {
1✔
394
                    continue;
1✔
395
                }
396
                ClassLoader candidate = classLoader;
1✔
397
                do {
398
                    Iterator<ClassLoader> iterator = filtered.iterator();
1✔
399
                    while (iterator.hasNext()) {
1✔
400
                        ClassLoader previous = iterator.next();
1✔
401
                        if (previous.equals(candidate)) {
1✔
402
                            iterator.remove();
1✔
403
                        }
404
                    }
1✔
405
                } while ((candidate = candidate.getParent()) != null);
1✔
406
                for (ClassLoader previous : filtered) {
1✔
407
                    do {
408
                        if (previous.equals(classLoader)) {
1✔
409
                            continue consideration;
1✔
410
                        }
411
                    } while ((previous = previous.getParent()) != null);
1✔
412
                }
1✔
413
                filtered.add(classLoader);
1✔
414
            }
1✔
415
            return new Builder(filtered, sealed);
1✔
416
        }
417

418
        /**
419
         * Only retains all class loaders that match the given matcher.
420
         *
421
         * @param matcher The matcher to be used for filtering.
422
         * @return A builder that does not longer consider any appended class loaders that matched the provided matcher.
423
         */
424
        public Builder filter(ElementMatcher<? super ClassLoader> matcher) {
425
            List<ClassLoader> classLoaders = new ArrayList<ClassLoader>(this.classLoaders.size());
1✔
426
            for (ClassLoader classLoader : this.classLoaders) {
1✔
427
                if (matcher.matches(classLoader)) {
1✔
428
                    classLoaders.add(classLoader);
1✔
429
                }
430
            }
1✔
431
            return new Builder(classLoaders, sealed);
1✔
432
        }
433

434
        /**
435
         * <p>
436
         * Returns the only class loader that was appended if exactly one class loader was appended or a multiple parent class loader as
437
         * a parent of all supplied class loader and with the bootstrap class loader as an implicit parent. If no class loader was appended,
438
         * a new class loader is created that declares no parents. If a class loader is created, its explicit parent is set to be the
439
         * bootstrap class loader.
440
         * </p>
441
         * <p>
442
         * <b>Important</b>: Byte Buddy does not provide any access control for the creation of the class loader. It is the responsibility
443
         * of the user of this builder to provide such privileges.
444
         * </p>
445
         *
446
         * @return A suitable class loader.
447
         */
448
        public ClassLoader build() {
449
            return classLoaders.size() == 1
1✔
450
                    ? classLoaders.get(0)
1✔
451
                    : doBuild(ClassLoadingStrategy.BOOTSTRAP_LOADER);
1✔
452
        }
453

454
        /**
455
         * <p>
456
         * Returns the only class loader that was appended if exactly one class loader was appended or a multiple parent class loader as
457
         * a parent of all supplied class loader and with the bootstrap class loader as an implicit parent. If no class loader was appended,
458
         * or if only the supplied parent to this method was included, this class loader is returned,
459
         * </p>
460
         * <p>
461
         * <b>Important</b>: Byte Buddy does not provide any access control for the creation of the class loader. It is the responsibility
462
         * of the user of this builder to provide such privileges.
463
         * </p>
464
         *
465
         * @param parent The class loader's contractual parent which is accessible via {@link ClassLoader#getParent()}. If this class loader
466
         *               is also included in the appended class loaders, it is not
467
         * @return A suitable class loader.
468
         */
469
        public ClassLoader build(ClassLoader parent) {
470
            return classLoaders.isEmpty() || classLoaders.size() == 1 && classLoaders.contains(parent)
1✔
471
                    ? parent
472
                    : filter(not(is(parent))).doBuild(parent);
1✔
473
        }
474

475
        /**
476
         * Creates a multiple parent class loader with an explicit parent.
477
         *
478
         * @param parent The explicit parent class loader.
479
         * @return A multiple parent class loader that includes all collected class loaders and the explicit parent.
480
         */
481
        @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
482
        private ClassLoader doBuild(@MaybeNull ClassLoader parent) {
483
            return new MultipleParentClassLoader(parent, classLoaders, sealed);
1✔
484
        }
485
    }
486
}
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