Skip to content

Commit

Permalink
Merge pull request #17111 from ia3andy/improve-add-extension
Browse files Browse the repository at this point in the history
  • Loading branch information
ia3andy authored May 11, 2021
2 parents 02c6f77 + d50150d commit 100ede5
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.devtools.commands.handlers;

import static io.quarkus.devtools.commands.AddExtensions.EXTENSION_MANAGER;
import static io.quarkus.devtools.messagewriter.MessageIcons.ERROR_ICON;
import static io.quarkus.devtools.messagewriter.MessageIcons.NOK_ICON;

import io.quarkus.devtools.commands.AddExtensions;
Expand Down Expand Up @@ -42,17 +43,38 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws
invocation.getQuarkusProject().getExtensionManager());
try {
ExtensionInstallPlan extensionInstallPlan = planInstallation(invocation, extensionsQuery);
if (extensionInstallPlan.isNotEmpty()) {
if (extensionInstallPlan.isInstallable()) {
final InstallResult result = extensionManager.install(extensionInstallPlan);
result.getInstalled()
result.getInstalledPlatforms()
.forEach(a -> invocation.log()
.info(MessageIcons.OK_ICON + " Platform " + a.getGroupId() + ":" + a.getArtifactId()
+ " has been installed"));
result.getInstalledManagedExtensions()
.forEach(a -> invocation.log()
.info(MessageIcons.OK_ICON + " Extension " + a.getGroupId() + ":" + a.getArtifactId()
+ " has been installed"));
result.getInstalledIndependentExtensions()
.forEach(a -> invocation.log()
.info(MessageIcons.OK_ICON + " Extension " + a.getGroupId() + ":" + a.getArtifactId() + ":"
+ a.getVersion()
+ " has been installed"));
result.getAlreadyInstalled()
.forEach(a -> invocation.log()
.info(MessageIcons.NOOP_ICON + " Extension " + a.getGroupId() + ":" + a.getArtifactId()
+ " was already installed"));
return new QuarkusCommandOutcome(true).setValue(AddExtensions.OUTCOME_UPDATED, result.isSourceUpdated());
} else if (!extensionInstallPlan.getUnmatchedKeywords().isEmpty()) {
invocation.log()
.info(ERROR_ICON + " Nothing installed because keyword(s) '"
+ String.join("', '", extensionInstallPlan.getUnmatchedKeywords())
+ "' were not matched in the catalog.");
} else {
invocation.log()
.info(NOK_ICON + " The provided keyword(s) did not match any extension from the catalog.");
}
} catch (MultipleExtensionsFoundException m) {
StringBuilder sb = new StringBuilder();
sb.append(NOK_ICON + " Multiple extensions matching '").append(m.getKeyword()).append("'");
sb.append(ERROR_ICON + " Multiple extensions matching '").append(m.getKeyword()).append("'");
m.getExtensions()
.forEach(extension -> sb.append(System.lineSeparator()).append(" * ")
.append(extension.managementKey()));
Expand All @@ -68,12 +90,14 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws

public ExtensionInstallPlan planInstallation(QuarkusCommandInvocation invocation, Collection<String> keywords)
throws IOException {
if (keywords.isEmpty()) {
return ExtensionInstallPlan.EMPTY;
}
final ExtensionCatalog catalog = invocation.getExtensionsCatalog();
final String quarkusCore = catalog.getQuarkusCoreVersion();
final Collection<ArtifactCoords> importedPlatforms = invocation.getQuarkusProject().getExtensionManager()
.getInstalledPlatforms();
ExtensionInstallPlan.Builder builder = ExtensionInstallPlan.builder();
boolean multipleKeywords = keywords.size() > 1;
for (String keyword : keywords) {
int countColons = StringUtils.countMatches(keyword, ":");
// Check if it's just groupId:artifactId
Expand All @@ -87,9 +111,9 @@ public ExtensionInstallPlan planInstallation(QuarkusCommandInvocation invocation
continue;
}
List<Extension> listed = listInternalExtensions(quarkusCore, keyword, catalog.getExtensions());
if (listed.size() != 1 && multipleKeywords) {
// No extension found for this keyword. Return empty immediately
return ExtensionInstallPlan.EMPTY;
if (listed.isEmpty()) {
// No extension found for this keyword.
builder.addUnmatchedKeyword(keyword);
}
// If it's a pattern allow multiple results
// See https://github.com/quarkusio/quarkus/issues/11086#issuecomment-666360783
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,33 +32,44 @@ public BuildFile(final Path projectDirPath, ExtensionCatalog catalog) {

@Override
public final InstallResult install(Collection<ArtifactCoords> coords) throws IOException {
this.refreshData();
final Collection<ArtifactCoords> installed = withoutAlreadyInstalled(coords);
installed.forEach(e -> addDependency(e, e.getVersion() == null));
this.writeToDisk();
return new InstallResult(installed);
final ExtensionInstallPlan.Builder builder = ExtensionInstallPlan.builder();
for (ArtifactCoords coord : coords) {
if ("pom".equals(coord.getType())) {
builder.addPlatform(coord);
} else if (coord.getVersion() == null) {
builder.addManagedExtension(coord);
} else {
builder.addIndependentExtension(coord);
}
}
return install(builder.build());
}

@Override
public InstallResult install(ExtensionInstallPlan plan) throws IOException {
List<ArtifactCoords> installed = new ArrayList<>();
for (ArtifactCoords platform : withoutAlreadyInstalled(plan.getPlatforms())) {
this.refreshData();
List<ArtifactCoords> installedManagedExtensions = new ArrayList<>();
List<ArtifactCoords> installedIndependentExtensions = new ArrayList<>();
List<ArtifactCoords> installedPlatforms = new ArrayList<>();
final Set<ArtifactKey> alreadyInstalled = alreadyInstalled(plan.toCollection());
for (ArtifactCoords platform : withoutAlreadyInstalled(alreadyInstalled, plan.getPlatforms())) {
if (addDependency(platform, false)) {
installed.add(platform);
installedPlatforms.add(platform);
}
}
for (ArtifactCoords managedExtension : withoutAlreadyInstalled(plan.getManagedExtensions())) {
for (ArtifactCoords managedExtension : withoutAlreadyInstalled(alreadyInstalled, plan.getManagedExtensions())) {
if (addDependency(managedExtension, true)) {
installed.add(managedExtension);
installedManagedExtensions.add(managedExtension);
}
}
for (ArtifactCoords independentExtension : withoutAlreadyInstalled(plan.getIndependentExtensions())) {
for (ArtifactCoords independentExtension : withoutAlreadyInstalled(alreadyInstalled, plan.getIndependentExtensions())) {
if (addDependency(independentExtension, false)) {
installed.add(independentExtension);
installedIndependentExtensions.add(independentExtension);
}
}
writeToDisk();
return new InstallResult(installed);
return new InstallResult(installedPlatforms, installedManagedExtensions, installedIndependentExtensions,
alreadyInstalled);
}

@Override
Expand Down Expand Up @@ -88,8 +99,17 @@ public final UninstallResult uninstall(Collection<ArtifactKey> keys) throws IOEx
return new UninstallResult(uninstalled);
}

private Collection<ArtifactCoords> withoutAlreadyInstalled(Collection<ArtifactCoords> extensions) throws IOException {
private Set<ArtifactKey> alreadyInstalled(Collection<ArtifactCoords> extensions) throws IOException {
final Set<ArtifactKey> existingKeys = getDependenciesKeys();
return extensions.stream()
.distinct()
.filter(a -> existingKeys.contains(a.getKey()))
.map(ArtifactCoords::getKey)
.collect(Collectors.toSet());
}

private Collection<ArtifactCoords> withoutAlreadyInstalled(Set<ArtifactKey> existingKeys,
Collection<ArtifactCoords> extensions) {
return extensions.stream()
.distinct()
.filter(a -> !existingKeys.contains(a.getKey()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,13 @@ protected boolean addDependency(ArtifactCoords coords, boolean managed) {
if (model.getDependencies()
.stream()
.noneMatch(thisDep -> d.getManagementKey().equals(thisDep.getManagementKey()))) {
model.addDependency(d);
final int index = getIndexToAddExtension();
if (index >= 0) {
model.getDependencies().add(index, d);
} else {
model.getDependencies().add(d);
}

return true;
}
}
Expand Down Expand Up @@ -152,6 +158,16 @@ private Model getModel() {
});
}

private int getIndexToAddExtension() {
final List<Dependency> dependencies = getModel().getDependencies();
for (int i = 0; i < dependencies.size(); i++) {
if ("test".equals(dependencies.get(i).getScope())) {
return i;
}
}
return -1;
}

private Model initModel() throws IOException {
if (!hasProjectFile(BuildTool.MAVEN.getDependenciesFile())) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,13 @@ protected boolean addDependency(ArtifactCoords coords, boolean managed) {
} else if (model().getDependencies()
.stream()
.noneMatch(thisDep -> d.getManagementKey().equals(thisDep.getManagementKey()))) {
model().addDependency(d);
final int index = getIndexToAddExtension();
if (index >= 0) {
model().getDependencies().add(index, d);
} else {
model().getDependencies().add(d);
}

// it could still be a transitive dependency or inherited from the parent
if (!getDependencies().contains(coords)) {
getDependencies().add(coords);
Expand All @@ -241,8 +247,8 @@ protected void removeDependency(ArtifactKey key) throws IOException {
i.remove();
break;
}
model().getDependencies().removeIf(d -> Objects.equals(toKey(d), key));
}
model().getDependencies().removeIf(d -> Objects.equals(toKey(d), key));
}
}

Expand Down Expand Up @@ -293,6 +299,16 @@ protected String getProperty(String propertyName) {
protected void refreshData() {
}

private int getIndexToAddExtension() {
final List<Dependency> dependencies = model().getDependencies();
for (int i = 0; i < dependencies.size(); i++) {
if ("test".equals(dependencies.get(i).getScope())) {
return i;
}
}
return -1;
}

private Model model() {
return model;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.devtools.project.extensions;

import io.quarkus.maven.ArtifactCoords;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
Expand All @@ -9,27 +10,35 @@
public class ExtensionInstallPlan {

public static final ExtensionInstallPlan EMPTY = new ExtensionInstallPlan(
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet());

private final Set<ArtifactCoords> platforms;
private final Set<ArtifactCoords> managedExtensions;
private final Set<ArtifactCoords> independentExtensions;
private final Collection<String> unmatchedKeywords;

private ExtensionInstallPlan(Set<ArtifactCoords> platforms,
Set<ArtifactCoords> managedExtensions,
Set<ArtifactCoords> independentExtensions) {
Set<ArtifactCoords> independentExtensions,
Collection<String> unmatchedKeywords) {
this.platforms = platforms;
this.managedExtensions = managedExtensions;
this.independentExtensions = independentExtensions;
this.unmatchedKeywords = unmatchedKeywords;
}

public boolean isNotEmpty() {
return !this.platforms.isEmpty() || !this.managedExtensions.isEmpty()
|| !this.independentExtensions.isEmpty();
}

public boolean isInstallable() {
return isNotEmpty() && unmatchedKeywords.isEmpty();
}

/**
* @return a {@link Collection} of all extensions contained in this object
*/
Expand Down Expand Up @@ -63,12 +72,17 @@ public Collection<ArtifactCoords> getIndependentExtensions() {
return independentExtensions;
}

public Collection<String> getUnmatchedKeywords() {
return unmatchedKeywords;
}

@Override
public String toString() {
return "InstallRequest{" +
"platforms=" + platforms +
", managedExtensions=" + managedExtensions +
", independentExtensions=" + independentExtensions +
", unmatchedKeywords=" + unmatchedKeywords +
'}';
}

Expand All @@ -81,9 +95,10 @@ public static class Builder {
private final Set<ArtifactCoords> platforms = new LinkedHashSet<>();
private final Set<ArtifactCoords> extensionsInPlatforms = new LinkedHashSet<>();
private final Set<ArtifactCoords> independentExtensions = new LinkedHashSet<>();
private final Collection<String> unmatchedKeywords = new ArrayList<>();

public ExtensionInstallPlan build() {
return new ExtensionInstallPlan(platforms, extensionsInPlatforms, independentExtensions);
return new ExtensionInstallPlan(platforms, extensionsInPlatforms, independentExtensions, unmatchedKeywords);
}

public Builder addIndependentExtension(ArtifactCoords artifactCoords) {
Expand All @@ -101,6 +116,11 @@ public Builder addPlatform(ArtifactCoords artifactCoords) {
return this;
}

public Builder addUnmatchedKeyword(String unmatchedKeyword) {
this.unmatchedKeywords.add(unmatchedKeyword);
return this;
}

public boolean hasExtensionInPlatform() {
return !this.extensionsInPlatforms.isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ default boolean isInstalled(ArtifactKey key) throws IOException {
/**
* This is going to uninstall/remove all the specified extensions from the project build file(s).
*
* This is ignoring the {@link Extension} version
* This is ignoring the version
*
* @param keys the set of {@link ArtifactKey} for the extensions to uninstall
* @return the {@link InstallResult}
Expand All @@ -85,18 +85,39 @@ default boolean isInstalled(ArtifactKey key) throws IOException {
UninstallResult uninstall(Collection<ArtifactKey> keys) throws IOException;

class InstallResult {
private final Collection<ArtifactCoords> installed;
private final Collection<ArtifactCoords> installedPlatforms;
private final Collection<ArtifactCoords> installedManagedExtensions;
private final Collection<ArtifactCoords> installedIndependentExtensions;
private final Collection<ArtifactKey> alreadyInstalled;

public InstallResult(Collection<ArtifactCoords> installedPlatforms,
Collection<ArtifactCoords> installedManagedExtensions,
Collection<ArtifactCoords> installedIndependentExtensions, Collection<ArtifactKey> alreadyInstalled) {
this.installedPlatforms = installedPlatforms;
this.installedManagedExtensions = installedManagedExtensions;
this.installedIndependentExtensions = installedIndependentExtensions;
this.alreadyInstalled = alreadyInstalled;
}

public Collection<ArtifactCoords> getInstalledManagedExtensions() {
return installedManagedExtensions;
}

public Collection<ArtifactCoords> getInstalledIndependentExtensions() {
return installedIndependentExtensions;
}

public InstallResult(Collection<ArtifactCoords> installed) {
this.installed = installed;
public Collection<ArtifactCoords> getInstalledPlatforms() {
return installedPlatforms;
}

public Collection<ArtifactCoords> getInstalled() {
return installed;
public Collection<ArtifactKey> getAlreadyInstalled() {
return alreadyInstalled;
}

public boolean isSourceUpdated() {
return installed.size() > 0;
return !installedPlatforms.isEmpty() || !installedManagedExtensions.isEmpty()
|| !installedIndependentExtensions.isEmpty();
}
}

Expand Down

0 comments on commit 100ede5

Please sign in to comment.