use of io.quarkus.deployment.builditem.TransformedClassesBuildItem in project quarkus by quarkusio.
the class JarResultBuildStep method handleAppCDSSupportFileGeneration.
// the idea here is to just dump the class names of the generated and transformed classes into a file
// that is read at runtime when AppCDS generation is requested
private void handleAppCDSSupportFileGeneration(TransformedClassesBuildItem transformedClasses, List<GeneratedClassBuildItem> generatedClasses, AppCDSRequestedBuildItem appCDS) throws IOException {
Path appCDsDir = appCDS.getAppCDSDir();
Path generatedClassesFile = appCDsDir.resolve("generatedAndTransformed.lst");
try (BufferedWriter writer = Files.newBufferedWriter(generatedClassesFile, StandardOpenOption.CREATE)) {
StringBuilder classes = new StringBuilder();
for (GeneratedClassBuildItem generatedClass : generatedClasses) {
classes.append(generatedClass.getName().replace('/', '.')).append(System.lineSeparator());
}
for (Set<TransformedClassesBuildItem.TransformedClass> transformedClassesSet : transformedClasses.getTransformedClassesByJar().values()) {
for (TransformedClassesBuildItem.TransformedClass transformedClass : transformedClassesSet) {
if (transformedClass.getData() != null) {
classes.append(transformedClass.getFileName().replace('/', '.').replace(".class", "")).append(System.lineSeparator());
}
}
}
if (classes.length() != 0) {
writer.write(classes.toString());
}
}
}
use of io.quarkus.deployment.builditem.TransformedClassesBuildItem in project quarkus by quarkusio.
the class StartupActionImpl method extractTransformers.
private Map<String, byte[]> extractTransformers(Set<String> eagerClasses) {
Map<String, byte[]> ret = new HashMap<>();
TransformedClassesBuildItem transformers = buildResult.consume(TransformedClassesBuildItem.class);
for (Set<TransformedClassesBuildItem.TransformedClass> i : transformers.getTransformedClassesByJar().values()) {
for (TransformedClassesBuildItem.TransformedClass clazz : i) {
if (clazz.getData() != null) {
ret.put(clazz.getFileName(), clazz.getData());
if (clazz.isEager()) {
eagerClasses.add(clazz.getClassName());
}
}
}
}
return ret;
}
use of io.quarkus.deployment.builditem.TransformedClassesBuildItem 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);
}
use of io.quarkus.deployment.builditem.TransformedClassesBuildItem in project quarkus by quarkusio.
the class JarResultBuildStep method copyCommonContent.
private void copyCommonContent(FileSystem runnerZipFs, Map<String, List<byte[]>> concatenatedEntries, ApplicationArchivesBuildItem appArchives, TransformedClassesBuildItem transformedClassesBuildItem, List<GeneratedClassBuildItem> generatedClasses, List<GeneratedResourceBuildItem> generatedResources, Map<String, String> seen, Predicate<String> ignoredEntriesPredicate) throws IOException {
// }
for (Set<TransformedClassesBuildItem.TransformedClass> transformed : transformedClassesBuildItem.getTransformedClassesByJar().values()) {
for (TransformedClassesBuildItem.TransformedClass i : transformed) {
if (i.getData() != null) {
Path target = runnerZipFs.getPath(i.getFileName());
handleParent(runnerZipFs, i.getFileName(), seen);
try (final OutputStream out = wrapForJDK8232879(Files.newOutputStream(target))) {
out.write(i.getData());
}
seen.put(i.getFileName(), "Current Application");
}
}
}
for (GeneratedClassBuildItem i : generatedClasses) {
String fileName = i.getName().replace('.', '/') + ".class";
seen.put(fileName, "Current Application");
Path target = runnerZipFs.getPath(fileName);
handleParent(runnerZipFs, fileName, seen);
if (Files.exists(target)) {
continue;
}
try (final OutputStream os = wrapForJDK8232879(Files.newOutputStream(target))) {
os.write(i.getClassData());
}
}
for (GeneratedResourceBuildItem i : generatedResources) {
if (ignoredEntriesPredicate.test(i.getName())) {
continue;
}
Path target = runnerZipFs.getPath(i.getName());
handleParent(runnerZipFs, i.getName(), seen);
if (Files.exists(target)) {
continue;
}
if (i.getName().startsWith("META-INF/services/")) {
concatenatedEntries.computeIfAbsent(i.getName(), (u) -> new ArrayList<>()).add(i.getClassData());
} else {
try (final OutputStream os = wrapForJDK8232879(Files.newOutputStream(target))) {
os.write(i.getClassData());
}
}
}
copyFiles(appArchives.getRootArchive(), runnerZipFs, concatenatedEntries, ignoredEntriesPredicate);
for (Map.Entry<String, List<byte[]>> entry : concatenatedEntries.entrySet()) {
try (final OutputStream os = wrapForJDK8232879(Files.newOutputStream(runnerZipFs.getPath(entry.getKey())))) {
// TODO: Handle merging of XMLs
for (byte[] i : entry.getValue()) {
os.write(i);
os.write('\n');
}
}
}
}
use of io.quarkus.deployment.builditem.TransformedClassesBuildItem in project quarkus by quarkusio.
the class JarResultBuildStep method copyDependency.
private void copyDependency(Set<ArtifactKey> parentFirstArtifacts, OutputTargetBuildItem outputTargetBuildItem, Map<ArtifactKey, List<Path>> runtimeArtifacts, Path libDir, Path baseLib, List<Path> jars, boolean allowParentFirst, StringBuilder classPath, ResolvedDependency appDep, TransformedClassesBuildItem transformedClasses, Set<ArtifactKey> removedDeps) throws IOException {
// and are not part of the optional dependencies to include
if (!includeAppDep(appDep, outputTargetBuildItem.getIncludedOptionalDependencies(), removedDeps)) {
return;
}
if (runtimeArtifacts.containsKey(appDep.getKey())) {
return;
}
for (Path resolvedDep : appDep.getResolvedPaths()) {
final String fileName = appDep.getGroupId() + "." + resolvedDep.getFileName();
final Path targetPath;
if (allowParentFirst && parentFirstArtifacts.contains(appDep.getKey())) {
targetPath = baseLib.resolve(fileName);
classPath.append(" ").append(LIB).append("/").append(BOOT_LIB).append("/").append(fileName);
} else {
targetPath = libDir.resolve(fileName);
jars.add(targetPath);
}
runtimeArtifacts.computeIfAbsent(appDep.getKey(), (s) -> new ArrayList<>(1)).add(targetPath);
if (Files.isDirectory(resolvedDep)) {
// This case can happen when we are building a jar from inside the Quarkus repository
// and Quarkus Bootstrap's localProjectDiscovery has been set to true. In such a case
// the non-jar dependencies are the Quarkus dependencies picked up on the file system
packageClasses(resolvedDep, targetPath);
} else {
Set<TransformedClassesBuildItem.TransformedClass> transformedFromThisArchive = transformedClasses.getTransformedClassesByJar().get(resolvedDep);
Set<String> removedFromThisArchive = new HashSet<>();
if (transformedFromThisArchive != null) {
for (TransformedClassesBuildItem.TransformedClass i : transformedFromThisArchive) {
if (i.getData() == null) {
removedFromThisArchive.add(i.getFileName());
}
}
}
if (removedFromThisArchive.isEmpty()) {
Files.copy(resolvedDep, targetPath, StandardCopyOption.REPLACE_EXISTING);
} else {
// we have removed classes, we need to handle them correctly
filterZipFile(resolvedDep, targetPath, removedFromThisArchive);
}
}
}
}
Aggregations