The big ClassLoader change

This changes the way Quarkus ClassLoading works,
to allow for isolated class loaders.

It also unifies how Quarkus is launched, so every
different mode we support uses the same mechanism
for both curation and launch.

Tests are now run in an isolated ClassLoader, which
means that a proxy is created that runs the tests
from within the isolated ClassLoader. This currently
has a quirk where @BeforeAll methods are run twice,
which will be fixed in the next JUnit release. This
can be worked around by using @QuarkusBeforeAll.
This commit is contained in:
Stuart Douglas
2019-11-28 12:27:05 +11:00
committed by Guillaume Smet
parent dbeb73f5f4
commit b67491c1ff
281 changed files with 7529 additions and 5759 deletions

View File

@@ -256,6 +256,11 @@
<artifactId>quarkus-core</artifactId> <artifactId>quarkus-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-development-mode-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId> <artifactId>quarkus-arc</artifactId>

View File

@@ -117,11 +117,6 @@
<artifactId>quarkus-platform-descriptor-json</artifactId> <artifactId>quarkus-platform-descriptor-json</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-creator</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.freemarker</groupId> <groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId> <artifactId>freemarker</artifactId>

View File

@@ -32,5 +32,5 @@ steps:
goals: 'install' goals: 'install'
mavenOptions: $(MAVEN_OPTS) mavenOptions: $(MAVEN_OPTS)
jdkVersionOption: ${{ parameters.jdk }} jdkVersionOption: ${{ parameters.jdk }}
options: '-B --settings azure-mvn-settings.xml -Dnative-image.docker-build -Dtest-postgresql -Dtest-elasticsearch -Dtest-mysql -Dtest-dynamodb -Dtest-vault -Dtest-neo4j -Dno-format ${{ parameters.extra }}' options: '-e -B --settings azure-mvn-settings.xml -Dnative-image.docker-build -Dtest-postgresql -Dtest-elasticsearch -Dtest-mysql -Dtest-dynamodb -Dtest-vault -Dtest-neo4j -Dno-format ${{ parameters.extra }}'

View File

@@ -1,41 +0,0 @@
<?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">
<parent>
<artifactId>quarkus-build-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../../build-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>quarkus-creator</artifactId>
<name>Quarkus - Creator</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,22 +0,0 @@
package io.quarkus.creator;
/**
* Main application creator exception.
*
* @author Alexey Loubyansky
*/
public class AppCreatorException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public AppCreatorException(String message, Throwable cause) {
super(message, cause);
}
public AppCreatorException(String message) {
super(message);
}
}

View File

@@ -1,282 +0,0 @@
package io.quarkus.creator;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.bootstrap.resolver.maven.workspace.ModelUtils;
import io.quarkus.bootstrap.util.IoUtils;
import io.quarkus.creator.curator.CurateOutcome;
import io.quarkus.creator.curator.Curator;
/**
*
* @author Alexey Loubyansky
*/
public class CuratedApplicationCreator implements AutoCloseable {
/**
* Returns an instance of a builder that can be used to initialize an application creator.
*
* @return application creator builder
*/
public static Builder builder() {
return new Builder();
}
private final AppModelResolver artifactResolver;
private final AppArtifact appArtifact;
private final Path workDir;
private boolean deleteTmpDir = true;
private final DependenciesOrigin depsOrigin;
private final VersionUpdate update;
private final VersionUpdateNumber updateNumber;
private final Path localRepo;
private final String baseName;
private CuratedApplicationCreator(Builder builder) {
this.artifactResolver = builder.modelResolver;
this.appArtifact = builder.appArtifact;
this.depsOrigin = builder.depsOrigin;
this.update = builder.update;
this.updateNumber = builder.updateNumber;
this.localRepo = builder.localRepo;
boolean del;
if (builder.workDir != null) {
del = false;
this.workDir = builder.workDir;
} else {
try {
this.workDir = Files.createTempDirectory("quarkus-build");
} catch (IOException e) {
throw new RuntimeException(e);
}
del = true;
}
deleteTmpDir = del;
String finalName = builder.baseName;
if (finalName == null && appArtifact != null && appArtifact.getPath() != null) {
final String name = toUri(appArtifact.getPath().getFileName());
int i = name.lastIndexOf('.');
if (i > 0) {
finalName = name.substring(0, i);
}
}
this.baseName = finalName;
}
/**
* Work directory used by the phases to store various data.
*
* @return work dir
*/
public Path getWorkDir() {
return workDir;
}
/**
* Artifact resolver which can be used to resolve application dependencies.
*
* @return artifact resolver for application dependencies
*/
public AppModelResolver getArtifactResolver() {
return artifactResolver;
}
/**
* User application JAR file
*
* @return user application JAR file
* @throws AppCreatorException
*/
public AppArtifact getAppArtifact() throws AppCreatorException {
return appArtifact;
}
public DependenciesOrigin getDepsOrigin() {
return depsOrigin;
}
public VersionUpdate getUpdate() {
return update;
}
public VersionUpdateNumber getUpdateNumber() {
return updateNumber;
}
public Path getLocalRepo() {
return localRepo;
}
public String getBaseName() {
return baseName;
}
/**
* Creates a directory from a path relative to the creator's work directory.
*
* @param names represents a path relative to the creator's work directory
* @return created directory
* @throws AppCreatorException in case the directory could not be created
*/
public Path createWorkDir(String... names) throws AppCreatorException {
final Path p = getWorkPath(names);
try {
Files.createDirectories(p);
} catch (IOException e) {
throw new AppCreatorException("Failed to create directory " + p, e);
}
return p;
}
public <T> T runTask(CuratedTask<T> task) throws AppCreatorException {
CurateOutcome curateResult = Curator.run(this);
return task.run(curateResult, this);
}
/**
* Creates a path object from path relative to the creator's work directory.
*
* @param names represents a path relative to the creator's work directory
* @return path object
*/
public Path getWorkPath(String... names) {
if (names.length == 0) {
return workDir;
}
Path p = workDir;
for (String name : names) {
p = p.resolve(name);
}
return p;
}
@Override
public void close() {
if (deleteTmpDir) {
IoUtils.recursiveDelete(workDir);
}
}
private static StringBuilder toUri(StringBuilder b, Path path, int seg) {
b.append(path.getName(seg));
if (seg < path.getNameCount() - 1) {
b.append('/');
toUri(b, path, seg + 1);
}
return b;
}
private static String toUri(Path path) {
if (path.isAbsolute()) {
return path.toUri().getPath();
} else if (path.getNameCount() == 0) {
return "";
} else {
return toUri(new StringBuilder(), path, 0).toString();
}
}
public static class Builder {
public String baseName;
private AppArtifact appArtifact;
private Path workDir;
private AppModelResolver modelResolver;
private DependenciesOrigin depsOrigin = DependenciesOrigin.APPLICATION;
private VersionUpdate update = VersionUpdate.NONE;
private VersionUpdateNumber updateNumber = VersionUpdateNumber.MICRO;
private Path localRepo;
private Builder() {
}
public Builder setBaseName(String baseName) {
this.baseName = baseName;
return this;
}
/**
* Work directory used to store various data when processing phases.
* If it's not set by the user, a temporary directory will be created
* which will be automatically removed after the application have passed
* through all the phases necessary to produce the requested outcome.
*
* @param dir work directory
* @return this AppCreator instance
*/
public Builder setWorkDir(Path dir) {
this.workDir = dir;
return this;
}
/**
* Application model resolver which should be used to resolve
* application dependencies.
* If artifact resolver is not set by the user, the default one will be
* created based on the user Maven settings.xml file.
*
* @param resolver artifact resolver
*/
public Builder setModelResolver(AppModelResolver resolver) {
this.modelResolver = resolver;
return this;
}
/**
*
* @param appArtifact application JAR
* @throws AppCreatorException
*/
public Builder setAppArtifact(AppArtifact appArtifact) {
this.appArtifact = appArtifact;
return this;
}
public Builder setAppArtifact(Path path) throws AppCreatorException {
try {
this.appArtifact = ModelUtils.resolveAppArtifact(path);
this.appArtifact.setPath(path);
} catch (IOException e) {
throw new AppCreatorException("Unable to resolve app artifact " + path);
}
return this;
}
public Builder setDepsOrigin(DependenciesOrigin depsOrigin) {
this.depsOrigin = depsOrigin;
return this;
}
public Builder setUpdate(VersionUpdate update) {
this.update = update;
return this;
}
public Builder setUpdateNumber(VersionUpdateNumber updateNumber) {
this.updateNumber = updateNumber;
return this;
}
public Builder setLocalRepo(Path localRepo) {
this.localRepo = localRepo;
return this;
}
/**
* Builds an instance of an application creator.
*
* @return an instance of an application creator
* @throws AppCreatorException in case of a failure
*/
public CuratedApplicationCreator build() throws AppCreatorException {
return new CuratedApplicationCreator(this);
}
}
}

View File

@@ -1,18 +0,0 @@
package io.quarkus.creator;
import io.quarkus.creator.curator.CurateOutcome;
/**
* A task that requires a curated application to run
*/
public interface CuratedTask<T> {
/**
* Runs the curated task
*
* @param outcome The curate outcome
* @return The result, possibly null
*/
T run(CurateOutcome outcome, CuratedApplicationCreator creator) throws AppCreatorException;
}

View File

@@ -1,235 +0,0 @@
package io.quarkus.creator.curator;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Exclusion;
import org.apache.maven.model.Model;
import org.apache.maven.model.Repository;
import org.jboss.logging.Logger;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.bootstrap.resolver.AppModelResolverException;
import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver;
import io.quarkus.bootstrap.resolver.maven.workspace.ModelUtils;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
/**
*
* @author Alexey Loubyansky
*/
public class CurateOutcome {
static final String CREATOR_APP_GROUP_ID = "creator.app.groupId";
static final String CREATOR_APP_ARTIFACT_ID = "creator.app.artifactId";
static final String CREATOR_APP_CLASSIFIER = "creator.app.classifier";
static final String CREATOR_APP_TYPE = "creator.app.type";
static final String CREATOR_APP_VERSION = "creator.app.version";
private static final Logger log = Logger.getLogger(CurateOutcome.class);
public static class Builder {
private AppArtifact stateArtifact;
private AppModel appModel;
private List<AppDependency> updatedDeps = Collections.emptyList();
private AppModelResolver resolver;
private List<Repository> artifactRepos = Collections.emptyList();
private boolean loadedFromState;
private Builder() {
}
public Builder setStateArtifact(AppArtifact stateArtifact) {
this.stateArtifact = stateArtifact;
return this;
}
public Builder setAppModelResolver(AppModelResolver resolver) {
this.resolver = resolver;
return this;
}
public Builder setAppModel(AppModel appModel) {
this.appModel = appModel;
return this;
}
public Builder setUpdatedDeps(List<AppDependency> deps) {
this.updatedDeps = deps;
return this;
}
public void setArtifactRepos(List<Repository> artifactRepos) {
this.artifactRepos = artifactRepos;
}
public void setLoadedFromState() {
this.loadedFromState = true;
}
public CurateOutcome build() {
return new CurateOutcome(this);
}
}
public static Builder builder() {
return new Builder();
}
protected final AppArtifact stateArtifact;
protected final AppModel initialModel;
protected final List<AppDependency> updatedDeps;
protected final AppModelResolver resolver;
protected final List<Repository> artifactRepos;
protected final boolean loadedFromState;
protected AppModel effectiveModel;
protected boolean persisted;
public CurateOutcome(Builder builder) {
this.stateArtifact = builder.stateArtifact;
this.initialModel = builder.appModel;
this.updatedDeps = builder.updatedDeps.isEmpty() ? builder.updatedDeps
: Collections.unmodifiableList(builder.updatedDeps);
this.resolver = builder.resolver;
this.artifactRepos = builder.artifactRepos;
this.loadedFromState = builder.loadedFromState;
}
public AppModelResolver getArtifactResolver() {
return resolver;
}
public AppArtifact getAppArtifact() {
return initialModel.getAppArtifact();
}
public AppModel getInitialModel() {
return initialModel;
}
public boolean hasUpdatedDeps() {
return !updatedDeps.isEmpty();
}
public List<AppDependency> getUpdatedDeps() {
return updatedDeps;
}
public AppModel getEffectiveModel() throws AppCreatorException {
if (effectiveModel != null) {
return effectiveModel;
}
if (updatedDeps.isEmpty()) {
return effectiveModel = initialModel;
}
try {
return effectiveModel = resolver.resolveModel(initialModel.getAppArtifact(), updatedDeps);
} catch (AppModelResolverException e) {
throw new AppCreatorException("Failed to resolve effective application dependencies", e);
}
}
public boolean isPersisted() {
return persisted;
}
public void persist(CuratedApplicationCreator creator) throws AppCreatorException {
if (persisted || loadedFromState && !hasUpdatedDeps()) {
log.info("Skipping provisioning state persistence");
return;
}
log.info("Persisting provisioning state");
final Path stateDir = creator.createWorkDir("state");
final Path statePom = stateDir.resolve("pom.xml");
final AppArtifact appArtifact = initialModel.getAppArtifact();
AppArtifact stateArtifact;
if (this.stateArtifact == null) {
stateArtifact = ModelUtils.getStateArtifact(appArtifact);
} else {
stateArtifact = new AppArtifact(this.stateArtifact.getGroupId(),
this.stateArtifact.getArtifactId(),
this.stateArtifact.getClassifier(),
this.stateArtifact.getType(),
String.valueOf(Long.valueOf(this.stateArtifact.getVersion()) + 1));
}
final Model model = new Model();
model.setModelVersion("4.0.0");
model.setGroupId(stateArtifact.getGroupId());
model.setArtifactId(stateArtifact.getArtifactId());
model.setPackaging(stateArtifact.getType());
model.setVersion(stateArtifact.getVersion());
model.addProperty(CREATOR_APP_GROUP_ID, appArtifact.getGroupId());
model.addProperty(CREATOR_APP_ARTIFACT_ID, appArtifact.getArtifactId());
final String classifier = appArtifact.getClassifier();
if (!classifier.isEmpty()) {
model.addProperty(CREATOR_APP_CLASSIFIER, classifier);
}
model.addProperty(CREATOR_APP_TYPE, appArtifact.getType());
model.addProperty(CREATOR_APP_VERSION, appArtifact.getVersion());
final Dependency appDep = new Dependency();
appDep.setGroupId("${" + CREATOR_APP_GROUP_ID + "}");
appDep.setArtifactId("${" + CREATOR_APP_ARTIFACT_ID + "}");
if (!classifier.isEmpty()) {
appDep.setClassifier("${" + CREATOR_APP_CLASSIFIER + "}");
}
appDep.setType("${" + CREATOR_APP_TYPE + "}");
appDep.setVersion("${" + CREATOR_APP_VERSION + "}");
appDep.setScope("compile");
model.addDependency(appDep);
if (!updatedDeps.isEmpty()) {
for (AppDependency dep : getUpdatedDeps()) {
final AppArtifact depArtifact = dep.getArtifact();
final String groupId = depArtifact.getGroupId();
final Exclusion exclusion = new Exclusion();
exclusion.setGroupId(groupId);
exclusion.setArtifactId(depArtifact.getArtifactId());
appDep.addExclusion(exclusion);
final Dependency updateDep = new Dependency();
updateDep.setGroupId(groupId);
updateDep.setArtifactId(depArtifact.getArtifactId());
final String updateClassifier = depArtifact.getClassifier();
if (updateClassifier != null && !updateClassifier.isEmpty()) {
updateDep.setClassifier(updateClassifier);
}
updateDep.setType(depArtifact.getType());
updateDep.setVersion(depArtifact.getVersion());
updateDep.setScope(dep.getScope());
model.addDependency(updateDep);
}
}
/*
* if(!artifactRepos.isEmpty()) {
* for(Repository repo : artifactRepos) {
* model.addRepository(repo);
* }
* }
*/
try {
ModelUtils.persistModel(statePom, model);
((BootstrapAppModelResolver) resolver).install(stateArtifact, statePom);
} catch (Exception e) {
throw new AppCreatorException("Failed to persist application state artifact", e);
}
log.info("Persisted provisioning state as " + stateArtifact);
//ctx.getArtifactResolver().relink(stateArtifact, statePom);
}
}

View File

@@ -1,316 +0,0 @@
package io.quarkus.creator.curator;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Repository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.jboss.logging.Logger;
import io.quarkus.bootstrap.BootstrapConstants;
import io.quarkus.bootstrap.BootstrapDependencyProcessingException;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.bootstrap.resolver.AppModelResolverException;
import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.bootstrap.resolver.maven.workspace.ModelUtils;
import io.quarkus.bootstrap.util.ZipUtils;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.DependenciesOrigin;
import io.quarkus.creator.VersionUpdate;
/**
*
* @author Alexey Loubyansky
*/
public class Curator {
private static final Logger log = Logger.getLogger(Curator.class);
private static final Map<String, String> BANNED_DEPENDENCIES = createBannedDependenciesMap();
public static CurateOutcome run(CuratedApplicationCreator ctx) throws AppCreatorException {
log.debug("provideOutcome depsOrigin=" + ctx.getDepsOrigin() + ", versionUpdate=" + ctx.getUpdate()
+ ", versionUpdateNumber="
+ ctx.getUpdateNumber());
final AppArtifact appArtifact = ctx.getAppArtifact();
if (appArtifact == null) {
throw new AppCreatorException("Application artifact has not been provided");
}
Path appJar;
try {
appJar = ctx.getArtifactResolver().resolve(appArtifact);
} catch (AppModelResolverException e) {
throw new AppCreatorException("Failed to resolve artifact", e);
}
if (!Files.exists(appJar)) {
throw new AppCreatorException("Application " + appJar + " does not exist on disk");
}
final CurateOutcome.Builder outcome = CurateOutcome.builder();
AppModelResolver modelResolver = ctx.getArtifactResolver();
final AppModel initialDepsList;
try {
if (modelResolver == null) {
final BootstrapAppModelResolver bsResolver = new BootstrapAppModelResolver(
MavenArtifactResolver.builder()
.setRepoHome(ctx.getLocalRepo() == null ? ctx.getWorkPath("repo") : ctx.getLocalRepo())
.build());
bsResolver.relink(appArtifact, appJar);
final List<RemoteRepository> artifactRepos = bsResolver.resolveArtifactRepos(appArtifact);
if (!artifactRepos.isEmpty()) {
bsResolver.addRemoteRepositories(artifactRepos);
final List<Repository> modelRepos = new ArrayList<>(artifactRepos.size());
for (RemoteRepository repo : artifactRepos) {
final Repository modelRepo = new Repository();
modelRepo.setId(repo.getId());
modelRepo.setUrl(repo.getUrl());
modelRepo.setLayout(repo.getContentType());
RepositoryPolicy policy = repo.getPolicy(true);
if (policy != null) {
modelRepo.setSnapshots(toMavenRepoPolicy(policy));
}
policy = repo.getPolicy(false);
if (policy != null) {
modelRepo.setReleases(toMavenRepoPolicy(policy));
}
modelRepos.add(modelRepo);
}
outcome.setArtifactRepos(modelRepos);
}
modelResolver = bsResolver;
} else {
modelResolver.relink(appArtifact, appJar);
}
outcome.setAppModelResolver(modelResolver);
if (ctx.getDepsOrigin() == DependenciesOrigin.LAST_UPDATE) {
log.info("Looking for the state of the last update");
Path statePath = null;
try {
AppArtifact stateArtifact = ModelUtils.getStateArtifact(appArtifact);
final String latest = modelResolver.getLatestVersion(stateArtifact, null, false);
if (!stateArtifact.getVersion().equals(latest)) {
stateArtifact = new AppArtifact(stateArtifact.getGroupId(), stateArtifact.getArtifactId(),
stateArtifact.getClassifier(), stateArtifact.getType(), latest);
}
statePath = modelResolver.resolve(stateArtifact);
outcome.setStateArtifact(stateArtifact);
log.info("- located the state at " + statePath);
} catch (AppModelResolverException e) {
// for now let's assume this means artifact does not exist
// System.out.println(" no state found");
}
if (statePath != null) {
Model model;
try {
model = ModelUtils.readModel(statePath);
} catch (IOException e) {
throw new AppCreatorException("Failed to read application state " + statePath, e);
}
/*
* final Properties props = model.getProperties(); final String appGroupId =
* props.getProperty(CurateOutcome.CREATOR_APP_GROUP_ID); final String appArtifactId =
* props.getProperty(CurateOutcome.CREATOR_APP_ARTIFACT_ID); final String appClassifier =
* props.getProperty(CurateOutcome.CREATOR_APP_CLASSIFIER); final String appType =
* props.getProperty(CurateOutcome.CREATOR_APP_TYPE); final String appVersion =
* props.getProperty(CurateOutcome.CREATOR_APP_VERSION); final AppArtifact modelAppArtifact = new
* AppArtifact(appGroupId, appArtifactId, appClassifier, appType, appVersion);
*/
final List<Dependency> modelStateDeps = model.getDependencies();
final List<AppDependency> updatedDeps = new ArrayList<>(modelStateDeps.size());
final String groupIdProp = "${" + CurateOutcome.CREATOR_APP_GROUP_ID + "}";
for (Dependency modelDep : modelStateDeps) {
if (modelDep.getGroupId().equals(groupIdProp)) {
continue;
}
updatedDeps.add(new AppDependency(new AppArtifact(modelDep.getGroupId(), modelDep.getArtifactId(),
modelDep.getClassifier(), modelDep.getType(), modelDep.getVersion()), modelDep.getScope(),
modelDep.isOptional()));
}
initialDepsList = modelResolver.resolveModel(appArtifact, updatedDeps);
outcome.setLoadedFromState();
} else {
initialDepsList = modelResolver.resolveModel(appArtifact);
}
} else {
initialDepsList = modelResolver.resolveModel(appArtifact);
}
} catch (AppModelResolverException e) {
throw new AppCreatorException("Failed to resolve initial application dependencies", e);
}
outcome.setAppModel(initialDepsList);
log.debug("Checking for potential banned dependencies");
checkBannedDependencies(initialDepsList);
if (ctx.getUpdate() == VersionUpdate.NONE) {
return outcome.build();
}
log.info("Checking for available updates");
List<AppDependency> appDeps;
try {
appDeps = modelResolver.resolveUserDependencies(appArtifact, initialDepsList.getUserDependencies());
} catch (AppModelResolverException | BootstrapDependencyProcessingException e) {
throw new AppCreatorException("Failed to determine the list of dependencies to update", e);
}
final Iterator<AppDependency> depsI = appDeps.iterator();
while (depsI.hasNext()) {
final AppArtifact appDep = depsI.next().getArtifact();
if (!appDep.getType().equals(AppArtifact.TYPE_JAR)) {
depsI.remove();
continue;
}
final Path path = appDep.getPath();
if (Files.isDirectory(path)) {
if (!Files.exists(path.resolve(BootstrapConstants.DESCRIPTOR_PATH))) {
depsI.remove();
}
} else {
try (FileSystem artifactFs = ZipUtils.newFileSystem(path)) {
if (!Files.exists(artifactFs.getPath(BootstrapConstants.DESCRIPTOR_PATH))) {
depsI.remove();
}
} catch (IOException e) {
throw new AppCreatorException("Failed to open " + path, e);
}
}
}
final UpdateDiscovery ud = new DefaultUpdateDiscovery(modelResolver, ctx.getUpdateNumber());
List<AppDependency> availableUpdates = null;
int i = 0;
while (i < appDeps.size()) {
final AppDependency dep = appDeps.get(i++);
final AppArtifact depArtifact = dep.getArtifact();
final String updatedVersion = ctx.getUpdate() == VersionUpdate.NEXT ? ud.getNextVersion(depArtifact)
: ud.getLatestVersion(depArtifact);
if (updatedVersion == null || depArtifact.getVersion().equals(updatedVersion)) {
continue;
}
log.info(dep.getArtifact() + " -> " + updatedVersion);
if (availableUpdates == null) {
availableUpdates = new ArrayList<>();
}
availableUpdates.add(new AppDependency(new AppArtifact(depArtifact.getGroupId(), depArtifact.getArtifactId(),
depArtifact.getClassifier(), depArtifact.getType(), updatedVersion), dep.getScope()));
}
if (availableUpdates != null) {
outcome.setUpdatedDeps(availableUpdates);
return outcome.build();
} else {
log.info("- no updates available");
return outcome.build();
}
}
private static org.apache.maven.model.RepositoryPolicy toMavenRepoPolicy(RepositoryPolicy policy) {
final org.apache.maven.model.RepositoryPolicy mvnPolicy = new org.apache.maven.model.RepositoryPolicy();
mvnPolicy.setEnabled(policy.isEnabled());
mvnPolicy.setChecksumPolicy(policy.getChecksumPolicy());
mvnPolicy.setUpdatePolicy(policy.getUpdatePolicy());
return mvnPolicy;
}
private static void checkBannedDependencies(AppModel initialDepsList) {
List<String> detectedBannedDependencies = new ArrayList<>();
try {
for (AppDependency userDependency : initialDepsList.getUserDependencies()) {
String ga = userDependency.getArtifact().getGroupId() + ":" + userDependency.getArtifact().getArtifactId();
if (!"test".equals(userDependency.getScope()) && BANNED_DEPENDENCIES.containsKey(ga)) {
detectedBannedDependencies.add(ga);
}
}
} catch (BootstrapDependencyProcessingException e) {
// ignore this
}
if (!detectedBannedDependencies.isEmpty()) {
String warnMessage = detectedBannedDependencies.stream()
.sorted()
.map(d -> "\t- " + d + " should be replaced by " + BANNED_DEPENDENCIES.get(d))
.collect(Collectors.joining("\n"));
log.warnf(
"These dependencies are not recommended:%n" +
"%s%n" +
"You might end up with two different versions of the same classes or with an artifact you shouldn't have in your classpath.",
warnMessage);
}
}
private static Map<String, String> createBannedDependenciesMap() {
Map<String, String> bannedDependencies = new HashMap<>();
bannedDependencies.put("org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec",
"jakarta.annotation:jakarta.annotation-api");
bannedDependencies.put("org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec",
"jakarta.annotation:jakarta.annotation-api");
bannedDependencies.put("org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec",
"jakarta.transaction:jakarta.transaction-api");
bannedDependencies.put("org.jboss.spec.javax.transaction:jboss-transaction-api_1.3_spec",
"jakarta.transaction:jakarta.transaction-api");
bannedDependencies.put("org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec",
"jakarta.servlet:jakarta.servlet-api");
bannedDependencies.put("org.jboss.spec.javax.security.jacc:jboss-jacc-api_1.5_spec",
"jakarta.security.jacc:jakarta.security.jacc-api");
bannedDependencies.put("org.jboss.spec.javax.security.auth.message:jboss-jaspi-api_1.1_spec",
"jakarta.security.auth.message:jakarta.security.auth.message-api");
bannedDependencies.put("org.jboss.spec.javax.websocket:jboss-websocket-api_1.1_spec",
"jakarta.websocket:jakarta.websocket-api");
bannedDependencies.put("org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.2_spec",
"jakarta.interceptor:jakarta.interceptor-api");
bannedDependencies.put("javax.activation:activation", "com.sun.activation:jakarta.activation");
bannedDependencies.put("javax.activation:javax.activation-api", "jakarta.activation:jakarta.activation-api");
bannedDependencies.put("javax.annotation:javax.annotation-api", "jakarta.annotation:jakarta.annotation-api");
bannedDependencies.put("javax.enterprise:cdi-api", "jakarta.enterprise:jakarta.enterprise.cdi-api");
bannedDependencies.put("javax.inject:javax.inject", "jakarta.inject:jakarta.inject-api");
bannedDependencies.put("javax.json:javax.json-api", "jakarta.json:jakarta.json-api");
bannedDependencies.put("javax.json.bind:javax.json.bind-api", "jakarta.json.bind:jakarta.json.bind-api");
bannedDependencies.put("org.glassfish:javax.json", "org.glassfish:jakarta.json");
bannedDependencies.put("org.glassfish:javax.el", "org.glassfish:jakarta.el");
bannedDependencies.put("javax.persistence:javax.persistence-api", "jakarta.persistence:jakarta.persistence-api");
bannedDependencies.put("javax.persistence:persistence-api", "jakarta.persistence:jakarta.persistence-api");
bannedDependencies.put("javax.security.enterprise:javax.security.enterprise-api", "");
bannedDependencies.put("javax.servlet:servlet-api", "jakarta.servlet:jakarta.servlet-api");
bannedDependencies.put("javax.servlet:javax.servlet-api", "jakarta.servlet:jakarta.servlet-api");
bannedDependencies.put("javax.transaction:jta", "jakarta.transaction:jakarta.transaction-api");
bannedDependencies.put("javax.transaction:javax.transaction-api", "jakarta.transaction:jakarta.transaction-api");
bannedDependencies.put("javax.validation:validation-api", "jakarta.validation:jakarta.validation-api");
bannedDependencies.put("javax.xml.bind:jaxb-api", "org.jboss.spec.javax.xml.bind:jboss-jaxb-api_2.3_spec");
bannedDependencies.put("javax.websocket:javax.websocket-api", "jakarta.websocket:jakarta.websocket-api");
bannedDependencies.put("javax.ws.rs:javax.ws.rs-api", "org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_2.1_spec");
// for now, we use the JBoss API Spec artifacts for those two as that's what RESTEasy use
bannedDependencies.put("jakarta.xml.bind:jakarta.xml.bind-api",
"org.jboss.spec.javax.xml.bind:jboss-jaxb-api_2.3_spec");
bannedDependencies.put("jakarta.ws.rs:jakarta.ws.rs-api", "org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_2.1_spec");
return Collections.unmodifiableMap(bannedDependencies);
}
}

View File

@@ -1,19 +0,0 @@
package io.quarkus.creator.curator;
import java.util.List;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.creator.AppCreatorException;
/**
*
* @author Alexey Loubyansky
*/
public interface UpdateDiscovery {
List<String> listUpdates(AppArtifact artifact) throws AppCreatorException;
String getNextVersion(AppArtifact artifact) throws AppCreatorException;
String getLatestVersion(AppArtifact artifact) throws AppCreatorException;
}

View File

@@ -1,41 +0,0 @@
package io.quarkus.creator.phase.augment;
import java.util.List;
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
import io.quarkus.deployment.pkg.builditem.JarBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem;
/**
* Represents an outcome of {@link AugmentTask}
*
* @author Alexey Loubyansky
*/
public class AugmentOutcome {
private final List<ArtifactResultBuildItem> packageOutput;
private final JarBuildItem jar;
private final NativeImageBuildItem nativeImage;
public AugmentOutcome(List<ArtifactResultBuildItem> packageOutput, JarBuildItem thinJar,
NativeImageBuildItem nativeImage) {
this.packageOutput = packageOutput;
this.jar = thinJar;
this.nativeImage = nativeImage;
}
/**
* The result of building the application
*/
public List<ArtifactResultBuildItem> getPackageOutput() {
return packageOutput;
}
public JarBuildItem getJar() {
return jar;
}
public NativeImageBuildItem getNativeImage() {
return nativeImage;
}
}

View File

@@ -1,312 +0,0 @@
package io.quarkus.creator.phase.augment;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.CopyOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.function.Consumer;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.spi.ConfigBuilder;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.jboss.logging.Logger;
import io.quarkus.bootstrap.BootstrapDependencyProcessingException;
import io.quarkus.bootstrap.DefineClassVisibleURLClassLoader;
import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.bootstrap.util.IoUtils;
import io.quarkus.bootstrap.util.ZipUtils;
import io.quarkus.builder.BuildResult;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.CuratedTask;
import io.quarkus.creator.curator.CurateOutcome;
import io.quarkus.deployment.QuarkusAugmentor;
import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
import io.quarkus.deployment.builditem.MainClassBuildItem;
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
import io.quarkus.deployment.pkg.builditem.JarBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem;
import io.quarkus.runtime.configuration.ConfigUtils;
import io.quarkus.runtime.configuration.QuarkusConfigFactory;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;
/**
* This phase consumes {@link CurateOutcome} and processes
* user application and its dependency classes for phases that generate a runnable application.
*
* @author Alexey Loubyansky
*/
public class AugmentTask implements CuratedTask<AugmentOutcome> {
private static final Logger log = Logger.getLogger(AugmentTask.class);
private static final String META_INF = "META-INF";
private final Path outputDir;
private final Path appClassesDir;
private final Path configDir;
private final Properties buildSystemProperties;
private final Consumer<ConfigBuilder> configCustomizer;
public AugmentTask(Builder builder) {
outputDir = builder.outputDir;
this.appClassesDir = builder.appClassesDir;
this.configDir = builder.configDir;
this.buildSystemProperties = builder.buildSystemProperties;
this.configCustomizer = builder.configCustomizer;
}
@Override
public AugmentOutcome run(CurateOutcome appState, CuratedApplicationCreator ctx) throws AppCreatorException {
if (this.outputDir != null) {
IoUtils.mkdirs(outputDir);
}
Path outputDir = this.outputDir == null ? ctx.getWorkDir() : this.outputDir;
Path appClassesDir = this.appClassesDir == null ? outputDir.resolve("classes") : this.appClassesDir;
if (!Files.exists(appClassesDir)) {
final Path appJar = appState.getAppArtifact().getPath();
//manage project without src directory
if (appJar == null) {
try {
Files.createDirectory(appClassesDir);
} catch (IOException e) {
throw new AppCreatorException("Failed to create classes directory " + appClassesDir, e);
}
} else {
try {
ZipUtils.unzip(appJar, appClassesDir);
} catch (IOException e) {
throw new AppCreatorException("Failed to unzip " + appJar, e);
}
}
final Path metaInf = appClassesDir.resolve(META_INF);
IoUtils.recursiveDelete(metaInf.resolve("maven"));
IoUtils.recursiveDelete(metaInf.resolve("INDEX.LIST"));
IoUtils.recursiveDelete(metaInf.resolve("MANIFEST.MF"));
}
Path configDir;
if (this.configDir == null) {
//lets default to appClassesDir for now
configDir = appClassesDir;
} else {
configDir = this.configDir;
//if we use gradle we copy the configDir contents to appClassesDir
try {
if (Files.exists(this.configDir) && !Files.isSameFile(this.configDir, appClassesDir)) {
Files.walkFileTree(configDir,
new CopyDirVisitor(configDir, appClassesDir, StandardCopyOption.REPLACE_EXISTING));
}
} catch (IOException e) {
throw new AppCreatorException("Failed while copying files from " + configDir + " to " + appClassesDir, e);
}
}
//first lets look for some config, as it is not on the current class path
//and we need to load it to run the build process
Path configPath = configDir.resolve("application.properties");
SmallRyeConfigBuilder configBuilder = ConfigUtils.configBuilder(false);
if (Files.exists(configPath)) {
try {
configBuilder.withSources(new PropertiesConfigSource(configPath.toUri().toURL()));
} catch (IOException e) {
throw new IllegalArgumentException("Failed to convert config URL", e);
}
}
if (configCustomizer != null) {
configCustomizer.accept(configBuilder);
}
final SmallRyeConfig config = configBuilder.build();
QuarkusConfigFactory.setConfig(config);
final ConfigProviderResolver cpr = ConfigProviderResolver.instance();
final Config existing = cpr.getConfig();
if (existing != config) {
cpr.releaseConfig(existing);
// subsequent calls will get the new config
}
final AppModelResolver depResolver = appState.getArtifactResolver();
List<AppDependency> appDeps;
try {
appDeps = appState.getEffectiveModel().getAllDependencies();
} catch (BootstrapDependencyProcessingException e) {
throw new AppCreatorException("Failed to resolve application build classpath", e);
}
URLClassLoader runnerClassLoader = null;
try {
// we need to make sure all the deployment artifacts are on the class path
final List<URL> cpUrls = new ArrayList<>(appDeps.size() + 1);
cpUrls.add(appClassesDir.toUri().toURL());
for (AppDependency appDep : appDeps) {
final Path resolvedDep = depResolver.resolve(appDep.getArtifact());
cpUrls.add(resolvedDep.toUri().toURL());
}
runnerClassLoader = new DefineClassVisibleURLClassLoader(cpUrls.toArray(new URL[cpUrls.size()]),
getClass().getClassLoader());
ClassLoader old = Thread.currentThread().getContextClassLoader();
BuildResult result;
try {
Thread.currentThread().setContextClassLoader(runnerClassLoader);
QuarkusAugmentor.Builder builder = QuarkusAugmentor.builder();
builder.setRoot(appClassesDir);
builder.setBaseName(ctx.getBaseName());
builder.setTargetDir(outputDir);
builder.setResolver(appState.getArtifactResolver());
builder.setEffectiveModel(appState.getEffectiveModel());
builder.setClassLoader(runnerClassLoader);
builder.setConfigCustomizer(configCustomizer);
builder.setBuildSystemProperties(buildSystemProperties);
builder.addFinal(BytecodeTransformerBuildItem.class)
.addFinal(ApplicationArchivesBuildItem.class)
.addFinal(MainClassBuildItem.class)
.addFinal(ArtifactResultBuildItem.class);
result = builder.build().run();
} finally {
Thread.currentThread().setContextClassLoader(old);
}
return new AugmentOutcome(result.consumeMulti(ArtifactResultBuildItem.class),
result.consumeOptional(JarBuildItem.class),
result.consumeOptional(NativeImageBuildItem.class));
} catch (Exception e) {
throw new AppCreatorException("Failed to augment application classes", e);
} finally {
if (runnerClassLoader != null) {
try {
runnerClassLoader.close();
} catch (IOException e) {
log.warn("Failed to close runner classloader", e);
}
}
}
}
public static Builder builder() {
return new Builder();
}
public Path getOutputDir() {
return outputDir;
}
public Path getAppClassesDir() {
return appClassesDir;
}
public Path getConfigDir() {
return configDir;
}
public Properties getBuildSystemProperties() {
return buildSystemProperties;
}
public static class CopyDirVisitor extends SimpleFileVisitor<Path> {
private final Path fromPath;
private final Path toPath;
private final CopyOption copyOption;
public CopyDirVisitor(Path fromPath, Path toPath, CopyOption copyOption) {
this.fromPath = fromPath;
this.toPath = toPath;
this.copyOption = copyOption;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path targetPath = toPath.resolve(fromPath.relativize(dir));
if (!Files.exists(targetPath)) {
Files.createDirectory(targetPath);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.copy(file, toPath.resolve(fromPath.relativize(file)), copyOption);
return FileVisitResult.CONTINUE;
}
}
public static class Builder {
private Path outputDir;
private Path appClassesDir;
private Path configDir;
private Properties buildSystemProperties;
private Consumer<ConfigBuilder> configCustomizer;
/**
* Output directory for the outcome of this phase.
* If not set by the user the work directory of the creator
* will be used instead.
*
* @param outputDir output directory for this phase
* @return this phase instance
*/
public Builder setOutputDir(Path outputDir) {
this.outputDir = outputDir;
return this;
}
/**
* Directory containing application classes. If none is set by the user,
* the creation process has to be initiated with an application JAR which
* will be unpacked into classes directory in the creator's work directory.
*
* @param appClassesDir directory for application classes
* @return this phase instance
*/
public Builder setAppClassesDir(Path appClassesDir) {
this.appClassesDir = appClassesDir;
return this;
}
/**
* Directory containing the configuration files.
*
* @param configDir directory the configuration files (application.properties)
* @return this phase instance
*/
public Builder setConfigDir(Path configDir) {
this.configDir = configDir;
return this;
}
/**
* Set the build system's properties, if any.
*
* @param buildSystemProperties the build system properties or {@code null} to unset
* @return this phase instance
*/
public Builder setBuildSystemProperties(final Properties buildSystemProperties) {
this.buildSystemProperties = buildSystemProperties;
return this;
}
public Builder setConfigCustomizer(Consumer<ConfigBuilder> configCustomizer) {
this.configCustomizer = configCustomizer;
return this;
}
public AugmentTask build() {
return new AugmentTask(this);
}
}
}

View File

@@ -1,244 +0,0 @@
package io.quarkus.creator.phase.generateconfig;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.jboss.logging.Logger;
import io.quarkus.bootstrap.BootstrapDependencyProcessingException;
import io.quarkus.bootstrap.DefineClassVisibleURLClassLoader;
import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.builder.BuildChain;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildExecutionBuilder;
import io.quarkus.builder.BuildResult;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.CuratedTask;
import io.quarkus.creator.curator.CurateOutcome;
import io.quarkus.deployment.ExtensionLoader;
import io.quarkus.deployment.builditem.ArchiveRootBuildItem;
import io.quarkus.deployment.builditem.ConfigDescriptionBuildItem;
import io.quarkus.deployment.builditem.ExtensionClassLoaderBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.util.FileUtil;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ConfigUtils;
import io.quarkus.runtime.configuration.QuarkusConfigFactory;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;
/**
* This phase generates an example configuration file
*
* @author Stuart Douglas
*/
public class GenerateConfigTask implements CuratedTask<Path> {
private static final Logger log = Logger.getLogger(GenerateConfigTask.class);
private final Path configFile;
public GenerateConfigTask(Path configFile) {
this.configFile = configFile;
}
@Override
public Path run(CurateOutcome appState, CuratedApplicationCreator creator) throws AppCreatorException {
//first lets look for some config, as it is not on the current class path
//and we need to load it to run the build process
//TODO: do we actually need to load this config? Does it affect resolution?
if (Files.exists(configFile)) {
try {
SmallRyeConfigBuilder builder = ConfigUtils.configBuilder(false)
.withSources(new PropertiesConfigSource(configFile.toUri().toURL()));
final SmallRyeConfig config = builder.build();
QuarkusConfigFactory.setConfig(config);
final ConfigProviderResolver cpr = ConfigProviderResolver.instance();
final Config existing = cpr.getConfig();
if (existing != config) {
cpr.releaseConfig(existing);
// subsequent calls will get the new config
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
final AppModelResolver depResolver = appState.getArtifactResolver();
List<AppDependency> appDeps;
try {
appDeps = appState.getEffectiveModel().getAllDependencies();
} catch (BootstrapDependencyProcessingException e) {
throw new AppCreatorException("Failed to resolve application build classpath", e);
}
URLClassLoader runnerClassLoader = null;
try {
// we need to make sure all the deployment artifacts are on the class path
final List<URL> cpUrls = new ArrayList<>(appDeps.size());
for (AppDependency appDep : appDeps) {
final Path resolvedDep = depResolver.resolve(appDep.getArtifact());
cpUrls.add(resolvedDep.toUri().toURL());
}
runnerClassLoader = new DefineClassVisibleURLClassLoader(cpUrls.toArray(new URL[cpUrls.size()]),
getClass().getClassLoader());
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(runnerClassLoader);
final BuildChainBuilder chainBuilder = BuildChain.builder();
ExtensionLoader.loadStepsFrom(runnerClassLoader).accept(chainBuilder);
chainBuilder.loadProviders(runnerClassLoader);
chainBuilder
.addInitial(ShutdownContextBuildItem.class)
.addInitial(LaunchModeBuildItem.class)
.addInitial(ArchiveRootBuildItem.class)
.addInitial(LiveReloadBuildItem.class)
.addInitial(ExtensionClassLoaderBuildItem.class);
chainBuilder.addFinal(ConfigDescriptionBuildItem.class);
BuildChain chain = chainBuilder
.build();
BuildExecutionBuilder execBuilder = chain.createExecutionBuilder("main")
.produce(new LaunchModeBuildItem(LaunchMode.NORMAL))
.produce(new ShutdownContextBuildItem())
.produce(new LiveReloadBuildItem())
.produce(new ArchiveRootBuildItem(Files.createTempDirectory("empty")))
.produce(new ExtensionClassLoaderBuildItem(runnerClassLoader));
BuildResult buildResult = execBuilder
.execute();
List<ConfigDescriptionBuildItem> descriptions = buildResult.consumeMulti(ConfigDescriptionBuildItem.class);
Collections.sort(descriptions);
String existing = "";
if (Files.exists(configFile)) {
try (InputStream in = new FileInputStream(configFile.toFile())) {
existing = new String(FileUtil.readFileContents(in), StandardCharsets.UTF_8);
}
}
StringBuilder sb = new StringBuilder();
for (ConfigDescriptionBuildItem i : descriptions) {
//we don't want to add these if they already exist
//either in commended or uncommented form
if (existing.contains("\n" + i.getPropertyName() + "=") ||
existing.contains("\n#" + i.getPropertyName() + "=")) {
continue;
}
sb.append("\n#\n");
sb.append(formatDocs(i.getDocs()));
sb.append("\n#\n#");
sb.append(i.getPropertyName() + "=" + i.getDefaultValue());
sb.append("\n");
}
try (FileOutputStream out = new FileOutputStream(configFile.toFile(), true)) {
out.write(sb.toString().getBytes(StandardCharsets.UTF_8));
}
} finally {
Thread.currentThread().setContextClassLoader(old);
}
} catch (Exception e) {
throw new AppCreatorException("Failed to generate config file", e);
} finally {
if (runnerClassLoader != null) {
try {
runnerClassLoader.close();
} catch (IOException e) {
log.warn("Failed to close runner classloader", e);
}
}
}
return configFile;
}
private String formatDocs(String docs) {
if (docs == null) {
return "";
}
StringBuilder builder = new StringBuilder();
boolean lastEmpty = false;
boolean first = true;
for (String line : docs.replace("<p>", "\n").split("\n")) {
//process line by line
String trimmed = line.trim();
//if the lines are empty we only include a single empty line at most, and add a # character
if (trimmed.isEmpty()) {
if (!lastEmpty && !first) {
lastEmpty = true;
builder.append("\n#");
}
continue;
}
//add the newlines
lastEmpty = false;
if (first) {
first = false;
} else {
builder.append("\n");
}
//replace some special characters, others are taken care of by regex below
builder.append("# " + trimmed.replace("\n", "\n#")
.replace("<ul>", "")
.replace("</ul>", "")
.replace("<li>", " - ")
.replace("</li>", ""));
}
String ret = builder.toString();
//replace @code
ret = Pattern.compile("\\{@code (.*?)\\}").matcher(ret).replaceAll("'$1'");
//replace @link with a reference to the field name
Matcher matcher = Pattern.compile("\\{@link #(.*?)\\}").matcher(ret);
while (matcher.find()) {
ret = ret.replace(matcher.group(0), "'" + configify(matcher.group(1)) + "'");
}
return ret;
}
private String configify(String group) {
//replace uppercase characters with a - followed by lowercase
StringBuilder ret = new StringBuilder();
for (int i = 0; i < group.length(); ++i) {
char c = group.charAt(i);
if (Character.isUpperCase(c)) {
ret.append("-");
ret.append(Character.toLowerCase(c));
} else {
ret.append(c);
}
}
return ret.toString();
}
}

View File

@@ -1,4 +0,0 @@
io.quarkus.creator.phase.augment.AugmentTask
io.quarkus.creator.curator.Curator
io.quarkus.creator.phase.nativeimage.NativeImagePhase
io.quarkus.creator.phase.runnerjar.ExecutableOutputTask

View File

@@ -1,16 +0,0 @@
package io.quarkus.creator.phase.curate.test;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.CuratedTask;
import io.quarkus.creator.curator.CurateOutcome;
class CurateOutcomeCuratedTask implements CuratedTask<CurateOutcome> {
public static final CurateOutcomeCuratedTask INSTANCE = new CurateOutcomeCuratedTask();
@Override
public CurateOutcome run(CurateOutcome outcome, CuratedApplicationCreator creator) throws AppCreatorException {
return outcome;
}
}

View File

@@ -31,6 +31,10 @@
<groupId>org.ow2.asm</groupId> <groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId> <artifactId>asm</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-development-mode-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-core</artifactId> <artifactId>quarkus-bootstrap-core</artifactId>
@@ -69,6 +73,12 @@
<groupId>org.graalvm.sdk</groupId> <groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId> <artifactId>graal-sdk</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -1,5 +1,6 @@
package io.quarkus.deployment; package io.quarkus.deployment;
import java.io.Closeable;
import java.nio.file.FileSystem; import java.nio.file.FileSystem;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.nio.file.Files; import java.nio.file.Files;
@@ -14,10 +15,10 @@ import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.eclipse.microprofile.config.spi.ConfigBuilder; import org.eclipse.microprofile.config.spi.ConfigBuilder;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import io.quarkus.bootstrap.model.AppModel; import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.builder.BuildChain; import io.quarkus.builder.BuildChain;
import io.quarkus.builder.BuildChainBuilder; import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildExecutionBuilder; import io.quarkus.builder.BuildExecutionBuilder;
@@ -25,7 +26,7 @@ import io.quarkus.builder.BuildResult;
import io.quarkus.builder.item.BuildItem; import io.quarkus.builder.item.BuildItem;
import io.quarkus.deployment.builditem.AdditionalApplicationArchiveBuildItem; import io.quarkus.deployment.builditem.AdditionalApplicationArchiveBuildItem;
import io.quarkus.deployment.builditem.ArchiveRootBuildItem; import io.quarkus.deployment.builditem.ArchiveRootBuildItem;
import io.quarkus.deployment.builditem.ExtensionClassLoaderBuildItem; import io.quarkus.deployment.builditem.DeploymentClassLoaderBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem; import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem;
@@ -40,6 +41,7 @@ public class QuarkusAugmentor {
private static final Logger log = Logger.getLogger(QuarkusAugmentor.class); private static final Logger log = Logger.getLogger(QuarkusAugmentor.class);
private final ClassLoader classLoader; private final ClassLoader classLoader;
private final ClassLoader deploymentClassLoader;
private final Path root; private final Path root;
private final Set<Class<? extends BuildItem>> finalResults; private final Set<Class<? extends BuildItem>> finalResults;
private final List<Consumer<BuildChainBuilder>> buildChainCustomizers; private final List<Consumer<BuildChainBuilder>> buildChainCustomizers;
@@ -50,7 +52,6 @@ public class QuarkusAugmentor {
private final Properties buildSystemProperties; private final Properties buildSystemProperties;
private final Path targetDir; private final Path targetDir;
private final AppModel effectiveModel; private final AppModel effectiveModel;
private final AppModelResolver resolver;
private final String baseName; private final String baseName;
private final Consumer<ConfigBuilder> configCustomizer; private final Consumer<ConfigBuilder> configCustomizer;
@@ -66,9 +67,9 @@ public class QuarkusAugmentor {
this.buildSystemProperties = builder.buildSystemProperties; this.buildSystemProperties = builder.buildSystemProperties;
this.targetDir = builder.targetDir; this.targetDir = builder.targetDir;
this.effectiveModel = builder.effectiveModel; this.effectiveModel = builder.effectiveModel;
this.resolver = builder.resolver;
this.baseName = builder.baseName; this.baseName = builder.baseName;
this.configCustomizer = builder.configCustomizer; this.configCustomizer = builder.configCustomizer;
this.deploymentClassLoader = builder.deploymentClassLoader;
} }
public BuildResult run() throws Exception { public BuildResult run() throws Exception {
@@ -77,25 +78,29 @@ public class QuarkusAugmentor {
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
FileSystem rootFs = null; FileSystem rootFs = null;
try { try {
Thread.currentThread().setContextClassLoader(classLoader); Thread.currentThread().setContextClassLoader(deploymentClassLoader);
final BuildChainBuilder chainBuilder = BuildChain.builder(); final BuildChainBuilder chainBuilder = BuildChain.builder();
//TODO: we load everything from the deployment class loader
//this allows the deployment config (application.properties) to be loaded, but in theory could result
//in additional stuff from the deployment leaking in, this is unlikely but has a bit of a smell.
if (buildSystemProperties != null) { if (buildSystemProperties != null) {
ExtensionLoader.loadStepsFrom(classLoader, buildSystemProperties, launchMode, configCustomizer) ExtensionLoader.loadStepsFrom(deploymentClassLoader, buildSystemProperties, launchMode, configCustomizer)
.accept(chainBuilder); .accept(chainBuilder);
} else { } else {
ExtensionLoader.loadStepsFrom(classLoader, launchMode, configCustomizer).accept(chainBuilder); ExtensionLoader.loadStepsFrom(deploymentClassLoader, launchMode, configCustomizer).accept(chainBuilder);
} }
Thread.currentThread().setContextClassLoader(classLoader);
chainBuilder.loadProviders(classLoader); chainBuilder.loadProviders(classLoader);
chainBuilder chainBuilder
.addInitial(DeploymentClassLoaderBuildItem.class)
.addInitial(ArchiveRootBuildItem.class) .addInitial(ArchiveRootBuildItem.class)
.addInitial(ShutdownContextBuildItem.class) .addInitial(ShutdownContextBuildItem.class)
.addInitial(LaunchModeBuildItem.class) .addInitial(LaunchModeBuildItem.class)
.addInitial(LiveReloadBuildItem.class) .addInitial(LiveReloadBuildItem.class)
.addInitial(AdditionalApplicationArchiveBuildItem.class) .addInitial(AdditionalApplicationArchiveBuildItem.class)
.addInitial(ExtensionClassLoaderBuildItem.class)
.addInitial(BuildSystemTargetBuildItem.class) .addInitial(BuildSystemTargetBuildItem.class)
.addInitial(CurateOutcomeBuildItem.class); .addInitial(CurateOutcomeBuildItem.class);
for (Class<? extends BuildItem> i : finalResults) { for (Class<? extends BuildItem> i : finalResults) {
@@ -118,9 +123,9 @@ public class QuarkusAugmentor {
.produce(new ArchiveRootBuildItem(root, rootFs == null ? root : rootFs.getPath("/"), excludedFromIndexing)) .produce(new ArchiveRootBuildItem(root, rootFs == null ? root : rootFs.getPath("/"), excludedFromIndexing))
.produce(new ShutdownContextBuildItem()) .produce(new ShutdownContextBuildItem())
.produce(new LaunchModeBuildItem(launchMode)) .produce(new LaunchModeBuildItem(launchMode))
.produce(new ExtensionClassLoaderBuildItem(classLoader))
.produce(new BuildSystemTargetBuildItem(targetDir, baseName)) .produce(new BuildSystemTargetBuildItem(targetDir, baseName))
.produce(new CurateOutcomeBuildItem(effectiveModel, resolver)); .produce(new DeploymentClassLoaderBuildItem(deploymentClassLoader))
.produce(new CurateOutcomeBuildItem(effectiveModel));
for (Path i : additionalApplicationArchives) { for (Path i : additionalApplicationArchives) {
execBuilder.produce(new AdditionalApplicationArchiveBuildItem(i)); execBuilder.produce(new AdditionalApplicationArchiveBuildItem(i));
} }
@@ -141,6 +146,15 @@ public class QuarkusAugmentor {
} catch (Exception e) { } catch (Exception e) {
} }
} }
try {
ConfigProviderResolver.instance()
.releaseConfig(ConfigProviderResolver.instance().getConfig(deploymentClassLoader));
} catch (Exception ignore) {
}
if (deploymentClassLoader instanceof Closeable) {
((Closeable) deploymentClassLoader).close();
}
Thread.currentThread().setContextClassLoader(originalClassLoader); Thread.currentThread().setContextClassLoader(originalClassLoader);
} }
} }
@@ -163,9 +177,9 @@ public class QuarkusAugmentor {
Properties buildSystemProperties; Properties buildSystemProperties;
AppModel effectiveModel; AppModel effectiveModel;
AppModelResolver resolver;
String baseName = "quarkus-application"; String baseName = "quarkus-application";
Consumer<ConfigBuilder> configCustomizer; Consumer<ConfigBuilder> configCustomizer;
ClassLoader deploymentClassLoader;
public Builder addBuildChainCustomizer(Consumer<BuildChainBuilder> customizer) { public Builder addBuildChainCustomizer(Consumer<BuildChainBuilder> customizer) {
this.buildChainCustomizers.add(customizer); this.buildChainCustomizers.add(customizer);
@@ -259,8 +273,12 @@ public class QuarkusAugmentor {
return this; return this;
} }
public Builder setResolver(AppModelResolver resolver) { public ClassLoader getDeploymentClassLoader() {
this.resolver = resolver; return deploymentClassLoader;
}
public Builder setDeploymentClassLoader(ClassLoader deploymentClassLoader) {
this.deploymentClassLoader = deploymentClassLoader;
return this; return this;
} }

View File

@@ -1,18 +0,0 @@
package io.quarkus.deployment.builditem;
import io.quarkus.builder.item.SimpleBuildItem;
/**
* The extension class loader.
*/
public final class ExtensionClassLoaderBuildItem extends SimpleBuildItem {
private final ClassLoader extensionClassLoader;
public ExtensionClassLoaderBuildItem(final ClassLoader extensionClassLoader) {
this.extensionClassLoader = extensionClassLoader;
}
public ClassLoader getExtensionClassLoader() {
return extensionClassLoader;
}
}

View File

@@ -285,6 +285,7 @@ public final class RunTimeConfigurationGenerator {
// create <clinit> // create <clinit>
clinit = cc.getMethodCreator(MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "<clinit>", void.class)); clinit = cc.getMethodCreator(MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "<clinit>", void.class));
clinit.setModifiers(Opcodes.ACC_STATIC); clinit.setModifiers(Opcodes.ACC_STATIC);
clinit.invokeStaticMethod(PM_SET_RUNTIME_DEFAULT_PROFILE, clinit.load(ProfileManager.getActiveProfile())); clinit.invokeStaticMethod(PM_SET_RUNTIME_DEFAULT_PROFILE, clinit.load(ProfileManager.getActiveProfile()));
clinitNameBuilder = clinit.newInstance(SB_NEW); clinitNameBuilder = clinit.newInstance(SB_NEW);
clinit.invokeVirtualMethod(SB_APPEND_STRING, clinitNameBuilder, clinit.load("quarkus")); clinit.invokeVirtualMethod(SB_APPEND_STRING, clinitNameBuilder, clinit.load("quarkus"));
@@ -337,7 +338,6 @@ public final class RunTimeConfigurationGenerator {
public void run() { public void run() {
// in clinit, load the build-time config // in clinit, load the build-time config
// make the build time config global until we read the run time config - // make the build time config global until we read the run time config -
// at run time (when we're ready) we update the factory and then release the build time config // at run time (when we're ready) we update the factory and then release the build time config
clinit.invokeStaticMethod(QCF_SET_CONFIG, clinitConfig); clinit.invokeStaticMethod(QCF_SET_CONFIG, clinitConfig);
@@ -599,6 +599,7 @@ public final class RunTimeConfigurationGenerator {
readConfig.returnValue(null); readConfig.returnValue(null);
readConfig.close(); readConfig.close();
clinit.returnValue(null); clinit.returnValue(null);
clinit.close(); clinit.close();
cc.close(); cc.close();

View File

@@ -76,7 +76,7 @@ public class ApplicationArchiveBuildStep {
} }
} }
@BuildStep @BuildStep(loadsApplicationClasses = true)
ApplicationArchivesBuildItem build(ArchiveRootBuildItem root, ApplicationIndexBuildItem appindex, ApplicationArchivesBuildItem build(ArchiveRootBuildItem root, ApplicationIndexBuildItem appindex,
List<AdditionalApplicationArchiveMarkerBuildItem> appMarkers, List<AdditionalApplicationArchiveMarkerBuildItem> appMarkers,
List<AdditionalApplicationArchiveBuildItem> additionalApplicationArchiveBuildItem, List<AdditionalApplicationArchiveBuildItem> additionalApplicationArchiveBuildItem,

View File

@@ -1,21 +1,14 @@
package io.quarkus.deployment.pkg.builditem; package io.quarkus.deployment.pkg.builditem;
import io.quarkus.bootstrap.model.AppModel; import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.builder.item.SimpleBuildItem; import io.quarkus.builder.item.SimpleBuildItem;
public final class CurateOutcomeBuildItem extends SimpleBuildItem { public final class CurateOutcomeBuildItem extends SimpleBuildItem {
private final AppModel effectiveModel; private final AppModel effectiveModel;
private final AppModelResolver resolver;
public CurateOutcomeBuildItem(AppModel effectiveModel, AppModelResolver resolver) { public CurateOutcomeBuildItem(AppModel effectiveModel) {
this.effectiveModel = effectiveModel; this.effectiveModel = effectiveModel;
this.resolver = resolver;
}
public AppModelResolver getResolver() {
return resolver;
} }
public AppModel getEffectiveModel() { public AppModel getEffectiveModel() {

View File

@@ -2,6 +2,7 @@ package io.quarkus.deployment.pkg.builditem;
import java.nio.file.Path; import java.nio.file.Path;
import io.quarkus.bootstrap.app.JarResult;
import io.quarkus.builder.item.SimpleBuildItem; import io.quarkus.builder.item.SimpleBuildItem;
public final class JarBuildItem extends SimpleBuildItem { public final class JarBuildItem extends SimpleBuildItem {
@@ -31,4 +32,8 @@ public final class JarBuildItem extends SimpleBuildItem {
public Path getOriginalArtifact() { public Path getOriginalArtifact() {
return originalArtifact; return originalArtifact;
} }
public JarResult toJarResult() {
return new JarResult(path, originalArtifact, libraryDir);
}
} }

View File

@@ -47,7 +47,6 @@ import org.jboss.logging.Logger;
import io.quarkus.bootstrap.BootstrapDependencyProcessingException; import io.quarkus.bootstrap.BootstrapDependencyProcessingException;
import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppDependency; import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.bootstrap.resolver.AppModelResolverException;
import io.quarkus.bootstrap.util.IoUtils; import io.quarkus.bootstrap.util.IoUtils;
import io.quarkus.bootstrap.util.ZipUtils; import io.quarkus.bootstrap.util.ZipUtils;
@@ -184,7 +183,6 @@ public class JarResultBuildStep {
log.info("Building fat jar: " + runnerJar); log.info("Building fat jar: " + runnerJar);
final AppModelResolver depResolver = curateOutcomeBuildItem.getResolver();
final Map<String, String> seen = new HashMap<>(); final Map<String, String> seen = new HashMap<>();
final Map<String, Set<AppDependency>> duplicateCatcher = new HashMap<>(); final Map<String, Set<AppDependency>> duplicateCatcher = new HashMap<>();
final StringBuilder classPath = new StringBuilder(); final StringBuilder classPath = new StringBuilder();
@@ -201,7 +199,7 @@ public class JarResultBuildStep {
for (AppDependency appDep : appDeps) { for (AppDependency appDep : appDeps) {
final AppArtifact depArtifact = appDep.getArtifact(); final AppArtifact depArtifact = appDep.getArtifact();
final Path resolvedDep = depResolver.resolve(depArtifact); final Path resolvedDep = depArtifact.getPath();
// Exclude files that are not jars (typically, we can have XML files here, see https://github.com/quarkusio/quarkus/issues/2852) // Exclude files that are not jars (typically, we can have XML files here, see https://github.com/quarkusio/quarkus/issues/2852)
if (!resolvedDep.getFileName().toString().endsWith(".jar")) { if (!resolvedDep.getFileName().toString().endsWith(".jar")) {
@@ -381,23 +379,25 @@ public class JarResultBuildStep {
throws IOException { throws IOException {
// this will contain all the resources in both maven and gradle cases - the latter is true because we copy them in AugmentTask // this will contain all the resources in both maven and gradle cases - the latter is true because we copy them in AugmentTask
Path classesLocation = applicationArchivesBuildItem.getRootArchive().getArchiveLocation(); Path classesLocation = applicationArchivesBuildItem.getRootArchive().getArchiveLocation();
Files.find(classesLocation, 1, new BiPredicate<Path, BasicFileAttributes>() { try (Stream<Path> stream = Files.find(classesLocation, 1, new BiPredicate<Path, BasicFileAttributes>() {
@Override @Override
public boolean test(Path path, BasicFileAttributes basicFileAttributes) { public boolean test(Path path, BasicFileAttributes basicFileAttributes) {
return basicFileAttributes.isRegularFile() && path.toString().endsWith(".json"); return basicFileAttributes.isRegularFile() && path.toString().endsWith(".json");
} }
}).forEach(new Consumer<Path>() { })) {
@Override stream.forEach(new Consumer<Path>() {
public void accept(Path jsonPath) { @Override
try { public void accept(Path jsonPath) {
Files.copy(jsonPath, thinJarDirectory.resolve(jsonPath.getFileName())); try {
} catch (IOException e) { Files.copy(jsonPath, thinJarDirectory.resolve(jsonPath.getFileName()));
throw new UncheckedIOException( } catch (IOException e) {
"Unable to copy json config file from " + jsonPath + " to " + thinJarDirectory, throw new UncheckedIOException(
e); "Unable to copy json config file from " + jsonPath + " to " + thinJarDirectory,
e);
}
} }
} });
}); }
} }
private void doThinJarGeneration(CurateOutcomeBuildItem curateOutcomeBuildItem, private void doThinJarGeneration(CurateOutcomeBuildItem curateOutcomeBuildItem,
@@ -410,14 +410,13 @@ public class JarResultBuildStep {
List<GeneratedClassBuildItem> allClasses, List<GeneratedClassBuildItem> allClasses,
FileSystem runnerZipFs) FileSystem runnerZipFs)
throws BootstrapDependencyProcessingException, AppModelResolverException, IOException { throws BootstrapDependencyProcessingException, AppModelResolverException, IOException {
final AppModelResolver depResolver = curateOutcomeBuildItem.getResolver();
final Map<String, String> seen = new HashMap<>(); final Map<String, String> seen = new HashMap<>();
final StringBuilder classPath = new StringBuilder(); final StringBuilder classPath = new StringBuilder();
final Map<String, List<byte[]>> services = new HashMap<>(); final Map<String, List<byte[]>> services = new HashMap<>();
final List<AppDependency> appDeps = curateOutcomeBuildItem.getEffectiveModel().getUserDependencies(); final List<AppDependency> appDeps = curateOutcomeBuildItem.getEffectiveModel().getUserDependencies();
copyLibraryJars(transformedClasses, libDir, depResolver, classPath, appDeps); copyLibraryJars(transformedClasses, libDir, classPath, appDeps);
AppArtifact appArtifact = curateOutcomeBuildItem.getEffectiveModel().getAppArtifact(); AppArtifact appArtifact = curateOutcomeBuildItem.getEffectiveModel().getAppArtifact();
// the manifest needs to be the first entry in the jar, otherwise JarInputStream does not work properly // the manifest needs to be the first entry in the jar, otherwise JarInputStream does not work properly
@@ -427,11 +426,11 @@ public class JarResultBuildStep {
generatedResources, seen); generatedResources, seen);
} }
private void copyLibraryJars(TransformedClassesBuildItem transformedClasses, Path libDir, AppModelResolver depResolver, private void copyLibraryJars(TransformedClassesBuildItem transformedClasses, Path libDir,
StringBuilder classPath, List<AppDependency> appDeps) throws AppModelResolverException, IOException { StringBuilder classPath, List<AppDependency> appDeps) throws IOException {
for (AppDependency appDep : appDeps) { for (AppDependency appDep : appDeps) {
final AppArtifact depArtifact = appDep.getArtifact(); final AppArtifact depArtifact = appDep.getArtifact();
final Path resolvedDep = depResolver.resolve(depArtifact); final Path resolvedDep = depArtifact.getPath();
// Exclude files that are not jars (typically, we can have XML files here, see https://github.com/quarkusio/quarkus/issues/2852) // Exclude files that are not jars (typically, we can have XML files here, see https://github.com/quarkusio/quarkus/issues/2852)
if (!resolvedDep.getFileName().toString().endsWith(".jar")) { if (!resolvedDep.getFileName().toString().endsWith(".jar")) {

View File

@@ -5,6 +5,8 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import io.quarkus.gizmo.ClassOutput;
/** /**
* Basic configuration needed to generate a proxy of a class. * Basic configuration needed to generate a proxy of a class.
* This was inspired from jboss-invocations's org.jboss.invocation.proxy.ProxyConfiguration * This was inspired from jboss-invocations's org.jboss.invocation.proxy.ProxyConfiguration
@@ -16,6 +18,8 @@ public class ProxyConfiguration<T> {
private ClassLoader classLoader; private ClassLoader classLoader;
private Class<T> superClass; private Class<T> superClass;
private List<Class<?>> additionalInterfaces = new ArrayList<>(0); private List<Class<?>> additionalInterfaces = new ArrayList<>(0);
private ClassOutput classOutput;
private boolean allowPackagePrivate = false;
public List<Class<?>> getAdditionalInterfaces() { public List<Class<?>> getAdditionalInterfaces() {
return Collections.unmodifiableList(additionalInterfaces); return Collections.unmodifiableList(additionalInterfaces);
@@ -68,4 +72,22 @@ public class ProxyConfiguration<T> {
this.superClass = superClass; this.superClass = superClass;
return this; return this;
} }
public ClassOutput getClassOutput() {
return classOutput;
}
public ProxyConfiguration<T> setClassOutput(ClassOutput classOutput) {
this.classOutput = classOutput;
return this;
}
public boolean isAllowPackagePrivate() {
return allowPackagePrivate;
}
public ProxyConfiguration<T> setAllowPackagePrivate(boolean allowPackagePrivate) {
this.allowPackagePrivate = allowPackagePrivate;
return this;
}
} }

View File

@@ -7,8 +7,11 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.FieldDescriptor; import io.quarkus.gizmo.FieldDescriptor;
@@ -39,7 +42,12 @@ public class ProxyFactory<T> {
Class<T> superClass = configuration.getSuperClass() != null ? configuration.getSuperClass() : (Class<T>) Object.class; Class<T> superClass = configuration.getSuperClass() != null ? configuration.getSuperClass() : (Class<T>) Object.class;
this.superClassName = superClass.getName(); this.superClassName = superClass.getName();
if (!hasNoArgsConstructor(superClass)) {
if (!configuration.isAllowPackagePrivate() && !Modifier.isPublic(superClass.getModifiers())) {
throw new IllegalArgumentException(
"A proxy cannot be created for class " + this.superClassName + " because the it is not public");
}
if (!hasNoArgsConstructor(superClass, configuration.isAllowPackagePrivate())) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"A proxy cannot be created for class " + this.superClassName "A proxy cannot be created for class " + this.superClassName
+ " because it does contain a no-arg constructor"); + " because it does contain a no-arg constructor");
@@ -48,10 +56,6 @@ public class ProxyFactory<T> {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"A proxy cannot be created for class " + this.superClassName + " because it is a final class"); "A proxy cannot be created for class " + this.superClassName + " because it is a final class");
} }
if (!Modifier.isPublic(superClass.getModifiers())) {
throw new IllegalArgumentException(
"A proxy cannot be created for class " + this.superClassName + " because the it is not public");
}
Objects.requireNonNull(configuration.getClassLoader(), "classLoader must be set"); Objects.requireNonNull(configuration.getClassLoader(), "classLoader must be set");
this.classLoader = configuration.getClassLoader(); this.classLoader = configuration.getClassLoader();
@@ -63,7 +67,8 @@ public class ProxyFactory<T> {
} }
this.classBuilder = ClassCreator.builder() this.classBuilder = ClassCreator.builder()
.classOutput(new InjectIntoClassloaderClassOutput(configuration.getClassLoader())) .classOutput(configuration.getClassOutput() != null ? configuration.getClassOutput()
: new InjectIntoClassloaderClassOutput(configuration.getClassLoader()))
.className(this.proxyName) .className(this.proxyName)
.superClass(this.superClassName); .superClass(this.superClassName);
if (!configuration.getAdditionalInterfaces().isEmpty()) { if (!configuration.getAdditionalInterfaces().isEmpty()) {
@@ -71,23 +76,38 @@ public class ProxyFactory<T> {
} }
} }
private boolean hasNoArgsConstructor(Class<?> clazz) { private boolean hasNoArgsConstructor(Class<?> clazz, boolean allowPackagePrivate) {
for (Constructor<?> constructor : clazz.getConstructors()) { for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
if (constructor.getParameterCount() == 0) { if (constructor.getParameterCount() == 0) {
return true; if (allowPackagePrivate) {
return !Modifier.isPrivate(constructor.getModifiers());
}
return Modifier.isPublic(constructor.getModifiers()) || Modifier.isProtected(constructor.getModifiers());
} }
} }
return false; return false;
} }
private void addMethodsOfClass(Class<?> clazz) { private void addMethodsOfClass(Class<?> clazz) {
for (Method methodInfo : clazz.getMethods()) { addMethodsOfClass(clazz, new HashSet<>());
}
private void addMethodsOfClass(Class<?> clazz, Set<MethodKey> seen) {
for (Method methodInfo : clazz.getDeclaredMethods()) {
MethodKey key = new MethodKey(methodInfo.getReturnType(), methodInfo.getName(), methodInfo.getParameterTypes());
if (seen.contains(key)) {
continue;
}
seen.add(key);
if (!Modifier.isStatic(methodInfo.getModifiers()) && if (!Modifier.isStatic(methodInfo.getModifiers()) &&
!Modifier.isFinal(methodInfo.getModifiers()) && !Modifier.isFinal(methodInfo.getModifiers()) &&
!methodInfo.getName().equals("<init>")) { !methodInfo.getName().equals("<init>")) {
methods.add(methodInfo); methods.add(methodInfo);
} }
} }
if (clazz.getSuperclass() != null) {
addMethodsOfClass(clazz.getSuperclass(), seen);
}
} }
public Class<? extends T> defineClass() { public Class<? extends T> defineClass() {
@@ -190,4 +210,34 @@ public class ProxyFactory<T> {
} }
} }
static class MethodKey {
final Class<?> returnType;
final String name;
final Class<?>[] params;
MethodKey(Class<?> returnType, String name, Class<?>[] params) {
this.returnType = returnType;
this.name = name;
this.params = params;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
MethodKey methodKey = (MethodKey) o;
return Objects.equals(returnType, methodKey.returnType) &&
Objects.equals(name, methodKey.name) &&
Arrays.equals(params, methodKey.params);
}
@Override
public int hashCode() {
int result = Objects.hash(returnType, name);
result = 31 * result + Arrays.hashCode(params);
return result;
}
}
} }

View File

@@ -1,55 +0,0 @@
package io.quarkus.deployment.steps;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import io.quarkus.deployment.ApplicationArchive;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
import io.quarkus.deployment.builditem.DeploymentClassLoaderBuildItem;
import io.quarkus.deployment.util.FileUtil;
public class DeploymentClassLoaderBuildStep {
@BuildStep
DeploymentClassLoaderBuildItem classloader(ApplicationArchivesBuildItem archivesBuildItem) {
return new DeploymentClassLoaderBuildItem(
new DeploymentClassLoader(archivesBuildItem, Thread.currentThread().getContextClassLoader()));
}
static class DeploymentClassLoader extends ClassLoader {
private final ApplicationArchivesBuildItem archivesBuildItem;
DeploymentClassLoader(ApplicationArchivesBuildItem archivesBuildItem, ClassLoader parent) {
super(parent);
this.archivesBuildItem = archivesBuildItem;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
if (c != null) {
return c;
}
ApplicationArchive applicationArchive = archivesBuildItem.containingArchive(name);
if (applicationArchive != null) {
try {
try (InputStream res = Files
.newInputStream(applicationArchive.getChildPath(name.replace(".", "/") + ".class"))) {
byte[] data = FileUtil.readFileContents(res);
return defineClass(name, data, 0, data.length);
}
} catch (IOException e) {
}
}
return super.loadClass(name, resolve);
}
}
}

View File

@@ -42,6 +42,7 @@ import io.quarkus.gizmo.CatchBlockCreator;
import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.ClassOutput; import io.quarkus.gizmo.ClassOutput;
import io.quarkus.gizmo.FieldCreator; import io.quarkus.gizmo.FieldCreator;
import io.quarkus.gizmo.FieldDescriptor;
import io.quarkus.gizmo.MethodCreator; import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle; import io.quarkus.gizmo.ResultHandle;
@@ -117,6 +118,11 @@ class MainClassBuildStep {
mv.invokeStaticMethod(ofMethod(System.class, "setProperty", String.class, String.class, String.class), mv.invokeStaticMethod(ofMethod(System.class, "setProperty", String.class, String.class, String.class),
mv.load(i.getKey()), mv.load(i.getValue())); mv.load(i.getKey()), mv.load(i.getValue()));
} }
//set the launch mode
ResultHandle lm = mv
.readStaticField(FieldDescriptor.of(LaunchMode.class, launchMode.getLaunchMode().name(), LaunchMode.class));
mv.invokeStaticMethod(MethodDescriptor.ofMethod(ProfileManager.class, "setLaunchMode", void.class, LaunchMode.class),
lm);
mv.invokeStaticMethod(MethodDescriptor.ofMethod(Timing.class, "staticInitStarted", void.class)); mv.invokeStaticMethod(MethodDescriptor.ofMethod(Timing.class, "staticInitStarted", void.class));

View File

@@ -1,558 +0,0 @@
package io.quarkus.runner;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.CodeSource;
import java.security.MessageDigest;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.jboss.logging.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import io.quarkus.deployment.ClassOutput;
public class RuntimeClassLoader extends ClassLoader implements ClassOutput, TransformerTarget {
private static final Logger log = Logger.getLogger(RuntimeClassLoader.class);
private final Map<String, byte[]> appClasses = new ConcurrentHashMap<>();
private final Set<String> frameworkClasses = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final Map<String, byte[]> resources = new ConcurrentHashMap<>();
private volatile Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> bytecodeTransformers = null;
private volatile ClassLoader transformerSafeClassLoader;
private final List<Path> applicationClassDirectories;
private final Map<String, Path> applicationClasses;
private final ProtectionDomain defaultProtectionDomain;
private final Path frameworkClassesPath;
private final Path transformerCache;
private static final String DEBUG_CLASSES_DIR = System.getProperty("quarkus.debug.generated-classes-dir");
private final ConcurrentHashMap<String, LoadingClass> loadingClasses = new ConcurrentHashMap<>();
static {
registerAsParallelCapable();
}
public RuntimeClassLoader(ClassLoader parent, List<Path> applicationClassesDirectories, Path frameworkClassesDirectory,
Path transformerCache) {
super(parent);
try {
Map<String, Path> applicationClasses = new HashMap<>();
for (Path i : applicationClassesDirectories) {
if (Files.isDirectory(i)) {
try (Stream<Path> fileTreeElements = Files.walk(i)) {
fileTreeElements.forEach(new Consumer<Path>() {
@Override
public void accept(Path path) {
if (path.toString().endsWith(".class")) {
applicationClasses.put(i.relativize(path).toString().replace('\\', '/'), path);
}
}
});
}
}
}
this.defaultProtectionDomain = createDefaultProtectionDomain(applicationClassesDirectories.get(0));
this.applicationClasses = applicationClasses;
} catch (IOException e) {
throw new RuntimeException(e);
}
this.applicationClassDirectories = applicationClassesDirectories;
this.frameworkClassesPath = frameworkClassesDirectory;
if (!Files.isDirectory(frameworkClassesDirectory)) {
throw new IllegalStateException(
"Test classes directory path does not point to an existing directory: " + frameworkClassesPath);
}
this.transformerCache = transformerCache;
}
@Override
public Enumeration<URL> getResources(String nm) throws IOException {
String name = sanitizeName(nm);
List<URL> resources = new ArrayList<>();
// TODO: some superugly hack for bean provider
URL resource = getQuarkusResource(name);
if (resource != null) {
resources.add(resource);
}
URL appResource = findApplicationResource(name);
if (appResource != null) {
resources.add(appResource);
}
for (Enumeration<URL> e = super.getResources(name); e.hasMoreElements();) {
resources.add(e.nextElement());
}
return Collections.enumeration(resources);
}
@Override
public URL getResource(String nm) {
String name = sanitizeName(nm);
// TODO: some superugly hack for bean provider
URL resource = getQuarkusResource(name);
if (resource != null) {
return resource;
}
URL appResource = findApplicationResource(name);
if (appResource != null) {
return appResource;
}
return super.getResource(name);
}
@Override
public InputStream getResourceAsStream(String nm) {
String name = sanitizeName(nm);
byte[] data = resources.get(name);
if (data != null) {
return new ByteArrayInputStream(data);
}
data = findApplicationResourceContent(name);
if (data != null) {
return new ByteArrayInputStream(data);
}
return super.getResourceAsStream(name);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> ex = findLoadedClass(name);
if (ex != null) {
return ex;
}
if (appClasses.containsKey(name)
|| (!frameworkClasses.contains(name) && getClassInApplicationClassPaths(name) != null)) {
return findClass(name);
}
return super.loadClass(name, resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> existing = findLoadedClass(name);
if (existing != null) {
return existing;
}
byte[] bytes = appClasses.get(name);
if (bytes != null) {
try {
definePackage(name);
return defineClass(name, bytes, 0, bytes.length, defaultProtectionDomain);
} catch (Error e) {
//potential race conditions if another thread is loading the same class
existing = findLoadedClass(name);
if (existing != null) {
return existing;
}
throw e;
}
}
Path classLoc = getClassInApplicationClassPaths(name);
if (classLoc != null) {
LoadingClass res = new LoadingClass(new CompletableFuture<>(), Thread.currentThread());
LoadingClass loadingClass = loadingClasses.putIfAbsent(name, res);
if (loadingClass != null) {
if (loadingClass.initiator == Thread.currentThread()) {
throw new LinkageError(
"Load caused recursion in RuntimeClassLoader, this is a Quarkus bug loading class: " + name);
}
try {
return loadingClass.value.get();
} catch (Exception e) {
throw new ClassNotFoundException("Failed to load " + name, e);
}
}
try {
try {
bytes = Files.readAllBytes(classLoc);
} catch (IOException e) {
throw new ClassNotFoundException("Failed to load class", e);
}
bytes = handleTransform(name, bytes);
definePackage(name);
Class<?> clazz = defineClass(name, bytes, 0, bytes.length, defaultProtectionDomain);
res.value.complete(clazz);
return clazz;
} catch (RuntimeException e) {
res.value.completeExceptionally(e);
throw e;
} catch (Throwable e) {
res.value.completeExceptionally(e);
throw e;
}
}
throw new ClassNotFoundException(name);
}
@Override
public void writeClass(boolean applicationClass, String className, byte[] data) {
if (applicationClass) {
String dotName = className.replace('/', '.');
appClasses.put(dotName, data);
if (DEBUG_CLASSES_DIR != null) {
try {
File debugPath = new File(DEBUG_CLASSES_DIR);
if (!debugPath.exists()) {
debugPath.mkdir();
}
File classFile = new File(debugPath, dotName + ".class");
classFile.getParentFile().mkdirs();
try (FileOutputStream classWriter = new FileOutputStream(classFile)) {
classWriter.write(data);
}
log.infof("Wrote %s", classFile.getAbsolutePath());
} catch (Throwable t) {
t.printStackTrace();
}
}
} else {
//this is pretty horrible
//basically we add the framework level classes to the file system
//in the same dir as the actual app classes
//however as we add them to the frameworkClasses set we know to load them
//from the parent CL
frameworkClasses.add(className.replace('/', '.'));
final Path fileName = frameworkClassesPath.resolve(className.replace('.', '/') + ".class");
try {
Files.createDirectories(fileName.getParent());
try (FileOutputStream out = new FileOutputStream(fileName.toFile())) {
out.write(data);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public Writer writeSource(final String className) {
if (DEBUG_CLASSES_DIR != null) {
try {
File debugPath = new File(DEBUG_CLASSES_DIR);
if (!debugPath.exists()) {
debugPath.mkdir();
}
File classFile = new File(debugPath, className + ".zig");
classFile.getParentFile().mkdirs();
log.infof("Wrote %s", classFile.getAbsolutePath());
return new OutputStreamWriter(new FileOutputStream(classFile), StandardCharsets.UTF_8);
} catch (Throwable t) {
t.printStackTrace();
}
}
return ClassOutput.super.writeSource(className);
}
@Override
public void setTransformers(Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> functions) {
this.bytecodeTransformers = functions;
this.transformerSafeClassLoader = Thread.currentThread().getContextClassLoader();
}
public void setApplicationArchives(List<Path> archives) {
//we also need to be able to transform application archives
//this is not great but I can't really see a better solution
if (bytecodeTransformers == null) {
return;
}
try {
for (Path root : archives) {
Map<String, Path> classes = new HashMap<>();
AtomicBoolean transform = new AtomicBoolean();
try (Stream<Path> fileTreeElements = Files.walk(root)) {
fileTreeElements.forEach(new Consumer<Path>() {
@Override
public void accept(Path path) {
if (path.toString().endsWith(".class")) {
String key = root.relativize(path).toString().replace('\\', '/');
classes.put(key, path);
if (bytecodeTransformers
.containsKey(key.substring(0, key.length() - ".class".length()).replace("/", "."))) {
transform.set(true);
}
}
}
});
}
if (transform.get()) {
applicationClasses.putAll(classes);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void writeResource(String name, byte[] data) throws IOException {
resources.put(name, data);
}
/**
* This is needed in order to easily inject classes into the classloader
* without having to resort to tricks (that don't work that well on new JDKs)
* See {@link io.quarkus.deployment.proxy.InjectIntoClassloaderClassOutput}
*/
public Class<?> visibleDefineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
return super.defineClass(name, b, off, len, defaultProtectionDomain);
}
private void definePackage(String name) {
final String pkgName = getPackageNameFromClassName(name);
if ((pkgName != null) && getPackage(pkgName) == null) {
synchronized (getClassLoadingLock(pkgName)) {
if (getPackage(pkgName) == null) {
// this could certainly be improved to use the actual manifest
definePackage(pkgName, null, null, null, null, null, null, null);
}
}
}
}
private String getPackageNameFromClassName(String className) {
final int index = className.lastIndexOf('.');
if (index == -1) {
// we return null here since in this case no package is defined
// this is same behavior as Package.getPackage(clazz) exhibits
// when the class is in the default package
return null;
}
return className.substring(0, index);
}
private static byte[] readFileContent(final Path path) {
final File file = path.toFile();
final long fileLength = file.length();
if (fileLength > Integer.MAX_VALUE) {
throw new RuntimeException("Can't process class files larger than Integer.MAX_VALUE bytes");
}
final int intLength = (int) fileLength;
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) {
//Might be large but we need a single byte[] at the end of things, might as well allocate it in one shot:
ByteArrayOutputStream out = new ByteArrayOutputStream(intLength);
final int reasonableBufferSize = Math.min(intLength, 2048);
byte[] buf = new byte[reasonableBufferSize];
int r;
while ((r = in.read(buf)) > 0) {
out.write(buf, 0, r);
}
return out.toByteArray();
} catch (IOException e) {
throw new IllegalArgumentException("Unable to read file " + path, e);
}
}
private byte[] handleTransform(String name, byte[] bytes) {
if (bytecodeTransformers == null || bytecodeTransformers.isEmpty()) {
return bytes;
}
List<BiFunction<String, ClassVisitor, ClassVisitor>> transformers = bytecodeTransformers.get(name);
if (transformers == null) {
return bytes;
}
Path hashPath = null;
if (transformerCache != null) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(bytes);
String hash = Base64.getUrlEncoder().encodeToString(thedigest);
hashPath = transformerCache.resolve(hash);
if (Files.exists(hashPath)) {
return readFileContent(hashPath);
}
} catch (Exception e) {
log.error("Unable to load transformed class from cache", e);
}
}
ClassReader cr = new ClassReader(bytes);
ClassWriter writer = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
@Override
protected ClassLoader getClassLoader() {
return transformerSafeClassLoader;
}
};
ClassVisitor visitor = writer;
for (BiFunction<String, ClassVisitor, ClassVisitor> i : transformers) {
visitor = i.apply(name, visitor);
}
cr.accept(visitor, 0);
byte[] data = writer.toByteArray();
if (hashPath != null) {
try {
File file = hashPath.toFile();
file.getParentFile().mkdirs();
try (FileOutputStream out = new FileOutputStream(file)) {
out.write(data);
}
} catch (Exception e) {
log.error("Unable to write class to cache", e);
}
}
return data;
}
private String sanitizeName(String name) {
if (name.startsWith("/")) {
return name.substring(1);
}
return name;
}
private Path getClassInApplicationClassPaths(String name) {
final String fileName = name.replace('.', '/') + ".class";
return applicationClasses.get(fileName);
}
private URL findApplicationResource(String name) {
Path resourcePath = null;
// Resource names are always separated by the "/" character.
// Here we are trying to resolve those resources using a filesystem
// Path, so we replace the "/" character with the filesystem
// specific separator before resolving
if (File.separatorChar != '/') {
name = name.replace('/', File.separatorChar);
}
for (Path i : applicationClassDirectories) {
resourcePath = i.resolve(name);
if (Files.exists(resourcePath)) {
break;
}
}
try {
return resourcePath != null && Files.exists(resourcePath) ? resourcePath.toUri().toURL() : null;
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
private byte[] findApplicationResourceContent(String name) {
Path resourcePath = null;
for (Path i : applicationClassDirectories) {
resourcePath = i.resolve(name);
if (Files.exists(resourcePath)) {
return readFileContent(resourcePath);
}
}
return null;
}
private URL getQuarkusResource(String name) {
byte[] data = resources.get(name);
if (data != null) {
String path = "quarkus:" + name;
try {
URL url = new URL(null, path, new URLStreamHandler() {
@Override
protected URLConnection openConnection(final URL u) throws IOException {
return new URLConnection(u) {
@Override
public void connect() throws IOException {
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(resources.get(name));
}
};
}
});
return url;
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Invalid URL: " + path);
}
}
return null;
}
private ProtectionDomain createDefaultProtectionDomain(Path applicationClasspath) {
URL url = null;
if (applicationClasspath != null) {
try {
URI uri = applicationClasspath.toUri();
url = uri.toURL();
} catch (MalformedURLException e) {
log.error("URL codeSource location for path " + applicationClasspath + " could not be created.", e);
}
}
CodeSource codesource = new CodeSource(url, (Certificate[]) null);
ProtectionDomain protectionDomain = new ProtectionDomain(codesource, null, this, null);
return protectionDomain;
}
static final class LoadingClass {
final CompletableFuture<Class<?>> value;
final Thread initiator;
LoadingClass(CompletableFuture<Class<?>> value, Thread initiator) {
this.value = value;
this.initiator = initiator;
}
}
}

View File

@@ -1,313 +0,0 @@
package io.quarkus.runner;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.logging.Handler;
import java.util.stream.Collectors;
import org.objectweb.asm.ClassVisitor;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildResult;
import io.quarkus.deployment.ApplicationArchive;
import io.quarkus.deployment.ClassOutput;
import io.quarkus.deployment.QuarkusAugmentor;
import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
import io.quarkus.deployment.builditem.ApplicationClassNameBuildItem;
import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
import io.quarkus.deployment.builditem.DeploymentClassLoaderBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.deployment.configuration.RunTimeConfigurationGenerator;
import io.quarkus.runtime.Application;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ProfileManager;
import io.quarkus.runtime.logging.InitialConfigurator;
/**
* Class that can be used to run quarkus directly, executing the build and runtime
* steps in the same JVM
*/
public class RuntimeRunner implements Runnable, Closeable {
private final Path target;
private final ClassLoader loader;
private final ClassOutput classOutput;
private final TransformerTarget transformerTarget;
private Closeable closeTask;
private final List<Path> additionalArchives;
private final Collection<Path> excludedFromIndexing;
private final List<Consumer<BuildChainBuilder>> chainCustomizers;
private final LaunchMode launchMode;
private final LiveReloadBuildItem liveReloadState;
private final Properties buildSystemProperties;
public RuntimeRunner(Builder builder) {
this.target = builder.target;
this.additionalArchives = new ArrayList<>(builder.additionalArchives);
this.excludedFromIndexing = builder.excludedFromIndexing;
this.chainCustomizers = new ArrayList<>(builder.chainCustomizers);
this.launchMode = builder.launchMode;
this.liveReloadState = builder.liveReloadState;
if (builder.classOutput == null) {
List<Path> allPaths = new ArrayList<>();
allPaths.add(target);
allPaths.addAll(builder.additionalHotDeploymentPaths);
RuntimeClassLoader runtimeClassLoader = new RuntimeClassLoader(builder.classLoader, allPaths,
builder.getWiringClassesDir(), builder.transformerCache);
this.loader = runtimeClassLoader;
this.classOutput = runtimeClassLoader;
this.transformerTarget = runtimeClassLoader;
} else {
this.classOutput = builder.classOutput;
this.transformerTarget = builder.transformerTarget;
this.loader = builder.classLoader;
}
this.buildSystemProperties = builder.buildSystemProperties;
}
@Override
public void close() throws IOException {
if (closeTask != null) {
closeTask.close();
}
}
@Override
public void run() {
Thread.currentThread().setContextClassLoader(loader);
ProfileManager.setLaunchMode(launchMode);
try {
QuarkusAugmentor.Builder builder = QuarkusAugmentor.builder();
builder.setRoot(target);
builder.setClassLoader(loader);
builder.setLaunchMode(launchMode);
builder.setBuildSystemProperties(buildSystemProperties);
if (liveReloadState != null) {
builder.setLiveReloadState(liveReloadState);
}
for (Path i : additionalArchives) {
builder.addAdditionalApplicationArchive(i);
}
builder.excludeFromIndexing(excludedFromIndexing);
for (Consumer<BuildChainBuilder> i : chainCustomizers) {
builder.addBuildChainCustomizer(i);
}
builder.addFinal(BytecodeTransformerBuildItem.class)
.addFinal(ApplicationClassNameBuildItem.class);
BuildResult result = builder.build().run();
List<BytecodeTransformerBuildItem> bytecodeTransformerBuildItems = result
.consumeMulti(BytecodeTransformerBuildItem.class);
if (!bytecodeTransformerBuildItems.isEmpty()) {
Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> functions = new HashMap<>();
for (BytecodeTransformerBuildItem i : bytecodeTransformerBuildItems) {
functions.computeIfAbsent(i.getClassToTransform(), (f) -> new ArrayList<>()).add(i.getVisitorFunction());
}
DeploymentClassLoaderBuildItem deploymentClassLoaderBuildItem = result
.consume(DeploymentClassLoaderBuildItem.class);
ClassLoader previous = Thread.currentThread().getContextClassLoader();
// make sure we use the DeploymentClassLoader for executing transformers since this is the only safe CL for transformations at this point
Thread.currentThread().setContextClassLoader(deploymentClassLoaderBuildItem.getClassLoader());
transformerTarget.setTransformers(functions);
Thread.currentThread().setContextClassLoader(previous);
}
if (loader instanceof RuntimeClassLoader) {
ApplicationArchivesBuildItem archives = result.consume(ApplicationArchivesBuildItem.class);
((RuntimeClassLoader) loader).setApplicationArchives(archives.getApplicationArchives().stream()
.map(ApplicationArchive::getArchiveRoot).collect(Collectors.toList()));
}
for (GeneratedClassBuildItem i : result.consumeMulti(GeneratedClassBuildItem.class)) {
classOutput.writeClass(i.isApplicationClass(), i.getName(), i.getClassData());
}
for (GeneratedResourceBuildItem i : result.consumeMulti(GeneratedResourceBuildItem.class)) {
classOutput.writeResource(i.getName(), i.getClassData());
}
final Application application;
final String className = result.consume(ApplicationClassNameBuildItem.class).getClassName();
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(loader);
Class<? extends Application> appClass;
try {
// force init here
appClass = Class.forName(className, true, loader).asSubclass(Application.class);
} catch (Throwable t) {
// todo: dev mode expects run time config to be available immediately even if static init didn't complete.
try {
final Class<?> configClass = Class.forName(RunTimeConfigurationGenerator.CONFIG_CLASS_NAME, true,
loader);
configClass.getDeclaredMethod(RunTimeConfigurationGenerator.C_CREATE_RUN_TIME_CONFIG.getName())
.invoke(null);
} catch (Throwable t2) {
t.addSuppressed(t2);
}
throw t;
}
application = appClass.newInstance();
application.start(null);
} finally {
Thread.currentThread().setContextClassLoader(old);
}
closeTask = new Closeable() {
@Override
public void close() {
application.stop();
}
};
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// if the log handler is not activated, activate it with a default configuration to flush the messages
if (!InitialConfigurator.DELAYED_HANDLER.isActivated()) {
InitialConfigurator.DELAYED_HANDLER.setHandlers(new Handler[] { InitialConfigurator.createDefaultHandler() });
}
}
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private ClassLoader classLoader;
private Path target;
private Path frameworkClassesPath;
private Path wiringClassesDir;
private Path transformerCache;
private LaunchMode launchMode = LaunchMode.NORMAL;
private final List<Path> additionalArchives = new ArrayList<>();
private Set<Path> excludedFromIndexing = Collections.emptySet();
/**
* additional classes directories that may be hot deployed
*/
private final List<Path> additionalHotDeploymentPaths = new ArrayList<>();
private final List<Consumer<BuildChainBuilder>> chainCustomizers = new ArrayList<>();
private ClassOutput classOutput;
private TransformerTarget transformerTarget;
private LiveReloadBuildItem liveReloadState;
private Properties buildSystemProperties;
public Builder setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
return this;
}
public Builder setTarget(Path target) {
this.target = target;
return this;
}
public Builder setFrameworkClassesPath(Path frameworkClassesPath) {
this.frameworkClassesPath = frameworkClassesPath;
return this;
}
public Builder setWiringClassesDir(Path wiringClassesDir) {
this.wiringClassesDir = wiringClassesDir;
return this;
}
public Builder setTransformerCache(Path transformerCache) {
this.transformerCache = transformerCache;
return this;
}
public Builder addAdditionalArchive(Path additionalArchive) {
this.additionalArchives.add(additionalArchive);
return this;
}
public Builder addAdditionalHotDeploymentPath(Path additionalPath) {
this.additionalHotDeploymentPaths.add(additionalPath);
return this;
}
public Builder addAdditionalArchives(Collection<Path> additionalArchives) {
this.additionalArchives.addAll(additionalArchives);
return this;
}
public Builder addChainCustomizer(Consumer<BuildChainBuilder> chainCustomizer) {
this.chainCustomizers.add(chainCustomizer);
return this;
}
public Builder addChainCustomizers(Collection<Consumer<BuildChainBuilder>> chainCustomizer) {
this.chainCustomizers.addAll(chainCustomizer);
return this;
}
public Builder excludeFromIndexing(Path p) {
if (excludedFromIndexing.isEmpty()) {
excludedFromIndexing = new HashSet<>(1);
}
excludedFromIndexing.add(p);
return this;
}
public Builder setLaunchMode(LaunchMode launchMode) {
this.launchMode = launchMode;
return this;
}
public Builder setClassOutput(ClassOutput classOutput) {
this.classOutput = classOutput;
return this;
}
public Builder setTransformerTarget(TransformerTarget transformerTarget) {
this.transformerTarget = transformerTarget;
return this;
}
public Builder setLiveReloadState(LiveReloadBuildItem liveReloadState) {
this.liveReloadState = liveReloadState;
return this;
}
public Builder setBuildSystemProperties(final Properties buildSystemProperties) {
this.buildSystemProperties = buildSystemProperties;
return this;
}
Path getWiringClassesDir() {
if (wiringClassesDir != null) {
return wiringClassesDir;
}
if (frameworkClassesPath != null && Files.isDirectory(frameworkClassesPath)) {
return frameworkClassesPath;
}
return Paths.get("").normalize().resolve("target").resolve("test-classes");
}
public RuntimeRunner build() {
final RuntimeRunner runtimeRunner = new RuntimeRunner(this);
excludedFromIndexing = Collections.emptySet();
return runtimeRunner;
}
}
}

View File

@@ -1,13 +0,0 @@
package io.quarkus.runner;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import org.objectweb.asm.ClassVisitor;
public interface TransformerTarget {
void setTransformers(Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> functions);
}

View File

@@ -0,0 +1,225 @@
package io.quarkus.runner.bootstrap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import io.quarkus.bootstrap.app.AdditionalDependency;
import io.quarkus.bootstrap.app.ArtifactResult;
import io.quarkus.bootstrap.app.AugmentAction;
import io.quarkus.bootstrap.app.AugmentResult;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.builder.BuildChain;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildExecutionBuilder;
import io.quarkus.builder.BuildResult;
import io.quarkus.builder.item.BuildItem;
import io.quarkus.deployment.ExtensionLoader;
import io.quarkus.deployment.QuarkusAugmentor;
import io.quarkus.deployment.builditem.ApplicationClassNameBuildItem;
import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
import io.quarkus.deployment.builditem.ConfigDescriptionBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
import io.quarkus.deployment.pkg.builditem.JarBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ProfileManager;
/**
* The augmentation task that produces the application.
*/
public class AugmentActionImpl implements AugmentAction {
private final QuarkusBootstrap quarkusBootstrap;
private final CuratedApplication curatedApplication;
private final LaunchMode launchMode;
private final List<Consumer<BuildChainBuilder>> chainCustomizers;
/**
* A map that is shared between all re-runs of the same augment instance. This is
* only really relevant in dev mode, however it is present in all modes for consistency.
*
*/
private final Map<Class<?>, Object> reloadContext = new ConcurrentHashMap<>();
public AugmentActionImpl(CuratedApplication curatedApplication) {
this(curatedApplication, Collections.emptyList());
}
public AugmentActionImpl(CuratedApplication curatedApplication, List<Consumer<BuildChainBuilder>> chainCustomizers) {
this.quarkusBootstrap = curatedApplication.getQuarkusBootstrap();
this.curatedApplication = curatedApplication;
this.chainCustomizers = chainCustomizers;
this.launchMode = quarkusBootstrap.getMode() == QuarkusBootstrap.Mode.PROD ? LaunchMode.NORMAL
: quarkusBootstrap.getMode() == QuarkusBootstrap.Mode.TEST ? LaunchMode.TEST : LaunchMode.DEVELOPMENT;
}
@Override
public AugmentResult createProductionApplication() {
try {
if (launchMode != LaunchMode.NORMAL) {
throw new IllegalStateException("Can only create a production application when using NORMAL launch mode");
}
BuildResult result = runAugment(true, Collections.emptySet(), ArtifactResultBuildItem.class);
JarBuildItem jarBuildItem = result.consumeOptional(JarBuildItem.class);
NativeImageBuildItem nativeImageBuildItem = result.consumeOptional(NativeImageBuildItem.class);
return new AugmentResult(result.consumeMulti(ArtifactResultBuildItem.class).stream()
.map(a -> new ArtifactResult(a.getPath(), a.getType(), a.getAdditionalPaths()))
.collect(Collectors.toList()),
jarBuildItem != null ? jarBuildItem.toJarResult() : null,
nativeImageBuildItem != null ? nativeImageBuildItem.getPath() : null);
} finally {
curatedApplication.close();
}
}
@Override
public StartupActionImpl createInitialRuntimeApplication() {
if (launchMode == LaunchMode.NORMAL) {
throw new IllegalStateException("Cannot launch a runtime application with NORMAL launch mode");
}
BuildResult result = runAugment(true, Collections.emptySet(), GeneratedClassBuildItem.class,
GeneratedResourceBuildItem.class, BytecodeTransformerBuildItem.class, ApplicationClassNameBuildItem.class);
return new StartupActionImpl(curatedApplication, result);
}
@Override
public StartupActionImpl reloadExistingApplication(Set<String> changedResources) {
if (launchMode != LaunchMode.DEVELOPMENT) {
throw new IllegalStateException("Only application with launch mode DEVELOPMENT can restart");
}
BuildResult result = runAugment(false, changedResources, GeneratedClassBuildItem.class,
GeneratedResourceBuildItem.class, BytecodeTransformerBuildItem.class, ApplicationClassNameBuildItem.class);
return new StartupActionImpl(curatedApplication, result);
}
/**
* Runs a custom augmentation action, such as generating config.
*
* @param chainBuild A consumer that customises the build to select the output targets
* @param executionBuild A consumer that can see the initial build execution
* @return The build result
*/
public BuildResult runCustomAction(Consumer<BuildChainBuilder> chainBuild, Consumer<BuildExecutionBuilder> executionBuild) {
ProfileManager.setLaunchMode(launchMode);
QuarkusClassLoader classLoader = curatedApplication.getAugmentClassLoader();
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(classLoader);
final BuildChainBuilder chainBuilder = BuildChain.builder();
ExtensionLoader.loadStepsFrom(classLoader).accept(chainBuilder);
chainBuilder.loadProviders(classLoader);
for (Consumer<BuildChainBuilder> c : chainCustomizers) {
c.accept(chainBuilder);
}
chainBuilder
.addInitial(ShutdownContextBuildItem.class)
.addInitial(LaunchModeBuildItem.class)
.addInitial(LiveReloadBuildItem.class)
.addFinal(ConfigDescriptionBuildItem.class);
chainBuild.accept(chainBuilder);
BuildChain chain = chainBuilder
.build();
BuildExecutionBuilder execBuilder = chain.createExecutionBuilder("main")
.produce(new LaunchModeBuildItem(launchMode))
.produce(new ShutdownContextBuildItem())
.produce(new LiveReloadBuildItem());
executionBuild.accept(execBuilder);
return execBuilder
.execute();
} catch (Exception e) {
throw new RuntimeException("Failed to run task", e);
} finally {
try {
ConfigProviderResolver.instance().releaseConfig(ConfigProviderResolver.instance().getConfig(classLoader));
} catch (Exception ignore) {
}
Thread.currentThread().setContextClassLoader(old);
}
}
private BuildResult runAugment(boolean firstRun, Set<String> changedResources, Class<? extends BuildItem>... finalOutputs) {
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(curatedApplication.getAugmentClassLoader());
ProfileManager.setLaunchMode(launchMode);
QuarkusClassLoader classLoader = curatedApplication.getAugmentClassLoader();
QuarkusAugmentor.Builder builder = QuarkusAugmentor.builder()
.setRoot(quarkusBootstrap.getApplicationRoot())
.setClassLoader(classLoader)
.addFinal(ApplicationClassNameBuildItem.class)
.setTargetDir(quarkusBootstrap.getTargetDirectory())
.setDeploymentClassLoader(curatedApplication.createDeploymentClassLoader())
.setBuildSystemProperties(quarkusBootstrap.getBuildSystemProperties())
.setEffectiveModel(curatedApplication.getAppModel());
if (quarkusBootstrap.getBaseName() != null) {
builder.setBaseName(quarkusBootstrap.getBaseName());
}
builder.setLaunchMode(launchMode);
if (firstRun) {
builder.setLiveReloadState(new LiveReloadBuildItem(false, Collections.emptySet(), reloadContext));
} else {
builder.setLiveReloadState(new LiveReloadBuildItem(true, changedResources, reloadContext));
}
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) {
//this gets added to the class path either way
//but we only need to add it to the additional app archives
//if it is forced as an app archive
if (i.isForceApplicationArchive()) {
builder.addAdditionalApplicationArchive(i.getArchivePath());
}
}
builder.excludeFromIndexing(quarkusBootstrap.getExcludeFromClassPath());
for (Consumer<BuildChainBuilder> i : chainCustomizers) {
builder.addBuildChainCustomizer(i);
}
for (Class<? extends BuildItem> i : finalOutputs) {
builder.addFinal(i);
}
try {
return builder.build().run();
} catch (Exception e) {
throw new RuntimeException(e);
}
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
/**
* A task that can be used in isolated environments to do a build
*/
@SuppressWarnings("unused")
public static class BuildTask implements BiConsumer<CuratedApplication, Map<String, Object>> {
@Override
public void accept(CuratedApplication application, Map<String, Object> stringObjectMap) {
AugmentAction action = new AugmentActionImpl(application);
action.createProductionApplication();
}
}
}

View File

@@ -0,0 +1,157 @@
package io.quarkus.runner.bootstrap;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jboss.logging.Logger;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildExecutionBuilder;
import io.quarkus.builder.BuildResult;
import io.quarkus.deployment.builditem.ArchiveRootBuildItem;
import io.quarkus.deployment.builditem.ConfigDescriptionBuildItem;
/**
* This phase generates an example configuration file
*
* @author Stuart Douglas
*/
public class GenerateConfigTask {
private static final Logger log = Logger.getLogger(GenerateConfigTask.class);
private final Path configFile;
public GenerateConfigTask(Path configFile) {
this.configFile = configFile;
}
public Path run(CuratedApplication application) {
//first lets look for some config, as it is not on the current class path
//and we need to load it to run the build process
try {
Path temp = Files.createTempDirectory("empty");
try {
AugmentActionImpl augmentAction = new AugmentActionImpl(application, Collections.emptyList());
BuildResult buildResult = augmentAction.runCustomAction(new Consumer<BuildChainBuilder>() {
@Override
public void accept(BuildChainBuilder chainBuilder) {
chainBuilder.addFinal(ConfigDescriptionBuildItem.class);
chainBuilder.addInitial(ArchiveRootBuildItem.class);
}
}, new Consumer<BuildExecutionBuilder>() {
@Override
public void accept(BuildExecutionBuilder buildExecutionBuilder) {
buildExecutionBuilder.produce(new ArchiveRootBuildItem(temp));
}
});
List<ConfigDescriptionBuildItem> descriptions = buildResult.consumeMulti(ConfigDescriptionBuildItem.class);
Collections.sort(descriptions);
String existing = "";
if (Files.exists(configFile)) {
existing = new String(Files.readAllBytes(configFile), StandardCharsets.UTF_8);
}
StringBuilder sb = new StringBuilder();
for (ConfigDescriptionBuildItem i : descriptions) {
//we don't want to add these if they already exist
//either in commended or uncommented form
if (existing.contains("\n" + i.getPropertyName() + "=") ||
existing.contains("\n#" + i.getPropertyName() + "=")) {
continue;
}
sb.append("\n#\n");
sb.append(formatDocs(i.getDocs()));
sb.append("\n#\n#");
sb.append(i.getPropertyName() + "=" + i.getDefaultValue());
sb.append("\n");
}
Files.createDirectories(configFile.getParent());
Files.write(configFile, sb.toString().getBytes(StandardCharsets.UTF_8),
Files.exists(configFile) ? new OpenOption[] { StandardOpenOption.APPEND } : new OpenOption[] {});
} finally {
Files.deleteIfExists(temp);
}
} catch (Exception e) {
throw new RuntimeException("Failed to generate config file", e);
}
return configFile;
}
private String formatDocs(String docs) {
if (docs == null) {
return "";
}
StringBuilder builder = new StringBuilder();
boolean lastEmpty = false;
boolean first = true;
for (String line : docs.replace("<p>", "\n").split("\n")) {
//process line by line
String trimmed = line.trim();
//if the lines are empty we only include a single empty line at most, and add a # character
if (trimmed.isEmpty()) {
if (!lastEmpty && !first) {
lastEmpty = true;
builder.append("\n#");
}
continue;
}
//add the newlines
lastEmpty = false;
if (first) {
first = false;
} else {
builder.append("\n");
}
//replace some special characters, others are taken care of by regex below
builder.append("# " + trimmed.replace("\n", "\n#")
.replace("<ul>", "")
.replace("</ul>", "")
.replace("<li>", " - ")
.replace("</li>", ""));
}
String ret = builder.toString();
//replace @code
ret = Pattern.compile("\\{@code (.*?)\\}").matcher(ret).replaceAll("'$1'");
//replace @link with a reference to the field name
Matcher matcher = Pattern.compile("\\{@link #(.*?)\\}").matcher(ret);
while (matcher.find()) {
ret = ret.replace(matcher.group(0), "'" + configify(matcher.group(1)) + "'");
}
return ret;
}
private String configify(String group) {
//replace uppercase characters with a - followed by lowercase
StringBuilder ret = new StringBuilder();
for (int i = 0; i < group.length(); ++i) {
char c = group.charAt(i);
if (Character.isUpperCase(c)) {
ret.append("-");
ret.append(Character.toLowerCase(c));
} else {
ret.append(c);
}
}
return ret.toString();
}
}

View File

@@ -0,0 +1,83 @@
package io.quarkus.runner.bootstrap;
import java.io.Closeable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Optional;
import org.eclipse.microprofile.config.ConfigProvider;
import io.quarkus.bootstrap.app.RunningQuarkusApplication;
public class RunningQuarkusApplicationImpl implements RunningQuarkusApplication {
private final Closeable closeTask;
private final ClassLoader classLoader;
public RunningQuarkusApplicationImpl(Closeable closeTask, ClassLoader classLoader) {
this.closeTask = closeTask;
this.classLoader = classLoader;
}
@Override
public ClassLoader getClassLoader() {
return classLoader;
}
@Override
public void close() throws Exception {
closeTask.close();
}
@Override
public <T> Optional<T> getConfigValue(String key, Class<T> type) {
//the config is in an isolated CL
//we need to extract it via reflection
//this is pretty yuck, but I don't really see a solution
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Class<?> configProviderClass = classLoader.loadClass(ConfigProvider.class.getName());
Method getConfig = configProviderClass.getMethod("getConfig", ClassLoader.class);
Thread.currentThread().setContextClassLoader(classLoader);
Object config = getConfig.invoke(null, classLoader);
return (Optional<T>) getConfig.getReturnType().getMethod("getOptionalValue", String.class, Class.class)
.invoke(config, key, type);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
@Override
public Iterable<String> getConfigKeys() {
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Class<?> configProviderClass = classLoader.loadClass(ConfigProvider.class.getName());
Method getConfig = configProviderClass.getMethod("getConfig", ClassLoader.class);
Thread.currentThread().setContextClassLoader(classLoader);
Object config = getConfig.invoke(null, classLoader);
return (Iterable<String>) getConfig.getReturnType().getMethod("getPropertyNames", String.class, Class.class)
.invoke(config);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
@Override
public Object instance(Class<?> clazz, Annotation... qualifiers) {
try {
Class<?> actualClass = Class.forName(clazz.getName(), true,
classLoader);
Class<?> cdi = classLoader.loadClass("javax.enterprise.inject.spi.CDI");
Object instance = cdi.getMethod("current").invoke(null);
Method selectMethod = cdi.getMethod("select", Class.class, Annotation[].class);
Object cdiInstance = selectMethod.invoke(instance, actualClass, qualifiers);
return selectMethod.getReturnType().getMethod("get").invoke(cdiInstance);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,170 @@
package io.quarkus.runner.bootstrap;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import org.jboss.logging.Logger;
import org.objectweb.asm.ClassVisitor;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.app.RunningQuarkusApplication;
import io.quarkus.bootstrap.app.StartupAction;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.builder.BuildResult;
import io.quarkus.deployment.builditem.ApplicationClassNameBuildItem;
import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
import io.quarkus.deployment.builditem.DeploymentClassLoaderBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.configuration.RunTimeConfigurationGenerator;
public class StartupActionImpl implements StartupAction {
private static final Logger log = Logger.getLogger(StartupActionImpl.class);
static final String DEBUG_CLASSES_DIR = System.getProperty("quarkus.debug.generated-classes-dir");
private final CuratedApplication curatedApplication;
private final BuildResult buildResult;
public StartupActionImpl(CuratedApplication curatedApplication, BuildResult buildResult) {
this.curatedApplication = curatedApplication;
this.buildResult = buildResult;
}
/**
* Runs the application, and returns a handle that can be used to shut it down.
*/
public RunningQuarkusApplication run(String... args) throws Exception {
//first
Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> bytecodeTransformers = extractTransformers();
QuarkusClassLoader baseClassLoader = curatedApplication.getBaseRuntimeClassLoader();
ClassLoader transformerClassLoader = buildResult.consume(DeploymentClassLoaderBuildItem.class).getClassLoader();
QuarkusClassLoader runtimeClassLoader;
//so we have some differences between dev and test mode here.
//test mode only has a single class loader, while dev uses a disposable runtime class loader
//that is discarded between restarts
if (curatedApplication.getQuarkusBootstrap().getMode() == QuarkusBootstrap.Mode.DEV) {
baseClassLoader.reset(extractGeneratedResources(false), bytecodeTransformers, transformerClassLoader);
runtimeClassLoader = curatedApplication.createRuntimeClassLoader(baseClassLoader,
bytecodeTransformers,
transformerClassLoader, extractGeneratedResources(true));
} else {
Map<String, byte[]> resources = new HashMap<>();
resources.putAll(extractGeneratedResources(false));
resources.putAll(extractGeneratedResources(true));
baseClassLoader.reset(resources, bytecodeTransformers, transformerClassLoader);
runtimeClassLoader = baseClassLoader;
}
//we have our class loaders
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(runtimeClassLoader);
final String className = buildResult.consume(ApplicationClassNameBuildItem.class).getClassName();
Class<?> appClass;
try {
// force init here
appClass = Class.forName(className, true, runtimeClassLoader);
} catch (Throwable t) {
// todo: dev mode expects run time config to be available immediately even if static init didn't complete.
try {
final Class<?> configClass = Class.forName(RunTimeConfigurationGenerator.CONFIG_CLASS_NAME, true,
runtimeClassLoader);
configClass.getDeclaredMethod(RunTimeConfigurationGenerator.C_CREATE_RUN_TIME_CONFIG.getName())
.invoke(null);
} catch (Throwable t2) {
t.addSuppressed(t2);
}
throw t;
}
Method start = appClass.getMethod("start", String[].class);
Object application = appClass.newInstance();
start.invoke(application, (Object) args);
Closeable closeTask = (Closeable) application;
return new RunningQuarkusApplicationImpl(new Closeable() {
@Override
public void close() throws IOException {
try {
try {
closeTask.close();
} finally {
runtimeClassLoader.close();
}
} finally {
if (curatedApplication.getQuarkusBootstrap().getMode() == QuarkusBootstrap.Mode.TEST) {
//for tests we just always shut down the curated application, as it is only used once
//dev mode might be about to restart, so we leave it
curatedApplication.close();
}
}
}
}, runtimeClassLoader);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause();
}
throw new RuntimeException("Failed to start Quarkus", e.getCause());
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
private Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> extractTransformers() {
Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> bytecodeTransformers = new HashMap<>();
List<BytecodeTransformerBuildItem> transformers = buildResult.consumeMulti(BytecodeTransformerBuildItem.class);
for (BytecodeTransformerBuildItem i : transformers) {
List<BiFunction<String, ClassVisitor, ClassVisitor>> list = bytecodeTransformers.get(i.getClassToTransform());
if (list == null) {
bytecodeTransformers.put(i.getClassToTransform(), list = new ArrayList<>());
}
list.add(i.getVisitorFunction());
}
return bytecodeTransformers;
}
private Map<String, byte[]> extractGeneratedResources(boolean applicationClasses) {
Map<String, byte[]> data = new HashMap<>();
for (GeneratedClassBuildItem i : buildResult.consumeMulti(GeneratedClassBuildItem.class)) {
if (i.isApplicationClass() == applicationClasses) {
data.put(i.getName().replace(".", "/") + ".class", i.getClassData());
if (DEBUG_CLASSES_DIR != null) {
try {
File debugPath = new File(DEBUG_CLASSES_DIR);
if (!debugPath.exists()) {
debugPath.mkdir();
}
File classFile = new File(debugPath, i.getName() + ".class");
classFile.getParentFile().mkdirs();
try (FileOutputStream classWriter = new FileOutputStream(classFile)) {
classWriter.write(i.getClassData());
}
log.infof("Wrote %s", classFile.getAbsolutePath());
} catch (Exception t) {
log.errorf(t, "Failed to write debug class files %s", i.getName());
}
}
}
}
if (applicationClasses) {
for (GeneratedResourceBuildItem i : buildResult.consumeMulti(GeneratedResourceBuildItem.class)) {
data.put(i.getName(), i.getClassData());
}
}
return data;
}
}

View File

@@ -1,4 +1,4 @@
package io.quarkus.creator.phase.runnerjar.test; package io.quarkus.deployment.runnerjar;
import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsArtifact;
import io.quarkus.bootstrap.resolver.TsDependency; import io.quarkus.bootstrap.resolver.TsDependency;

View File

@@ -1,4 +1,4 @@
package io.quarkus.creator.phase.runnerjar.test; package io.quarkus.deployment.runnerjar;
import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsArtifact;
import io.quarkus.bootstrap.resolver.TsQuarkusExt; import io.quarkus.bootstrap.resolver.TsQuarkusExt;

View File

@@ -1,4 +1,4 @@
package io.quarkus.creator.phase.runnerjar.test; package io.quarkus.deployment.runnerjar;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -18,10 +18,12 @@ import java.util.jar.Attributes;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.stream.Stream; import java.util.stream.Stream;
import io.quarkus.bootstrap.app.AugmentAction;
import io.quarkus.bootstrap.app.AugmentResult;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsArtifact;
import io.quarkus.creator.CuratedApplicationCreator; import io.quarkus.bootstrap.resolver.update.CreatorOutcomeTestBase;
import io.quarkus.creator.phase.augment.AugmentOutcome;
import io.quarkus.creator.phase.augment.AugmentTask;
public abstract class ExecutableOutputOutcomeTestBase extends CreatorOutcomeTestBase { public abstract class ExecutableOutputOutcomeTestBase extends CreatorOutcomeTestBase {
@@ -35,9 +37,10 @@ public abstract class ExecutableOutputOutcomeTestBase extends CreatorOutcomeTest
} }
@Override @Override
protected void testCreator(CuratedApplicationCreator creator) throws Exception { protected void testCreator(QuarkusBootstrap creator) throws Exception {
final AugmentOutcome outcome = creator CuratedApplication curated = creator.bootstrap();
.runTask(AugmentTask.builder().build()); AugmentAction action = curated.createAugmentor();
AugmentResult outcome = action.createProductionApplication();
final Path libDir = outcome.getJar().getLibraryDir(); final Path libDir = outcome.getJar().getLibraryDir();
assertTrue(Files.isDirectory(libDir)); assertTrue(Files.isDirectory(libDir));
@@ -104,4 +107,4 @@ public abstract class ExecutableOutputOutcomeTestBase extends CreatorOutcomeTest
fail(buf.toString()); fail(buf.toString());
} }
} }
} }

View File

@@ -1,4 +1,4 @@
package io.quarkus.creator.phase.runnerjar.test; package io.quarkus.deployment.runnerjar;
import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsArtifact;
import io.quarkus.bootstrap.resolver.TsQuarkusExt; import io.quarkus.bootstrap.resolver.TsQuarkusExt;

View File

@@ -1,4 +1,4 @@
package io.quarkus.creator.phase.runnerjar.test; package io.quarkus.deployment.runnerjar;
import io.quarkus.bootstrap.resolver.TsArtifact; import io.quarkus.bootstrap.resolver.TsArtifact;
import io.quarkus.bootstrap.resolver.TsQuarkusExt; import io.quarkus.bootstrap.resolver.TsQuarkusExt;

View File

@@ -0,0 +1,52 @@
package io.quarkus.runner.classloading;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import io.quarkus.bootstrap.classloading.ClassPathResource;
import io.quarkus.bootstrap.classloading.DirectoryClassPathElement;
import io.quarkus.deployment.util.FileUtil;
public class DirectoryClassPathElementTestCase {
static Path root;
@BeforeAll
public static void before() throws Exception {
root = Files.createTempDirectory("quarkus-test");
Files.write(root.resolve("a.txt"), "A file".getBytes(StandardCharsets.UTF_8));
Files.write(root.resolve("b.txt"), "another file".getBytes(StandardCharsets.UTF_8));
Files.createDirectories(root.resolve("foo"));
Files.write(root.resolve("foo/sub.txt"), "subdir file".getBytes(StandardCharsets.UTF_8));
}
@AfterAll
public static void after() throws Exception {
FileUtil.deleteDirectory(root);
}
@Test
public void testGetAllResources() {
DirectoryClassPathElement f = new DirectoryClassPathElement(root);
Set<String> res = f.getProvidedResources();
Assertions.assertEquals(4, res.size());
Assertions.assertEquals(new HashSet<>(Arrays.asList("a.txt", "b.txt", "foo", "foo/sub.txt")), res);
}
@Test
public void testGetResource() {
DirectoryClassPathElement f = new DirectoryClassPathElement(root);
ClassPathResource res = f.getResource("foo/sub.txt");
Assertions.assertNotNull(res);
Assertions.assertEquals("subdir file", new String(res.getData(), StandardCharsets.UTF_8));
}
}

View File

@@ -0,0 +1,50 @@
package io.quarkus.runner.classloading;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import io.quarkus.bootstrap.classloading.ClassPathResource;
import io.quarkus.bootstrap.classloading.MemoryClassPathElement;
public class MemoryClassPathElementTestCase {
static Map<String, byte[]> data;
@BeforeAll
public static void before() throws Exception {
data = new HashMap<>();
data.put("a.txt", "A file".getBytes(StandardCharsets.UTF_8));
data.put("b.txt", "another file".getBytes(StandardCharsets.UTF_8));
data.put("foo/sub.txt", "subdir file".getBytes(StandardCharsets.UTF_8));
}
@AfterAll
public static void after() throws Exception {
data = null;
}
@Test
public void testGetAllResources() {
MemoryClassPathElement f = new MemoryClassPathElement(data);
Set<String> res = f.getProvidedResources();
Assertions.assertEquals(3, res.size());
Assertions.assertEquals(new HashSet<>(Arrays.asList("a.txt", "b.txt", "foo/sub.txt")), res);
}
@Test
public void testGetResource() {
MemoryClassPathElement f = new MemoryClassPathElement(data);
ClassPathResource res = f.getResource("foo/sub.txt");
Assertions.assertNotNull(res);
Assertions.assertEquals("subdir file", new String(res.getData(), StandardCharsets.UTF_8));
}
}

20
core/devmode-spi/pom.xml Normal file
View File

@@ -0,0 +1,20 @@
<?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">
<parent>
<artifactId>quarkus-build-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../../build-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>quarkus-development-mode-spi</artifactId>
<name>Quarkus - Development mode - SPI</name>
<description>SPI classes for Quarkus Development mode.</description>
<dependencies>
</dependencies>
</project>

View File

@@ -1,12 +1,10 @@
package io.quarkus.deployment.devmode; package io.quarkus.dev.spi;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
public interface HotReplacementContext { public interface HotReplacementContext {
Path getClassesDir(); Path getClassesDir();
@@ -35,7 +33,7 @@ public interface HotReplacementContext {
/** /**
* The consumer is invoked if only files which don't require restart are modified. * The consumer is invoked if only files which don't require restart are modified.
* *
* @param consumer The input is a set of chaned file paths * @param consumer The input is a set of changed file paths
* @see HotDeploymentWatchedFileBuildItem#isRestartNeeded() * @see HotDeploymentWatchedFileBuildItem#isRestartNeeded()
*/ */
void consumeNoRestartChanges(Consumer<Set<String>> consumer); void consumeNoRestartChanges(Consumer<Set<String>> consumer);

View File

@@ -1,4 +1,4 @@
package io.quarkus.deployment.devmode; package io.quarkus.dev.spi;
/** /**
* Service interface that is used to abstract away the details of how hot deployment is performed * Service interface that is used to abstract away the details of how hot deployment is performed

View File

@@ -18,6 +18,10 @@
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId> <artifactId>quarkus-core-deployment</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-development-mode-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.jboss.logmanager</groupId> <groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager-embedded</artifactId> <artifactId>jboss-logmanager-embedded</artifactId>

View File

@@ -5,15 +5,12 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque; import java.util.Deque;
import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -26,6 +23,9 @@ import java.util.regex.Pattern;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.model.AppDependency;
/** /**
* Class that handles compilation of source files * Class that handles compilation of source files
* *
@@ -44,32 +44,16 @@ public class ClassLoaderCompiler {
private final Set<String> allHandledExtensions; private final Set<String> allHandledExtensions;
public ClassLoaderCompiler(ClassLoader classLoader, public ClassLoaderCompiler(ClassLoader classLoader,
CuratedApplication application,
List<CompilationProvider> compilationProviders, List<CompilationProvider> compilationProviders,
DevModeContext context) DevModeContext context)
throws IOException { throws IOException {
this.compilationProviders = compilationProviders; this.compilationProviders = compilationProviders;
Set<URL> urls = new HashSet<>(); Set<URL> urls = new HashSet<>();
ClassLoader c = classLoader; for (AppDependency i : application.getAppModel().getUserDependencies()) {
while (c != null) { urls.add(i.getArtifact().getPath().toUri().toURL());
if (c instanceof URLClassLoader) {
urls.addAll(Arrays.asList(((URLClassLoader) c).getURLs()));
}
c = c.getParent();
} }
//this is pretty yuck, but under JDK11 the URLClassLoader trick does not work
Enumeration<URL> manifests = classLoader.getResources("META-INF/MANIFEST.MF");
while (manifests.hasMoreElements()) {
URL url = manifests.nextElement();
if (url.getProtocol().equals("jar")) {
String path = url.getPath();
if (path.startsWith("file:")) {
path = path.substring(5, path.lastIndexOf('!'));
urls.add(new File(URLDecoder.decode(path, StandardCharsets.UTF_8.name())).toURI().toURL());
}
}
}
urls.addAll(context.getClassPath()); urls.addAll(context.getClassPath());
Set<String> parsedFiles = new HashSet<>(); Set<String> parsedFiles = new HashSet<>();

View File

@@ -12,6 +12,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import io.quarkus.bootstrap.model.AppModel;
/** /**
* Object that is used to pass context data from the plugin doing the invocation * Object that is used to pass context data from the plugin doing the invocation
* into the dev mode process using java serialization. * into the dev mode process using java serialization.
@@ -29,10 +31,12 @@ public class DevModeContext implements Serializable {
private final List<File> classesRoots = new ArrayList<>(); private final List<File> classesRoots = new ArrayList<>();
private File frameworkClassesDir; private File frameworkClassesDir;
private File cacheDir; private File cacheDir;
private File projectDir;
private boolean test; private boolean test;
private boolean abortOnFailedStart; private boolean abortOnFailedStart;
// the jar file which is used to launch the DevModeMain // the jar file which is used to launch the DevModeMain
private File devModeRunnerJarFile; private File devModeRunnerJarFile;
private boolean localProjectDiscovery = true;
private List<String> compilerOptions; private List<String> compilerOptions;
private String sourceJavaVersion; private String sourceJavaVersion;
@@ -41,6 +45,17 @@ public class DevModeContext implements Serializable {
private List<String> compilerPluginArtifacts; private List<String> compilerPluginArtifacts;
private List<String> compilerPluginsOptions; private List<String> compilerPluginsOptions;
private AppModel appModel;
public boolean isLocalProjectDiscovery() {
return localProjectDiscovery;
}
public DevModeContext setLocalProjectDiscovery(boolean localProjectDiscovery) {
this.localProjectDiscovery = localProjectDiscovery;
return this;
}
public List<URL> getClassPath() { public List<URL> getClassPath() {
return classPath; return classPath;
} }
@@ -149,6 +164,24 @@ public class DevModeContext implements Serializable {
this.devModeRunnerJarFile = devModeRunnerJarFile; this.devModeRunnerJarFile = devModeRunnerJarFile;
} }
public File getProjectDir() {
return projectDir;
}
public DevModeContext setProjectDir(File projectDir) {
this.projectDir = projectDir;
return this;
}
public AppModel getAppModel() {
return appModel;
}
public DevModeContext setAppModel(AppModel appModel) {
this.appModel = appModel;
return this;
}
public static class ModuleInfo implements Serializable { public static class ModuleInfo implements Serializable {
private final String name; private final String name;

View File

@@ -2,38 +2,24 @@ package io.quarkus.dev;
import java.io.Closeable; import java.io.Closeable;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.net.URI;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;
import java.util.function.Consumer;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import io.quarkus.builder.BuildChainBuilder; import io.quarkus.bootstrap.app.AdditionalDependency;
import io.quarkus.builder.BuildContext; import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.builder.BuildStep; import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.deployment.builditem.ApplicationClassPredicateBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.deployment.devmode.HotReplacementSetup;
import io.quarkus.runner.RuntimeRunner;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.Timing;
import io.quarkus.runtime.configuration.QuarkusConfigFactory;
/** /**
* The main entry point for the dev mojo execution * The main entry point for the dev mojo execution
@@ -43,34 +29,26 @@ public class DevModeMain implements Closeable {
public static final String DEV_MODE_CONTEXT = "META-INF/dev-mode-context.dat"; public static final String DEV_MODE_CONTEXT = "META-INF/dev-mode-context.dat";
private static final Logger log = Logger.getLogger(DevModeMain.class); private static final Logger log = Logger.getLogger(DevModeMain.class);
private static volatile ClassLoader currentAppClassLoader;
private static volatile URLClassLoader runtimeCl;
private final DevModeContext context; private final DevModeContext context;
private static volatile Closeable runner; private static volatile CuratedApplication curatedApplication;
static volatile Throwable deploymentProblem; private Closeable realCloseable;
static volatile Throwable compileProblem;
static volatile RuntimeUpdatesProcessor runtimeUpdatesProcessor;
private List<HotReplacementSetup> hotReplacement = new ArrayList<>();
private final Map<Class<?>, Object> liveReloadContext = new ConcurrentHashMap<>();
public DevModeMain(DevModeContext context) { public DevModeMain(DevModeContext context) {
this.context = context; this.context = context;
} }
public static void main(String... args) throws Exception { public static void main(String... args) throws Exception {
Timing.staticInitStarted();
try (InputStream devModeCp = DevModeMain.class.getClassLoader().getResourceAsStream(DEV_MODE_CONTEXT)) { try (InputStream devModeCp = DevModeMain.class.getClassLoader().getResourceAsStream(DEV_MODE_CONTEXT)) {
DevModeContext context = (DevModeContext) new ObjectInputStream(new DataInputStream(devModeCp)).readObject(); DevModeContext context = (DevModeContext) new ObjectInputStream(new DataInputStream(devModeCp)).readObject();
new DevModeMain(context).start(); try (DevModeMain devModeMain = new DevModeMain(context)) {
devModeMain.start();
LockSupport.park(); LockSupport.park();
}
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally {
} }
} }
@@ -82,204 +60,58 @@ public class DevModeMain implements Closeable {
} }
} }
for (HotReplacementSetup service : ServiceLoader.load(HotReplacementSetup.class)) {
hotReplacement.add(service);
}
runtimeUpdatesProcessor = setupRuntimeCompilation(context);
if (runtimeUpdatesProcessor != null) {
runtimeUpdatesProcessor.checkForFileChange();
runtimeUpdatesProcessor.checkForChangedClasses();
}
//TODO: we can't handle an exception on startup with hot replacement, as Undertow might not have started
doStart(false, Collections.emptySet());
if (deploymentProblem != null || compileProblem != null) {
if (context.isAbortOnFailedStart()) {
throw new RuntimeException(deploymentProblem == null ? compileProblem : deploymentProblem);
}
}
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
synchronized (DevModeMain.class) {
if (runner != null) {
try {
runner.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (runtimeCl != null) {
try {
runtimeCl.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}, "Quarkus Shutdown Thread"));
}
private synchronized void doStart(boolean liveReload, Set<String> changedResources) {
try { try {
final URL[] urls = new URL[context.getClassesRoots().size()]; URL thisArchive = getClass().getResource(DevModeMain.class.getSimpleName() + ".class");
for (int i = 0; i < context.getClassesRoots().size(); i++) { int endIndex = thisArchive.getPath().indexOf("!");
urls[i] = context.getClassesRoots().get(i).toURI().toURL(); Path path;
if (endIndex != -1) {
path = Paths.get(new URI(thisArchive.getPath().substring(0, endIndex)));
} else {
path = Paths.get(thisArchive.toURI());
path = path.getParent();
for (char i : DevModeMain.class.getName().toCharArray()) {
if (i == '.') {
path = path.getParent();
}
}
} }
runtimeCl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); QuarkusBootstrap.Builder bootstrapBuilder = QuarkusBootstrap.builder(context.getClassesRoots().get(0).toPath())
currentAppClassLoader = runtimeCl; .setIsolateDeployment(true)
ClassLoader old = Thread.currentThread().getContextClassLoader(); .setLocalProjectDiscovery(context.isLocalProjectDiscovery())
//we can potentially throw away this class loader, and reload the app .addAdditionalDeploymentArchive(path)
try { .setMode(QuarkusBootstrap.Mode.DEV);
Thread.currentThread().setContextClassLoader(runtimeCl); if (context.getProjectDir() != null) {
RuntimeRunner.Builder builder = RuntimeRunner.builder() bootstrapBuilder.setProjectRoot(context.getProjectDir().toPath());
.setLaunchMode(LaunchMode.DEVELOPMENT) } else {
.setLiveReloadState(new LiveReloadBuildItem(liveReload, changedResources, liveReloadContext)) bootstrapBuilder.setProjectRoot(new File(".").toPath());
.setClassLoader(runtimeCl)
// just use the first item in classesRoot which is where the actual class files are written
.setTarget(context.getClassesRoots().get(0).toPath())
.setTransformerCache(context.getCacheDir().toPath());
if (context.getFrameworkClassesDir() != null) {
builder.setFrameworkClassesPath(context.getFrameworkClassesDir().toPath());
}
List<Path> addAdditionalHotDeploymentPaths = new ArrayList<>();
for (DevModeContext.ModuleInfo i : context.getModules()) {
if (i.getClassesPath() != null) {
Path classesPath = Paths.get(i.getClassesPath());
addAdditionalHotDeploymentPaths.add(classesPath);
builder.addAdditionalHotDeploymentPath(classesPath);
}
}
// Make it possible to identify wiring classes generated for classes from additional hot deployment paths
builder.addChainCustomizer(new Consumer<BuildChainBuilder>() {
@Override
public void accept(BuildChainBuilder buildChainBuilder) {
buildChainBuilder.addBuildStep(new BuildStep() {
@Override
public void execute(BuildContext context) {
context.produce(new ApplicationClassPredicateBuildItem(n -> {
return getClassInApplicationClassPaths(n, addAdditionalHotDeploymentPaths) != null;
}));
}
}).produces(ApplicationClassPredicateBuildItem.class).build();
}
});
Properties buildSystemProperties = new Properties();
buildSystemProperties.putAll(context.getBuildSystemProperties());
builder.setBuildSystemProperties(buildSystemProperties);
RuntimeRunner runner = builder
.build();
runner.run();
DevModeMain.runner = runner;
deploymentProblem = null;
} catch (Throwable t) {
deploymentProblem = t;
if (context.isAbortOnFailedStart() || liveReload) {
log.error("Failed to start quarkus", t);
} else {
//we need to set this here, while we still have the correct TCCL
//this is so the config is still valid, and we can read HTTP config from application.properties
log.error("Failed to start Quarkus", t);
log.info("Attempting to start hot replacement endpoint to recover from previous Quarkus startup failure");
if (runtimeUpdatesProcessor != null) {
runtimeUpdatesProcessor.startupFailed();
}
}
} finally {
Thread.currentThread().setContextClassLoader(old);
} }
for (int i = 1; i < context.getClassesRoots().size(); ++i) {
bootstrapBuilder.addAdditionalApplicationArchive(
new AdditionalDependency(context.getClassesRoots().get(i).toPath(), false, false));
}
for (DevModeContext.ModuleInfo i : context.getModules()) {
if (i.getClassesPath() != null) {
Path classesPath = Paths.get(i.getClassesPath());
bootstrapBuilder.addAdditionalApplicationArchive(new AdditionalDependency(classesPath, true, false));
}
}
Properties buildSystemProperties = new Properties();
buildSystemProperties.putAll(context.getBuildSystemProperties());
bootstrapBuilder.setBuildSystemProperties(buildSystemProperties);
curatedApplication = bootstrapBuilder.setTest(context.isTest()).build().bootstrap();
realCloseable = (Closeable) curatedApplication.runInAugmentClassLoader(IsolatedDevModeMain.class.getName(),
Collections.singletonMap(DevModeContext.class.getName(), context));
} catch (Throwable t) { } catch (Throwable t) {
deploymentProblem = t; log.error("Quarkus dev mode failed to start in curation phase", t);
log.error("Failed to start quarkus", t); throw new RuntimeException(t);
//System.exit(1);
} }
} }
public synchronized void restartApp(Set<String> changedResources) { @Override
stop(); public void close() throws IOException {
Timing.restart(); realCloseable.close();
doStart(true, changedResources);
}
public static ClassLoader getCurrentAppClassLoader() {
return currentAppClassLoader;
}
private static Path getClassInApplicationClassPaths(String name, List<Path> addAdditionalHotDeploymentPaths) {
final String fileName = name.replace('.', '/') + ".class";
Path classLocation;
for (Path i : addAdditionalHotDeploymentPaths) {
classLocation = i.resolve(fileName);
if (Files.exists(classLocation)) {
return classLocation;
}
}
return null;
}
private RuntimeUpdatesProcessor setupRuntimeCompilation(DevModeContext context) throws Exception {
if (!context.getModules().isEmpty()) {
ServiceLoader<CompilationProvider> serviceLoader = ServiceLoader.load(CompilationProvider.class);
List<CompilationProvider> compilationProviders = new ArrayList<>();
for (CompilationProvider provider : serviceLoader) {
compilationProviders.add(provider);
context.getModules().forEach(moduleInfo -> moduleInfo.addSourcePaths(provider.handledSourcePaths()));
}
ClassLoaderCompiler compiler;
try {
compiler = new ClassLoaderCompiler(Thread.currentThread().getContextClassLoader(),
compilationProviders, context);
} catch (Exception e) {
log.error("Failed to create compiler, runtime compilation will be unavailable", e);
return null;
}
RuntimeUpdatesProcessor processor = new RuntimeUpdatesProcessor(context, compiler, this);
for (HotReplacementSetup service : hotReplacement) {
service.setupHotDeployment(processor);
processor.addHotReplacementSetup(service);
}
return processor;
}
return null;
}
public void stop() {
if (runner != null) {
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(runtimeCl);
try {
runner.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
QuarkusConfigFactory.setConfig(null);
final ConfigProviderResolver cpr = ConfigProviderResolver.instance();
try {
cpr.releaseConfig(cpr.getConfig());
} catch (IllegalStateException ignored) {
// just means no config was installed, which is fine
}
DevModeMain.runner = null;
}
public void close() {
try {
stop();
} finally {
for (HotReplacementSetup i : hotReplacement) {
i.close();
}
}
} }
} }

View File

@@ -1,6 +1,7 @@
package io.quarkus.dev; package io.quarkus.dev;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.BuildStep;
@@ -12,11 +13,12 @@ public class HotDeploymentConfigFileBuildStep {
@BuildStep @BuildStep
ServiceStartBuildItem setupConfigFileHotDeployment(List<HotDeploymentWatchedFileBuildItem> files) { ServiceStartBuildItem setupConfigFileHotDeployment(List<HotDeploymentWatchedFileBuildItem> files) {
// TODO: this should really be an output of the RuntimeRunner // TODO: this should really be an output of the RuntimeRunner
RuntimeUpdatesProcessor processor = DevModeMain.runtimeUpdatesProcessor; RuntimeUpdatesProcessor processor = IsolatedDevModeMain.runtimeUpdatesProcessor;
if (processor != null) { if (processor != null) {
processor.setWatchedFilePaths(files.stream() Map<String, Boolean> watchedFilePaths = files.stream()
.collect(Collectors.toMap(HotDeploymentWatchedFileBuildItem::getLocation, .collect(Collectors.toMap(HotDeploymentWatchedFileBuildItem::getLocation,
HotDeploymentWatchedFileBuildItem::isRestartNeeded))); HotDeploymentWatchedFileBuildItem::isRestartNeeded));
processor.setWatchedFilePaths(watchedFilePaths);
} }
return null; return null;
} }

View File

@@ -0,0 +1,257 @@
package io.quarkus.dev;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.jboss.logging.Logger;
import io.quarkus.bootstrap.app.AdditionalDependency;
import io.quarkus.bootstrap.app.AugmentAction;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.RunningQuarkusApplication;
import io.quarkus.bootstrap.app.StartupAction;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildContext;
import io.quarkus.builder.BuildStep;
import io.quarkus.deployment.builditem.ApplicationClassPredicateBuildItem;
import io.quarkus.dev.spi.HotReplacementSetup;
import io.quarkus.runner.bootstrap.AugmentActionImpl;
import io.quarkus.runtime.Timing;
import io.quarkus.runtime.configuration.QuarkusConfigFactory;
import io.quarkus.runtime.logging.InitialConfigurator;
import io.quarkus.runtime.logging.LoggingSetupRecorder;
public class IsolatedDevModeMain implements BiConsumer<CuratedApplication, Map<String, Object>>, Closeable {
private static final Logger log = Logger.getLogger(DevModeMain.class);
private DevModeContext context;
private final List<HotReplacementSetup> hotReplacementSetups = new ArrayList<>();
private static volatile RunningQuarkusApplication runner;
static volatile Throwable deploymentProblem;
static volatile Throwable compileProblem;
static volatile RuntimeUpdatesProcessor runtimeUpdatesProcessor;
private static volatile CuratedApplication curatedApplication;
private static volatile AugmentAction augmentAction;
private synchronized void firstStart() {
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
//ok, we have resolved all the deps
try {
StartupAction start = augmentAction.createInitialRuntimeApplication();
runner = start.run();
} catch (Throwable t) {
deploymentProblem = t;
if (context.isAbortOnFailedStart()) {
log.error("Failed to start quarkus", t);
} else {
//we need to set this here, while we still have the correct TCCL
//this is so the config is still valid, and we can read HTTP config from application.properties
log.error("Failed to start Quarkus", t);
log.info("Attempting to start hot replacement endpoint to recover from previous Quarkus startup failure");
if (runtimeUpdatesProcessor != null) {
Thread.currentThread().setContextClassLoader(curatedApplication.getBaseRuntimeClassLoader());
try {
if (!InitialConfigurator.DELAYED_HANDLER.isActivated()) {
Class<?> cl = Thread.currentThread().getContextClassLoader()
.loadClass(LoggingSetupRecorder.class.getName());
cl.getMethod("handleFailedStart").invoke(null);
}
runtimeUpdatesProcessor.startupFailed();
} catch (Exception e) {
t.addSuppressed(new RuntimeException("Failed to recover after failed start", e));
throw new RuntimeException(t);
}
}
}
}
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
public synchronized void restartApp(Set<String> changedResources) {
stop();
Timing.restart(curatedApplication.getAugmentClassLoader());
deploymentProblem = null;
ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
//ok, we have resolved all the deps
try {
StartupAction start = augmentAction.reloadExistingApplication(changedResources);
runner = start.run();
} catch (Throwable t) {
deploymentProblem = t;
log.error("Failed to start quarkus", t);
}
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
private RuntimeUpdatesProcessor setupRuntimeCompilation(DevModeContext context, CuratedApplication application)
throws Exception {
if (!context.getModules().isEmpty()) {
ServiceLoader<CompilationProvider> serviceLoader = ServiceLoader.load(CompilationProvider.class);
List<CompilationProvider> compilationProviders = new ArrayList<>();
for (CompilationProvider provider : serviceLoader) {
compilationProviders.add(provider);
context.getModules().forEach(moduleInfo -> moduleInfo.addSourcePaths(provider.handledSourcePaths()));
}
ClassLoaderCompiler compiler;
try {
compiler = new ClassLoaderCompiler(Thread.currentThread().getContextClassLoader(), curatedApplication,
compilationProviders, context);
} catch (Exception e) {
log.error("Failed to create compiler, runtime compilation will be unavailable", e);
return null;
}
RuntimeUpdatesProcessor processor = new RuntimeUpdatesProcessor(context, compiler, this);
for (HotReplacementSetup service : ServiceLoader.load(HotReplacementSetup.class,
curatedApplication.getBaseRuntimeClassLoader())) {
hotReplacementSetups.add(service);
service.setupHotDeployment(processor);
processor.addHotReplacementSetup(service);
}
return processor;
}
return null;
}
public void stop() {
if (runner != null) {
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(runner.getClassLoader());
try {
runner.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
QuarkusConfigFactory.setConfig(null);
final ConfigProviderResolver cpr = ConfigProviderResolver.instance();
try {
cpr.releaseConfig(cpr.getConfig());
} catch (Throwable ignored) {
// just means no config was installed, which is fine
}
runner = null;
}
public void close() {
try {
stop();
} finally {
try {
for (HotReplacementSetup i : hotReplacementSetups) {
i.close();
}
} finally {
curatedApplication.close();
}
}
}
//the main entry point, but loaded inside the augmentation class loader
@Override
public void accept(CuratedApplication o, Map<String, Object> o2) {
Timing.staticInitStarted(o.getBaseRuntimeClassLoader());
try {
curatedApplication = o;
Object potentialContext = o2.get(DevModeContext.class.getName());
if (potentialContext instanceof DevModeContext) {
context = (DevModeContext) potentialContext;
} else {
//this was from the external class loader
//we need to copy it into this one
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(out);
oo.writeObject(potentialContext);
context = (DevModeContext) new ObjectInputStream(new ByteArrayInputStream(out.toByteArray())).readObject();
}
augmentAction = new AugmentActionImpl(curatedApplication,
Collections.singletonList(new Consumer<BuildChainBuilder>() {
@Override
public void accept(BuildChainBuilder buildChainBuilder) {
buildChainBuilder.addBuildStep(new BuildStep() {
@Override
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) {
for (AdditionalDependency i : curatedApplication.getQuarkusBootstrap()
.getAdditionalApplicationArchives()) {
if (i.isHotReloadable()) {
Path p = i.getArchivePath().resolve(s.replace(".", "/") + ".class");
if (Files.exists(p)) {
return true;
}
}
}
return false;
}
}));
}
}).produces(ApplicationClassPredicateBuildItem.class).build();
}
}));
runtimeUpdatesProcessor = setupRuntimeCompilation(context, o);
if (runtimeUpdatesProcessor != null) {
runtimeUpdatesProcessor.checkForFileChange();
runtimeUpdatesProcessor.checkForChangedClasses();
}
firstStart();
// doStart(false, Collections.emptySet());
if (deploymentProblem != null || compileProblem != null) {
if (context.isAbortOnFailedStart()) {
throw new RuntimeException(deploymentProblem == null ? compileProblem : deploymentProblem);
}
}
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
synchronized (DevModeMain.class) {
if (runner != null) {
try {
runner.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}, "Quarkus Shutdown Thread"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -27,9 +27,9 @@ import java.util.stream.Stream;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import io.quarkus.deployment.devmode.HotReplacementContext;
import io.quarkus.deployment.devmode.HotReplacementSetup;
import io.quarkus.deployment.util.FileUtil; import io.quarkus.deployment.util.FileUtil;
import io.quarkus.dev.spi.HotReplacementContext;
import io.quarkus.dev.spi.HotReplacementSetup;
import io.quarkus.runtime.Timing; import io.quarkus.runtime.Timing;
public class RuntimeUpdatesProcessor implements HotReplacementContext { public class RuntimeUpdatesProcessor implements HotReplacementContext {
@@ -65,9 +65,9 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext {
private final List<Runnable> preScanSteps = new CopyOnWriteArrayList<>(); private final List<Runnable> preScanSteps = new CopyOnWriteArrayList<>();
private final List<Consumer<Set<String>>> noRestartChangesConsumers = new CopyOnWriteArrayList<>(); private final List<Consumer<Set<String>>> noRestartChangesConsumers = new CopyOnWriteArrayList<>();
private final List<HotReplacementSetup> hotReplacementSetup = new ArrayList<>(); private final List<HotReplacementSetup> hotReplacementSetup = new ArrayList<>();
private final DevModeMain devModeMain; private final IsolatedDevModeMain devModeMain;
public RuntimeUpdatesProcessor(DevModeContext context, ClassLoaderCompiler compiler, DevModeMain devModeMain) { public RuntimeUpdatesProcessor(DevModeContext context, ClassLoaderCompiler compiler, IsolatedDevModeMain devModeMain) {
this.context = context; this.context = context;
this.compiler = compiler; this.compiler = compiler;
this.devModeMain = devModeMain; this.devModeMain = devModeMain;
@@ -102,7 +102,8 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext {
@Override @Override
public Throwable getDeploymentProblem() { public Throwable getDeploymentProblem() {
//we differentiate between these internally, however for the error reporting they are the same //we differentiate between these internally, however for the error reporting they are the same
return DevModeMain.compileProblem != null ? DevModeMain.compileProblem : DevModeMain.deploymentProblem; return IsolatedDevModeMain.compileProblem != null ? IsolatedDevModeMain.compileProblem
: IsolatedDevModeMain.deploymentProblem;
} }
@Override @Override
@@ -129,7 +130,7 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext {
//in an ideal world we would just check every resource file for changes, however as everything is already //in an ideal world we would just check every resource file for changes, however as everything is already
//all broken we just assume the reason that they have refreshed is because they have fixed something //all broken we just assume the reason that they have refreshed is because they have fixed something
//trying to watch all resource files is complex and this is likely a good enough solution for what is already an edge case //trying to watch all resource files is complex and this is likely a good enough solution for what is already an edge case
boolean restartNeeded = classChanged || (DevModeMain.deploymentProblem != null && userInitiated); boolean restartNeeded = classChanged || (IsolatedDevModeMain.deploymentProblem != null && userInitiated);
if (!restartNeeded && !filesChanged.isEmpty()) { if (!restartNeeded && !filesChanged.isEmpty()) {
restartNeeded = filesChanged.stream().map(watchedFilePaths::get).anyMatch(Boolean.TRUE::equals); restartNeeded = filesChanged.stream().map(watchedFilePaths::get).anyMatch(Boolean.TRUE::equals);
} }
@@ -188,9 +189,9 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext {
moduleChangedSourceFilePaths.addAll(changedPaths); moduleChangedSourceFilePaths.addAll(changedPaths);
compiler.compile(sourcePath, changedSourceFiles.stream() compiler.compile(sourcePath, changedSourceFiles.stream()
.collect(groupingBy(this::getFileExtension, Collectors.toSet()))); .collect(groupingBy(this::getFileExtension, Collectors.toSet())));
DevModeMain.compileProblem = null; IsolatedDevModeMain.compileProblem = null;
} catch (Exception e) { } catch (Exception e) {
DevModeMain.compileProblem = e; IsolatedDevModeMain.compileProblem = e;
return false; return false;
} }
} }

View File

@@ -19,8 +19,8 @@
<module>processor</module> <module>processor</module>
<module>devmode</module> <module>devmode</module>
<module>builder</module> <module>builder</module>
<module>creator</module>
<module>test-extension</module> <module>test-extension</module>
<module>devmode-spi</module>
</modules> </modules>
<build> <build>

View File

@@ -102,6 +102,25 @@
<plugin> <plugin>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-maven-plugin</artifactId> <artifactId>quarkus-bootstrap-maven-plugin</artifactId>
<configuration>
<parentFirstArtifacts>
<parentFirstArtifact>io.quarkus:quarkus-bootstrap-core</parentFirstArtifact>
<parentFirstArtifact>io.quarkus:quarkus-development-mode-spi</parentFirstArtifact>
<parentFirstArtifact>org.jboss.logmanager:jboss-logmanager-embedded</parentFirstArtifact>
<parentFirstArtifact>org.jboss.logging:jboss-logging</parentFirstArtifact>
<parentFirstArtifact>org.ow2.asm:asm</parentFirstArtifact>
</parentFirstArtifacts>
<excludedArtifacts>
<excludedArtifact>io.smallrye:smallrye-config</excludedArtifact>
<excludedArtifact>javax.enterprise:cdi-api</excludedArtifact>
<excludedArtifact>org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec</excludedArtifact>
<excludedArtifact>org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec</excludedArtifact>
<excludedArtifact>javax.inject:javax.inject</excludedArtifact>
<excludedArtifact>org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.2_spec</excludedArtifact>
<excludedArtifact>org.glassfish:javax.el</excludedArtifact>
<excludedArtifact>javax.annotation:javax.annotation-api</excludedArtifact>
</excludedArtifacts>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

View File

@@ -1,9 +1,11 @@
package io.quarkus.runtime; package io.quarkus.runtime;
import java.io.Closeable;
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.graalvm.nativeimage.ImageInfo; import org.graalvm.nativeimage.ImageInfo;
import org.wildfly.common.Assert; import org.wildfly.common.Assert;
import org.wildfly.common.lock.Locks; import org.wildfly.common.lock.Locks;
@@ -19,7 +21,7 @@ import sun.misc.SignalHandler;
* setup logic. The base class does some basic error checking. * setup logic. The base class does some basic error checking.
*/ */
@SuppressWarnings("restriction") @SuppressWarnings("restriction")
public abstract class Application { public abstract class Application implements Closeable {
// WARNING: do not inject a logger here, it's too early: the log manager has not been properly set up yet // WARNING: do not inject a logger here, it's too early: the log manager has not been properly set up yet
@@ -106,6 +108,20 @@ public abstract class Application {
protected abstract void doStart(String[] args); protected abstract void doStart(String[] args);
public final void close() {
try {
stop();
} finally {
try {
ConfigProviderResolver.instance()
.releaseConfig(
ConfigProviderResolver.instance().getConfig(Thread.currentThread().getContextClassLoader()));
} catch (Throwable ignored) {
}
}
}
/** /**
* Stop the application. If another thread is also trying to stop the application, this method waits for that * Stop the application. If another thread is also trying to stop the application, this method waits for that
* thread to finish. Returns immediately if the application is already stopped. If an exception is thrown during * thread to finish. Returns immediately if the application is already stopped. If an exception is thrown during

View File

@@ -29,6 +29,15 @@ public class Timing {
} }
} }
public static void staticInitStarted(ClassLoader cl) {
try {
Class<?> realTiming = cl.loadClass(Timing.class.getName());
realTiming.getMethod("staticInitStarted").invoke(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void staticInitStopped() { public static void staticInitStopped() {
if (bootStopTime < 0) { if (bootStopTime < 0) {
bootStopTime = System.nanoTime(); bootStopTime = System.nanoTime();
@@ -55,6 +64,15 @@ public class Timing {
bootStartTime = System.nanoTime(); bootStartTime = System.nanoTime();
} }
public static void restart(ClassLoader cl) {
try {
Class<?> realTiming = cl.loadClass(Timing.class.getName());
realTiming.getMethod("restart").invoke(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void printStartupTime(String name, String version, String quarkusVersion, String features, String profile, public static void printStartupTime(String name, String version, String quarkusVersion, String features, String profile,
boolean liveCoding) { boolean liveCoding) {
final long bootTimeNanoSeconds = System.nanoTime() - bootStartTime; final long bootTimeNanoSeconds = System.nanoTime() - bootStartTime;

View File

@@ -25,11 +25,16 @@ public abstract class ApplicationPropertiesConfigSource extends PropertiesConfig
private static final long serialVersionUID = -4694780118527396798L; private static final long serialVersionUID = -4694780118527396798L;
static final String APPLICATION_PROPERTIES = "application.properties"; static final String APPLICATION_PROPERTIES = "application.properties";
static final String MP_PROPERTIES = "META-INF/microprofile-config.properties";
ApplicationPropertiesConfigSource(InputStream is, int ordinal) { ApplicationPropertiesConfigSource(InputStream is, int ordinal) {
super(readProperties(is), APPLICATION_PROPERTIES, ordinal); super(readProperties(is), APPLICATION_PROPERTIES, ordinal);
} }
ApplicationPropertiesConfigSource(InputStream is, String nm, int ordinal) {
super(readProperties(is), nm, ordinal);
}
private static Map<String, String> readProperties(final InputStream is) { private static Map<String, String> readProperties(final InputStream is) {
if (is == null) { if (is == null) {
return Collections.emptyMap(); return Collections.emptyMap();
@@ -67,6 +72,29 @@ public abstract class ApplicationPropertiesConfigSource extends PropertiesConfig
} }
} }
/**
* Config that makes sure that the MP config in the application takes precedence over any other on the class path
*/
public static final class MpConfigInJar extends ApplicationPropertiesConfigSource {
public MpConfigInJar() {
super(openStream(), MP_PROPERTIES, 240);
}
private static InputStream openStream() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = ApplicationPropertiesConfigSource.class.getClassLoader();
}
InputStream is;
if (cl == null) {
is = ClassLoader.getSystemResourceAsStream(MP_PROPERTIES);
} else {
is = cl.getResourceAsStream(MP_PROPERTIES);
}
return is;
}
}
public static final class InFileSystem extends ApplicationPropertiesConfigSource { public static final class InFileSystem extends ApplicationPropertiesConfigSource {
public InFileSystem() { public InFileSystem() {
super(openStream(), 260); super(openStream(), 260);

View File

@@ -1,14 +1,18 @@
package io.quarkus.runtime.configuration; package io.quarkus.runtime.configuration;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@@ -61,12 +65,20 @@ public class ConfigInstantiator {
return; return;
} }
for (Field field : cls.getDeclaredFields()) { for (Field field : cls.getDeclaredFields()) {
if (Modifier.isFinal(field.getModifiers())) {
continue;
}
field.setAccessible(true);
ConfigItem configItem = field.getDeclaredAnnotation(ConfigItem.class); ConfigItem configItem = field.getDeclaredAnnotation(ConfigItem.class);
final Class<?> fieldClass = field.getType(); final Class<?> fieldClass = field.getType();
if (configItem == null || fieldClass.isAnnotationPresent(ConfigGroup.class)) { if (configItem == null || fieldClass.isAnnotationPresent(ConfigGroup.class)) {
Object newInstance = fieldClass.getConstructor().newInstance(); Constructor<?> constructor = fieldClass.getConstructor();
constructor.setAccessible(true);
Object newInstance = constructor.newInstance();
field.set(o, newInstance); field.set(o, newInstance);
handleObject(prefix + "." + dashify(field.getName()), newInstance, config); handleObject(prefix + "." + dashify(field.getName()), newInstance, config);
} else if (fieldClass == Map.class) { //TODO: FIXME, this cannot handle Map yet
field.set(o, new HashMap<>());
} else { } else {
String name = configItem.name(); String name = configItem.name();
if (name.equals(ConfigItem.HYPHENATED_ELEMENT_NAME)) { if (name.equals(ConfigItem.HYPHENATED_ELEMENT_NAME)) {
@@ -78,7 +90,14 @@ public class ConfigInstantiator {
final Type genericType = field.getGenericType(); final Type genericType = field.getGenericType();
final Converter<?> conv = getConverterFor(genericType); final Converter<?> conv = getConverterFor(genericType);
try { try {
field.set(o, config.getValue(fullName, conv)); Optional<?> value = config.getOptionalValue(fullName, conv);
if (value.isPresent()) {
field.set(o, value.get());
} else if (!configItem.defaultValue().equals(ConfigItem.NO_DEFAULT)) {
//the runtime config source handles default automatically
//however this may not have actually been installed depending on where the failure occured
field.set(o, conv.convert(configItem.defaultValue()));
}
} catch (NoSuchElementException ignored) { } catch (NoSuchElementException ignored) {
} }
} }
@@ -92,7 +111,9 @@ public class ConfigInstantiator {
// hopefully this is enough // hopefully this is enough
final SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig(); final SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig();
Class<?> rawType = rawTypeOf(type); Class<?> rawType = rawTypeOf(type);
if (rawType == Optional.class) { if (Enum.class.isAssignableFrom(rawType)) {
return new HyphenateEnumConverter(rawType);
} else if (rawType == Optional.class) {
return Converters.newOptionalConverter(getConverterFor(typeOfParameter(type, 0))); return Converters.newOptionalConverter(getConverterFor(typeOfParameter(type, 0)));
} else if (rawType == List.class) { } else if (rawType == List.class) {
return Converters.newCollectionConverter(getConverterFor(typeOfParameter(type, 0)), ArrayList::new); return Converters.newCollectionConverter(getConverterFor(typeOfParameter(type, 0)), ArrayList::new);

View File

@@ -48,7 +48,8 @@ public final class ConfigUtils {
final SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder(); final SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder();
final ApplicationPropertiesConfigSource.InFileSystem inFileSystem = new ApplicationPropertiesConfigSource.InFileSystem(); final ApplicationPropertiesConfigSource.InFileSystem inFileSystem = new ApplicationPropertiesConfigSource.InFileSystem();
final ApplicationPropertiesConfigSource.InJar inJar = new ApplicationPropertiesConfigSource.InJar(); final ApplicationPropertiesConfigSource.InJar inJar = new ApplicationPropertiesConfigSource.InJar();
builder.withSources(inFileSystem, inJar); final ApplicationPropertiesConfigSource.MpConfigInJar mpConfig = new ApplicationPropertiesConfigSource.MpConfigInJar();
builder.withSources(inFileSystem, inJar, mpConfig);
final ExpandingConfigSource.Cache cache = new ExpandingConfigSource.Cache(); final ExpandingConfigSource.Cache cache = new ExpandingConfigSource.Cache();
builder.withWrapper(ExpandingConfigSource.wrapper(cache)); builder.withWrapper(ExpandingConfigSource.wrapper(cache));
builder.withWrapper(DeploymentProfileConfigSource.wrapper()); builder.withWrapper(DeploymentProfileConfigSource.wrapper());

View File

@@ -20,6 +20,16 @@ public final class QuarkusConfigFactory extends SmallRyeConfigFactory {
public SmallRyeConfig getConfigFor(final SmallRyeConfigProviderResolver configProviderResolver, public SmallRyeConfig getConfigFor(final SmallRyeConfigProviderResolver configProviderResolver,
final ClassLoader classLoader) { final ClassLoader classLoader) {
if (config == null) {
//TODO: this code path is only hit when start fails in dev mode very early in the process
//the recovery code will fail without this as it cannot read any properties such as
//the HTTP port or logging info
return configProviderResolver.getBuilder().forClassLoader(classLoader)
.addDefaultSources()
.addDiscoveredSources()
.addDiscoveredConverters()
.build();
}
return config; return config;
} }

View File

@@ -14,7 +14,27 @@ import org.jboss.logmanager.handlers.DelayedHandler;
*/ */
public final class InitialConfigurator implements EmbeddedConfigurator { public final class InitialConfigurator implements EmbeddedConfigurator {
public static final DelayedHandler DELAYED_HANDLER = new DelayedHandler(); public static final DelayedHandler DELAYED_HANDLER;
static {
//a hack around class loading
//this is always loaded in the root class loader with jboss-logmanager,
//however it may also be loaded in an isolated CL when running in dev
//or test mode. If it is in an isolated CL we load the handler from
//the class on the system class loader so they are equal
//TODO: should this class go in its own module and be excluded from isolated class loading?
DelayedHandler handler = new DelayedHandler();
ClassLoader cl = InitialConfigurator.class.getClassLoader();
try {
Class<?> root = Class.forName(InitialConfigurator.class.getName(), false, ClassLoader.getSystemClassLoader());
if (root.getClassLoader() != cl) {
handler = (DelayedHandler) root.getDeclaredField("DELAYED_HANDLER").get(null);
}
} catch (Exception e) {
//ignore, happens on native image build
}
DELAYED_HANDLER = handler;
}
@Override @Override
public Level getMinimumLevelOf(final String loggerName) { public Level getMinimumLevelOf(final String loggerName) {

View File

@@ -6,6 +6,7 @@ import static org.wildfly.common.os.Process.getProcessName;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -34,6 +35,7 @@ import org.jboss.logmanager.handlers.SyslogHandler;
import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.annotations.Recorder; import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.configuration.ConfigInstantiator;
/** /**
* *
@@ -66,6 +68,14 @@ public class LoggingSetupRecorder {
public LoggingSetupRecorder() { public LoggingSetupRecorder() {
} }
@SuppressWarnings("unsed") //called via reflection, as it is in an isolated CL
public static void handleFailedStart() {
LogConfig config = new LogConfig();
ConfigInstantiator.handleObject(config);
new LoggingSetupRecorder().initializeLogging(config, Collections.emptyList(),
Collections.emptyList());
}
public void initializeLogging(LogConfig config, final List<RuntimeValue<Optional<Handler>>> additionalHandlers, public void initializeLogging(LogConfig config, final List<RuntimeValue<Optional<Handler>>> additionalHandlers,
final List<RuntimeValue<Optional<Formatter>>> possibleFormatters) { final List<RuntimeValue<Optional<Formatter>>> possibleFormatters) {

View File

@@ -1,4 +1,4 @@
package io.quarkus.deployment.test; package io.quarkus.runtime.test;
public interface TestScopeSetup { public interface TestScopeSetup {

View File

@@ -352,7 +352,7 @@ public final class TestProcessor {
* @param beanArchiveIndex - index of type information * @param beanArchiveIndex - index of type information
* @param testBeanProducer - producer for located Class<IConfigConsumer> bean types * @param testBeanProducer - producer for located Class<IConfigConsumer> bean types
*/ */
@BuildStep @BuildStep(loadsApplicationClasses = true)
@Record(STATIC_INIT) @Record(STATIC_INIT)
void scanForBeans(TestRecorder recorder, BeanArchiveIndexBuildItem beanArchiveIndex, void scanForBeans(TestRecorder recorder, BeanArchiveIndexBuildItem beanArchiveIndex,
BuildProducer<TestBeanBuildItem> testBeanProducer) { BuildProducer<TestBeanBuildItem> testBeanProducer) {
@@ -365,7 +365,8 @@ public final class TestProcessor {
.stream() .stream()
.anyMatch(dotName -> dotName.equals(DotName.createSimple(IConfigConsumer.class.getName()))); .anyMatch(dotName -> dotName.equals(DotName.createSimple(IConfigConsumer.class.getName())));
if (isConfigConsumer) { if (isConfigConsumer) {
Class<IConfigConsumer> beanClass = (Class<IConfigConsumer>) Class.forName(beanClassInfo.name().toString()); Class<IConfigConsumer> beanClass = (Class<IConfigConsumer>) Class.forName(beanClassInfo.name().toString(),
true, Thread.currentThread().getContextClassLoader());
testBeanProducer.produce(new TestBeanBuildItem(beanClass)); testBeanProducer.produce(new TestBeanBuildItem(beanClass));
log.infof("The configured bean: %s", beanClass); log.infof("The configured bean: %s", beanClass);
} }

View File

@@ -20,6 +20,7 @@ public class AdditionalHandlersTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-additional-handlers.properties") .withConfigurationResource("application-additional-handlers.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")); .addAsManifestResource("application.properties", "microprofile-config.properties"));
@Test @Test

View File

@@ -22,6 +22,7 @@ public class AsyncConsoleHandlerTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-async-console-log.properties") .withConfigurationResource("application-async-console-log.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")) .addAsManifestResource("application.properties", "microprofile-config.properties"))
.setLogFileName("AsyncConsoleHandlerTest.log"); .setLogFileName("AsyncConsoleHandlerTest.log");

View File

@@ -22,6 +22,7 @@ public class AsyncFileHandlerTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-async-file-log.properties") .withConfigurationResource("application-async-file-log.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")) .addAsManifestResource("application.properties", "microprofile-config.properties"))
.setLogFileName("AsyncFileHandlerTest.log"); .setLogFileName("AsyncFileHandlerTest.log");

View File

@@ -22,6 +22,7 @@ public class AsyncSyslogHandlerTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-async-syslog.properties") .withConfigurationResource("application-async-syslog.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")) .addAsManifestResource("application.properties", "microprofile-config.properties"))
.setLogFileName("AsyncSyslogHandlerTest.log"); .setLogFileName("AsyncSyslogHandlerTest.log");

View File

@@ -22,6 +22,7 @@ public class ConsoleHandlerTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-console-output.properties") .withConfigurationResource("application-console-output.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")); .addAsManifestResource("application.properties", "microprofile-config.properties"));
@Test @Test

View File

@@ -22,6 +22,7 @@ public class FileHandlerTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-file-output-log.properties") .withConfigurationResource("application-file-output-log.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")); .addAsManifestResource("application.properties", "microprofile-config.properties"));
@Test @Test

View File

@@ -22,6 +22,7 @@ public class PeriodicRotatingLoggingTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-periodic-file-log-rotating.properties") .withConfigurationResource("application-periodic-file-log-rotating.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")) .addAsManifestResource("application.properties", "microprofile-config.properties"))
.setLogFileName("PeriodicRotatingLoggingTest.log"); .setLogFileName("PeriodicRotatingLoggingTest.log");

View File

@@ -22,6 +22,7 @@ public class PeriodicSizeRotatingLoggingTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-periodic-size-file-log-rotating.properties") .withConfigurationResource("application-periodic-size-file-log-rotating.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")) .addAsManifestResource("application.properties", "microprofile-config.properties"))
.setLogFileName("PeriodicSizeRotatingLoggingTest.log"); .setLogFileName("PeriodicSizeRotatingLoggingTest.log");

View File

@@ -22,6 +22,7 @@ public class SizeRotatingLoggingTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-size-file-log-rotating.properties") .withConfigurationResource("application-size-file-log-rotating.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")) .addAsManifestResource("application.properties", "microprofile-config.properties"))
.setLogFileName("SizeRotatingLoggingTest.log"); .setLogFileName("SizeRotatingLoggingTest.log");

View File

@@ -24,6 +24,7 @@ public class SyslogHandlerTest {
static final QuarkusUnitTest config = new QuarkusUnitTest() static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-syslog-output.properties") .withConfigurationResource("application-syslog-output.properties")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(LoggingTestsHelper.class)
.addAsManifestResource("application.properties", "microprofile-config.properties")); .addAsManifestResource("application.properties", "microprofile-config.properties"));
@Test @Test

View File

@@ -25,7 +25,6 @@ dependencies {
implementation "io.quarkus:quarkus-platform-descriptor-json:${version}" implementation "io.quarkus:quarkus-platform-descriptor-json:${version}"
implementation "io.quarkus:quarkus-platform-descriptor-resolver-json:${version}" implementation "io.quarkus:quarkus-platform-descriptor-resolver-json:${version}"
implementation "io.quarkus:quarkus-development-mode:${version}" implementation "io.quarkus:quarkus-development-mode:${version}"
implementation "io.quarkus:quarkus-creator:${version}"
testImplementation 'org.assertj:assertj-core:3.14.0' testImplementation 'org.assertj:assertj-core:3.14.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'

View File

@@ -35,10 +35,6 @@
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-development-mode</artifactId> <artifactId>quarkus-development-mode</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-creator</artifactId>
</dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-platform-descriptor-json</artifactId> <artifactId>quarkus-platform-descriptor-json</artifactId>

View File

@@ -1,16 +1,15 @@
package io.quarkus.gradle; package io.quarkus.gradle;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.quarkus.cli.commands.CreateProject;
import io.quarkus.cli.commands.writer.FileProjectWriter;
import io.quarkus.generators.BuildTool;
import io.quarkus.generators.SourceType;
import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner; import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.TaskOutcome; import org.gradle.testkit.runner.TaskOutcome;
@@ -20,7 +19,10 @@ import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import static org.assertj.core.api.Assertions.assertThat; import io.quarkus.cli.commands.CreateProject;
import io.quarkus.cli.commands.writer.FileProjectWriter;
import io.quarkus.generators.BuildTool;
import io.quarkus.generators.SourceType;
public class QuarkusPluginFunctionalTest { public class QuarkusPluginFunctionalTest {
@@ -48,13 +50,13 @@ public class QuarkusPluginFunctionalTest {
@ParameterizedTest(name = "Build {0} project") @ParameterizedTest(name = "Build {0} project")
@EnumSource(SourceType.class) @EnumSource(SourceType.class)
public void canBuild(SourceType sourceType) throws IOException { public void canBuild(SourceType sourceType) throws IOException, InterruptedException {
createProject(sourceType); createProject(sourceType);
BuildResult build = GradleRunner.create() BuildResult build = GradleRunner.create()
.forwardOutput() .forwardOutput()
.withPluginClasspath() .withPluginClasspath()
.withArguments(arguments("build")) .withArguments(arguments("build", "--stacktrace"))
.withProjectDir(projectRoot) .withProjectDir(projectRoot)
.build(); .build();
@@ -63,9 +65,9 @@ public class QuarkusPluginFunctionalTest {
assertThat(build.task(":buildNative")).isNull(); assertThat(build.task(":buildNative")).isNull();
} }
private List<String> arguments(String argument) { private List<String> arguments(String... argument) {
List<String> arguments = new ArrayList<>(); List<String> arguments = new ArrayList<>();
arguments.add(argument); arguments.addAll(Arrays.asList(argument));
String mavenRepoLocal = System.getProperty("maven.repo.local", System.getenv("MAVEN_LOCAL_REPO")); String mavenRepoLocal = System.getProperty("maven.repo.local", System.getenv("MAVEN_LOCAL_REPO"));
if (mavenRepoLocal != null) { if (mavenRepoLocal != null) {
arguments.add("-Dmaven.repo.local=" + mavenRepoLocal); arguments.add("-Dmaven.repo.local=" + mavenRepoLocal);
@@ -74,17 +76,17 @@ public class QuarkusPluginFunctionalTest {
} }
private void createProject(SourceType sourceType) throws IOException { private void createProject(SourceType sourceType) throws IOException {
Map<String,Object> context = new HashMap<>(); Map<String, Object> context = new HashMap<>();
context.put("path", "/greeting"); context.put("path", "/greeting");
assertThat(new CreateProject(new FileProjectWriter(projectRoot)) assertThat(new CreateProject(new FileProjectWriter(projectRoot))
.groupId("com.acme.foo") .groupId("com.acme.foo")
.artifactId("foo") .artifactId("foo")
.version("1.0.0-SNAPSHOT") .version("1.0.0-SNAPSHOT")
.buildTool(BuildTool.GRADLE) .buildTool(BuildTool.GRADLE)
.className("org.acme.GreetingResource") .className("org.acme.GreetingResource")
.sourceType(sourceType) .sourceType(sourceType)
.doCreateProject(context)) .doCreateProject(context))
.withFailMessage("Project was not created") .withFailMessage("Project was not created")
.isTrue(); .isTrue();
} }
} }

View File

@@ -10,6 +10,7 @@ import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@@ -33,6 +34,7 @@ import org.gradle.jvm.tasks.Jar;
import io.quarkus.bootstrap.BootstrapConstants; import io.quarkus.bootstrap.BootstrapConstants;
import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppArtifactKey;
import io.quarkus.bootstrap.model.AppDependency; import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.model.AppModel; import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.AppModelResolver; import io.quarkus.bootstrap.resolver.AppModelResolver;
@@ -116,12 +118,14 @@ public class AppModelGradleResolver implements AppModelResolver {
@Override @Override
public AppModel resolveModel(AppArtifact appArtifact) throws AppModelResolverException { public AppModel resolveModel(AppArtifact appArtifact) throws AppModelResolverException {
AppModel.Builder appBuilder = new AppModel.Builder();
if (appModel != null && appModel.getAppArtifact().equals(appArtifact)) { if (appModel != null && appModel.getAppArtifact().equals(appArtifact)) {
return appModel; return appModel;
} }
final Configuration compileCp = project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME); final Configuration compileCp = project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME);
final List<Dependency> extensionDeps = new ArrayList<>(); final List<Dependency> extensionDeps = new ArrayList<>();
final List<AppDependency> userDeps = new ArrayList<>(); final List<AppDependency> userDeps = new ArrayList<>();
Map<AppArtifactKey, AppDependency> versionMap = new HashMap<>();
Map<ModuleIdentifier, ModuleVersionIdentifier> userModules = new HashMap<>(); Map<ModuleIdentifier, ModuleVersionIdentifier> userModules = new HashMap<>();
for (ResolvedArtifact a : compileCp.getResolvedConfiguration().getResolvedArtifacts()) { for (ResolvedArtifact a : compileCp.getResolvedConfiguration().getResolvedArtifacts()) {
final File f = a.getFile(); final File f = a.getFile();
@@ -131,14 +135,17 @@ public class AppModelGradleResolver implements AppModelResolver {
} }
userModules.put(getModuleId(a), a.getModuleVersion().getId()); userModules.put(getModuleId(a), a.getModuleVersion().getId());
userDeps.add(toAppDependency(a)); AppDependency dependency = toAppDependency(a);
userDeps.add(dependency);
versionMap.put(new AppArtifactKey(dependency.getArtifact().getGroupId(), dependency.getArtifact().getArtifactId(),
dependency.getArtifact().getClassifier()), dependency);
final Dependency dep; final Dependency dep;
if (f.isDirectory()) { if (f.isDirectory()) {
dep = processQuarkusDir(a, f.toPath().resolve(BootstrapConstants.META_INF)); dep = processQuarkusDir(a, f.toPath().resolve(BootstrapConstants.META_INF), appBuilder);
} else { } else {
try (FileSystem artifactFs = FileSystems.newFileSystem(f.toPath(), null)) { try (FileSystem artifactFs = FileSystems.newFileSystem(f.toPath(), null)) {
dep = processQuarkusDir(a, artifactFs.getPath(BootstrapConstants.META_INF)); dep = processQuarkusDir(a, artifactFs.getPath(BootstrapConstants.META_INF), appBuilder);
} catch (IOException e) { } catch (IOException e) {
throw new GradleException("Failed to process " + f, e); throw new GradleException("Failed to process " + f, e);
} }
@@ -148,6 +155,7 @@ public class AppModelGradleResolver implements AppModelResolver {
} }
} }
List<AppDependency> deploymentDeps = new ArrayList<>(); List<AppDependency> deploymentDeps = new ArrayList<>();
List<AppDependency> fullDeploymentDeps = new ArrayList<>();
if (!extensionDeps.isEmpty()) { if (!extensionDeps.isEmpty()) {
final Configuration deploymentConfig = project.getConfigurations() final Configuration deploymentConfig = project.getConfigurations()
.detachedConfiguration(extensionDeps.toArray(new Dependency[extensionDeps.size()])); .detachedConfiguration(extensionDeps.toArray(new Dependency[extensionDeps.size()]));
@@ -157,7 +165,18 @@ public class AppModelGradleResolver implements AppModelResolver {
if (userVersion != null) { if (userVersion != null) {
continue; continue;
} }
deploymentDeps.add(toAppDependency(a)); AppDependency dependency = toAppDependency(a);
deploymentDeps.add(alignVersion(dependency, versionMap));
}
}
fullDeploymentDeps.addAll(deploymentDeps);
fullDeploymentDeps.addAll(userDeps);
Iterator<AppDependency> it = deploymentDeps.iterator();
while (it.hasNext()) {
AppDependency val = it.next();
if (userDeps.contains(val)) {
it.remove();
} }
} }
@@ -176,7 +195,21 @@ public class AppModelGradleResolver implements AppModelResolver {
} }
} }
} }
return this.appModel = new AppModel(appArtifact, userDeps, deploymentDeps); appBuilder.addRuntimeDeps(userDeps)
.addFullDeploymentDeps(fullDeploymentDeps)
.addDeploymentDeps(deploymentDeps)
.setAppArtifact(appArtifact);
return this.appModel = appBuilder.build();
}
private AppDependency alignVersion(AppDependency dependency, Map<AppArtifactKey, AppDependency> versionMap) {
AppArtifactKey appKey = new AppArtifactKey(dependency.getArtifact().getGroupId(),
dependency.getArtifact().getArtifactId(),
dependency.getArtifact().getClassifier());
if (versionMap.containsKey(appKey)) {
return versionMap.get(appKey);
}
return dependency;
} }
@Override @Override
@@ -184,6 +217,12 @@ public class AppModelGradleResolver implements AppModelResolver {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public AppModel resolveManagedModel(AppArtifact appArtifact, List<AppDependency> directDeps, AppArtifact managingProject)
throws AppModelResolverException {
return resolveModel(appArtifact);
}
private ModuleIdentifier getModuleId(ResolvedArtifact a) { private ModuleIdentifier getModuleId(ResolvedArtifact a) {
final String[] split = a.getModuleVersion().toString().split(":"); final String[] split = a.getModuleVersion().toString().split(":");
return DefaultModuleIdentifier.newId(split[0], split[1]); return DefaultModuleIdentifier.newId(split[0], split[1]);
@@ -196,7 +235,7 @@ public class AppModelGradleResolver implements AppModelResolver {
return new AppDependency(appArtifact, "runtime"); return new AppDependency(appArtifact, "runtime");
} }
private Dependency processQuarkusDir(ResolvedArtifact a, Path quarkusDir) { private Dependency processQuarkusDir(ResolvedArtifact a, Path quarkusDir, AppModel.Builder appBuilder) {
if (!Files.exists(quarkusDir)) { if (!Files.exists(quarkusDir)) {
return null; return null;
} }
@@ -208,6 +247,7 @@ public class AppModelGradleResolver implements AppModelResolver {
if (extProps == null) { if (extProps == null) {
return null; return null;
} }
appBuilder.handleExtensionProperties(extProps, a.toString());
String value = extProps.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT); String value = extProps.getProperty(BootstrapConstants.PROP_DEPLOYMENT_ARTIFACT);
final String[] split = value.split(":"); final String[] split = value.split(":");

View File

@@ -74,6 +74,7 @@ public class QuarkusPlugin implements Plugin<Project> {
Task classesTask = tasks.getByName(JavaPlugin.CLASSES_TASK_NAME); Task classesTask = tasks.getByName(JavaPlugin.CLASSES_TASK_NAME);
quarkusDev.dependsOn(classesTask); quarkusDev.dependsOn(classesTask);
quarkusBuild.dependsOn(classesTask, tasks.getByName(JavaPlugin.JAR_TASK_NAME)); quarkusBuild.dependsOn(classesTask, tasks.getByName(JavaPlugin.JAR_TASK_NAME));
quarkusTestConfig.dependsOn(classesTask);
buildNative.dependsOn(tasks.getByName(BasePlugin.ASSEMBLE_TASK_NAME)); buildNative.dependsOn(tasks.getByName(BasePlugin.ASSEMBLE_TASK_NAME));

View File

@@ -2,7 +2,6 @@ package io.quarkus.gradle.tasks;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import org.gradle.api.GradleException; import org.gradle.api.GradleException;
@@ -11,12 +10,12 @@ import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.options.Option; import org.gradle.api.tasks.options.Option;
import io.quarkus.bootstrap.BootstrapException;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.resolver.AppModelResolver; import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.bootstrap.resolver.AppModelResolverException;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.phase.augment.AugmentTask;
public class QuarkusBuild extends QuarkusTask { public class QuarkusBuild extends QuarkusTask {
@@ -62,35 +61,29 @@ public class QuarkusBuild extends QuarkusTask {
} catch (AppModelResolverException e) { } catch (AppModelResolverException e) {
throw new GradleException("Failed to resolve application model " + appArtifact + " dependencies", e); throw new GradleException("Failed to resolve application model " + appArtifact + " dependencies", e);
} }
final Map<String, ?> properties = getProject().getProperties(); final Properties realProperties = getBuildSystemProperties(appArtifact);
final Properties realProperties = new Properties();
for (Map.Entry<String, ?> entry : properties.entrySet()) {
final String key = entry.getKey();
final Object value = entry.getValue();
if (key != null && value instanceof String && key.startsWith("quarkus.")) {
realProperties.setProperty(key, (String) value);
}
}
realProperties.putIfAbsent("quarkus.application.name", appArtifact.getArtifactId());
realProperties.putIfAbsent("quarkus.application.version", appArtifact.getVersion());
boolean clear = false; boolean clear = false;
if (uberJar && System.getProperty("quarkus.package.uber-jar") == null) { if (uberJar && System.getProperty("quarkus.package.uber-jar") == null) {
System.setProperty("quarkus.package.uber-jar", "true"); System.setProperty("quarkus.package.uber-jar", "true");
clear = true; clear = true;
} }
try (CuratedApplicationCreator appCreationContext = CuratedApplicationCreator.builder() try (CuratedApplication appCreationContext = QuarkusBootstrap.builder(appArtifact.getPath())
.setWorkDir(getProject().getBuildDir().toPath()) .setBaseClassLoader(getClass().getClassLoader())
.setModelResolver(modelResolver) .setAppModelResolver(modelResolver)
.setTargetDirectory(getProject().getBuildDir().toPath())
.setBaseName(extension().finalName()) .setBaseName(extension().finalName())
.setAppArtifact(appArtifact).build()) { .setBuildSystemProperties(realProperties)
.setAppArtifact(appArtifact)
.setLocalProjectDiscovery(false)
.setIsolateDeployment(true)
//.setConfigDir(extension().outputConfigDirectory().toPath())
//.setTargetDirectory(extension().outputDirectory().toPath())
.build().bootstrap()) {
AugmentTask task = AugmentTask.builder().setBuildSystemProperties(realProperties) appCreationContext.createAugmentor().createProductionApplication();
.setAppClassesDir(extension().outputDirectory().toPath())
.setConfigDir(extension().outputConfigDirectory().toPath()).build();
appCreationContext.runTask(task);
} catch (AppCreatorException e) { } catch (BootstrapException e) {
throw new GradleException("Failed to build a runnable JAR", e); throw new GradleException("Failed to build a runnable JAR", e);
} finally { } finally {
if (clear) { if (clear) {

View File

@@ -306,7 +306,7 @@ public class QuarkusDev extends QuarkusTask {
context.getModules().add(wsModuleInfo); context.getModules().add(wsModuleInfo);
} }
for (AppDependency appDependency : appModel.getAllDependencies()) { for (AppDependency appDependency : appModel.getFullDeploymentDeps()) {
if (!projectDependencies.contains(appDependency.getArtifact().getKey())) { if (!projectDependencies.contains(appDependency.getArtifact().getKey())) {
addToClassPaths(classPathManifest, context, appDependency.getArtifact().getPath().toFile()); addToClassPaths(classPathManifest, context, appDependency.getArtifact().getPath().toFile());
} }

View File

@@ -8,13 +8,12 @@ import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.options.Option; import org.gradle.api.tasks.options.Option;
import io.quarkus.bootstrap.BootstrapException;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.AppModelResolver; import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.runner.bootstrap.GenerateConfigTask;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.phase.generateconfig.GenerateConfigTask;
public class QuarkusGenerateConfig extends QuarkusTask { public class QuarkusGenerateConfig extends QuarkusTask {
@@ -40,13 +39,7 @@ public class QuarkusGenerateConfig extends QuarkusTask {
getLogger().lifecycle("generating example config"); getLogger().lifecycle("generating example config");
final AppArtifact appArtifact = extension().getAppArtifact(); final AppArtifact appArtifact = extension().getAppArtifact();
final AppModel appModel;
final AppModelResolver modelResolver = extension().resolveAppModel(); final AppModelResolver modelResolver = extension().resolveAppModel();
try {
appModel = modelResolver.resolveModel(appArtifact);
} catch (AppModelResolverException e) {
throw new GradleException("Failed to resolve application model " + appArtifact + " dependencies", e);
}
if (extension().resourcesDir().isEmpty()) { if (extension().resourcesDir().isEmpty()) {
throw new GradleException("No resources directory, cannot create application.properties"); throw new GradleException("No resources directory, cannot create application.properties");
} }
@@ -56,14 +49,17 @@ public class QuarkusGenerateConfig extends QuarkusTask {
if (name == null || name.isEmpty()) { if (name == null || name.isEmpty()) {
name = "application.properties.example"; name = "application.properties.example";
} }
try (CuratedApplication bootstrap = QuarkusBootstrap.builder(getProject().getBuildDir().toPath())
try (CuratedApplicationCreator appCreationContext = CuratedApplicationCreator.builder() .setMode(QuarkusBootstrap.Mode.PROD)
.setWorkDir(getProject().getBuildDir().toPath()) .setAppModelResolver(modelResolver)
.build()) { .setBuildSystemProperties(getBuildSystemProperties(appArtifact))
appCreationContext.runTask(new GenerateConfigTask(new File(target, name).toPath())); .build()
.bootstrap()) {
GenerateConfigTask ct = new GenerateConfigTask(new File(target, name).toPath());
ct.run(bootstrap);
getLogger().lifecycle("Generated config file " + name); getLogger().lifecycle("Generated config file " + name);
} catch (AppCreatorException e) { } catch (BootstrapException e) {
throw new GradleException("Failed to generate config file", e); throw new RuntimeException(e);
} }
} }
} }

View File

@@ -7,9 +7,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import org.eclipse.microprofile.config.spi.ConfigBuilder;
import org.eclipse.microprofile.config.spi.ConfigSource; import org.eclipse.microprofile.config.spi.ConfigSource;
import org.gradle.api.GradleException; import org.gradle.api.GradleException;
import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Input;
@@ -17,12 +15,12 @@ import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.options.Option; import org.gradle.api.tasks.options.Option;
import io.quarkus.bootstrap.BootstrapException;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.resolver.AppModelResolver; import io.quarkus.bootstrap.resolver.AppModelResolver;
import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.bootstrap.resolver.AppModelResolverException;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.phase.augment.AugmentTask;
public class QuarkusNative extends QuarkusTask { public class QuarkusNative extends QuarkusTask {
@@ -343,102 +341,100 @@ public class QuarkusNative extends QuarkusTask {
} catch (AppModelResolverException e) { } catch (AppModelResolverException e) {
throw new GradleException("Failed to resolve application model " + appArtifact + " dependencies", e); throw new GradleException("Failed to resolve application model " + appArtifact + " dependencies", e);
} }
final Map<String, ?> properties = getProject().getProperties(); final Properties realProperties = getBuildSystemProperties(appArtifact);
final Properties realProperties = new Properties();
for (Map.Entry<String, ?> entry : properties.entrySet()) { Map<String, String> config = createCustomConfig();
final String key = entry.getKey(); Map<String, String> old = new HashMap<>();
final Object value = entry.getValue(); for (Map.Entry<String, String> e : config.entrySet()) {
if (key != null && value instanceof String && key.startsWith("quarkus.")) { old.put(e.getKey(), System.getProperty(e.getKey()));
realProperties.setProperty(key, (String) value); System.setProperty(e.getKey(), e.getValue());
}
try (CuratedApplication appCreationContext = QuarkusBootstrap.builder(appArtifact.getPath())
.setAppModelResolver(modelResolver)
.setBaseClassLoader(getClass().getClassLoader())
.setTargetDirectory(getProject().getBuildDir().toPath())
.setBaseName(extension().finalName())
.setLocalProjectDiscovery(false)
.setBuildSystemProperties(realProperties)
.setIsolateDeployment(true)
//.setConfigDir(extension().outputConfigDirectory().toPath())
//.setTargetDirectory(extension().outputDirectory().toPath())
.build().bootstrap()) {
appCreationContext.createAugmentor().createProductionApplication();
} catch (BootstrapException e) {
throw new GradleException("Failed to build a runnable JAR", e);
} finally {
for (Map.Entry<String, String> e : old.entrySet()) {
if (e.getValue() == null) {
System.clearProperty(e.getKey());
} else {
System.setProperty(e.getKey(), e.getValue());
}
} }
} }
realProperties.putIfAbsent("quarkus.application.name", appArtifact.getArtifactId());
realProperties.putIfAbsent("quarkus.application.version", appArtifact.getVersion());
try (CuratedApplicationCreator appCreationContext = CuratedApplicationCreator.builder()
.setWorkDir(getProject().getBuildDir().toPath())
.setModelResolver(modelResolver)
.setBaseName(extension().finalName())
.setAppArtifact(appArtifact).build()) {
AugmentTask task = AugmentTask.builder().setBuildSystemProperties(realProperties)
.setConfigCustomizer(createCustomConfig())
.setAppClassesDir(extension().outputDirectory().toPath())
.setConfigDir(extension().outputConfigDirectory().toPath()).build();
appCreationContext.runTask(task);
} catch (AppCreatorException e) {
throw new GradleException("Failed to generate a native image", e);
}
} }
private Consumer<ConfigBuilder> createCustomConfig() { private Map<String, String> createCustomConfig() {
return new Consumer<ConfigBuilder>() { Map<String, String> configs = new HashMap<>();
@Override configs.put("quarkus.package.type", "native");
public void accept(ConfigBuilder configBuilder) {
InMemoryConfigSource type = new InMemoryConfigSource(Integer.MAX_VALUE, "Native Image Type")
.add("quarkus.package.type", "native");
InMemoryConfigSource configs = new InMemoryConfigSource(0, "Native Image Maven Settings"); configs.put("quarkus.native.add-all-charsets", Boolean.toString(addAllCharsets));
configs.add("quarkus.native.add-all-charsets", addAllCharsets); if (additionalBuildArgs != null && !additionalBuildArgs.isEmpty()) {
configs.put("quarkus.native.additional-build-args",
additionalBuildArgs.stream()
.map(val -> val.replace("\\", "\\\\"))
.map(val -> val.replace(",", "\\,"))
.collect(joining(",")));
}
configs.put("quarkus.native.auto-service-loader-registration", Boolean.toString(autoServiceLoaderRegistration));
if (additionalBuildArgs != null && !additionalBuildArgs.isEmpty()) { configs.put("quarkus.native.cleanup-server", Boolean.toString(cleanupServer));
configs.add("quarkus.native.additional-build-args", configs.put("quarkus.native.debug-build-process", Boolean.toString(debugBuildProcess));
additionalBuildArgs.stream()
.map(val -> val.replace("\\", "\\\\")) configs.put("quarkus.native.debug-symbols", Boolean.toString(debugSymbols));
.map(val -> val.replace(",", "\\,")) configs.put("quarkus.native.enable-reports", Boolean.toString(enableReports));
.collect(joining(","))); if (containerRuntime != null && !containerRuntime.trim().isEmpty()) {
configs.put("quarkus.native.container-runtime", containerRuntime);
} else if (dockerBuild != null && !dockerBuild.trim().isEmpty()) {
if (!dockerBuild.isEmpty() && !dockerBuild.toLowerCase().equals("false")) {
if (dockerBuild.toLowerCase().equals("true")) {
configs.put("quarkus.native.container-runtime", "docker");
} else {
configs.put("quarkus.native.container-runtime", dockerBuild);
} }
configs.add("quarkus.native.auto-service-loader-registration", autoServiceLoaderRegistration);
configs.add("quarkus.native.cleanup-server", cleanupServer);
configs.add("quarkus.native.debug-build-process", debugBuildProcess);
configs.add("quarkus.native.debug-symbols", debugSymbols);
configs.add("quarkus.native.enable-reports", enableReports);
if (containerRuntime != null && !containerRuntime.trim().isEmpty()) {
configs.add("quarkus.native.container-runtime", containerRuntime);
} else if (dockerBuild != null && !dockerBuild.trim().isEmpty()) {
if (!dockerBuild.isEmpty() && !dockerBuild.toLowerCase().equals("false")) {
if (dockerBuild.toLowerCase().equals("true")) {
configs.add("quarkus.native.container-runtime", "docker");
} else {
configs.add("quarkus.native.container-runtime", dockerBuild);
}
}
}
if (containerRuntimeOptions != null && !containerRuntimeOptions.trim().isEmpty()) {
configs.add("quarkus.native.container-runtime-options", containerRuntimeOptions);
}
configs.add("quarkus.native.dump-proxies", dumpProxies);
configs.add("quarkus.native.enable-all-security-services", enableAllSecurityServices);
configs.add("quarkus.native.enable-fallback-images", enableFallbackImages);
configs.add("quarkus.native.enable-https-url-handler", enableHttpsUrlHandler);
configs.add("quarkus.native.enable-http-url-handler", enableHttpUrlHandler);
configs.add("quarkus.native.enable-isolates", enableIsolates);
configs.add("quarkus.native.enable-jni", enableJni);
configs.add("quarkus.native.enable-server", enableServer);
configs.add("quarkus.native.enable-vm-inspection", enableVMInspection);
configs.add("quarkus.native.full-stack-traces", fullStackTraces);
if (graalvmHome != null && !graalvmHome.trim().isEmpty()) {
configs.add("quarkus.native.graalvm-home", graalvmHome);
}
if (nativeImageXmx != null && !nativeImageXmx.trim().isEmpty()) {
configs.add("quarkus.native.native-image-xmx", nativeImageXmx);
}
configs.add("quarkus.native.report-errors-at-runtime", reportErrorsAtRuntime);
configs.add("quarkus.native.report-exception-stack-traces", reportExceptionStackTraces);
configBuilder.withSources(type, configs);
} }
}; }
if (containerRuntimeOptions != null && !containerRuntimeOptions.trim().isEmpty()) {
configs.put("quarkus.native.container-runtime-options", containerRuntimeOptions);
}
configs.put("quarkus.native.dump-proxies", Boolean.toString(dumpProxies));
configs.put("quarkus.native.enable-all-security-services", Boolean.toString(enableAllSecurityServices));
configs.put("quarkus.native.enable-fallback-images", Boolean.toString(enableFallbackImages));
configs.put("quarkus.native.enable-https-url-handler", Boolean.toString(enableHttpsUrlHandler));
configs.put("quarkus.native.enable-http-url-handler", Boolean.toString(enableHttpUrlHandler));
configs.put("quarkus.native.enable-isolates", Boolean.toString(enableIsolates));
configs.put("quarkus.native.enable-jni", Boolean.toString(enableJni));
configs.put("quarkus.native.enable-server", Boolean.toString(enableServer));
configs.put("quarkus.native.enable-vm-inspection", Boolean.toString(enableVMInspection));
configs.put("quarkus.native.full-stack-traces", Boolean.toString(fullStackTraces));
if (graalvmHome != null && !graalvmHome.trim().isEmpty()) {
configs.put("quarkus.native.graalvm-home", graalvmHome);
}
if (nativeImageXmx != null && !nativeImageXmx.trim().isEmpty()) {
configs.put("quarkus.native.native-image-xmx", nativeImageXmx);
}
configs.put("quarkus.native.report-errors-at-runtime", Boolean.toString(reportErrorsAtRuntime));
configs.put("quarkus.native.report-exception-stack-traces", Boolean.toString(reportExceptionStackTraces));
return configs;
} }

View File

@@ -1,7 +1,11 @@
package io.quarkus.gradle.tasks; package io.quarkus.gradle.tasks;
import java.util.Map;
import java.util.Properties;
import org.gradle.api.DefaultTask; import org.gradle.api.DefaultTask;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.gradle.QuarkusPluginExtension; import io.quarkus.gradle.QuarkusPluginExtension;
public abstract class QuarkusTask extends DefaultTask { public abstract class QuarkusTask extends DefaultTask {
@@ -21,4 +25,19 @@ public abstract class QuarkusTask extends DefaultTask {
} }
return extension; return extension;
} }
protected Properties getBuildSystemProperties(AppArtifact appArtifact) {
final Map<String, ?> properties = getProject().getProperties();
final Properties realProperties = new Properties();
for (Map.Entry<String, ?> entry : properties.entrySet()) {
final String key = entry.getKey();
final Object value = entry.getValue();
if (key != null && value instanceof String && key.startsWith("quarkus.")) {
realProperties.setProperty(key, (String) value);
}
}
realProperties.putIfAbsent("quarkus.application.name", appArtifact.getArtifactId());
realProperties.putIfAbsent("quarkus.application.version", appArtifact.getVersion());
return realProperties;
}
} }

View File

@@ -1,13 +1,18 @@
package io.quarkus.gradle.tasks; package io.quarkus.gradle.tasks;
import java.util.List; import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.Map; import java.util.Map;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.testing.Test; import org.gradle.api.tasks.testing.Test;
import io.quarkus.bootstrap.BootstrapClassLoaderFactory; import io.quarkus.bootstrap.BootstrapConstants;
import io.quarkus.bootstrap.model.AppDependency; import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.gradle.QuarkusPluginExtension; import io.quarkus.gradle.QuarkusPluginExtension;
public class QuarkusTestConfig extends QuarkusTask { public class QuarkusTestConfig extends QuarkusTask {
@@ -20,21 +25,23 @@ public class QuarkusTestConfig extends QuarkusTask {
public void setupTest() { public void setupTest() {
final QuarkusPluginExtension quarkusExt = extension(); final QuarkusPluginExtension quarkusExt = extension();
try { try {
final List<AppDependency> deploymentDeps = quarkusExt.resolveAppModel().resolveModel(quarkusExt.getAppArtifact()) final AppModel deploymentDeps = quarkusExt.resolveAppModel().resolveModel(quarkusExt.getAppArtifact());
.getDeploymentDependencies();
final StringBuilder buf = new StringBuilder();
for (AppDependency dep : deploymentDeps) {
buf.append(dep.getArtifact().getPath().toUri().toURL().toExternalForm());
buf.append(' ');
}
final String deploymentCp = buf.toString();
final String nativeRunner = getProject().getBuildDir().toPath().resolve(quarkusExt.finalName() + "-runner") final String nativeRunner = getProject().getBuildDir().toPath().resolve(quarkusExt.finalName() + "-runner")
.toAbsolutePath() .toAbsolutePath()
.toString(); .toString();
SourceSetContainer sourceSets = getProject().getConvention().getPlugin(JavaPluginConvention.class)
.getSourceSets();
SourceSet testSourceSet = sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME);
File classesDir = testSourceSet.getOutput().getClassesDirs().getFiles().iterator().next();
classesDir.mkdirs();
try (ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream(new File(classesDir, BootstrapConstants.SERIALIZED_APP_MODEL)))) {
out.writeObject(deploymentDeps);
}
for (Test test : getProject().getTasks().withType(Test.class)) { for (Test test : getProject().getTasks().withType(Test.class)) {
final Map<String, Object> props = test.getSystemProperties(); final Map<String, Object> props = test.getSystemProperties();
props.put(BootstrapClassLoaderFactory.PROP_DEPLOYMENT_CP, deploymentCp);
props.put("native.image.path", nativeRunner); props.put("native.image.path", nativeRunner);
} }
} catch (Exception e) { } catch (Exception e) {

View File

@@ -67,10 +67,6 @@
<artifactId>maven-plugin-annotations</artifactId> <artifactId>maven-plugin-annotations</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-creator</artifactId>
</dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-development-mode</artifactId> <artifactId>quarkus-development-mode</artifactId>

View File

@@ -18,14 +18,11 @@ import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RemoteRepository;
import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.app.AugmentAction;
import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.bootstrap.app.AugmentResult;
import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.phase.augment.AugmentOutcome;
import io.quarkus.creator.phase.augment.AugmentTask;
/** /**
* Build the application. * Build the application.
@@ -138,48 +135,42 @@ public class BuildMojo extends AbstractMojo {
return; return;
} }
final Artifact projectArtifact = project.getArtifact();
final AppArtifact appArtifact = new AppArtifact(projectArtifact.getGroupId(), projectArtifact.getArtifactId(),
projectArtifact.getClassifier(), projectArtifact.getArtifactHandler().getExtension(),
projectArtifact.getVersion());
final BootstrapAppModelResolver modelResolver;
try {
modelResolver = new BootstrapAppModelResolver(
MavenArtifactResolver.builder()
.setRepositorySystem(repoSystem)
.setRepositorySystemSession(repoSession)
.setRemoteRepositories(repos)
.build());
} catch (AppModelResolverException e) {
throw new MojoExecutionException("Failed to resolve application model " + appArtifact + " dependencies", e);
}
final Properties projectProperties = project.getProperties();
final Properties realProperties = new Properties();
for (String name : projectProperties.stringPropertyNames()) {
if (name.startsWith("quarkus.")) {
realProperties.setProperty(name, projectProperties.getProperty(name));
}
}
boolean clear = false; boolean clear = false;
if (uberJar && System.getProperty(QUARKUS_PACKAGE_UBER_JAR) == null) { try {
System.setProperty(QUARKUS_PACKAGE_UBER_JAR, "true");
clear = true; final Properties projectProperties = project.getProperties();
} final Properties realProperties = new Properties();
realProperties.putIfAbsent("quarkus.application.name", project.getArtifactId()); for (String name : projectProperties.stringPropertyNames()) {
realProperties.putIfAbsent("quarkus.application.version", project.getVersion()); if (name.startsWith("quarkus.")) {
try (CuratedApplicationCreator appCreationContext = CuratedApplicationCreator.builder() realProperties.setProperty(name, projectProperties.getProperty(name));
.setModelResolver(modelResolver) }
.setWorkDir(buildDir.toPath()) }
.setBaseName(finalName) if (uberJar && System.getProperty(QUARKUS_PACKAGE_UBER_JAR) == null) {
.setAppArtifact(appArtifact) System.setProperty(QUARKUS_PACKAGE_UBER_JAR, "true");
.build()) { clear = true;
}
realProperties.putIfAbsent("quarkus.application.name", project.getArtifactId());
realProperties.putIfAbsent("quarkus.application.version", project.getVersion());
MavenArtifactResolver resolver = MavenArtifactResolver.builder()
.setRepositorySystem(repoSystem)
.setRepositorySystemSession(repoSession)
.setRemoteRepositories(repos)
.build();
CuratedApplication curatedApplication = QuarkusBootstrap.builder(outputDirectory.toPath())
.setProjectRoot(project.getBasedir().toPath())
.setMavenArtifactResolver(resolver)
.setBaseClassLoader(BuildMojo.class.getClassLoader())
.setBuildSystemProperties(realProperties)
.setLocalProjectDiscovery(false)
.setBaseName(finalName)
.setTargetDirectory(buildDir.toPath())
.build().bootstrap();
AugmentAction action = curatedApplication.createAugmentor();
AugmentResult result = action.createProductionApplication();
// resolve the outcome we need here
AugmentOutcome result = appCreationContext.runTask(
AugmentTask.builder()
.setAppClassesDir(outputDirectory.toPath())
.setConfigDir(outputDirectory.toPath())
.setBuildSystemProperties(realProperties).build());
Artifact original = project.getArtifact(); Artifact original = project.getArtifact();
if (result.getJar() != null) { if (result.getJar() != null) {
if (result.getJar().isUberJar() && result.getJar().getOriginalArtifact() != null) { if (result.getJar().isUberJar() && result.getJar().getOriginalArtifact() != null) {
@@ -190,12 +181,13 @@ public class BuildMojo extends AbstractMojo {
} }
} }
} catch (AppCreatorException e) { } catch (Exception e) {
throw new MojoExecutionException("Failed to build a runnable JAR", e); throw new MojoExecutionException("Failed to build quarkus application", e);
} finally { } finally {
if (clear) { if (clear) {
System.clearProperty(QUARKUS_PACKAGE_UBER_JAR); System.clearProperty(QUARKUS_PACKAGE_UBER_JAR);
} }
} }
} }
} }

View File

@@ -53,11 +53,9 @@ import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException; import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyResult;
import org.twdata.maven.mojoexecutor.MojoExecutor; import org.twdata.maven.mojoexecutor.MojoExecutor;
import io.quarkus.bootstrap.model.AppDependency;
import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.bootstrap.resolver.maven.MavenRepoInitializer; import io.quarkus.bootstrap.resolver.maven.MavenRepoInitializer;
import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject;
@@ -236,9 +234,9 @@ public class DevMojo extends AbstractMojo {
public void execute() throws MojoFailureException, MojoExecutionException { public void execute() throws MojoFailureException, MojoExecutionException {
mavenVersionEnforcer.ensureMavenVersion(getLog(), session); mavenVersionEnforcer.ensureMavenVersion(getLog(), session);
boolean found = MojoUtils.checkProjectForMavenBuildPlugin(project); Plugin pluginDef = MojoUtils.checkProjectForMavenBuildPlugin(project);
if (!found) { if (pluginDef == null) {
getLog().warn("The quarkus-maven-plugin build goal was not configured for this project, " + getLog().warn("The quarkus-maven-plugin build goal was not configured for this project, " +
"skipping quarkus:dev as this is assumed to be a support library. If you want to run quarkus dev" + "skipping quarkus:dev as this is assumed to be a support library. If you want to run quarkus dev" +
" on this project make sure the quarkus-maven-plugin is configured with a build goal."); " on this project make sure the quarkus-maven-plugin is configured with a build goal.");
@@ -319,7 +317,7 @@ public class DevMojo extends AbstractMojo {
args.add("-Xverify:none"); args.add("-Xverify:none");
} }
DevModeRunner runner = new DevModeRunner(args); DevModeRunner runner = new DevModeRunner(args, pluginDef);
runner.prepare(); runner.prepare();
runner.run(); runner.run();
@@ -344,7 +342,7 @@ public class DevMojo extends AbstractMojo {
} }
} }
if (changed) { if (changed) {
DevModeRunner newRunner = new DevModeRunner(args); DevModeRunner newRunner = new DevModeRunner(args, pluginDef);
try { try {
newRunner.prepare(); newRunner.prepare();
} catch (Exception e) { } catch (Exception e) {
@@ -441,9 +439,11 @@ public class DevMojo extends AbstractMojo {
private final List<String> args; private final List<String> args;
private Process process; private Process process;
private Set<Path> pomFiles = new HashSet<>(); private Set<Path> pomFiles = new HashSet<>();
private final Plugin pluginDef;
DevModeRunner(List<String> args) { DevModeRunner(List<String> args, Plugin pluginDef) {
this.args = new ArrayList<>(args); this.args = new ArrayList<>(args);
this.pluginDef = pluginDef;
} }
/** /**
@@ -490,7 +490,7 @@ public class DevMojo extends AbstractMojo {
for (Map.Entry<Object, Object> e : System.getProperties().entrySet()) { for (Map.Entry<Object, Object> e : System.getProperties().entrySet()) {
devModeContext.getSystemProperties().put(e.getKey().toString(), (String) e.getValue()); devModeContext.getSystemProperties().put(e.getKey().toString(), (String) e.getValue());
} }
devModeContext.setProjectDir(project.getFile().getParentFile());
devModeContext.getBuildSystemProperties().putAll((Map) project.getProperties()); devModeContext.getBuildSystemProperties().putAll((Map) project.getProperties());
// this is a minor hack to allow ApplicationConfig to be populated with defaults // this is a minor hack to allow ApplicationConfig to be populated with defaults
@@ -528,49 +528,44 @@ public class DevMojo extends AbstractMojo {
} }
setKotlinSpecificFlags(devModeContext); setKotlinSpecificFlags(devModeContext);
final LocalProject localProject;
final AppModel appModel; if (noDeps) {
try { localProject = LocalProject.load(outputDirectory.toPath());
RepositorySystem repoSystem = DevMojo.this.repoSystem; addProject(devModeContext, localProject);
final LocalProject localProject; pomFiles.add(localProject.getDir().resolve("pom.xml"));
if (noDeps) { } else {
localProject = LocalProject.load(outputDirectory.toPath()); localProject = LocalProject.loadWorkspace(outputDirectory.toPath());
addProject(devModeContext, localProject); for (LocalProject project : localProject.getSelfWithLocalDeps()) {
pomFiles.add(localProject.getDir().resolve("pom.xml")); if (project.getClassesDir() != null) {
} else { //if this project also contains Quarkus extensions we do no want to include these in the discovery
localProject = LocalProject.loadWorkspace(outputDirectory.toPath()); //a bit of an edge case, but if you try and include a sample project with your extension you will
for (LocalProject project : localProject.getSelfWithLocalDeps()) { //run into problems without this
if (project.getClassesDir() != null) { if (Files.exists(project.getClassesDir().resolve("META-INF/quarkus-extension.properties")) ||
//if this project also contains Quarkus extensions we do no want to include these in the discovery Files.exists(project.getClassesDir().resolve("META-INF/quarkus-build-steps.list"))) {
//a bit of an edge case, but if you try and include a sample project with your extension you will continue;
//run into problems without this
if (Files.exists(project.getClassesDir().resolve("META-INF/quarkus-extension.properties")) ||
Files.exists(project.getClassesDir().resolve("META-INF/quarkus-build-steps.list"))) {
continue;
}
} }
addProject(devModeContext, project);
pomFiles.add(project.getDir().resolve("pom.xml"));
} }
repoSystem = MavenRepoInitializer.getRepositorySystem(repoSession.isOffline(), localProject.getWorkspace()); addProject(devModeContext, project);
pomFiles.add(project.getDir().resolve("pom.xml"));
} }
repoSystem = MavenRepoInitializer.getRepositorySystem(repoSession.isOffline(), localProject.getWorkspace());
appModel = new BootstrapAppModelResolver(MavenArtifactResolver.builder()
.setRepositorySystem(repoSystem)
.setRepositorySystemSession(repoSession)
.setRemoteRepositories(repos)
.setWorkspace(localProject.getWorkspace())
.build())
.setDevMode(true)
.resolveModel(localProject.getAppArtifact());
if (appModel.getAllDependencies().isEmpty()) {
throw new RuntimeException("Unable to resolve application dependencies");
}
} catch (Exception e) {
throw new MojoExecutionException("Failed to resolve Quarkus application model", e);
} }
for (AppDependency appDep : appModel.getAllDependencies()) { DefaultArtifact bootstrap = new DefaultArtifact("io.quarkus", "quarkus-development-mode", "jar",
addToClassPaths(classPathManifest, devModeContext, appDep.getArtifact().getPath().toFile()); pluginDef.getVersion());
MavenArtifactResolver mavenArtifactResolver = MavenArtifactResolver.builder()
.setRepositorySystem(repoSystem)
.setRepositorySystemSession(repoSession)
.setRemoteRepositories(repos)
.build();
DependencyResult cpRes = mavenArtifactResolver.resolveDependencies(
bootstrap,
Collections.emptyList(), Collections.emptyList());
addToClassPaths(classPathManifest, devModeContext,
mavenArtifactResolver.resolve(bootstrap).getArtifact().getFile());
for (ArtifactResult appDep : cpRes.getArtifactResults()) {
addToClassPaths(classPathManifest, devModeContext, appDep.getArtifact().getFile());
} }
args.add("-Djava.util.logging.manager=org.jboss.logmanager.LogManager"); args.add("-Djava.util.logging.manager=org.jboss.logmanager.LogManager");

View File

@@ -1,9 +1,10 @@
package io.quarkus.maven; package io.quarkus.maven;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List; import java.util.List;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Resource; import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
@@ -18,14 +19,11 @@ import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RemoteRepository;
import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.model.AppModel; import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; import io.quarkus.bootstrap.resolver.AppModelResolverException;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; import io.quarkus.runner.bootstrap.GenerateConfigTask;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.phase.generateconfig.GenerateConfigTask;
/** /**
* Generates an example application-config.properties, with all properties commented out * Generates an example application-config.properties, with all properties commented out
@@ -95,49 +93,41 @@ public class GenerateConfigMojo extends AbstractMojo {
getLog().info("Type of the artifact is POM, skipping generate-config goal"); getLog().info("Type of the artifact is POM, skipping generate-config goal");
return; return;
} }
final Artifact projectArtifact = project.getArtifact();
final AppArtifact appArtifact = new AppArtifact(projectArtifact.getGroupId(), projectArtifact.getArtifactId(),
projectArtifact.getClassifier(), "pom",
projectArtifact.getVersion());
final AppModel appModel;
final BootstrapAppModelResolver modelResolver;
try {
LocalProject localProject = LocalProject.load(project.getBasedir().toPath());
modelResolver = new BootstrapAppModelResolver(
MavenArtifactResolver.builder()
.setRepositorySystem(repoSystem)
.setRepositorySystemSession(repoSession)
.setRemoteRepositories(repos)
.setWorkspace(localProject.getWorkspace())
.build());
appModel = modelResolver.resolveModel(appArtifact);
} catch (Exception e) {
throw new MojoExecutionException("Failed to resolve application model " + appArtifact + " dependencies", e);
}
if (project.getResources().isEmpty()) { if (project.getResources().isEmpty()) {
throw new MojoExecutionException("No resources directory, cannot create application.properties"); throw new MojoExecutionException("No resources directory, cannot create application.properties");
} }
Resource res = project.getResources().get(0); try {
File target = new File(res.getDirectory()); MavenArtifactResolver resolver = MavenArtifactResolver.builder()
.setRepositorySystem(repoSystem)
.setRepositorySystemSession(repoSession)
.setRemoteRepositories(repos)
.build();
String name = file; try (CuratedApplication curatedApplication = QuarkusBootstrap
if (name == null || name.isEmpty()) { .builder(Paths.get(project.getBuild().getOutputDirectory()))
name = "application.properties.example"; .setMavenArtifactResolver(resolver)
} .setProjectRoot(project.getBasedir().toPath())
.setBaseClassLoader(getClass().getClassLoader())
.setBuildSystemProperties(project.getProperties())
.build().bootstrap()) {
try (CuratedApplicationCreator appCreationContext = CuratedApplicationCreator.builder() Resource res = project.getResources().get(0);
// configure the build phases we want the app to go through File target = new File(res.getDirectory());
.setWorkDir(buildDir.toPath())
.setModelResolver(modelResolver)
.setAppArtifact(appModel.getAppArtifact())
.build()) {
appCreationContext.runTask(new GenerateConfigTask(new File(target, name).toPath())); String name = file;
getLog().info("Generated config file " + name); if (name == null || name.isEmpty()) {
} catch (AppCreatorException e) { name = "application.properties.example";
throw new MojoExecutionException("Failed to generate config file", e); }
Path configFile = new File(target, name).toPath();
GenerateConfigTask generateConfigTask = new GenerateConfigTask(configFile);
generateConfigTask.run(curatedApplication);
} catch (Exception e) {
throw new MojoExecutionException("Failed to generate config file", e);
}
} catch (AppModelResolverException e) {
throw new RuntimeException(e);
} }
} }
} }

View File

@@ -3,13 +3,10 @@ package io.quarkus.maven;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import java.io.File; import java.io.File;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;
import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.AbstractMojo;
@@ -25,17 +22,13 @@ import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.microprofile.config.spi.ConfigBuilder;
import org.eclipse.microprofile.config.spi.ConfigSource;
import io.quarkus.bootstrap.app.AugmentAction;
import io.quarkus.bootstrap.app.AugmentResult;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.model.AppArtifact; import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.bootstrap.model.AppModel;
import io.quarkus.bootstrap.resolver.AppModelResolverException;
import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.creator.AppCreatorException;
import io.quarkus.creator.CuratedApplicationCreator;
import io.quarkus.creator.phase.augment.AugmentTask;
/** /**
* Legacy mojo for backwards compatibility reasons. This should not be used in new projects * Legacy mojo for backwards compatibility reasons. This should not be used in new projects
@@ -61,7 +54,7 @@ public class NativeImageMojo extends AbstractMojo {
/** /**
* The directory for compiled classes. * The directory for compiled classes.
*/ */
@Parameter(readonly = true, required = true, defaultValue = "${project.build.directory}") @Parameter(readonly = true, required = true, defaultValue = "${project.build.outputDirectory}")
private File outputDirectory; private File outputDirectory;
@Parameter @Parameter
@@ -191,8 +184,6 @@ public class NativeImageMojo extends AbstractMojo {
return; return;
} }
final CuratedApplicationCreator.Builder creatorBuilder = CuratedApplicationCreator.builder();
// The runner JAR has not been built yet, so we are going to build it // The runner JAR has not been built yet, so we are going to build it
final AppArtifact appCoords; final AppArtifact appCoords;
AppArtifact managingProject = null; AppArtifact managingProject = null;
@@ -258,206 +249,161 @@ public class NativeImageMojo extends AbstractMojo {
project.getArtifact().getArtifactHandler().getExtension(), project.getArtifact().getArtifactHandler().getExtension(),
project.getArtifact().getVersion()); project.getArtifact().getVersion());
} }
final AppModel appModel;
final BootstrapAppModelResolver modelResolver;
try { try {
final MavenArtifactResolver mvn = MavenArtifactResolver.builder()
final Properties projectProperties = project.getProperties();
final Properties realProperties = new Properties();
for (String name : projectProperties.stringPropertyNames()) {
if (name.startsWith("quarkus.")) {
realProperties.setProperty(name, projectProperties.getProperty(name));
}
}
realProperties.putIfAbsent("quarkus.application.name", project.getArtifactId());
realProperties.putIfAbsent("quarkus.application.version", project.getVersion());
Map<String, String> config = createCustomConfig();
Map<String, String> old = new HashMap<>();
for (Map.Entry<String, String> e : config.entrySet()) {
old.put(e.getKey(), System.getProperty(e.getKey()));
System.setProperty(e.getKey(), e.getValue());
}
MavenArtifactResolver resolver = MavenArtifactResolver.builder()
.setRepositorySystem(repoSystem) .setRepositorySystem(repoSystem)
.setRepositorySystemSession(repoSession) .setRepositorySystemSession(repoSession)
.setRemoteRepositories(repos) .setRemoteRepositories(repos)
.build(); .build();
appCoords.setPath(mvn.resolve(appMvnArtifact).getArtifact().getFile().toPath()); appCoords.setPath(resolver.resolve(appMvnArtifact).getArtifact().getFile().toPath());
modelResolver = new BootstrapAppModelResolver(mvn);
appModel = modelResolver.resolveManagedModel(appCoords, Collections.emptyList(), managingProject);
} catch (AppModelResolverException e) {
throw new MojoExecutionException("Failed to resolve application model dependencies for " + appCoords, e);
}
final Properties buildSystemProperties = project.getProperties(); try (CuratedApplication curatedApplication = QuarkusBootstrap.builder(appCoords.getPath())
final Properties projectProperties = new Properties(); .setProjectRoot(project.getBasedir().toPath())
projectProperties.putAll(buildSystemProperties); .setBuildSystemProperties(realProperties)
projectProperties.putIfAbsent("quarkus.application.name", project.getArtifactId()); .setAppArtifact(appCoords)
projectProperties.putIfAbsent("quarkus.application.version", project.getVersion()); .setBaseName(finalName)
.setManagingProject(managingProject)
.setMavenArtifactResolver(resolver)
.setLocalProjectDiscovery(false)
.setBaseClassLoader(BuildMojo.class.getClassLoader())
.setTargetDirectory(buildDir.toPath())
.build().bootstrap()) {
Consumer<ConfigBuilder> config = createCustomConfig(); AugmentAction action = curatedApplication.createAugmentor();
String old = System.getProperty(QUARKUS_PACKAGE_TYPE); AugmentResult result = action.createProductionApplication();
System.setProperty(QUARKUS_PACKAGE_TYPE, "native"); } finally {
try (CuratedApplicationCreator appCreationContext = creatorBuilder for (Map.Entry<String, String> e : old.entrySet()) {
.setWorkDir(buildDir.toPath()) if (e.getValue() == null) {
.setModelResolver(modelResolver) System.clearProperty(e.getKey());
.setBaseName(finalName) } else {
.setAppArtifact(appModel.getAppArtifact()) System.setProperty(e.getKey(), e.getValue());
.build()) {
AugmentTask task = AugmentTask.builder().setConfigCustomizer(config)
.setAppClassesDir(new File(outputDirectory, "classes").toPath())
.setBuildSystemProperties(projectProperties).build();
appCreationContext.runTask(task);
} catch (AppCreatorException e) {
throw new MojoExecutionException("Failed to generate a native image", e);
} finally {
if (old == null) {
System.clearProperty(QUARKUS_PACKAGE_TYPE);
} else {
System.setProperty(QUARKUS_PACKAGE_TYPE, old);
}
}
}
private Consumer<ConfigBuilder> createCustomConfig() {
return new Consumer<ConfigBuilder>() {
@Override
public void accept(ConfigBuilder configBuilder) {
InMemoryConfigSource type = new InMemoryConfigSource(Integer.MAX_VALUE, "Native Image Type")
.add("quarkus.package.type", "native");
configBuilder.withSources(type);
InMemoryConfigSource configs = new InMemoryConfigSource(0, "Native Image Maven Settings");
if (addAllCharsets != null) {
configs.add("quarkus.native.add-all-charsets", addAllCharsets.toString());
}
if (additionalBuildArgs != null && !additionalBuildArgs.isEmpty()) {
configs.add("quarkus.native.additional-build-args", additionalBuildArgs);
}
if (autoServiceLoaderRegistration != null) {
configs.add("quarkus.native.auto-service-loader-registration", autoServiceLoaderRegistration.toString());
}
if (cleanupServer != null) {
configs.add("quarkus.native.cleanup-server", cleanupServer.toString());
}
if (debugBuildProcess != null) {
configs.add("quarkus.native.debug-build-process", debugBuildProcess.toString());
}
if (debugSymbols != null) {
configs.add("quarkus.native.debug-symbols", debugSymbols.toString());
}
if (disableReports != null) {
configs.add("quarkus.native.enable-reports", Boolean.toString(!disableReports));
}
if (enableReports != null) {
configs.add("quarkus.native.enable-reports", enableReports.toString());
}
if (containerRuntime != null && !containerRuntime.trim().isEmpty()) {
configs.add("quarkus.native.container-runtime", containerRuntime);
} else if (dockerBuild != null && !dockerBuild.trim().isEmpty()) {
if (!dockerBuild.toLowerCase().equals("false")) {
if (dockerBuild.toLowerCase().equals("true")) {
configs.add("quarkus.native.container-runtime", "docker");
} else {
configs.add("quarkus.native.container-runtime", dockerBuild);
}
} }
} }
if (containerRuntimeOptions != null && !containerRuntimeOptions.trim().isEmpty()) {
configs.add("quarkus.native.container-runtime-options", containerRuntimeOptions);
}
if (dumpProxies != null) {
configs.add("quarkus.native.dump-proxies", dumpProxies.toString());
}
if (enableAllSecurityServices != null) {
configs.add("quarkus.native.enable-all-security-services", enableAllSecurityServices.toString());
}
if (enableFallbackImages != null) {
configs.add("quarkus.native.enable-fallback-images", enableFallbackImages.toString());
}
if (enableHttpsUrlHandler != null) {
configs.add("quarkus.native.enable-https-url-handler", enableHttpsUrlHandler.toString());
}
if (enableHttpUrlHandler != null) {
configs.add("quarkus.native.enable-http-url-handler", enableHttpUrlHandler.toString());
}
if (enableIsolates != null) {
configs.add("quarkus.native.enable-isolates", enableIsolates.toString());
}
if (enableJni != null) {
configs.add("quarkus.native.enable-jni", enableJni.toString());
}
if (enableServer != null) {
configs.add("quarkus.native.enable-server", enableServer.toString());
}
if (enableVMInspection != null) {
configs.add("quarkus.native.enable-vm-inspection", enableVMInspection.toString());
}
if (fullStackTraces != null) {
configs.add("quarkus.native.full-stack-traces", fullStackTraces.toString());
}
if (graalvmHome != null && !graalvmHome.trim().isEmpty()) {
configs.add("quarkus.native.graalvm-home", graalvmHome);
}
if (javaHome != null && !javaHome.toString().isEmpty()) {
configs.add("quarkus.native.java-home", javaHome.toString());
}
if (nativeImageXmx != null && !nativeImageXmx.trim().isEmpty()) {
configs.add("quarkus.native.native-image-xmx", nativeImageXmx);
}
if (reportErrorsAtRuntime != null) {
configs.add("quarkus.native.report-errors-at-runtime", reportErrorsAtRuntime.toString());
}
if (reportExceptionStackTraces != null) {
configs.add("quarkus.native.report-exception-stack-traces", reportExceptionStackTraces.toString());
}
if (publishDebugBuildProcessPort) {
configs.add("quarkus.native.publish-debug-build-process-port",
Boolean.toString(publishDebugBuildProcessPort));
}
configBuilder.withSources(configs);
} }
};
} catch (Exception e) {
throw new MojoExecutionException("Failed to generate native image", e);
}
} }
private static final class InMemoryConfigSource implements ConfigSource { private Map<String, String> createCustomConfig() {
Map<String, String> configs = new HashMap<>();
private final Map<String, String> values = new HashMap<>(); configs.put("quarkus.package.type", "native");
private final int ordinal; if (addAllCharsets != null) {
private final String name; configs.put("quarkus.native.add-all-charsets", addAllCharsets.toString());
}
private InMemoryConfigSource(int ordinal, String name) { if (additionalBuildArgs != null && !additionalBuildArgs.isEmpty()) {
this.ordinal = ordinal; configs.put("quarkus.native.additional-build-args",
this.name = name; additionalBuildArgs.stream()
.map(val -> val.replace("\\", "\\\\"))
.map(val -> val.replace(",", "\\,"))
.collect(joining(",")));
}
if (autoServiceLoaderRegistration != null) {
configs.put("quarkus.native.auto-service-loader-registration", autoServiceLoaderRegistration.toString());
}
if (cleanupServer != null) {
configs.put("quarkus.native.cleanup-server", cleanupServer.toString());
}
if (debugBuildProcess != null) {
configs.put("quarkus.native.debug-build-process", debugBuildProcess.toString());
}
if (debugSymbols != null) {
configs.put("quarkus.native.debug-symbols", debugSymbols.toString());
}
if (disableReports != null) {
configs.put("quarkus.native.enable-reports", Boolean.toString(!disableReports));
}
if (enableReports != null) {
configs.put("quarkus.native.enable-reports", enableReports.toString());
}
if (containerRuntime != null && !containerRuntime.trim().isEmpty()) {
configs.put("quarkus.native.container-runtime", containerRuntime);
} else if (dockerBuild != null && !dockerBuild.trim().isEmpty()) {
if (!dockerBuild.toLowerCase().equals("false")) {
if (dockerBuild.toLowerCase().equals("true")) {
configs.put("quarkus.native.container-runtime", "docker");
} else {
configs.put("quarkus.native.container-runtime", dockerBuild);
}
}
}
if (containerRuntimeOptions != null && !containerRuntimeOptions.trim().isEmpty()) {
configs.put("quarkus.native.container-runtime-options", containerRuntimeOptions);
}
if (dumpProxies != null) {
configs.put("quarkus.native.dump-proxies", dumpProxies.toString());
}
if (enableAllSecurityServices != null) {
configs.put("quarkus.native.enable-all-security-services", enableAllSecurityServices.toString());
}
if (enableFallbackImages != null) {
configs.put("quarkus.native.enable-fallback-images", enableFallbackImages.toString());
}
if (enableHttpsUrlHandler != null) {
configs.put("quarkus.native.enable-https-url-handler", enableHttpsUrlHandler.toString());
}
if (enableHttpUrlHandler != null) {
configs.put("quarkus.native.enable-http-url-handler", enableHttpUrlHandler.toString());
}
if (enableIsolates != null) {
configs.put("quarkus.native.enable-isolates", enableIsolates.toString());
}
if (enableJni != null) {
configs.put("quarkus.native.enable-jni", enableJni.toString());
} }
public InMemoryConfigSource add(String key, String value) { if (enableServer != null) {
values.put(key, value); configs.put("quarkus.native.enable-server", enableServer.toString());
return this;
} }
public InMemoryConfigSource add(String key, List<String> value) { if (enableVMInspection != null) {
values.put(key, value.stream() configs.put("quarkus.native.enable-vm-inspection", enableVMInspection.toString());
.map(val -> val.replace("\\", "\\\\"))
.map(val -> val.replace(",", "\\,"))
.collect(joining(",")));
return this;
} }
if (fullStackTraces != null) {
configs.put("quarkus.native.full-stack-traces", fullStackTraces.toString());
}
if (graalvmHome != null && !graalvmHome.trim().isEmpty()) {
configs.put("quarkus.native.graalvm-home", graalvmHome);
}
if (javaHome != null && !javaHome.toString().isEmpty()) {
configs.put("quarkus.native.java-home", javaHome.toString());
}
if (nativeImageXmx != null && !nativeImageXmx.trim().isEmpty()) {
configs.put("quarkus.native.native-image-xmx", nativeImageXmx);
}
if (reportErrorsAtRuntime != null) {
configs.put("quarkus.native.report-errors-at-runtime", reportErrorsAtRuntime.toString());
}
if (reportExceptionStackTraces != null) {
configs.put("quarkus.native.report-exception-stack-traces", reportExceptionStackTraces.toString());
}
if (publishDebugBuildProcessPort) {
configs.put("quarkus.native.publish-debug-build-process-port",
Boolean.toString(publishDebugBuildProcessPort));
}
return configs;
@Override
public Map<String, String> getProperties() {
return values;
}
@Override
public Set<String> getPropertyNames() {
return values.keySet();
}
@Override
public int getOrdinal() {
return ordinal;
}
@Override
public String getValue(String propertyName) {
return values.get(propertyName);
}
@Override
public String getName() {
return name;
}
} }
} }

View File

@@ -7,6 +7,7 @@ import java.nio.file.Paths;
import java.util.Optional; import java.util.Optional;
import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Resource; import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
@@ -74,9 +75,9 @@ public class RemoteDevMojo extends AbstractMojo {
@Override @Override
public void execute() throws MojoFailureException, MojoExecutionException { public void execute() throws MojoFailureException, MojoExecutionException {
mavenVersionEnforcer.ensureMavenVersion(getLog(), session); mavenVersionEnforcer.ensureMavenVersion(getLog(), session);
boolean found = MojoUtils.checkProjectForMavenBuildPlugin(project); Plugin found = MojoUtils.checkProjectForMavenBuildPlugin(project);
if (!found) { if (found == null) {
getLog().warn("The quarkus-maven-plugin build goal was not configured for this project, " + getLog().warn("The quarkus-maven-plugin build goal was not configured for this project, " +
"skipping quarkus:remote-dev as this is assumed to be a support library. If you want to run Quarkus remote-dev" "skipping quarkus:remote-dev as this is assumed to be a support library. If you want to run Quarkus remote-dev"
+ +

View File

@@ -32,6 +32,10 @@
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-devtools-common</artifactId> <artifactId>quarkus-devtools-common</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.wildfly.common</groupId>
<artifactId>wildfly-common</artifactId>
</dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-platform-descriptor-resolver-json</artifactId> <artifactId>quarkus-platform-descriptor-resolver-json</artifactId>
@@ -42,10 +46,6 @@
<artifactId>maven-plugin-annotations</artifactId> <artifactId>maven-plugin-annotations</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-creator</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.glassfish</groupId> <groupId>org.glassfish</groupId>

View File

@@ -0,0 +1,173 @@
////
This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/master/docs/src/main/asciidoc
////
= Quarkus - Class Loading Reference
include::./attributes.adoc[]
This document explains the Quarkus class loading architecture. It is intended for extension
authors and advanced users who want to understand exactly how Quarkus works.
The Quarkus class loading architecture is slightly different depending on the mode that
the application is run in. When running a production application everything is loaded
in the system ClassLoader, so it is a completely flat class path. This also applies to
native image mode which does not really support multiple ClassLoaders, and is based on
a normal production Quarkus application.
For all other use cases (e.g. tests, dev mode, and building the application) Quarkus
uses the class loading architecture outlined here.
== Bootstrapping Quarkus
All Quarkus applications are created by the QuarkusBootstrap class in the `independent-projects/bootstrap` module. This
class is used to resolve all the relevant dependencies (both deployment and runtime) that are needed for the Quarkus
application. The end result of this process is a `CuratedApplication`, which contains all the class loading information
for the application.
The `CuratedApplication` can then be used to create an `AugmentAction` instance, which can create production application
and start/restart runtime ones. This application instance exists within an isolated ClassLoader, it is not necessary
to have any of the Quarkus deployment classes on the class path as the curate process will resolve them for you.
This bootstrap process should be the same no matter how Quarkus is launched, just with different parameters passed in.
=== Current Run Modes
At the moment we have the following use cases for bootstrapping Quarkus:
- Maven creating production application
- Maven dev mode
- Gradle creating a production application
- Gradle dev mode
- QuarkusTest (Maven, Gradle and IDE)
- QuarkusUnitTest (Maven, Gradle and IDE)
- QuarkusDevModeTest (Maven, Gradle and IDE)
- Arquillian Adaptor
One of the goals of this refactor is to have all these different run modes boot Quarkus in fundamentally the same way.
=== Notes on Transformer Safety
A ClassLoader is said to be 'transformer safe' if it is safe to load classes in the class loader before the transformers
are ready. Once a class has been loaded it cannot be changed, so if a class is loaded before the transformers have been
prepared this will prevent the transformation from working. Loading classes in a transformer safe ClassLoader will not
prevent the transformation, as the loaded class is not used at runtime.
== ClassLoader Implementations
Quarkus has the following ClassLoaders:
Base ClassLoader::
This is usually the normal JVM System ClassLoader. In some environments such as Maven it may be different. This ClassLoader
is used to load the bootstrap classes, and other ClassLoader instances will delegate the loading of JDK classes to it.
Augment ClassLoader::
This loads all the `-deployment` artifacts and their dependencies, as well as other user dependencies. It does not load the
application root or any hot deployed code. This ClassLoader is persistent, even if the application restarts it will remain
(which is why it cannot load application classes that may be hot deployed). Its parent is the base ClassLoader, and it is
transformer safe.
At present this can be configured to delegate to the Base ClassLoader, however the plan is for this option to go away and
always have this as an isolated ClassLoader. Making this an isolated ClassLoader is complicated as it means that all
the builder classes are isolated, which means that use cases that want to customise the build chains are slightly more complex.
Deployment ClassLoader::
This can load all application classes, its parent is the Augment ClassLoader so it can also load all deployment classes.
This ClassLoader is non-persistent, it will be re-created when the application is started, and is isolated. This ClassLoader
is the context ClassLoader that is used when running the build steps. It is also transformer safe.
Base Runtime ClassLoader::
This loads all the runtime extension dependencies, as well as other user dependencies (note that this may include duplicate
copies of classes also loaded by the Augment ClassLoader). It does not load the application root or any hot deployed
code. This ClassLoader is persistent, even if the application restarts it will remain (which is why it cannot load
application classes that may be hot deployed). Its parent is the base ClassLoader.
This loads code that is not hot-reloadable, but it does support transformation (although once the class is loaded this
transformation is no longer possible). This means that only transformers registered in the first application start
will take effect, however as these transformers are expected to be idempotent this should not cause problems. An example
of the sort of transformation that might be required here is a Panache entity packaged in an external jar. This class
needs to be transformed to have its static methods implemented, however this transformation only happens once, so
restarts use the copy of the class that was created on the first start.
This ClassLoader is isolated from the Augment and Deployment ClassLoaders. This means that it is not possible to set
values in a static field in the deployment side, and expect to read it at runtime. This allows dev and test applications
to behave more like a production application (production applications are isolated in that they run in a whole new JVM).
This also means that the runtime version can be linked against a different set of dependencies, e.g. the hibernate
version used at deployment time might want to include ByteBuddy, while the version used at runtime does not.
Runtime Class Loader::
This ClassLoader is used to load the application classes and other hot deployable resources. Its parent is the base runtime
ClassLoader, and it is recreated when the application is restarted.
== Isolated ClassLoaders
The runtime ClassLoader is always isolated. This means that it will have its own copies of almost every class from the
resolved dependency list. The exception to this are:
- JDK classes
- Classes from artifacts that extensions have marked as parent first (more on this later).
=== Parent First Dependencies
There are some classes that should not be loaded in an isolated manner, but that should always be loaded by the system
ClassLoader (or whatever ClassLoader is responsible for bootstrapping Quarkus). Most extensions do not need to worry about
this, however there are a few cases where this is necessary:
- Some logging related classes, as logging must be loaded by the system ClassLoader
- Quarkus bootstrap itself
If this is required it can be configured in the `quarkus-bootstrap-maven-plugin`. Note that if you
mark a dependency as parent first then all of its dependencies must also be parent first,
or a `LinkageError` can occur.
[source,xml]
----
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-maven-plugin</artifactId>
<configuration>
<parentFirstArtifacts>
<parentFirstArtifact>io.quarkus:quarkus-bootstrap-core</parentFirstArtifact>
<parentFirstArtifact>io.quarkus:quarkus-development-mode-spi</parentFirstArtifact>
<parentFirstArtifact>org.jboss.logmanager:jboss-logmanager-embedded</parentFirstArtifact>
<parentFirstArtifact>org.jboss.logging:jboss-logging</parentFirstArtifact>
<parentFirstArtifact>org.ow2.asm:asm</parentFirstArtifact>
</parentFirstArtifacts>
</configuration>
</plugin>
----
=== Banned Dependencies
There are some dependencies that we can be sure we do not want. This generally happens when a dependency has had a name
change (e.g. smallrye-config changing groups from `org.smallrye` to `org.smallrye.config`, the `javax` -> `jakarta` rename).
This can cause problems, as if these artifacts end up in the dependency tree out of date classes can be loaded that are
not compatible with Quarkus. To deal with this extensions can specify artifacts that should never be loaded. This is
done by modifying the `quarkus-bootstrap-maven-plugin` config in the pom (which generates the `quarkus-extension.properties`
file). Simply add an `excludedArtifacts` section as shown below:
[source,xml]
----
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-maven-plugin</artifactId>
<configuration>
<excludedArtifacts>
<excludedArtifact>io.smallrye:smallrye-config</excludedArtifact>
<excludedArtifact>javax.enterprise:cdi-api</excludedArtifact>
</excludedArtifacts>
</configuration>
</plugin>
----
This should only be done if the extension depends on a newer version of these artifacts. If the extension does not bring
in a replacement artifact as a dependency then classes the application needs might end up missing.

View File

@@ -46,7 +46,7 @@
<!-- test dependencies --> <!-- test dependencies -->
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-deployment</artifactId> <artifactId>quarkus-resteasy</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@@ -229,7 +229,8 @@ public class DynamodbClientProducer {
private ExecutionInterceptor createInterceptor(Class<?> interceptorClass) { private ExecutionInterceptor createInterceptor(Class<?> interceptorClass) {
try { try {
return (ExecutionInterceptor) Class.forName(interceptorClass.getName()).newInstance(); return (ExecutionInterceptor) Class
.forName(interceptorClass.getName(), true, Thread.currentThread().getContextClassLoader()).newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
LOG.error("Unable to create interceptor", e); LOG.error("Unable to create interceptor", e);
return null; return null;

View File

@@ -125,7 +125,7 @@ public class ArcProcessor {
BeanProcessor.Builder builder = BeanProcessor.builder(); BeanProcessor.Builder builder = BeanProcessor.builder();
IndexView applicationClassesIndex = applicationArchivesBuildItem.getRootArchive().getIndex(); IndexView applicationClassesIndex = applicationArchivesBuildItem.getRootArchive().getIndex();
builder.setApplicationClassPredicate(new AbstractCompositeApplicationClassesPredicate<DotName>( builder.setApplicationClassPredicate(new AbstractCompositeApplicationClassesPredicate<DotName>(
applicationClassesIndex, generatedClassNames, applicationClassPredicates) { applicationClassesIndex, generatedClassNames, applicationClassPredicates, testClassPredicate) {
@Override @Override
protected DotName getDotName(DotName dotName) { protected DotName getDotName(DotName dotName) {
return dotName; return dotName;
@@ -201,7 +201,7 @@ public class ArcProcessor {
builder.setRemoveUnusedBeans(arcConfig.shouldEnableBeanRemoval()); builder.setRemoveUnusedBeans(arcConfig.shouldEnableBeanRemoval());
if (arcConfig.shouldOnlyKeepAppBeans()) { if (arcConfig.shouldOnlyKeepAppBeans()) {
builder.addRemovalExclusion(new AbstractCompositeApplicationClassesPredicate<BeanInfo>( builder.addRemovalExclusion(new AbstractCompositeApplicationClassesPredicate<BeanInfo>(
applicationClassesIndex, generatedClassNames, applicationClassPredicates) { applicationClassesIndex, generatedClassNames, applicationClassPredicates, testClassPredicate) {
@Override @Override
protected DotName getDotName(BeanInfo bean) { protected DotName getDotName(BeanInfo bean) {
return bean.getBeanClass(); return bean.getBeanClass();
@@ -370,15 +370,18 @@ public class ArcProcessor {
private final IndexView applicationClassesIndex; private final IndexView applicationClassesIndex;
private final Set<DotName> generatedClassNames; private final Set<DotName> generatedClassNames;
private final List<ApplicationClassPredicateBuildItem> applicationClassPredicateBuildItems; private final List<ApplicationClassPredicateBuildItem> applicationClassPredicateBuildItems;
private final Optional<TestClassPredicateBuildItem> testClassPredicate;
protected abstract DotName getDotName(T t); protected abstract DotName getDotName(T t);
private AbstractCompositeApplicationClassesPredicate(IndexView applicationClassesIndex, private AbstractCompositeApplicationClassesPredicate(IndexView applicationClassesIndex,
Set<DotName> generatedClassNames, Set<DotName> generatedClassNames,
List<ApplicationClassPredicateBuildItem> applicationClassPredicateBuildItems) { List<ApplicationClassPredicateBuildItem> applicationClassPredicateBuildItems,
Optional<TestClassPredicateBuildItem> testClassPredicate) {
this.applicationClassesIndex = applicationClassesIndex; this.applicationClassesIndex = applicationClassesIndex;
this.generatedClassNames = generatedClassNames; this.generatedClassNames = generatedClassNames;
this.applicationClassPredicateBuildItems = applicationClassPredicateBuildItems; this.applicationClassPredicateBuildItems = applicationClassPredicateBuildItems;
this.testClassPredicate = testClassPredicate;
} }
@Override @Override
@@ -390,14 +393,19 @@ public class ArcProcessor {
if (generatedClassNames.contains(dotName)) { if (generatedClassNames.contains(dotName)) {
return true; return true;
} }
String className = dotName.toString();
if (!applicationClassPredicateBuildItems.isEmpty()) { if (!applicationClassPredicateBuildItems.isEmpty()) {
String className = dotName.toString();
for (ApplicationClassPredicateBuildItem predicate : applicationClassPredicateBuildItems) { for (ApplicationClassPredicateBuildItem predicate : applicationClassPredicateBuildItems) {
if (predicate.test(className)) { if (predicate.test(className)) {
return true; return true;
} }
} }
} }
if (testClassPredicate.isPresent()) {
if (testClassPredicate.get().getPredicate().test(className)) {
return true;
}
}
return false; return false;
} }
} }

View File

@@ -45,7 +45,7 @@ public class BeanArchiveProcessor {
@Inject @Inject
BuildProducer<GeneratedClassBuildItem> generatedClass; BuildProducer<GeneratedClassBuildItem> generatedClass;
@BuildStep @BuildStep(loadsApplicationClasses = true)
public BeanArchiveIndexBuildItem build() throws Exception { public BeanArchiveIndexBuildItem build() throws Exception {
// First build an index from application archives // First build an index from application archives
@@ -61,12 +61,12 @@ public class BeanArchiveProcessor {
Set<DotName> additionalIndex = new HashSet<>(); Set<DotName> additionalIndex = new HashSet<>();
for (String beanClass : additionalBeans) { for (String beanClass : additionalBeans) {
IndexingUtil.indexClass(beanClass, additionalBeanIndexer, applicationIndex, additionalIndex, IndexingUtil.indexClass(beanClass, additionalBeanIndexer, applicationIndex, additionalIndex,
ArcProcessor.class.getClassLoader()); Thread.currentThread().getContextClassLoader());
} }
Set<DotName> generatedClassNames = new HashSet<>(); Set<DotName> generatedClassNames = new HashSet<>();
for (GeneratedBeanBuildItem beanClass : generatedBeans) { for (GeneratedBeanBuildItem beanClass : generatedBeans) {
IndexingUtil.indexClass(beanClass.getName(), additionalBeanIndexer, applicationIndex, additionalIndex, IndexingUtil.indexClass(beanClass.getName(), additionalBeanIndexer, applicationIndex, additionalIndex,
ArcProcessor.class.getClassLoader(), Thread.currentThread().getContextClassLoader(),
beanClass.getData()); beanClass.getData());
generatedClassNames.add(DotName.createSimple(beanClass.getName().replace('/', '.'))); generatedClassNames.add(DotName.createSimple(beanClass.getName().replace('/', '.')));
generatedClass.produce(new GeneratedClassBuildItem(true, beanClass.getName(), beanClass.getData())); generatedClass.produce(new GeneratedClassBuildItem(true, beanClass.getName(), beanClass.getData()));
@@ -74,7 +74,9 @@ public class BeanArchiveProcessor {
// Finally, index ArC/CDI API built-in classes // Finally, index ArC/CDI API built-in classes
return new BeanArchiveIndexBuildItem( return new BeanArchiveIndexBuildItem(
BeanArchives.buildBeanArchiveIndex(applicationIndex, additionalBeanIndexer.complete()), generatedClassNames, BeanArchives.buildBeanArchiveIndex(Thread.currentThread().getContextClassLoader(), applicationIndex,
additionalBeanIndexer.complete()),
generatedClassNames,
additionalBeans); additionalBeans);
} }

View File

@@ -1 +0,0 @@
io.quarkus.arc.deployment.ArcTestRequestScopeProvider

View File

@@ -1,10 +1,10 @@
package io.quarkus.arc.deployment; package io.quarkus.arc.runtime;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import io.quarkus.arc.Arc; import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer; import io.quarkus.arc.ArcContainer;
import io.quarkus.deployment.test.TestScopeSetup; import io.quarkus.runtime.test.TestScopeSetup;
public class ArcTestRequestScopeProvider implements TestScopeSetup { public class ArcTestRequestScopeProvider implements TestScopeSetup {

View File

@@ -0,0 +1 @@
io.quarkus.arc.runtime.ArcTestRequestScopeProvider

View File

@@ -12,6 +12,7 @@ import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.elytron.security.runtime.DefaultRoleDecoder; import io.quarkus.elytron.security.runtime.DefaultRoleDecoder;
import io.quarkus.elytron.security.runtime.ElytronPasswordIdentityProvider; import io.quarkus.elytron.security.runtime.ElytronPasswordIdentityProvider;
@@ -100,8 +101,8 @@ class ElytronDeploymentProcessor {
@BuildStep @BuildStep
@Record(ExecutionTime.RUNTIME_INIT) @Record(ExecutionTime.RUNTIME_INIT)
public void registerPasswordProvider(ElytronRecorder recorder) { public void registerPasswordProvider(ElytronRecorder recorder, ShutdownContextBuildItem shutdownContextBuildItem) {
recorder.registerPasswordProvider(); recorder.registerPasswordProvider(shutdownContextBuildItem);
} }
@BuildStep @BuildStep

Some files were not shown because too many files have changed in this diff Show More