![JAR search and dependency download from the Maven repository](/logo.png)
org.dinky.shaded.paimon.plugin.ComponentClassLoader Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dinky.shaded.paimon.plugin;
import org.dinky.shaded.paimon.annotation.VisibleForTesting;
import org.dinky.shaded.paimon.utils.FunctionWithException;
import org.dinky.shaded.paimon.shade.guava30.com.google.common.collect.Iterators;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
/**
* A {@link URLClassLoader} that restricts which classes can be loaded to those contained within the
* given classpath, except classes from a given set of packages that are either loaded owner or
* component-first.
*
* Depiction of the class loader hierarchy:
*
*
* Owner Bootstrap
* ^ ^
* |---------|
* |
* Component
*
*
* For loading classes/resources, class loaders are accessed in one of the following orders:
*
*
* - component-only: component -> bootstrap; default.
*
- component-first: component -> bootstrap -> owner; opt-in.
*
- owner-first: owner -> component -> bootstrap; opt-in.
*
*/
public class ComponentClassLoader extends URLClassLoader {
private static final ClassLoader PLATFORM_OR_BOOTSTRAP_LOADER;
private final ClassLoader ownerClassLoader;
private final String[] ownerFirstPackages;
private final String[] componentFirstPackages;
private final String[] ownerFirstResourcePrefixes;
private final String[] componentFirstResourcePrefixes;
public ComponentClassLoader(
URL[] classpath,
ClassLoader ownerClassLoader,
String[] ownerFirstPackages,
String[] componentFirstPackages) {
super(classpath, PLATFORM_OR_BOOTSTRAP_LOADER);
this.ownerClassLoader = ownerClassLoader;
this.ownerFirstPackages = ownerFirstPackages;
this.componentFirstPackages = componentFirstPackages;
ownerFirstResourcePrefixes = convertPackagePrefixesToPathPrefixes(ownerFirstPackages);
componentFirstResourcePrefixes =
convertPackagePrefixesToPathPrefixes(componentFirstPackages);
}
// ----------------------------------------------------------------------------------------------
// Class loading
// ----------------------------------------------------------------------------------------------
@Override
protected Class> loadClass(final String name, final boolean resolve)
throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
final Class> loadedClass = findLoadedClass(name);
if (loadedClass != null) {
return resolveIfNeeded(resolve, loadedClass);
}
if (isComponentFirstClass(name)) {
return loadClassFromComponentFirst(name, resolve);
}
if (isOwnerFirstClass(name)) {
return loadClassFromOwnerFirst(name, resolve);
}
// making this behavior configurable (component-only/component-first/owner-first)
// would allow this class to subsume the FlinkUserCodeClassLoader (with an added
// exception handler)
return loadClassFromComponentOnly(name, resolve);
}
}
private Class> resolveIfNeeded(final boolean resolve, final Class> loadedClass) {
if (resolve) {
resolveClass(loadedClass);
}
return loadedClass;
}
private boolean isOwnerFirstClass(final String name) {
return Arrays.stream(ownerFirstPackages).anyMatch(name::startsWith);
}
private boolean isComponentFirstClass(final String name) {
return Arrays.stream(componentFirstPackages).anyMatch(name::startsWith);
}
private Class> loadClassFromComponentOnly(final String name, final boolean resolve)
throws ClassNotFoundException {
return super.loadClass(name, resolve);
}
private Class> loadClassFromComponentFirst(final String name, final boolean resolve)
throws ClassNotFoundException {
try {
return loadClassFromComponentOnly(name, resolve);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
return loadClassFromOwnerOnly(name, resolve);
}
}
private Class> loadClassFromOwnerOnly(final String name, final boolean resolve)
throws ClassNotFoundException {
return resolveIfNeeded(resolve, ownerClassLoader.loadClass(name));
}
private Class> loadClassFromOwnerFirst(final String name, final boolean resolve)
throws ClassNotFoundException {
try {
return loadClassFromOwnerOnly(name, resolve);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
return loadClassFromComponentOnly(name, resolve);
}
}
// ----------------------------------------------------------------------------------------------
// Resource loading
// ----------------------------------------------------------------------------------------------
@Override
public URL getResource(final String name) {
try {
final Enumeration resources = getResources(name);
if (resources.hasMoreElements()) {
return resources.nextElement();
}
} catch (IOException ignored) {
// mimics the behavior of the JDK
}
return null;
}
@Override
public Enumeration getResources(final String name) throws IOException {
if (isComponentFirstResource(name)) {
return loadResourceFromComponentFirst(name);
}
if (isOwnerFirstResource(name)) {
return loadResourceFromOwnerFirst(name);
}
return loadResourceFromComponentOnly(name);
}
private boolean isOwnerFirstResource(final String name) {
return Arrays.stream(ownerFirstResourcePrefixes).anyMatch(name::startsWith);
}
private boolean isComponentFirstResource(final String name) {
return Arrays.stream(componentFirstResourcePrefixes).anyMatch(name::startsWith);
}
private Enumeration loadResourceFromComponentOnly(final String name) throws IOException {
return super.getResources(name);
}
private Enumeration loadResourceFromComponentFirst(final String name) throws IOException {
return loadResourcesInOrder(
name, this::loadResourceFromComponentOnly, this::loadResourceFromOwnerOnly);
}
private Enumeration loadResourceFromOwnerOnly(final String name) throws IOException {
return ownerClassLoader.getResources(name);
}
private Enumeration loadResourceFromOwnerFirst(final String name) throws IOException {
return loadResourcesInOrder(
name, this::loadResourceFromOwnerOnly, this::loadResourceFromComponentOnly);
}
private interface ResourceLoadingFunction
extends FunctionWithException, IOException> {}
private Enumeration loadResourcesInOrder(
String name,
ResourceLoadingFunction firstClassLoader,
ResourceLoadingFunction secondClassLoader)
throws IOException {
final Iterator iterator =
Iterators.concat(
Iterators.forEnumeration(firstClassLoader.apply(name)),
Iterators.forEnumeration(secondClassLoader.apply(name)));
return new IteratorBackedEnumeration<>(iterator);
}
@VisibleForTesting
static class IteratorBackedEnumeration implements Enumeration {
private final Iterator backingIterator;
public IteratorBackedEnumeration(Iterator backingIterator) {
this.backingIterator = backingIterator;
}
@Override
public boolean hasMoreElements() {
return backingIterator.hasNext();
}
@Override
public T nextElement() {
return backingIterator.next();
}
}
// ----------------------------------------------------------------------------------------------
// Utils
// ----------------------------------------------------------------------------------------------
private static String[] convertPackagePrefixesToPathPrefixes(String[] packagePrefixes) {
return Arrays.stream(packagePrefixes)
.map(packageName -> packageName.replace('.', '/'))
.toArray(String[]::new);
}
static {
ClassLoader platformLoader = null;
try {
platformLoader =
(ClassLoader)
ClassLoader.class.getMethod("getPlatformClassLoader").invoke(null);
} catch (NoSuchMethodException e) {
// on Java 8 this method does not exist, but using null indicates the bootstrap
// loader that we want to have
} catch (Exception e) {
throw new IllegalStateException("Cannot retrieve platform classloader on Java 9+", e);
}
PLATFORM_OR_BOOTSTRAP_LOADER = platformLoader;
ClassLoader.registerAsParallelCapable();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy