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());
}
}
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);
}
Aggregations