Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ClassNotFound #881

Merged
merged 4 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import org.bithon.agent.controller.config.AgentSettingFetchTask;
import org.bithon.agent.controller.config.ConfigurationCommandImpl;
import org.bithon.agent.instrumentation.loader.AgentClassLoader;
import org.bithon.agent.instrumentation.loader.PluginClassLoaderManager;
import org.bithon.agent.instrumentation.loader.PluginClassLoader;
import org.bithon.agent.starter.IAgentService;
import org.bithon.component.commons.logging.ILogAdaptor;
import org.bithon.component.commons.logging.LoggerFactory;
Expand Down Expand Up @@ -73,7 +73,7 @@ public void start() throws Exception {
attachCommand(controller, new InstrumentationCommand());
attachCommand(controller, new ConfigurationCommandImpl());
loadAgentCommands(controller, AgentClassLoader.getClassLoader());
loadAgentCommands(controller, PluginClassLoaderManager.getDefaultLoader());
loadAgentCommands(controller, PluginClassLoader.getClassLoader());


//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package org.bithon.agent.instrumentation.aop.interceptor;

import org.bithon.agent.instrumentation.aop.interceptor.declaration.AbstractInterceptor;
import org.bithon.agent.instrumentation.loader.PluginClassLoaderManager;
import org.bithon.agent.instrumentation.loader.InterceptorClassLoaderManager;
import org.bithon.agent.instrumentation.logging.LoggerFactory;

import java.io.PrintWriter;
Expand Down Expand Up @@ -85,7 +85,7 @@ public AbstractInterceptor get() {
private AbstractInterceptor createInterceptor(String interceptorClassName, ClassLoader userClassLoader) {
try {
// Load class out of lock in case of deadlock
ClassLoader interceptorClassLoader = PluginClassLoaderManager.getClassLoader(userClassLoader);
ClassLoader interceptorClassLoader = InterceptorClassLoaderManager.getClassLoader(userClassLoader);
Class<?> interceptorClass = Class.forName(interceptorClassName, true, interceptorClassLoader);

return (AbstractInterceptor) interceptorClass.getConstructor().newInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import org.bithon.agent.instrumentation.aop.interceptor.descriptor.MethodPointCutDescriptor;
import org.bithon.agent.instrumentation.expt.AgentException;
import org.bithon.agent.instrumentation.loader.JarClassLoader;
import org.bithon.agent.instrumentation.loader.PluginClassLoaderManager;
import org.bithon.agent.instrumentation.loader.PluginClassLoader;
import org.bithon.agent.instrumentation.logging.ILogger;
import org.bithon.agent.instrumentation.logging.LoggerFactory;

Expand All @@ -45,7 +45,7 @@ public abstract class PluginResolver {

public PluginResolver() {
// create plugin class loader first
PluginClassLoaderManager.createDefault();
PluginClassLoader.createClassLoader();
}

public Descriptors resolveInterceptors() {
Expand All @@ -72,7 +72,7 @@ public Descriptors resolveInterceptors() {
*/
private void resolveInterceptorType(Collection<Descriptors.Descriptor> descriptors) {
LOG.info("Resolving interceptor type from all enabled plugins...");
InterceptorTypeResolver resolver = new InterceptorTypeResolver(PluginClassLoaderManager.getDefaultLoader());
InterceptorTypeResolver resolver = new InterceptorTypeResolver(PluginClassLoader.getClassLoader());

for (Descriptors.Descriptor descriptor : descriptors) {
for (Descriptors.MethodPointCuts pointcut : descriptor.getMethodPointCuts()) {
Expand All @@ -92,7 +92,7 @@ private void resolveInterceptorType(Collection<Descriptors.Descriptor> descripto
}

private List<IPlugin> loadPlugins() {
JarClassLoader pluginClassLoader = PluginClassLoaderManager.getDefaultLoader();
JarClassLoader pluginClassLoader = PluginClassLoader.getClassLoader();
return pluginClassLoader.getJars()
.stream()
.flatMap(JarFile::stream)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright 2020 bithon.org
*
* Licensed 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.bithon.agent.instrumentation.loader;

import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Locale;

/**
* @author [email protected]
* @date 28/12/21 10:31 AM
*/
public class InterceptorClassLoader extends ClassLoader {
private final JarClassLoader pluginClassLoader;
private final ClassLoader applicationClassLoader;

public InterceptorClassLoader(JarClassLoader pluginClassLoader) {
this(pluginClassLoader, null);
}

public InterceptorClassLoader(JarClassLoader pluginClassLoader, ClassLoader applicationClassLoader) {
// NOTE: parent is assigned to parent class loader
// This is the key to implement agent lib isolation from app libs
super(null);

this.pluginClassLoader = pluginClassLoader;
this.applicationClassLoader = applicationClassLoader;
}

/**
* For classes under org.bithon.agent.plugin, we need to make sure the classes are loaded in this class loader,
* instead of the parent class loader.
* <p>
* This makes sure that this class loader can find those classes referenced in the plugin.
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.startsWith("org.bithon.agent.plugin.")) {
try {
byte[] classBytes = pluginClassLoader.getClassByteCode(name);
if (classBytes != null) {
// define the package object for customer-loaded classes,
// so that getPackage could work
int lastIndex = name.lastIndexOf(".");
if (lastIndex > 0) {
String pkg = name.substring(0, lastIndex);
if (getPackage(pkg) == null) {
definePackage(pkg, null, null, null, null, null, null, null);
}
}
return defineClass(name, classBytes, 0, classBytes.length);
}
} catch (IOException ignored) {
}
}

// Find class in parent, they might be classes in the agent library
try {
return pluginClassLoader.loadClass(name);
} catch (ClassNotFoundException ignored) {
}

// Find in application class loader if it's referenced by the class in the plugin
if (applicationClassLoader != null) {
try {
return applicationClassLoader.loadClass(name);
} catch (ClassNotFoundException e) {
throw new ClassNotFoundException(String.format(Locale.ENGLISH,
"%s not found in parent [%s] and agent plugins.",
name,
applicationClassLoader.getClass().getName()));
}
}

throw new ClassNotFoundException(String.format(Locale.ENGLISH,
"%s not found in agent plugins.",
name));
}

@Override
public URL getResource(String name) {
// delegate to parent to get resource
URL url = this.pluginClassLoader.getResource(name);
if (url != null) {
return url;
}
return applicationClassLoader.getResource(name);
}

@Override
public Enumeration<URL> getResources(String name) throws IOException {
return applicationClassLoader.getResources(name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package org.bithon.agent.instrumentation.loader;

import org.bithon.agent.instrumentation.utils.AgentDirectory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

Expand All @@ -27,31 +25,20 @@
* @author frankchen
* @date 2020-12-31 22:28:23
*/
public final class PluginClassLoaderManager {
private static JarClassLoader defaultLoader;
public final class InterceptorClassLoaderManager {
private static final Map<ClassLoader, ClassLoader> LOADER_MAPPING = new ConcurrentHashMap<>();

private static final InterceptorClassLoader DEFAULT_LOADER = new InterceptorClassLoader(PluginClassLoader.getClassLoader());

/**
* class loader for class which is being transformed.
* it can be null if the class is loaded by bootstrap class loader
*/
public static ClassLoader getClassLoader(ClassLoader appClassLoader) {
return appClassLoader == null
? defaultLoader
: LOADER_MAPPING.computeIfAbsent(appClassLoader,
k -> new PluginClassLoader(defaultLoader,
appClassLoader));
}

public static JarClassLoader getDefaultLoader() {
return defaultLoader;
}

public static synchronized void createDefault() {
if (defaultLoader == null) {
defaultLoader = new JarClassLoader("plugin",
AgentDirectory.getSubDirectory("plugins"),
AgentClassLoader.getClassLoader());
}
? DEFAULT_LOADER
: LOADER_MAPPING.computeIfAbsent(appClassLoader,
k -> new InterceptorClassLoader(PluginClassLoader.getClassLoader(),
appClassLoader));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,86 +16,24 @@

package org.bithon.agent.instrumentation.loader;

import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Locale;
import org.bithon.agent.instrumentation.utils.AgentDirectory;

/**
* @author [email protected]
* @date 28/12/21 10:31 AM
* @date 2024/11/2 14:21
*/
public class PluginClassLoader extends ClassLoader {
private final JarClassLoader pluginClassLoader;
public class PluginClassLoader {
private static JarClassLoader instance;

private final ClassLoader appClassLoader;

public PluginClassLoader(JarClassLoader pluginClassLoader, ClassLoader appClassLoader) {
// NOTE: parent is assigned to parent class loader
// This is the key to implement agent lib isolation from app libs
super(null);

this.pluginClassLoader = pluginClassLoader;
this.appClassLoader = appClassLoader;
public static JarClassLoader getClassLoader() {
return instance;
}

/**
* For classes under org.bithon.agent.plugin, we need to make sure the classes are loaded in this class loader,
* instead of the parent class loader.
* <p>
* This makes sure that this class loader can find those classes referenced in the plugin.
*
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.startsWith("org.bithon.agent.plugin.")) {
try {
byte[] classBytes = pluginClassLoader.getClassByteCode(name);
if (classBytes != null) {
// define the package object for customer-loaded classes,
// so that getPackage could work
int lastIndex = name.lastIndexOf(".");
if (lastIndex > 0) {
String pkg = name.substring(0, lastIndex);
if (getPackage(pkg) == null) {
definePackage(pkg, null, null, null, null, null, null, null);
}
}
return defineClass(name, classBytes, 0, classBytes.length);
}
} catch (IOException ignored) {
}
}

// Find class in parent, they might be classes in the agent library
try {
return pluginClassLoader.loadClass(name);
} catch (ClassNotFoundException ignored) {
}

// Find in application class loader if it's referenced by the class in the plugin
try {
return appClassLoader.loadClass(name);
} catch (ClassNotFoundException e) {
throw new ClassNotFoundException(String.format(Locale.ENGLISH,
"%s not found in parent [%s] and agent plugins.",
name,
appClassLoader.getClass().getName()));
public static synchronized void createClassLoader() {
if (instance == null) {
instance = new JarClassLoader("plugin",
AgentDirectory.getSubDirectory("plugins"),
AgentClassLoader.getClassLoader());
}
}

@Override
public URL getResource(String name) {
// delegate to parent to get resource
URL url = this.pluginClassLoader.getResource(name);
if (url != null) {
return url;
}
return appClassLoader.getResource(name);
}

@Override
public Enumeration<URL> getResources(String name) throws IOException {
return appClassLoader.getResources(name);
}
}
Loading
Loading