Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Created May 6, 2022 07:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomasdarimont/adf602cabf3f5771892cc3549b429ea4 to your computer and use it in GitHub Desktop.
Save thomasdarimont/adf602cabf3f5771892cc3549b429ea4 to your computer and use it in GitHub Desktop.
PoC for Quarkus Jar Patcher
package demo;
import io.quarkus.bootstrap.app.AdditionalDependency;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.model.AppArtifactKey;
import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.bootstrap.model.DefaultApplicationModel;
import io.quarkus.bootstrap.model.MutableJarApplicationModel;
import io.quarkus.deployment.mutability.ReaugmentTask;
import io.quarkus.deployment.pkg.steps.JarResultBuildStep;
import io.quarkus.maven.dependency.ArtifactCoords;
import io.quarkus.maven.dependency.ArtifactKey;
import io.quarkus.maven.dependency.ResolvedArtifactDependency;
import io.quarkus.maven.dependency.ResolvedDependency;
import io.quarkus.maven.dependency.ResolvedDependencyBuilder;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static io.quarkus.deployment.pkg.steps.JarResultBuildStep.BUILD_SYSTEM_PROPERTIES;
import static io.quarkus.deployment.pkg.steps.JarResultBuildStep.DEPLOYMENT_LIB;
import static io.quarkus.deployment.pkg.steps.JarResultBuildStep.LIB;
import static io.quarkus.deployment.pkg.steps.JarResultBuildStep.QUARKUS;
public class Main {
public static void main(String[] args) throws Exception {
/*
String appRoot = "/home/tom/dev/playground/keycloak/keycloak.x-18.0.0/lib";
var appModelLocation = appRoot + "/lib/deployment/appmodel.dat";
try (var ois = new ObjectInputStream(new FileInputStream(appModelLocation))) {
var appModel = (MutableJarApplicationModel) ois.readObject();
System.out.println(appModel);
}
*/
main(Path.of(appRoot));
}
public static void main(Path appRoot) throws Exception {
Path deploymentLib = appRoot.resolve(LIB).resolve(DEPLOYMENT_LIB);
Path buildSystemProps = appRoot.resolve(QUARKUS).resolve(BUILD_SYSTEM_PROPERTIES);
try (ObjectInputStream in = new ObjectInputStream(
Files.newInputStream(deploymentLib.resolve(JarResultBuildStep.APPMODEL_DAT)))) {
Properties buildSystemProperties = new Properties();
try (InputStream buildIn = Files
.newInputStream(buildSystemProps)) {
buildSystemProperties.load(buildIn);
}
MutableJarApplicationModel appModel = (MutableJarApplicationModel) in.readObject();
List<AdditionalDependency> additional = new ArrayList<>();
if (appModel.getUserProvidersDirectory() != null) {
System.setProperty("quarkus.package.user-providers-directory", appModel.getUserProvidersDirectory()); //bit of a hack, but keeps things simple
try (Stream<Path> files = Files.list(appRoot.resolve(appModel.getUserProvidersDirectory()))) {
files.forEach(new Consumer<Path>() {
@Override
public void accept(Path path) {
if (path.toString().endsWith(".jar")) {
additional.add(new AdditionalDependency(path, false, true));
}
}
});
}
}
List<AdditionalDependency> patches = new ArrayList<>();
try (Stream<Path> files = Files.list(appRoot.resolve("lib/patch"))) {
files.forEach(new Consumer<Path>() {
@Override
public void accept(Path path) {
if (path.toString().endsWith(".jar")) {
patches.add(new AdditionalDependency(path, false, true));
}
}
});
}
final ApplicationModel existingModel = appModel.getAppModel(appRoot);
Collection<ResolvedDependency> dependencies = existingModel.getDependencies();
// Field depsField = DefaultApplicationModel.class.getDeclaredField("dependencies");
// depsField.setAccessible(true);
// var foo = depsField.get(existingModel);
for(var dep : new ArrayList<>(dependencies)) {
ResolvedArtifactDependency resolvedDep = (ResolvedArtifactDependency) dep;
//System.out.println(dep);
String libraryFileName = resolvedDep.getResolvedPaths().getSinglePath().toFile().getName();
String jarPrefix = resolvedDep.getGroupId() + "." + resolvedDep.getArtifactId();
for (var patch : patches) {
String patchFileName = patch.getResolvedPaths().getSinglePath().toFile().getName();
if (patchFileName.startsWith(jarPrefix)) {
System.out.printf("Patching %s with %s%n", libraryFileName, patchFileName);
dependencies.remove(resolvedDep);
var patchVersion = patchFileName.substring(patchFileName.lastIndexOf('-'), patchFileName.lastIndexOf('.'));
var resolvedPatchDependency = new ResolvedArtifactDependency(
ResolvedArtifactDependency.pom(resolvedDep.getGroupId(), resolvedDep.getArtifactId(), patchVersion),
patch.getResolvedPaths()
);
dependencies.add(resolvedPatchDependency);
}
}
}
var appModelLocation = appRoot.resolve("lib/deployment/appmodel.dat");
// existingModel.getDependencies().stream().map(f -> AppArtifactKey.fromString(f.^))
MutableJarApplicationModel newAppModel = new MutableJarApplicationModel(appModel.getBaseName(), null , existingModel, appModel.getUserProvidersDirectory(), appRoot.toFile().getAbsolutePath());
try (var out = new ObjectOutputStream(Files.newOutputStream(appModelLocation))) {
out.writeObject(newAppModel);
}
System.setProperty("quarkus.package.type", "mutable-jar");
try (CuratedApplication bootstrap = QuarkusBootstrap.builder()
.setAppArtifact(existingModel.getAppArtifact())
.setExistingModel(existingModel)
.setRebuild(true)
.setBuildSystemProperties(buildSystemProperties)
.setBaseName(appModel.getBaseName())
.addAdditionalApplicationArchives(additional)
.setApplicationRoot(existingModel.getAppArtifact().getResolvedPaths().getSinglePath())
.setTargetDirectory(appRoot.getParent())
.setBaseClassLoader(ReaugmentTask.class.getClassLoader())
.build().bootstrap()) {
bootstrap.createAugmentor().createProductionApplication();
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>keycloakx-patcher</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<quarkus.version>2.7.5.Final</quarkus.version>
</properties>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-app-model</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-core</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>
</dependencies>
</project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment