Search in sources :

Example 1 with ClassLoadingConfig

use of io.quarkus.deployment.configuration.ClassLoadingConfig in project quarkus by quarkusio.

the class ClassTransformingBuildStep method handleRemovedResources.

private void handleRemovedResources(ClassLoadingConfig classLoadingConfig, CurateOutcomeBuildItem curateOutcomeBuildItem, Map<Path, Set<TransformedClassesBuildItem.TransformedClass>> transformedClassesByJar, List<RemovedResourceBuildItem> removedResourceBuildItems) {
    // a little bit of a hack, but we use an empty transformed class to represent removed resources, as transforming a class removes it from the original archive
    Map<ArtifactKey, Set<String>> removed = new HashMap<>();
    for (Map.Entry<String, Set<String>> entry : classLoadingConfig.removedResources.entrySet()) {
        removed.put(new GACT(entry.getKey().split(":")), entry.getValue());
    }
    for (RemovedResourceBuildItem i : removedResourceBuildItems) {
        removed.computeIfAbsent(i.getArtifact(), k -> new HashSet<>()).addAll(i.getResources());
    }
    if (!removed.isEmpty()) {
        ApplicationModel applicationModel = curateOutcomeBuildItem.getApplicationModel();
        Collection<ResolvedDependency> runtimeDependencies = applicationModel.getRuntimeDependencies();
        List<ResolvedDependency> allArtifacts = new ArrayList<>(runtimeDependencies.size() + 1);
        allArtifacts.addAll(runtimeDependencies);
        allArtifacts.add(applicationModel.getAppArtifact());
        for (ResolvedDependency i : allArtifacts) {
            Set<String> filtered = removed.remove(i.getKey());
            if (filtered != null) {
                for (Path path : i.getResolvedPaths()) {
                    transformedClassesByJar.computeIfAbsent(path, s -> new HashSet<>()).addAll(filtered.stream().map(file -> new TransformedClassesBuildItem.TransformedClass(null, null, file, false)).collect(Collectors.toSet()));
                }
            }
        }
    }
    if (!removed.isEmpty()) {
        log.warn("Could not remove configured resources from the following artifacts as they were not found in the model: " + removed.keySet());
    }
}
Also used : TransformedClassesBuildItem(io.quarkus.deployment.builditem.TransformedClassesBuildItem) QuarkusClassWriter(io.quarkus.deployment.QuarkusClassWriter) BiFunction(java.util.function.BiFunction) Future(java.util.concurrent.Future) ArchiveRootBuildItem(io.quarkus.deployment.builditem.ArchiveRootBuildItem) Map(java.util.Map) QuarkusClassVisitor(io.quarkus.deployment.QuarkusClassVisitor) Path(java.nio.file.Path) ClassVisitor(org.objectweb.asm.ClassVisitor) Collection(java.util.Collection) StandardOpenOption(java.nio.file.StandardOpenOption) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) BootstrapDebug(io.quarkus.bootstrap.BootstrapDebug) QuarkusClassLoader(io.quarkus.bootstrap.classloading.QuarkusClassLoader) Collectors(java.util.stream.Collectors) Executors(java.util.concurrent.Executors) RemovedResourceBuildItem(io.quarkus.deployment.builditem.RemovedResourceBuildItem) PackageConfig(io.quarkus.deployment.pkg.PackageConfig) Objects(java.util.Objects) List(java.util.List) ApplicationModel(io.quarkus.bootstrap.model.ApplicationModel) ClassReader(org.objectweb.asm.ClassReader) GACT(io.quarkus.maven.dependency.GACT) ConstPoolScanner(io.quarkus.deployment.index.ConstPoolScanner) ResolvedDependency(io.quarkus.maven.dependency.ResolvedDependency) ClassWriter(org.objectweb.asm.ClassWriter) Logger(org.jboss.logging.Logger) LaunchMode(io.quarkus.runtime.LaunchMode) HashMap(java.util.HashMap) Callable(java.util.concurrent.Callable) CurateOutcomeBuildItem(io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) BuildStep(io.quarkus.deployment.annotations.BuildStep) ArtifactKey(io.quarkus.maven.dependency.ArtifactKey) ClassLoadingConfig(io.quarkus.deployment.configuration.ClassLoadingConfig) ExecutorService(java.util.concurrent.ExecutorService) ClassPathElement(io.quarkus.bootstrap.classloading.ClassPathElement) ApplicationArchivesBuildItem(io.quarkus.deployment.builditem.ApplicationArchivesBuildItem) Files(java.nio.file.Files) FileOutputStream(java.io.FileOutputStream) IOException(java.io.IOException) File(java.io.File) ConcurrentLinkedDeque(java.util.concurrent.ConcurrentLinkedDeque) ExecutionException(java.util.concurrent.ExecutionException) LaunchModeBuildItem(io.quarkus.deployment.builditem.LaunchModeBuildItem) BytecodeTransformerBuildItem(io.quarkus.deployment.builditem.BytecodeTransformerBuildItem) Collections(java.util.Collections) LiveReloadBuildItem(io.quarkus.deployment.builditem.LiveReloadBuildItem) Path(java.nio.file.Path) ArtifactKey(io.quarkus.maven.dependency.ArtifactKey) Set(java.util.Set) HashSet(java.util.HashSet) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) ResolvedDependency(io.quarkus.maven.dependency.ResolvedDependency) ArrayList(java.util.ArrayList) ApplicationModel(io.quarkus.bootstrap.model.ApplicationModel) GACT(io.quarkus.maven.dependency.GACT) TransformedClassesBuildItem(io.quarkus.deployment.builditem.TransformedClassesBuildItem) RemovedResourceBuildItem(io.quarkus.deployment.builditem.RemovedResourceBuildItem) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) HashSet(java.util.HashSet)

Example 2 with ClassLoadingConfig

use of io.quarkus.deployment.configuration.ClassLoadingConfig in project quarkus by quarkusio.

the class ClassTransformingBuildStep method handleClassTransformation.

@BuildStep
TransformedClassesBuildItem handleClassTransformation(List<BytecodeTransformerBuildItem> bytecodeTransformerBuildItems, ApplicationArchivesBuildItem appArchives, LiveReloadBuildItem liveReloadBuildItem, LaunchModeBuildItem launchModeBuildItem, ClassLoadingConfig classLoadingConfig, CurateOutcomeBuildItem curateOutcomeBuildItem, List<RemovedResourceBuildItem> removedResourceBuildItems, ArchiveRootBuildItem archiveRoot, LaunchModeBuildItem launchMode, PackageConfig packageConfig) throws ExecutionException, InterruptedException {
    if (bytecodeTransformerBuildItems.isEmpty() && classLoadingConfig.removedResources.isEmpty() && removedResourceBuildItems.isEmpty()) {
        return new TransformedClassesBuildItem(Collections.emptyMap());
    }
    final Map<String, List<BytecodeTransformerBuildItem>> bytecodeTransformers = new HashMap<>(bytecodeTransformerBuildItems.size());
    Set<String> noConstScanning = new HashSet<>();
    Map<String, Set<String>> constScanning = new HashMap<>();
    Set<String> eager = new HashSet<>();
    Set<String> nonCacheable = new HashSet<>();
    Map<String, Integer> classReaderOptions = new HashMap<>();
    for (BytecodeTransformerBuildItem i : bytecodeTransformerBuildItems) {
        bytecodeTransformers.computeIfAbsent(i.getClassToTransform(), (h) -> new ArrayList<>()).add(i);
        if (i.getRequireConstPoolEntry() == null || i.getRequireConstPoolEntry().isEmpty()) {
            noConstScanning.add(i.getClassToTransform());
        } else {
            constScanning.computeIfAbsent(i.getClassToTransform(), (s) -> new HashSet<>()).addAll(i.getRequireConstPoolEntry());
        }
        if (i.isEager()) {
            eager.add(i.getClassToTransform());
        }
        if (!i.isCacheable()) {
            nonCacheable.add(i.getClassToTransform());
        }
        classReaderOptions.merge(i.getClassToTransform(), i.getClassReaderOptions(), // class reader options are bit flags (see org.objectweb.asm.ClassReader)
        (oldValue, newValue) -> oldValue | newValue);
    }
    QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader();
    Map<String, Path> transformedToArchive = new ConcurrentHashMap<>();
    // now copy all the contents to the runner jar
    // we also record if any additional archives needed transformation
    // when we copy these archives we will remove the problematic classes
    final ExecutorService executorPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    final ConcurrentLinkedDeque<Future<TransformedClassesBuildItem.TransformedClass>> transformed = new ConcurrentLinkedDeque<>();
    final Map<Path, Set<TransformedClassesBuildItem.TransformedClass>> transformedClassesByJar = new HashMap<>();
    ClassLoader transformCl = Thread.currentThread().getContextClassLoader();
    lastTransformers = new BiFunction<String, byte[], byte[]>() {

        @Override
        public byte[] apply(String className, byte[] originalBytes) {
            List<BytecodeTransformerBuildItem> classTransformers = bytecodeTransformers.get(className);
            if (classTransformers == null) {
                return originalBytes;
            }
            boolean continueOnFailure = classTransformers.stream().filter(a -> !a.isContinueOnFailure()).findAny().isEmpty();
            List<BiFunction<String, ClassVisitor, ClassVisitor>> visitors = classTransformers.stream().map(BytecodeTransformerBuildItem::getVisitorFunction).filter(Objects::nonNull).collect(Collectors.toList());
            List<BiFunction<String, byte[], byte[]>> preVisitFunctions = classTransformers.stream().map(BytecodeTransformerBuildItem::getInputTransformer).filter(Objects::nonNull).collect(Collectors.toList());
            ClassLoader old = Thread.currentThread().getContextClassLoader();
            try {
                Thread.currentThread().setContextClassLoader(transformCl);
                String classFileName = className.replace('.', '/') + ".class";
                List<ClassPathElement> archives = cl.getElementsWithResource(classFileName);
                if (!archives.isEmpty()) {
                    ClassPathElement classPathElement = archives.get(0);
                    byte[] classData = classPathElement.getResource(classFileName).getData();
                    Set<String> constValues = constScanning.get(className);
                    if (constValues != null && !noConstScanning.contains(className)) {
                        if (!ConstPoolScanner.constPoolEntryPresent(classData, constValues)) {
                            return originalBytes;
                        }
                    }
                    byte[] data = transformClass(className, visitors, classData, preVisitFunctions, classReaderOptions.getOrDefault(className, 0));
                    TransformedClassesBuildItem.TransformedClass transformedClass = new TransformedClassesBuildItem.TransformedClass(className, data, classFileName, eager.contains(className));
                    return transformedClass.getData();
                } else {
                    return originalBytes;
                }
            } catch (Throwable e) {
                if (continueOnFailure) {
                    if (log.isDebugEnabled()) {
                        log.errorf(e, "Failed to transform %s", className);
                    } else {
                        log.errorf("Failed to transform %s", className);
                    }
                    return originalBytes;
                } else {
                    throw e;
                }
            } finally {
                Thread.currentThread().setContextClassLoader(old);
            }
        }
    };
    try {
        for (Map.Entry<String, List<BytecodeTransformerBuildItem>> entry : bytecodeTransformers.entrySet()) {
            String className = entry.getKey();
            boolean cacheable = !nonCacheable.contains(className);
            if (cacheable && transformedClassesCache.containsKey(className)) {
                if (liveReloadBuildItem.getChangeInformation() != null) {
                    if (!liveReloadBuildItem.getChangeInformation().getChangedClasses().contains(className)) {
                        // we can use the cached transformation
                        handleTransformedClass(transformedToArchive, transformedClassesByJar, transformedClassesCache.get(className));
                        continue;
                    }
                }
            }
            String classFileName = className.replace('.', '/') + ".class";
            List<ClassPathElement> archives = cl.getElementsWithResource(classFileName);
            if (!archives.isEmpty()) {
                ClassPathElement classPathElement = archives.get(0);
                Path jar = classPathElement.getRoot();
                if (jar == null) {
                    log.warnf("Cannot transform %s as its containing application archive could not be found.", entry.getKey());
                    continue;
                }
                boolean continueOnFailure = entry.getValue().stream().filter(a -> !a.isContinueOnFailure()).findAny().isEmpty();
                List<BiFunction<String, ClassVisitor, ClassVisitor>> visitors = entry.getValue().stream().map(BytecodeTransformerBuildItem::getVisitorFunction).filter(Objects::nonNull).collect(Collectors.toList());
                List<BiFunction<String, byte[], byte[]>> preVisitFunctions = entry.getValue().stream().map(BytecodeTransformerBuildItem::getInputTransformer).filter(Objects::nonNull).collect(Collectors.toList());
                transformedToArchive.put(classFileName, jar);
                transformed.add(executorPool.submit(new Callable<TransformedClassesBuildItem.TransformedClass>() {

                    @Override
                    public TransformedClassesBuildItem.TransformedClass call() throws Exception {
                        ClassLoader old = Thread.currentThread().getContextClassLoader();
                        try {
                            byte[] classData = classPathElement.getResource(classFileName).getData();
                            Thread.currentThread().setContextClassLoader(transformCl);
                            Set<String> constValues = constScanning.get(className);
                            if (constValues != null && !noConstScanning.contains(className)) {
                                if (!ConstPoolScanner.constPoolEntryPresent(classData, constValues)) {
                                    return null;
                                }
                            }
                            byte[] data = transformClass(className, visitors, classData, preVisitFunctions, classReaderOptions.getOrDefault(className, 0));
                            TransformedClassesBuildItem.TransformedClass transformedClass = new TransformedClassesBuildItem.TransformedClass(className, data, classFileName, eager.contains(className));
                            if (cacheable && launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT && classData != null) {
                                transformedClassesCache.put(className, transformedClass);
                            }
                            return transformedClass;
                        } catch (Throwable e) {
                            if (continueOnFailure) {
                                if (log.isDebugEnabled()) {
                                    log.errorf(e, "Failed to transform %s", className);
                                } else {
                                    log.errorf("Failed to transform %s", className);
                                }
                                return null;
                            } else {
                                throw e;
                            }
                        } finally {
                            Thread.currentThread().setContextClassLoader(old);
                        }
                    }
                }));
            } else {
                log.warnf("Cannot transform %s as its containing application archive could not be found.", entry.getKey());
            }
        }
    } finally {
        executorPool.shutdown();
    }
    handleRemovedResources(classLoadingConfig, curateOutcomeBuildItem, transformedClassesByJar, removedResourceBuildItems);
    if (!transformed.isEmpty()) {
        for (Future<TransformedClassesBuildItem.TransformedClass> i : transformed) {
            final TransformedClassesBuildItem.TransformedClass res = i.get();
            if (res != null) {
                handleTransformedClass(transformedToArchive, transformedClassesByJar, res);
            }
        }
    }
    if (packageConfig.writeTransformedBytecodeToBuildOutput && (launchMode.getLaunchMode() == LaunchMode.NORMAL)) {
        for (Path path : archiveRoot.getRootDirectories()) {
            copyTransformedClasses(path, transformedClassesByJar.get(path));
        }
    }
    return new TransformedClassesBuildItem(transformedClassesByJar);
}
Also used : TransformedClassesBuildItem(io.quarkus.deployment.builditem.TransformedClassesBuildItem) QuarkusClassWriter(io.quarkus.deployment.QuarkusClassWriter) BiFunction(java.util.function.BiFunction) Future(java.util.concurrent.Future) ArchiveRootBuildItem(io.quarkus.deployment.builditem.ArchiveRootBuildItem) Map(java.util.Map) QuarkusClassVisitor(io.quarkus.deployment.QuarkusClassVisitor) Path(java.nio.file.Path) ClassVisitor(org.objectweb.asm.ClassVisitor) Collection(java.util.Collection) StandardOpenOption(java.nio.file.StandardOpenOption) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) BootstrapDebug(io.quarkus.bootstrap.BootstrapDebug) QuarkusClassLoader(io.quarkus.bootstrap.classloading.QuarkusClassLoader) Collectors(java.util.stream.Collectors) Executors(java.util.concurrent.Executors) RemovedResourceBuildItem(io.quarkus.deployment.builditem.RemovedResourceBuildItem) PackageConfig(io.quarkus.deployment.pkg.PackageConfig) Objects(java.util.Objects) List(java.util.List) ApplicationModel(io.quarkus.bootstrap.model.ApplicationModel) ClassReader(org.objectweb.asm.ClassReader) GACT(io.quarkus.maven.dependency.GACT) ConstPoolScanner(io.quarkus.deployment.index.ConstPoolScanner) ResolvedDependency(io.quarkus.maven.dependency.ResolvedDependency) ClassWriter(org.objectweb.asm.ClassWriter) Logger(org.jboss.logging.Logger) LaunchMode(io.quarkus.runtime.LaunchMode) HashMap(java.util.HashMap) Callable(java.util.concurrent.Callable) CurateOutcomeBuildItem(io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) BuildStep(io.quarkus.deployment.annotations.BuildStep) ArtifactKey(io.quarkus.maven.dependency.ArtifactKey) ClassLoadingConfig(io.quarkus.deployment.configuration.ClassLoadingConfig) ExecutorService(java.util.concurrent.ExecutorService) ClassPathElement(io.quarkus.bootstrap.classloading.ClassPathElement) ApplicationArchivesBuildItem(io.quarkus.deployment.builditem.ApplicationArchivesBuildItem) Files(java.nio.file.Files) FileOutputStream(java.io.FileOutputStream) IOException(java.io.IOException) File(java.io.File) ConcurrentLinkedDeque(java.util.concurrent.ConcurrentLinkedDeque) ExecutionException(java.util.concurrent.ExecutionException) LaunchModeBuildItem(io.quarkus.deployment.builditem.LaunchModeBuildItem) BytecodeTransformerBuildItem(io.quarkus.deployment.builditem.BytecodeTransformerBuildItem) Collections(java.util.Collections) LiveReloadBuildItem(io.quarkus.deployment.builditem.LiveReloadBuildItem) Set(java.util.Set) HashSet(java.util.HashSet) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) QuarkusClassVisitor(io.quarkus.deployment.QuarkusClassVisitor) ClassVisitor(org.objectweb.asm.ClassVisitor) ClassPathElement(io.quarkus.bootstrap.classloading.ClassPathElement) Callable(java.util.concurrent.Callable) QuarkusClassLoader(io.quarkus.bootstrap.classloading.QuarkusClassLoader) List(java.util.List) ArrayList(java.util.ArrayList) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) BytecodeTransformerBuildItem(io.quarkus.deployment.builditem.BytecodeTransformerBuildItem) HashSet(java.util.HashSet) Path(java.nio.file.Path) QuarkusClassLoader(io.quarkus.bootstrap.classloading.QuarkusClassLoader) ConcurrentLinkedDeque(java.util.concurrent.ConcurrentLinkedDeque) TransformedClassesBuildItem(io.quarkus.deployment.builditem.TransformedClassesBuildItem) BiFunction(java.util.function.BiFunction) ExecutorService(java.util.concurrent.ExecutorService) Objects(java.util.Objects) Future(java.util.concurrent.Future) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) BuildStep(io.quarkus.deployment.annotations.BuildStep)

Aggregations

BootstrapDebug (io.quarkus.bootstrap.BootstrapDebug)2 ClassPathElement (io.quarkus.bootstrap.classloading.ClassPathElement)2 QuarkusClassLoader (io.quarkus.bootstrap.classloading.QuarkusClassLoader)2 ApplicationModel (io.quarkus.bootstrap.model.ApplicationModel)2 QuarkusClassVisitor (io.quarkus.deployment.QuarkusClassVisitor)2 QuarkusClassWriter (io.quarkus.deployment.QuarkusClassWriter)2 BuildStep (io.quarkus.deployment.annotations.BuildStep)2 ApplicationArchivesBuildItem (io.quarkus.deployment.builditem.ApplicationArchivesBuildItem)2 ArchiveRootBuildItem (io.quarkus.deployment.builditem.ArchiveRootBuildItem)2 BytecodeTransformerBuildItem (io.quarkus.deployment.builditem.BytecodeTransformerBuildItem)2 LaunchModeBuildItem (io.quarkus.deployment.builditem.LaunchModeBuildItem)2 LiveReloadBuildItem (io.quarkus.deployment.builditem.LiveReloadBuildItem)2 RemovedResourceBuildItem (io.quarkus.deployment.builditem.RemovedResourceBuildItem)2 TransformedClassesBuildItem (io.quarkus.deployment.builditem.TransformedClassesBuildItem)2 ClassLoadingConfig (io.quarkus.deployment.configuration.ClassLoadingConfig)2 ConstPoolScanner (io.quarkus.deployment.index.ConstPoolScanner)2 PackageConfig (io.quarkus.deployment.pkg.PackageConfig)2 CurateOutcomeBuildItem (io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem)2 ArtifactKey (io.quarkus.maven.dependency.ArtifactKey)2 GACT (io.quarkus.maven.dependency.GACT)2