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

Create a lossy index of resource -> ClassPathElement mapping #42525

Merged
merged 6 commits into from
Aug 30, 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 @@ -340,7 +340,7 @@ public static <T> T readConfig(ApplicationModel appModel, LaunchMode launchMode,
final QuarkusClassLoader.Builder configClBuilder = QuarkusClassLoader.builder("CodeGenerator Config ClassLoader",
deploymentClassLoader, false);
if (!allowedConfigServices.isEmpty()) {
configClBuilder.addElement(new MemoryClassPathElement(allowedConfigServices, true));
configClBuilder.addNormalPriorityElement(new MemoryClassPathElement(allowedConfigServices, true));
}
if (!bannedConfigServices.isEmpty()) {
configClBuilder.addBannedElement(new MemoryClassPathElement(bannedConfigServices, true));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package io.quarkus.deployment;

import static io.quarkus.commons.classloading.ClassLoaderHelper.fromClassNameToResourceName;

import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

import io.quarkus.bootstrap.BootstrapDebug;
import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
Expand Down Expand Up @@ -75,12 +71,7 @@ public Writer getSourceWriter(String className) {
}

public static boolean isApplicationClass(String className) {
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread()
.getContextClassLoader();
//if the class file is present in this (and not the parent) CL then it is an application class
List<ClassPathElement> res = cl
.getElementsWithResource(fromClassNameToResourceName(className), true);
return !res.isEmpty();
return QuarkusClassLoader.isApplicationClass(className);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,12 @@
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.RunningQuarkusApplication;
import io.quarkus.bootstrap.app.StartupAction;
import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.bootstrap.logging.InitialConfigurator;
import io.quarkus.bootstrap.runner.Timing;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildContext;
import io.quarkus.builder.BuildStep;
import io.quarkus.commons.classloading.ClassLoaderHelper;
import io.quarkus.deployment.builditem.ApplicationClassPredicateBuildItem;
import io.quarkus.deployment.console.ConsoleCommand;
import io.quarkus.deployment.console.ConsoleStateManager;
Expand Down Expand Up @@ -478,14 +476,8 @@ public void execute(BuildContext context) {
//we need to make sure all hot reloadable classes are application classes
context.produce(new ApplicationClassPredicateBuildItem(new Predicate<String>() {
@Override
public boolean test(String s) {
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread()
.getContextClassLoader();
String resourceName = ClassLoaderHelper.fromClassNameToResourceName(s);
//if the class file is present in this (and not the parent) CL then it is an application class
List<ClassPathElement> res = cl
.getElementsWithResource(resourceName, true);
return !res.isEmpty();
public boolean test(String className) {
return QuarkusClassLoader.isApplicationClass(className);
}
}));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ public String apply(Class<?> aClass) {
//this is a lot more complex
//we need to transform the classes to make the tracing magic work
QuarkusClassLoader deploymentClassLoader = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader();
Set<String> classesToTransform = new HashSet<>(deploymentClassLoader.getLocalClassNames());
Set<String> classesToTransform = new HashSet<>(deploymentClassLoader.getReloadableClassNames());
Map<String, byte[]> transformedClasses = new HashMap<>();
for (String i : classesToTransform) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public void init() {
+ curatedApplication.getClassLoaderNameSuffix(),
getClass().getClassLoader().getParent(), false);
}
clBuilder.addElement(ClassPathElement.fromDependency(d));
clBuilder.addNormalPriorityElement(ClassPathElement.fromDependency(d));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
Expand Down Expand Up @@ -155,13 +154,8 @@ public ServiceStartBuildItem searchForTags(CombinedIndexBuildItem combinedIndexB
return null;
}

public boolean isAppClass(String theClassName) {
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread()
.getContextClassLoader();
//if the class file is present in this (and not the parent) CL then it is an application class
List<ClassPathElement> res = cl
.getElementsWithResource(theClassName.replace(".", "/") + ".class", true);
return !res.isEmpty();
public boolean isAppClass(String className) {
return QuarkusClassLoader.isApplicationClass(className);
}

public static class TracingClassVisitor extends ClassVisitor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ static ArchivePathTree forPath(Path path, PathFilter filter, boolean manifestEna
this.pathFilter = pathFilter;
}

@Override
public boolean isArchiveOrigin() {
return true;
}

@Override
public Collection<Path> getRoots() {
return List.of(archive);
Expand Down Expand Up @@ -240,6 +245,11 @@ protected OpenArchivePathTree(FileSystem fs) {
this.rootPath = fs.getPath("/");
}

@Override
public boolean isArchiveOrigin() {
return true;
}

@Override
protected Path getContainerPath() {
return ArchivePathTree.this.archive;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ protected DirectoryPathTree(Path dir, PathFilter pathFilter, PathTreeWithManifes
this.dir = dir;
}

@Override
public boolean isArchiveOrigin() {
return false;
}

@Override
protected Path getRootPath() {
return dir;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public static EmptyPathTree getInstance() {
return INSTANCE;
}

@Override
public boolean isArchiveOrigin() {
return false;
}

@Override
public Collection<Path> getRoots() {
return Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class FilePathTree implements OpenPathTree {
this.pathFilter = pathFilter;
}

@Override
public boolean isArchiveOrigin() {
return false;
}

@Override
public Collection<Path> getRoots() {
return Collections.singletonList(file);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public FilteredPathTree(PathTree tree, PathFilter filter) {
this.filter = Objects.requireNonNull(filter, "filter is null");
}

@Override
public boolean isArchiveOrigin() {
return original.isArchiveOrigin();
}

@Override
public Collection<Path> getRoots() {
return original.getRoots();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ public MultiRootPathTree(PathTree... trees) {
roots = tmp;
}

/**
* If at least one of the PathTrees is not an archive, we return false.
*/
@Override
public boolean isArchiveOrigin() {
for (PathTree tree : trees) {
if (!tree.isArchiveOrigin()) {
return false;
}
}

return true;
}

@Override
public Collection<Path> getRoots() {
return roots;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ static PathTree ofArchive(Path archive, PathFilter filter) {
return ArchivePathTree.forPath(archive, filter);
}

/**
* Whether the content of this tree comes from an archive or not.
* <p>
* This is useful for instance when you want to determine if the resources can be updated in dev mode.
*/
boolean isArchiveOrigin();

/**
* The roots of the path tree.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ private CallerOpenPathTree(SharedOpenArchivePathTree delegate) {
this.delegate = delegate;
}

@Override
public boolean isArchiveOrigin() {
return delegate.isArchiveOrigin();
}

@Override
public PathTree getOriginalTree() {
return delegate.getOriginalTree();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public final class ClassLoaderHelper {
private static final String JDK_INTERNAL = "jdk.internal.";
private static final String SUN_MISC = "sun.misc.";

private static final String CLASS_SUFFIX = ".class";

private ClassLoaderHelper() {
//Not meant to be instantiated
}
Expand All @@ -19,7 +21,23 @@ private ClassLoaderHelper() {
*/
public static String fromClassNameToResourceName(final String className) {
//Important: avoid indy!
return className.replace('.', '/').concat(".class");
return className.replace('.', '/').concat(CLASS_SUFFIX);
}

/**
* Helper method to convert a resource name into the corresponding class name:
* replace all "/" with "." and remove the ".class" postfix.
*
* @param resourceName
* @return the name of the respective class
*/
public static String fromResourceNameToClassName(final String resourceName) {
if (!resourceName.endsWith(CLASS_SUFFIX)) {
throw new IllegalArgumentException(
String.format("%s is not a valid resource name as it doesn't end with .class", resourceName));
}

return resourceName.substring(0, resourceName.length() - CLASS_SUFFIX.length()).replace('/', '.');
}

public static boolean isInJdkPackage(String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,12 @@ private void addCpElement(QuarkusClassLoader.Builder builder, ResolvedDependency
//we always load this from the parent if it is available, as this acts as a bridge between the running
//app and the dev mode code
builder.addParentFirstElement(element);
builder.addNormalPriorityElement(element);
} else if (dep.isFlagSet(DependencyFlags.CLASSLOADER_LESSER_PRIORITY)) {
builder.addLesserPriorityElement(element);
} else {
builder.addNormalPriorityElement(element);
}
builder.addElement(element);
Comment on lines 185 to -189
Copy link
Member Author

@gsmet gsmet Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semantic has changed a bit here: we used to store all the elements in elements and then do some logic to figure out if they were in lesserPriorityElements when building the index.
I chose to simplify things and have a clearer semantic.

Thus why the logic has changed here and also why I renamed the method.

}

public synchronized QuarkusClassLoader getOrCreateAugmentClassLoader() {
Expand All @@ -212,7 +214,7 @@ public synchronized QuarkusClassLoader getOrCreateAugmentClassLoader() {
}

for (Path i : quarkusBootstrap.getAdditionalDeploymentArchives()) {
builder.addElement(ClassPathElement.fromPath(i, false));
builder.addNormalPriorityElement(ClassPathElement.fromPath(i, false));
}
Map<String, byte[]> banned = new HashMap<>();
for (Collection<String> i : configuredClassLoading.getRemovedResources().values()) {
Expand Down Expand Up @@ -256,7 +258,7 @@ public synchronized QuarkusClassLoader getOrCreateBaseRuntimeClassLoader() {
//there is no need to restart so there is no need for an additional CL

for (Path root : quarkusBootstrap.getApplicationRoot()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
} else {
for (Path root : quarkusBootstrap.getApplicationRoot()) {
Expand All @@ -269,7 +271,7 @@ public synchronized QuarkusClassLoader getOrCreateBaseRuntimeClassLoader() {
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) {
if (!i.isHotReloadable()) {
for (Path root : i.getResolvedPaths()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
} else {
for (Path root : i.getResolvedPaths()) {
Expand Down Expand Up @@ -339,15 +341,15 @@ public QuarkusClassLoader createDeploymentClassLoader() {
.setAggregateParentResources(true);

for (Path root : quarkusBootstrap.getApplicationRoot()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}

builder.setResettableElement(new MemoryClassPathElement(Collections.emptyMap(), false));

//additional user class path elements first
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) {
for (Path root : i.getResolvedPaths()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
}
for (ResolvedDependency dependency : appModel.getDependencies()) {
Expand All @@ -361,7 +363,7 @@ public QuarkusClassLoader createDeploymentClassLoader() {
}
}
for (Path root : configuredClassLoading.getAdditionalClasspathElements()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
return builder.build();
}
Expand All @@ -387,15 +389,15 @@ public QuarkusClassLoader createRuntimeClassLoader(ClassLoader base, Map<String,
.setAggregateParentResources(true);
builder.setTransformedClasses(transformedClasses);

builder.addElement(new MemoryClassPathElement(resources, true));
builder.addNormalPriorityElement(new MemoryClassPathElement(resources, true));
for (Path root : quarkusBootstrap.getApplicationRoot()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}

for (AdditionalDependency i : getQuarkusBootstrap().getAdditionalApplicationArchives()) {
if (i.isHotReloadable()) {
for (Path root : i.getResolvedPaths()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
}
}
Expand All @@ -410,7 +412,7 @@ public QuarkusClassLoader createRuntimeClassLoader(ClassLoader base, Map<String,
}
}
for (Path root : configuredClassLoading.getAdditionalClasspathElements()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
return builder.build();
}
Expand Down Expand Up @@ -493,6 +495,11 @@ public Set<String> getProvidedResources() {
return delegate.getProvidedResources().stream().filter(s -> s.endsWith(".class")).collect(Collectors.toSet());
}

@Override
public boolean containsReloadableResources() {
return delegate.containsReloadableResources();
}

@Override
public ProtectionDomain getProtectionDomain() {
return delegate.getProtectionDomain();
Expand Down
Loading
Loading