From 69e427b1033687ff3c8baa74112a858c6d05ca74 Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Tue, 2 Jun 2020 09:11:07 +0200 Subject: [PATCH] Refactor and Introduce ExtensionsManager in CodeGen This is part of #8178 - Improved packages names and content - Improved general readability and consistency - Added ExtensionManager to define a high level way of managing (read/write) extensions in any QuarkusProject - Removed most unsafe Gradle operations which were outside of the Gradle plugin (we need to figure out a way to improve the "generic" gradle support to make it compatible again..) - Removed existing project support with the create command (throws an error) - Removed compatibility with project without the Quarkus platform bom defined --- .../main/java/io/quarkus/cli/QuarkusCli.java | 8 +- .../cli/commands/AddExtensionCommand.java | 2 + .../cli/commands/CreateProjectCommand.java | 1 + .../cli/commands/ListExtensionsCommand.java | 1 + .../quarkus/cli/commands/QuarkusCommand.java | 3 +- .../cli/commands/RemoveExtensionCommand.java | 2 + ...nsionToModuleInMultiModuleProjectTest.java | 1 - .../gradle/QuarkusPluginFunctionalTest.java | 4 +- .../gradle/GradleBuildFileFromConnector.java | 35 +- .../gradle/tasks/QuarkusAddExtension.java | 5 +- .../gradle/tasks/QuarkusListExtensions.java | 11 +- .../gradle/tasks/QuarkusPlatformTask.java | 14 +- .../gradle/tasks/QuarkusRemoveExtension.java | 2 +- .../quarkus/gradle/GradleBuildFileTest.java | 2 +- .../io/quarkus/maven/AddExtensionMojo.java | 6 +- .../io/quarkus/maven/CreateProjectMojo.java | 11 +- .../io/quarkus/maven/ListExtensionsMojo.java | 4 +- ...oBase.java => QuarkusProjectMojoBase.java} | 74 ++-- .../io/quarkus/maven/RemoveExtensionMojo.java | 6 +- .../commands/CreateProjectCommandHandler.java | 65 ---- .../ListExtensionsCommandHandler.java | 167 --------- .../java/io/quarkus/cli/commands/Printer.java | 24 -- .../quarkus/cli/commands/QuarkusCommand.java | 6 - .../RemoveExtensionsCommandHandler.java | 79 ----- .../quarkus/devtools/buildfile/BuildFile.java | 170 --------- .../devtools/buildfile/MavenBuildFile.java | 261 -------------- .../commands/AddExtensions.java | 18 +- .../commands/CreateProject.java | 30 +- .../commands/ListExtensions.java | 38 +- .../commands/RemoveExtensions.java | 15 +- .../data}/QuarkusCommandException.java | 2 +- .../data}/QuarkusCommandInvocation.java | 9 +- .../commands/data}/QuarkusCommandOutcome.java | 2 +- .../commands/data}/SelectionResult.java | 2 +- .../commands/data}/ValueMap.java | 14 +- .../handlers/AddExtensionsCommandHandler.java | 46 +++ .../handlers/CreateProjectCommandHandler.java | 82 +++++ .../ListExtensionsCommandHandler.java | 154 +++++++++ .../handlers/QuarkusCommandHandler.java | 10 + .../handlers/QuarkusCommandHandlers.java} | 133 +++---- .../RemoveExtensionsCommandHandler.java | 45 +++ .../quarkus/devtools/project/BuildTool.java | 19 +- .../devtools/project/QuarkusProject.java | 48 +-- .../buildfile/AbstractGradleBuildFile.java | 242 +++++++++++++ .../devtools/project/buildfile/BuildFile.java | 163 +++++++++ .../buildfile/GenericGradleBuildFile.java | 39 +++ .../buildfile/GradleBuildFilesCreator.java} | 324 +++++++----------- .../project/buildfile/MavenBuildFile.java | 108 ++++++ .../project/codegen}/ProjectGenerator.java | 8 +- .../codegen}/ProjectGeneratorRegistry.java | 2 +- .../project/codegen}/SourceType.java | 2 +- .../rest/BasicRestProjectGenerator.java | 23 +- .../codegen}/writer/FileProjectWriter.java | 24 +- .../codegen}/writer/ProjectWriter.java | 6 +- .../project/extensions/Extensions.java | 40 +++ .../project/extensions/ExtensionsManager.java | 71 ++++ ....devtools.project.codegen.ProjectGenerator | 1 + .../io.quarkus.generators.ProjectGenerator | 1 - .../cli/commands/CreateProjectTest.java | 314 ----------------- .../commands/AbstractAddExtensionsTest.java | 16 +- .../AbstractRemoveExtensionsTest.java | 8 +- .../commands/AddGradleExtensionsTest.java | 43 ++- .../commands/AddMavenExtensionsTest.java | 6 +- .../devtools/commands/CreateProjectTest.java | 142 ++++++++ .../commands/ListExtensionsTest.java | 31 +- .../commands/PlatformAwareTestBase.java | 2 +- .../commands/RemoveGradleExtensionsTest.java | 11 +- .../commands/RemoveMavenExtensionsTest.java | 7 +- .../handlers/QuarkusCommandHandlersTest.java} | 25 +- .../rest/BasicRestProjectGeneratorTest.java | 37 +- .../compress/QuarkusProjectCompressTest.java | 10 +- ...CombinedQuarkusPlatformDescriptorTest.java | 2 +- .../platform/tools/ConsoleMessageFormat.java | 23 ++ .../resources/projects/simple-pom-it/pom.xml | 17 + 74 files changed, 1719 insertions(+), 1660 deletions(-) rename devtools/maven/src/main/java/io/quarkus/maven/{BuildFileMojoBase.java => QuarkusProjectMojoBase.java} (75%) delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProjectCommandHandler.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensionsCommandHandler.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/Printer.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensionsCommandHandler.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/BuildFile.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/MavenBuildFile.java rename independent-projects/tools/common/src/main/java/io/quarkus/{cli => devtools}/commands/AddExtensions.java (52%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli => devtools}/commands/CreateProject.java (81%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli => devtools}/commands/ListExtensions.java (65%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli => devtools}/commands/RemoveExtensions.java (57%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/QuarkusCommandException.java (87%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/QuarkusCommandInvocation.java (87%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/QuarkusCommandOutcome.java (91%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/SelectionResult.java (94%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/ValueMap.java (83%) create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/AddExtensionsCommandHandler.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/ListExtensionsCommandHandler.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandler.java rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands/AddExtensionsCommandHandler.java => devtools/commands/handlers/QuarkusCommandHandlers.java} (56%) create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/RemoveExtensionsCommandHandler.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GenericGradleBuildFile.java rename independent-projects/tools/common/src/main/java/io/quarkus/devtools/{buildfile/GradleBuildFile.java => project/buildfile/GradleBuildFilesCreator.java} (52%) create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java rename independent-projects/tools/common/src/main/java/io/quarkus/{generators => devtools/project/codegen}/ProjectGenerator.java (77%) rename independent-projects/tools/common/src/main/java/io/quarkus/{generators => devtools/project/codegen}/ProjectGeneratorRegistry.java (96%) rename independent-projects/tools/common/src/main/java/io/quarkus/{generators => devtools/project/codegen}/SourceType.java (98%) rename independent-projects/tools/common/src/main/java/io/quarkus/{generators => devtools/project/codegen}/rest/BasicRestProjectGenerator.java (92%) rename independent-projects/tools/common/src/main/java/io/quarkus/devtools/{ => project/codegen}/writer/FileProjectWriter.java (73%) rename independent-projects/tools/common/src/main/java/io/quarkus/devtools/{ => project/codegen}/writer/ProjectWriter.java (84%) create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/Extensions.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/ExtensionsManager.java create mode 100644 independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.devtools.project.codegen.ProjectGenerator delete mode 100644 independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.generators.ProjectGenerator delete mode 100644 independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/AbstractAddExtensionsTest.java (93%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/AbstractRemoveExtensionsTest.java (93%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/AddGradleExtensionsTest.java (53%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/AddMavenExtensionsTest.java (87%) create mode 100644 independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/CreateProjectTest.java rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/ListExtensionsTest.java (86%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/PlatformAwareTestBase.java (98%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/RemoveGradleExtensionsTest.java (80%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/RemoveMavenExtensionsTest.java (88%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli/commands/AddExtensionsSelectTest.java => devtools/commands/handlers/QuarkusCommandHandlersTest.java} (86%) rename independent-projects/tools/common/src/test/java/io/quarkus/{generators => devtools/project/codegen}/rest/BasicRestProjectGeneratorTest.java (84%) create mode 100644 independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/platform/tools/ConsoleMessageFormat.java diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/QuarkusCli.java b/devtools/aesh/src/main/java/io/quarkus/cli/QuarkusCli.java index 97d3da8324c9c..2e272c4ba5887 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/QuarkusCli.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/QuarkusCli.java @@ -12,18 +12,18 @@ import org.aesh.command.validator.CommandValidatorException; import org.aesh.command.validator.OptionValidatorException; -import io.quarkus.cli.commands.QuarkusCommand; +import io.quarkus.devtools.commands.handlers.QuarkusCommandHandler; public class QuarkusCli { public static void main(String[] args) throws CommandRegistryException { CommandRuntime runtime = AeshCommandRuntimeBuilder .builder() - .commandRegistry(AeshCommandRegistryBuilder.builder().command(QuarkusCommand.class).create()) + .commandRegistry(AeshCommandRegistryBuilder.builder().command(QuarkusCommandHandler.class).create()) .build(); if (args.length > 0) { - StringBuilder sb = new StringBuilder(QuarkusCommand.COMMAND_NAME).append(" "); + StringBuilder sb = new StringBuilder(QuarkusCommandHandler.COMMAND_NAME).append(" "); if (args.length == 1) { sb.append(args[0]); } else { @@ -54,6 +54,6 @@ private static void showHelpIfNeeded(CommandRuntime runtime, Exception e) { if (e != null) { System.err.println(e.getMessage()); } - System.err.println(runtime.commandInfo(QuarkusCommand.COMMAND_NAME)); + System.err.println(runtime.commandInfo(QuarkusCommandHandler.COMMAND_NAME)); } } diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/commands/AddExtensionCommand.java b/devtools/aesh/src/main/java/io/quarkus/cli/commands/AddExtensionCommand.java index f28858c67e830..d9594101ba9b7 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/commands/AddExtensionCommand.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/commands/AddExtensionCommand.java @@ -5,6 +5,8 @@ import java.util.Collections; import java.util.List; +import io.quarkus.devtools.commands.AddExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import org.aesh.command.Command; import org.aesh.command.CommandDefinition; import org.aesh.command.CommandException; diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/commands/CreateProjectCommand.java b/devtools/aesh/src/main/java/io/quarkus/cli/commands/CreateProjectCommand.java index 68596a7b528cf..e639f7ca07495 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/commands/CreateProjectCommand.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/commands/CreateProjectCommand.java @@ -4,6 +4,7 @@ import java.nio.file.Paths; import java.util.HashMap; +import io.quarkus.devtools.commands.CreateProject; import org.aesh.command.Command; import org.aesh.command.CommandDefinition; import org.aesh.command.CommandResult; diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/commands/ListExtensionsCommand.java b/devtools/aesh/src/main/java/io/quarkus/cli/commands/ListExtensionsCommand.java index 380c4f6d04a95..98069512e6715 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/commands/ListExtensionsCommand.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/commands/ListExtensionsCommand.java @@ -2,6 +2,7 @@ import java.nio.file.Paths; +import io.quarkus.devtools.commands.ListExtensions; import org.aesh.command.Command; import org.aesh.command.CommandDefinition; import org.aesh.command.CommandException; diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java b/devtools/aesh/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java index 1fec366174193..dc9b15e63fd2e 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java @@ -1,5 +1,6 @@ package io.quarkus.cli.commands; +import io.quarkus.devtools.commands.handlers.QuarkusCommandHandler; import org.aesh.command.Command; import org.aesh.command.CommandException; import org.aesh.command.CommandResult; @@ -10,7 +11,7 @@ /** * @author Ståle Pedersen */ -@GroupCommandDefinition(name = QuarkusCommand.COMMAND_NAME, groupCommands = { ListExtensionsCommand.class, +@GroupCommandDefinition(name = QuarkusCommandHandler.COMMAND_NAME, groupCommands = { ListExtensionsCommand.class, AddExtensionCommand.class, CreateProjectCommand.class }, description = " [] \n\nThese are the common quarkus commands used in various situations") public class QuarkusCommand implements Command { diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/commands/RemoveExtensionCommand.java b/devtools/aesh/src/main/java/io/quarkus/cli/commands/RemoveExtensionCommand.java index cc3b718276cfc..71e109a00feae 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/commands/RemoveExtensionCommand.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/commands/RemoveExtensionCommand.java @@ -5,6 +5,8 @@ import java.util.Collections; import java.util.List; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.RemoveExtensions; import org.aesh.command.Command; import org.aesh.command.CommandDefinition; import org.aesh.command.CommandException; diff --git a/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/AddExtensionToModuleInMultiModuleProjectTest.java b/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/AddExtensionToModuleInMultiModuleProjectTest.java index 00b55e2260616..c992eef547c62 100644 --- a/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/AddExtensionToModuleInMultiModuleProjectTest.java +++ b/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/AddExtensionToModuleInMultiModuleProjectTest.java @@ -19,7 +19,6 @@ public class AddExtensionToModuleInMultiModuleProjectTest extends QuarkusGradleT public void testBasicMultiModuleBuild() throws Exception { final File projectDir = getProjectDir("add-extension-multi-module"); - BuildResult build = GradleRunner.create() .forwardOutput() .withPluginClasspath() diff --git a/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java b/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java index 38cfcc6d75e9e..63026d1af3a88 100644 --- a/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java +++ b/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java @@ -21,9 +21,9 @@ import com.google.common.collect.ImmutableMap; -import io.quarkus.cli.commands.CreateProject; +import io.quarkus.devtools.commands.CreateProject; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.SourceType; import io.quarkus.platform.tools.config.QuarkusPlatformConfig; import io.quarkus.test.devmode.util.DevModeTestUtils; diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/GradleBuildFileFromConnector.java b/devtools/gradle/src/main/java/io/quarkus/gradle/GradleBuildFileFromConnector.java index 01c4102868f7b..2cb675e18cd83 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/GradleBuildFileFromConnector.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/GradleBuildFileFromConnector.java @@ -1,6 +1,7 @@ package io.quarkus.gradle; import java.io.IOException; +import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -13,16 +14,20 @@ import org.gradle.tooling.model.eclipse.EclipseExternalDependency; import org.gradle.tooling.model.eclipse.EclipseProject; -import io.quarkus.devtools.buildfile.GradleBuildFile; -import io.quarkus.devtools.writer.ProjectWriter; +import io.quarkus.devtools.project.buildfile.AbstractGradleBuildFile; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; -public class GradleBuildFileFromConnector extends GradleBuildFile { +public class GradleBuildFileFromConnector extends AbstractGradleBuildFile { private List dependencies = null; - public GradleBuildFileFromConnector(ProjectWriter writer) { - super(writer); - // we need to initialize here since there is no other single point of entry + public GradleBuildFileFromConnector(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + super(projectFolderPath, platformDescriptor); + } + + public GradleBuildFileFromConnector(Path projectFolderPath, QuarkusPlatformDescriptor platformDescriptor, + Path rootProjectPath) { + super(projectFolderPath, platformDescriptor, rootProjectPath); } @Override @@ -30,16 +35,14 @@ public List getDependencies() throws IOException { if (dependencies == null) { EclipseProject eclipseProject = null; if (getBuildContent() != null) { - if (getWriter().hasFile()) { - try { - ProjectConnection connection = GradleConnector.newConnector() - .forProjectDirectory(getWriter().getProjectFolder()) - .connect(); - eclipseProject = connection.getModel(EclipseProject.class); - } catch (BuildException e) { - // ignore this error. - e.printStackTrace(); - } + try { + ProjectConnection connection = GradleConnector.newConnector() + .forProjectDirectory(getProjectFolderPath().toFile()) + .connect(); + eclipseProject = connection.getModel(EclipseProject.class); + } catch (BuildException e) { + // ignore this error. + e.printStackTrace(); } } if (eclipseProject != null) { diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusAddExtension.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusAddExtension.java index d640818576b84..57c498953d63a 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusAddExtension.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusAddExtension.java @@ -6,12 +6,13 @@ import java.util.List; import java.util.Set; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.gradle.api.GradleException; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; -import io.quarkus.cli.commands.AddExtensions; +import io.quarkus.devtools.commands.AddExtensions; public class QuarkusAddExtension extends QuarkusPlatformTask { @@ -44,7 +45,7 @@ public void addExtension() { .extensions(extensionsSet) .execute(); } catch (Exception e) { - throw new GradleException("Failed to add extensions " + getExtensionsToAdd(), e); + throw new GradleException("Failed to add extensions " + getExtensionsToAdd() + " " + ExceptionUtils.getStackTrace(e), e); } } } diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusListExtensions.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusListExtensions.java index 833679f5c61dd..aad55911e3c29 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusListExtensions.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusListExtensions.java @@ -6,10 +6,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; -import io.quarkus.cli.commands.ListExtensions; -import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.gradle.GradleBuildFileFromConnector; +import io.quarkus.devtools.commands.ListExtensions; public class QuarkusListExtensions extends QuarkusPlatformTask { @@ -58,11 +55,7 @@ public QuarkusListExtensions() { @TaskAction public void listExtensions() { try { - final GradleBuildFileFromConnector buildFileFromConnector = new GradleBuildFileFromConnector( - new FileProjectWriter(getProject().getProjectDir())); - QuarkusProject quarkusProject = QuarkusProject.of(getProject().getProjectDir().toPath(), platformDescriptor(), - buildFileFromConnector); - new ListExtensions(quarkusProject) + new ListExtensions(getQuarkusProject()) .all(isAll()) .format(getFormat()) .search(getSearchPattern()) diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java index 3433f111bb2ce..e422cb2593ebc 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java @@ -10,10 +10,9 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.tasks.Internal; -import io.quarkus.devtools.buildfile.GradleBuildFile; import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.devtools.writer.ProjectWriter; +import io.quarkus.devtools.project.buildfile.BuildFile; +import io.quarkus.gradle.GradleBuildFileFromConnector; import io.quarkus.platform.descriptor.CombinedQuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.resolver.json.QuarkusJsonPlatformDescriptorResolver; @@ -66,12 +65,11 @@ protected QuarkusPlatformDescriptor platformDescriptor() { } @Internal - protected GradleBuildFile getGradleBuildFile() { - final ProjectWriter writer = new FileProjectWriter(getProject().getProjectDir()); + protected BuildFile getGradleBuildFile() { return getProject().getParent() == null - ? new GradleBuildFile(writer) - : new GradleBuildFile(writer, - new FileProjectWriter(getProject().getRootProject().getProjectDir())); + ? new GradleBuildFileFromConnector(getProject().getProjectDir().toPath(), platformDescriptor()) + : new GradleBuildFileFromConnector(getProject().getProjectDir().toPath(), platformDescriptor(), + getProject().getRootProject().getProjectDir().toPath()); } @Internal diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusRemoveExtension.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusRemoveExtension.java index 482ed669a8cbc..1adf369f5c415 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusRemoveExtension.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusRemoveExtension.java @@ -11,7 +11,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; -import io.quarkus.cli.commands.RemoveExtensions; +import io.quarkus.devtools.commands.RemoveExtensions; public class QuarkusRemoveExtension extends QuarkusPlatformTask { diff --git a/devtools/gradle/src/test/java/io/quarkus/gradle/GradleBuildFileTest.java b/devtools/gradle/src/test/java/io/quarkus/gradle/GradleBuildFileTest.java index 899513bec48f5..74fdb9c753272 100644 --- a/devtools/gradle/src/test/java/io/quarkus/gradle/GradleBuildFileTest.java +++ b/devtools/gradle/src/test/java/io/quarkus/gradle/GradleBuildFileTest.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import io.quarkus.devtools.writer.FileProjectWriter; +import io.quarkus.devtools.project.codegen.writer.FileProjectWriter; class GradleBuildFileTest { diff --git a/devtools/maven/src/main/java/io/quarkus/maven/AddExtensionMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/AddExtensionMojo.java index 115d681b095af..4442aa26a4cfb 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/AddExtensionMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/AddExtensionMojo.java @@ -10,8 +10,8 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import io.quarkus.cli.commands.AddExtensions; -import io.quarkus.cli.commands.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.AddExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.tools.MessageWriter; @@ -22,7 +22,7 @@ * parameters. */ @Mojo(name = "add-extension") -public class AddExtensionMojo extends BuildFileMojoBase { +public class AddExtensionMojo extends QuarkusProjectMojoBase { /** * The list of extensions to be added. diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java index b560894b9ac45..fa68d67e6f3f9 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java @@ -46,11 +46,9 @@ import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; -import io.quarkus.cli.commands.AddExtensions; -import io.quarkus.cli.commands.CreateProject; +import io.quarkus.devtools.commands.CreateProject; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.SourceType; import io.quarkus.maven.components.MavenVersionEnforcer; import io.quarkus.maven.components.Prompter; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; @@ -214,11 +212,6 @@ public void execute() throws MojoExecutionException { success = createProject.execute().isSuccess(); File createdDependenciesBuildFile = new File(projectRoot, buildToolEnum.getDependenciesFile()); - if (success) { - success = new AddExtensions(QuarkusProject.of(projectFolderPath, platform, buildToolEnum)) - .extensions(extensions).execute() - .isSuccess(); - } if (BuildTool.MAVEN.equals(buildToolEnum)) { createMavenWrapper(createdDependenciesBuildFile, ToolsUtils.readQuarkusProperties(platform)); } else if (BuildTool.GRADLE.equals(buildToolEnum)) { diff --git a/devtools/maven/src/main/java/io/quarkus/maven/ListExtensionsMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/ListExtensionsMojo.java index 64486a685a6fa..91ae34967b52d 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/ListExtensionsMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/ListExtensionsMojo.java @@ -4,7 +4,7 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import io.quarkus.cli.commands.ListExtensions; +import io.quarkus.devtools.commands.ListExtensions; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.tools.MessageWriter; @@ -15,7 +15,7 @@ * You can list all extension or just installable. Choose between 3 output formats: name, concise and full. */ @Mojo(name = "list-extensions", requiresProject = false) -public class ListExtensionsMojo extends BuildFileMojoBase { +public class ListExtensionsMojo extends QuarkusProjectMojoBase { /** * List all extensions or just the installable. diff --git a/devtools/maven/src/main/java/io/quarkus/maven/BuildFileMojoBase.java b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java similarity index 75% rename from devtools/maven/src/main/java/io/quarkus/maven/BuildFileMojoBase.java rename to devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java index 961abc3440c53..c3fdcbb89d463 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/BuildFileMojoBase.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java @@ -1,11 +1,13 @@ package io.quarkus.maven; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; @@ -19,11 +21,8 @@ import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.buildfile.MavenBuildFile; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.devtools.writer.FileProjectWriter; import io.quarkus.platform.descriptor.CombinedQuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.resolver.json.QuarkusJsonPlatformDescriptorResolver; @@ -31,7 +30,7 @@ import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.platform.tools.maven.MojoMessageWriter; -public abstract class BuildFileMojoBase extends AbstractMojo { +public abstract class QuarkusProjectMojoBase extends AbstractMojo { @Parameter(defaultValue = "${project}") protected MavenProject project; @@ -60,37 +59,31 @@ public void execute() throws MojoExecutionException { // Validate Mojo parameters validateParameters(); + final MessageWriter log = new MojoMessageWriter(getLog()); + final Path projectFolderPath = project.getBasedir().toPath(); + final BuildTool buildTool = QuarkusProject.resolveExistingProjectBuildTool(projectFolderPath); + final QuarkusPlatformDescriptor platformDescriptor = resolvePlatformDescriptor(log); + + doExecute(QuarkusProject.of(project.getBasedir().toPath(), platformDescriptor, buildTool), log); + } + + private QuarkusPlatformDescriptor resolvePlatformDescriptor(final MessageWriter log) throws MojoExecutionException { // Resolve and setup the platform descriptor - final MavenArtifactResolver mvn; try { - mvn = MavenArtifactResolver.builder().setRepositorySystem(repoSystem).setRepositorySystemSession(repoSession) + final MavenArtifactResolver mvn = MavenArtifactResolver.builder().setRepositorySystem(repoSystem) + .setRepositorySystemSession(repoSession) .setRemoteRepositories(repos).build(); - } catch (Exception e) { - throw new MojoExecutionException("Failed to initialize maven artifact resolver", e); - } - - final MessageWriter log = new MojoMessageWriter(getLog()); - - QuarkusPlatformDescriptor platformDescr = null; - BuildTool buildTool = null; - BuildFile buildFile = null; - try (FileProjectWriter fileProjectWriter = new FileProjectWriter(project.getBasedir())) { if (project.getFile() != null) { - // Maven project - final MavenBuildFile mvnBuild = new MavenBuildFile(fileProjectWriter); - buildTool = BuildTool.MAVEN; - buildFile = new MavenBuildFile(fileProjectWriter); - final List descrArtifactList = new ArrayList<>(2); - for (Dependency dep : mvnBuild.getManagedDependencies()) { + for (Dependency dep : getManagedDependencies()) { if ((dep.getScope() == null || !dep.getScope().equals("import")) && (dep.getType() == null || !dep.getType().equals("pom"))) { continue; } // We don't know which BOM is the platform one, so we are trying every BOM here - final String bomVersion = resolveValue(dep.getVersion(), buildFile); - final String bomGroupId = resolveValue(dep.getGroupId(), buildFile); - final String bomArtifactId = resolveValue(dep.getArtifactId(), buildFile); + final String bomVersion = resolveValue(dep.getVersion()); + final String bomGroupId = resolveValue(dep.getGroupId()); + final String bomArtifactId = resolveValue(dep.getArtifactId()); if (bomVersion == null || bomGroupId == null || bomArtifactId == null) { continue; } @@ -102,28 +95,19 @@ public void execute() throws MojoExecutionException { } if (!descrArtifactList.isEmpty()) { if (descrArtifactList.size() == 1) { - platformDescr = loadPlatformDescriptor(mvn, log, descrArtifactList.get(0)); + return loadPlatformDescriptor(mvn, log, descrArtifactList.get(0)); } else { final CombinedQuarkusPlatformDescriptor.Builder builder = CombinedQuarkusPlatformDescriptor.builder(); for (Artifact descrArtifact : descrArtifactList) { builder.addPlatform(loadPlatformDescriptor(mvn, log, descrArtifact)); } - platformDescr = builder.build(); + return builder.build(); } } - } else if (new File(project.getBasedir(), "build.gradle").exists() - || new File(project.getBasedir(), "build.gradle.kts").exists()) { - // Gradle project - buildTool = BuildTool.GRADLE; } - - if (platformDescr == null) { - platformDescr = CreateUtils.resolvePlatformDescriptor(bomGroupId, bomArtifactId, bomVersion, mvn, getLog()); - } - - doExecute(QuarkusProject.of(project.getBasedir().toPath(), platformDescr, buildTool), log); - } catch (IOException e) { - throw new MojoExecutionException("Failed to initialize project reading tools", e); + return CreateUtils.resolvePlatformDescriptor(bomGroupId, bomArtifactId, bomVersion, mvn, getLog()); + } catch (Exception e) { + throw new MojoExecutionException("Failed to initialize maven artifact resolver", e); } } @@ -166,10 +150,10 @@ protected void validateParameters() throws MojoExecutionException { protected abstract void doExecute(QuarkusProject quarkusProject, MessageWriter log) throws MojoExecutionException; - private String resolveValue(String expr, BuildFile buildFile) throws IOException { + private String resolveValue(String expr) throws IOException { if (expr.startsWith("${") && expr.endsWith("}")) { final String name = expr.substring(2, expr.length() - 1); - final String v = buildFile.getProperty(name); + final String v = project.getModel().getProperties().getProperty(name); if (v == null) { if (getLog().isDebugEnabled()) { getLog().debug("Failed to resolve property " + name); @@ -179,4 +163,10 @@ private String resolveValue(String expr, BuildFile buildFile) throws IOException } return expr; } + + private List getManagedDependencies() throws IOException { + final DependencyManagement managed = project.getModel().getDependencyManagement(); + return managed != null ? managed.getDependencies() + : Collections.emptyList(); + } } diff --git a/devtools/maven/src/main/java/io/quarkus/maven/RemoveExtensionMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/RemoveExtensionMojo.java index 827f258f59243..165c951bc4cd6 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/RemoveExtensionMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/RemoveExtensionMojo.java @@ -10,8 +10,8 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import io.quarkus.cli.commands.QuarkusCommandOutcome; -import io.quarkus.cli.commands.RemoveExtensions; +import io.quarkus.devtools.commands.RemoveExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.tools.MessageWriter; @@ -22,7 +22,7 @@ * parameters. */ @Mojo(name = "remove-extension") -public class RemoveExtensionMojo extends BuildFileMojoBase { +public class RemoveExtensionMojo extends QuarkusProjectMojoBase { /** * The list of extensions to be removed. diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProjectCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProjectCommandHandler.java deleted file mode 100644 index 3bd04bea6cd84..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProjectCommandHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.quarkus.cli.commands; - -import static io.quarkus.generators.ProjectGenerator.*; - -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.generators.ProjectGeneratorRegistry; -import io.quarkus.generators.SourceType; -import io.quarkus.generators.rest.BasicRestProjectGenerator; -import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; -import io.quarkus.platform.tools.ToolsUtils; -import java.io.IOException; -import java.util.Properties; - -/** - * Instances of this class are thread-safe. They create a new project extracting all the necessary properties from an instance - * of {@link QuarkusCommandInvocation}. - */ -public class CreateProjectCommandHandler implements QuarkusCommand { - - @Override - public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - final ProjectWriter projectWriter = new FileProjectWriter( - invocation.getQuarkusProject().getProjectFolderPath().toFile()); - if (!projectWriter.init()) { - return QuarkusCommandOutcome.failure(); - } - - final QuarkusPlatformDescriptor platformDescr = invocation.getPlatformDescriptor(); - invocation.setValue(BOM_GROUP_ID, platformDescr.getBomGroupId()); - invocation.setValue(BOM_ARTIFACT_ID, platformDescr.getBomArtifactId()); - invocation.setValue(QUARKUS_VERSION, platformDescr.getQuarkusVersion()); - invocation.setValue(BOM_VERSION, platformDescr.getBomVersion()); - - final Properties quarkusProps = ToolsUtils.readQuarkusProperties(platformDescr); - quarkusProps.forEach((k, v) -> invocation.setValue(k.toString().replace("-", "_"), v.toString())); - - try (BuildFile buildFile = invocation.getBuildFile()) { - String className = invocation.getStringValue(CLASS_NAME); - if (className != null) { - className = invocation.getValue(SOURCE_TYPE, SourceType.JAVA).stripExtensionFrom(className); - int idx = className.lastIndexOf('.'); - if (idx >= 0) { - String pkgName = invocation.getStringValue(PACKAGE_NAME); - if (pkgName == null) { - invocation.setValue(PACKAGE_NAME, className.substring(0, idx)); - } - className = className.substring(idx + 1); - } - invocation.setValue(CLASS_NAME, className); - } - ProjectGeneratorRegistry.get(BasicRestProjectGenerator.NAME).generate(projectWriter, invocation); - - // call close at the end to save file - buildFile.completeFile(invocation.getStringValue(PROJECT_GROUP_ID), - invocation.getStringValue(PROJECT_ARTIFACT_ID), - invocation.getStringValue(PROJECT_VERSION), - platformDescr, quarkusProps); - } catch (IOException e) { - throw new QuarkusCommandException("Failed to create project", e); - } - return QuarkusCommandOutcome.success(); - } -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensionsCommandHandler.java deleted file mode 100644 index 4f5f1fe82ea24..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensionsCommandHandler.java +++ /dev/null @@ -1,167 +0,0 @@ -package io.quarkus.cli.commands; - -import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.buildfile.GradleBuildFile; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.apache.maven.model.Dependency; - -/** - * Instances of this class are thread-safe. It lists extensions according to the options passed in as properties of - * {@link QuarkusCommandInvocation} - */ -public class ListExtensionsCommandHandler implements QuarkusCommand { - - private static final String FULL_FORMAT = "%-8s %-50s %-50s %-25s%n%s"; - private static final String CONCISE_FORMAT = "%-50s %-50s"; - private static final String NAME_FORMAT = "%-50s"; - - @Override - public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - - final boolean all = invocation.getValue(ListExtensions.ALL, true); - final String format = invocation.getValue(ListExtensions.FORMAT, "concise"); - final String search = invocation.getValue(ListExtensions.SEARCH, "*"); - - Map installed; - try { - installed = findInstalled(invocation); - } catch (IOException e) { - throw new QuarkusCommandException("Failed to determine the list of installed extensions", e); - } - - Stream extensionsStream = invocation.getPlatformDescriptor().getExtensions().stream(); - extensionsStream = extensionsStream.filter(e -> filterUnlisted(e)); - if (search != null && !"*".equalsIgnoreCase(search)) { - final Pattern searchPattern = Pattern.compile(".*" + search + ".*", Pattern.CASE_INSENSITIVE); - extensionsStream = extensionsStream.filter(e -> filterBySearch(searchPattern, e)); - } - List loadedExtensions = extensionsStream.collect(Collectors.toList()); - - if (loadedExtensions.isEmpty()) { - System.out.println("No extension found with this pattern"); - } else { - String extensionStatus = all ? "available" : "installable"; - System.out.println(String.format("%nCurrent Quarkus extensions %s: ", extensionStatus)); - - Consumer currentFormatter; - switch (format.toLowerCase()) { - case "name": - currentFormatter = this::nameFormatter; - break; - case "full": - currentFormatter = this::fullFormatter; - currentFormatter.accept(new String[] { "Status", "Extension", "ArtifactId", "Updated Version", "Guide" }); - break; - case "concise": - default: - currentFormatter = this::conciseFormatter; - } - - final BuildFile buildFile = invocation.getBuildFile(); - loadedExtensions.forEach(extension -> display(extension, installed, all, currentFormatter, buildFile)); - - if (buildFile != null) { - if ("concise".equalsIgnoreCase(format)) { - if (buildFile instanceof GradleBuildFile) { - System.out.println("\nTo get more information, append --format=full to your command line."); - } else { - System.out.println( - "\nTo get more information, append -Dquarkus.extension.format=full to your command line."); - } - } - - if (buildFile instanceof GradleBuildFile) { - System.out.println("\nAdd an extension to your project by adding the dependency to your " + - "build.gradle or use `./gradlew addExtension --extensions=\"artifactId\"`"); - } else { - System.out.println("\nAdd an extension to your project by adding the dependency to your " + - "pom.xml or use `./mvnw quarkus:add-extension -Dextensions=\"artifactId\"`"); - } - } - } - - return QuarkusCommandOutcome.success(); - } - - Map findInstalled(QuarkusCommandInvocation invocation) throws IOException { - return invocation.getBuildFile().findInstalled(); - } - - private boolean filterUnlisted(Extension e) { - return !e.getMetadata().containsKey("unlisted"); - } - - private boolean filterBySearch(final Pattern searchPattern, Extension e) { - return searchPattern.matcher(e.getName()).matches(); - } - - private void conciseFormatter(String[] cols) { - System.out.println(String.format(CONCISE_FORMAT, cols[1], cols[2], cols[4])); - } - - private void fullFormatter(String[] cols) { - System.out.println(String.format(FULL_FORMAT, cols[0], cols[1], cols[2], cols[3], cols[4])); - } - - private void nameFormatter(String[] cols) { - System.out.println(String.format(NAME_FORMAT, cols[2])); - } - - private void display(Extension extension, final Map installed, boolean all, - Consumer formatter, BuildFile buildFile) { - final Dependency dependency = installed.get(extension.getGroupId() + ":" + extension.getArtifactId()); - if (!all && dependency != null) { - return; - } - - String label = ""; - String version = ""; - - final String extracted = extractVersion(dependency, buildFile); - if (extracted != null) { - if (extracted.equalsIgnoreCase(extension.getVersion())) { - label = "current"; - version = String.format("%s", extracted); - } else { - label = "update"; - version = String.format("%s <> %s", extracted, extension.getVersion()); - } - } - - String[] result = new String[] { label, extension.getName(), extension.getArtifactId(), version, extension.getGuide() }; - - for (int i = 0; i < result.length; i++) { - result[i] = Objects.toString(result[i], ""); - } - - formatter.accept(result); - } - - private String extractVersion(final Dependency dependency, BuildFile buildFile) { - String version = dependency != null ? dependency.getVersion() : null; - if (version != null && version.startsWith("$") && buildFile != null) { - String value = null; - try { - value = (String) buildFile.getProperty(propertyName(version)); - } catch (IOException e) { - // ignore this error. - } - if (value != null) { - version = value; - } - } - return version; - } - - private String propertyName(final String variable) { - return variable.substring(2, variable.length() - 1); - } -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/Printer.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/Printer.java deleted file mode 100644 index 1b74c43ca230b..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/Printer.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.quarkus.cli.commands; - -public class Printer { - private static final String OK = "\u2705"; - static final String NOK = "\u274c"; - private static final String NOOP = "\uD83D\uDC4D"; - - public void nok(String content) { - print(NOK + content); - } - - public void ok(String content) { - print(OK + content); - } - - public void noop(String content) { - print(NOOP + content); - } - - void print(String message) { - System.out.println(message); - } - -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java deleted file mode 100644 index 31025343198fd..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.quarkus.cli.commands; - -public interface QuarkusCommand { - - QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException; -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensionsCommandHandler.java deleted file mode 100644 index 24aea2fa73a71..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensionsCommandHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -package io.quarkus.cli.commands; - -import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.buildfile.BuildFile; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * This class is thread-safe. It extracts extensions to be removed from the project from an instance of - * {@link QuarkusCommandInvocation}. - */ -public class RemoveExtensionsCommandHandler implements QuarkusCommand { - - final static Printer PRINTER = new Printer(); - - @Override - public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - final Set extensions = invocation.getValue(RemoveExtensions.EXTENSIONS, Collections.emptySet()); - if (extensions.isEmpty()) { - return QuarkusCommandOutcome.success().setValue(RemoveExtensions.OUTCOME_UPDATED, false); - } - - boolean updated = false; - boolean success = true; - - final List registry = invocation.getPlatformDescriptor().getExtensions(); - final BuildFile buildFile = invocation.getBuildFile(); - - try { - for (String query : extensions) { - - if (query.contains(":")) { - // GAV case. - updated = buildFile.removeExtensionAsGAV(query) || updated; - } else { - SelectionResult result = AddExtensionsCommandHandler.select(query, registry, false); - if (!result.matches()) { - StringBuilder sb = new StringBuilder(); - // We have 3 cases, we can still have a single candidate, but the match is on label - // or we have several candidates, or none - Set candidates = result.getExtensions(); - if (candidates.isEmpty()) { - // No matches at all. - PRINTER.nok(" Cannot find a dependency matching '" + query + "', maybe a typo?"); - success = false; - } else { - sb.append(Printer.NOK).append(" Multiple extensions matching '").append(query).append("'"); - result.getExtensions() - .forEach(extension -> sb.append(System.lineSeparator()).append(" * ") - .append(extension.managementKey())); - sb.append(System.lineSeparator()) - .append(" Be more specific e.g using the exact name"); - PRINTER.print(sb.toString()); - success = false; - } - } else { // Matches. - for (Extension extension : result) { - updated = buildFile.removeDependency(invocation.getPlatformDescriptor(), extension) || updated; - } - } - } - } - } catch (IOException e) { - throw new QuarkusCommandException("Failed to add extensions", e); - } - - if (updated) { - try { - buildFile.close(); - } catch (IOException e) { - throw new QuarkusCommandException("Failed to update the project", e); - } - } - - return new QuarkusCommandOutcome(success).setValue(RemoveExtensions.OUTCOME_UPDATED, updated); - } -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/BuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/BuildFile.java deleted file mode 100644 index 725e0a2e5e8d1..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/BuildFile.java +++ /dev/null @@ -1,170 +0,0 @@ -package io.quarkus.devtools.buildfile; - -import static com.google.common.base.Preconditions.checkNotNull; -import static io.quarkus.maven.utilities.MojoUtils.credentials; -import static java.util.stream.Collectors.toList; - -import io.quarkus.cli.commands.Printer; -import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.maven.utilities.MojoUtils; -import io.quarkus.maven.utilities.QuarkusDependencyPredicate; -import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; -import java.io.Closeable; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.TreeMap; -import org.apache.maven.model.Dependency; - -public abstract class BuildFile implements Closeable { - - protected final static Printer PRINTER = new Printer(); - - private final ProjectWriter writer; - - private final BuildTool buildTool; - - public BuildFile(final ProjectWriter writer, BuildTool buildTool) { - this.writer = checkNotNull(writer, "writer is required"); - this.buildTool = checkNotNull(buildTool, "buildTool is required"); - } - - protected void write(String fileName, String content) throws IOException { - writer.write(fileName, content); - } - - public boolean addDependency(QuarkusPlatformDescriptor platform, Extension extension) throws IOException { - if (!hasDependency(extension)) { - PRINTER.ok(" Adding extension " + extension.managementKey()); - Dependency dep; - if (containsBOM(platform.getBomGroupId(), platform.getBomArtifactId()) - && isDefinedInBom(platform.getManagedDependencies(), extension)) { - dep = extension.toDependency(true); - } else { - dep = extension.toDependency(false); - if (getProperty(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_NAME) != null) { - dep.setVersion(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_VALUE); - } - } - addDependencyInBuildFile(dep); - return true; - } else { - PRINTER.noop(" Skipping already present extension " + extension.managementKey()); - return false; - } - } - - public boolean removeDependency(QuarkusPlatformDescriptor platform, Extension extension) throws IOException { - if (hasDependency(extension)) { - PRINTER.ok(" Removing extension " + extension.managementKey()); - Dependency dep; - if (containsBOM(platform.getBomGroupId(), platform.getBomArtifactId()) - && isDefinedInBom(platform.getManagedDependencies(), extension)) { - dep = extension.toDependency(true); - } else { - dep = extension.toDependency(false); - if (getProperty(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_NAME) != null) { - dep.setVersion(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_VALUE); - } - } - removeDependencyFromBuildFile(dep); - return true; - } else { - PRINTER.noop(" Skipping extension that does not exists " + extension.managementKey()); - return false; - } - } - - protected abstract void addDependencyInBuildFile(Dependency dependency) throws IOException; - - protected abstract void removeDependencyFromBuildFile(Dependency dependency) throws IOException; - - protected abstract boolean hasDependency(Extension extension) throws IOException; - - public boolean addExtensionAsGAV(String query) throws IOException { - Dependency parsed = MojoUtils.parse(query.trim()); - boolean alreadyThere = getDependencies().stream() - .anyMatch(d -> d.getManagementKey().equalsIgnoreCase(parsed.getManagementKey())); - if (!alreadyThere) { - PRINTER.ok(" Adding dependency " + parsed.getManagementKey()); - addDependencyInBuildFile(parsed); - return true; - } else { - PRINTER.noop(" Skipping already present dependency " + parsed.getManagementKey()); - return false; - } - } - - public boolean removeExtensionAsGAV(String query) throws IOException { - Dependency parsed = MojoUtils.parse(query.trim()); - PRINTER.ok(" Removing dependency " + parsed.getManagementKey()); - removeDependencyFromBuildFile(parsed); - return true; - } - - protected boolean isDefinedInBom(List dependencies, Extension extension) { - return dependencies.stream().anyMatch(dependency -> dependency.getGroupId().equalsIgnoreCase(extension.getGroupId()) - && dependency.getArtifactId().equalsIgnoreCase(extension.getArtifactId())); - } - - protected abstract boolean containsBOM(String groupId, String artifactId) throws IOException; - - public abstract List getDependencies() throws IOException; - - public Map findInstalled() throws IOException { - return mapDependencies(getDependencies(), loadManaged()); - } - - private Map loadManaged() throws IOException { - final List managedDependencies = getManagedDependencies(); - return managedDependencies.isEmpty() ? Collections.emptyMap() - : mapDependencies(managedDependencies, Collections.emptyMap()); - } - - protected Map mapDependencies(final List dependencies, - final Map managed) { - final Map map = new TreeMap<>(); - - if (dependencies != null) { - final List listed = dependencies.stream() - // THIS ASSUMES EXTENSIONS' groupId is always 'io.quarkus' which is wrong - .filter(new QuarkusDependencyPredicate()) - .collect(toList()); - - listed.forEach(d -> { - if (d.getVersion() == null) { - final Dependency managedDep = managed.get(credentials(d)); - if (managedDep != null) { - final String version = managedDep.getVersion(); - if (version != null) { - d.setVersion(version); - } - } - } - - map.put(credentials(d), d); - }); - } - return map; - } - - public abstract String getProperty(String propertyName) throws IOException; - - protected abstract List getManagedDependencies() throws IOException; - - public abstract void completeFile(String groupId, String artifactId, String version, QuarkusPlatformDescriptor platform, - Properties props) throws IOException; - - public BuildTool getBuildTool() { - return buildTool; - } - - protected ProjectWriter getWriter() { - return writer; - } - -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/MavenBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/MavenBuildFile.java deleted file mode 100644 index df55a72c8e6bd..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/MavenBuildFile.java +++ /dev/null @@ -1,261 +0,0 @@ -package io.quarkus.devtools.buildfile; - -import static io.quarkus.maven.utilities.MojoUtils.configuration; -import static io.quarkus.maven.utilities.MojoUtils.plugin; - -import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.maven.utilities.MojoUtils; -import io.quarkus.maven.utilities.MojoUtils.Element; -import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; -import io.quarkus.platform.tools.ToolsUtils; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import org.apache.maven.model.Activation; -import org.apache.maven.model.ActivationProperty; -import org.apache.maven.model.Build; -import org.apache.maven.model.BuildBase; -import org.apache.maven.model.Dependency; -import org.apache.maven.model.DependencyManagement; -import org.apache.maven.model.Model; -import org.apache.maven.model.Plugin; -import org.apache.maven.model.PluginExecution; -import org.apache.maven.model.PluginManagement; -import org.apache.maven.model.Profile; - -public class MavenBuildFile extends BuildFile { - - private Model model; - - public MavenBuildFile(ProjectWriter writer) { - super(writer, BuildTool.MAVEN); - } - - private void initModel() throws IOException { - if (getWriter().exists(BuildTool.MAVEN.getDependenciesFile())) { - byte[] content = getWriter().getContent(BuildTool.MAVEN.getDependenciesFile()); - this.model = MojoUtils.readPom(new ByteArrayInputStream(content)); - } - } - - @Override - public void close() throws IOException { - if (getModel() == null) { - return; - } - try (ByteArrayOutputStream pomOutputStream = new ByteArrayOutputStream()) { - MojoUtils.write(getModel(), pomOutputStream); - write(BuildTool.MAVEN.getDependenciesFile(), pomOutputStream.toString("UTF-8")); - } - } - - @Override - protected void addDependencyInBuildFile(Dependency dependency) throws IOException { - if (getModel() != null) { - getModel().addDependency(dependency); - } - } - - @Override - protected void removeDependencyFromBuildFile(Dependency dependency) throws IOException { - if (getModel() != null) { - getModel().getDependencies() - .removeIf(d -> d.getGroupId().equals(dependency.getGroupId()) - && d.getArtifactId().equals(dependency.getArtifactId())); - } - } - - @Override - protected boolean hasDependency(Extension extension) throws IOException { - return getModel() != null && MojoUtils.hasDependency(getModel(), extension.getGroupId(), extension.getArtifactId()); - } - - @Override - public List getDependencies() throws IOException { - return getModel() == null ? Collections.emptyList() : getModel().getDependencies(); - } - - @Override - protected boolean containsBOM(String groupId, String artifactId) throws IOException { - if (getModel() == null || getModel().getDependencyManagement() == null) { - return false; - } - List dependencies = getModel().getDependencyManagement().getDependencies(); - return dependencies.stream() - // Find bom - .filter(dependency -> "import".equals(dependency.getScope())) - .filter(dependency -> "pom".equals(dependency.getType())) - // Does it matches the bom artifact name - .anyMatch(dependency -> dependency.getArtifactId() - .equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_VALUE) - && dependency.getGroupId().equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_GROUP_ID_VALUE)); - } - - @Override - public void completeFile(String groupId, String artifactId, String version, QuarkusPlatformDescriptor platform, - Properties props) throws IOException { - addQuarkusProperties(platform); - addBom(platform); - final String pluginGroupId = ToolsUtils.getPluginGroupId(props); - final String pluginArtifactId = ToolsUtils.getPluginArtifactId(props); - addMainPluginConfig(pluginGroupId, pluginArtifactId); - addNativeProfile(pluginGroupId, pluginArtifactId); - } - - private void addBom(QuarkusPlatformDescriptor platform) throws IOException { - boolean hasBom = false; - DependencyManagement dm = getModel().getDependencyManagement(); - if (dm == null) { - dm = new DependencyManagement(); - getModel().setDependencyManagement(dm); - } else { - hasBom = containsBOM(platform.getBomGroupId(), platform.getBomArtifactId()); - } - - if (!hasBom) { - Dependency bom = new Dependency(); - bom.setGroupId(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_GROUP_ID_VALUE); - bom.setArtifactId(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_VALUE); - bom.setVersion(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_VERSION_VALUE); - bom.setType("pom"); - bom.setScope("import"); - - dm.addDependency(bom); - } - } - - private void addNativeProfile(String pluginGroupId, String pluginArtifactId) throws IOException { - final boolean match = getModel().getProfiles().stream().anyMatch(p -> p.getId().equals("native")); - if (!match) { - PluginExecution exec = new PluginExecution(); - exec.addGoal("native-image"); - exec.setConfiguration(configuration(new Element("enableHttpUrlHandler", "true"))); - - Plugin plg = plugin(pluginGroupId, pluginArtifactId, MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE); - plg.addExecution(exec); - - BuildBase buildBase = new BuildBase(); - buildBase.addPlugin(plg); - - Profile profile = new Profile(); - profile.setId("native"); - profile.setBuild(buildBase); - - final Activation activation = new Activation(); - final ActivationProperty property = new ActivationProperty(); - property.setName("native"); - - activation.setProperty(property); - profile.setActivation(activation); - getModel().addProfile(profile); - } - } - - private void addMainPluginConfig(String groupId, String artifactId) throws IOException { - if (!hasPlugin(groupId, artifactId)) { - Build build = createBuildSectionIfRequired(); - Plugin plugin = plugin(groupId, artifactId, MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE); - if (isParentPom()) { - addPluginManagementSection(plugin); - //strip the quarkusVersion off - plugin = plugin(groupId, artifactId); - } - PluginExecution pluginExec = new PluginExecution(); - pluginExec.addGoal("build"); - plugin.addExecution(pluginExec); - build.getPlugins().add(plugin); - } - } - - private boolean hasPlugin(String groupId, String artifactId) throws IOException { - if (getModel() == null) { - return false; - } - List plugins = null; - final Build build = getModel().getBuild(); - if (build != null) { - if (isParentPom()) { - final PluginManagement management = build.getPluginManagement(); - if (management != null) { - plugins = management.getPlugins(); - } - } else { - plugins = build.getPlugins(); - } - } - return plugins != null && build.getPlugins() - .stream() - .anyMatch(p -> p.getGroupId().equalsIgnoreCase(groupId) && - p.getArtifactId().equalsIgnoreCase(artifactId)); - } - - private void addPluginManagementSection(Plugin plugin) throws IOException { - final Build build = getModel().getBuild(); - if (build != null && build.getPluginManagement() != null) { - if (build.getPluginManagement().getPlugins() == null) { - build.getPluginManagement().setPlugins(new ArrayList<>()); - } - build.getPluginManagement().getPlugins().add(plugin); - } - } - - private Build createBuildSectionIfRequired() throws IOException { - Build build = getModel().getBuild(); - if (build == null) { - build = new Build(); - getModel().setBuild(build); - } - if (build.getPlugins() == null) { - build.setPlugins(new ArrayList<>()); - } - return build; - } - - private void addQuarkusProperties(QuarkusPlatformDescriptor platform) throws IOException { - Properties properties = getModel().getProperties(); - if (properties == null) { - properties = new Properties(); - getModel().setProperties(properties); - } - properties.putIfAbsent(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_NAME, - ToolsUtils.getPluginVersion(ToolsUtils.readQuarkusProperties(platform))); - properties.putIfAbsent(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_GROUP_ID_NAME, platform.getBomGroupId()); - properties.putIfAbsent(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_NAME, platform.getBomArtifactId()); - properties.putIfAbsent(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_VERSION_NAME, platform.getBomVersion()); - } - - private boolean isParentPom() throws IOException { - return getModel() != null && "pom".equals(getModel().getPackaging()); - } - - @Override - public List getManagedDependencies() throws IOException { - if (getModel() == null) { - return Collections.emptyList(); - } - final DependencyManagement managed = getModel().getDependencyManagement(); - return managed != null ? managed.getDependencies() - : Collections.emptyList(); - } - - @Override - public String getProperty(String propertyName) throws IOException { - if (getModel() == null) { - return null; - } - return getModel().getProperties().getProperty(propertyName); - } - - private Model getModel() throws IOException { - if (model == null) { - initModel(); - } - return model; - } -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensions.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/AddExtensions.java similarity index 52% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensions.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/AddExtensions.java index b0b1462bae823..c959edadc6c40 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensions.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/AddExtensions.java @@ -1,6 +1,13 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.handlers.AddExtensionsCommandHandler; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.extensions.ExtensionsManager; import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.platform.tools.ToolsUtils; import java.util.Set; @@ -13,19 +20,26 @@ public class AddExtensions { public static final String NAME = "add-extensions"; public static final String EXTENSIONS = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "extensions"); public static final String OUTCOME_UPDATED = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "outcome", "updated"); + public static final String EXTENSIONS_MANAGER = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "extensions-manager"); private final QuarkusCommandInvocation invocation; + private final AddExtensionsCommandHandler handler = new AddExtensionsCommandHandler(); public AddExtensions(final QuarkusProject quarkusProject) { this.invocation = new QuarkusCommandInvocation(quarkusProject); } + public AddExtensions extensionsManager(ExtensionsManager extensionsManager) { + invocation.setValue(EXTENSIONS_MANAGER, checkNotNull(extensionsManager, "extensionsManager is required")); + return this; + } + public AddExtensions extensions(Set extensions) { invocation.setValue(EXTENSIONS, extensions); return this; } public QuarkusCommandOutcome execute() throws QuarkusCommandException { - return new AddExtensionsCommandHandler().execute(invocation); + return handler.execute(invocation); } } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/CreateProject.java similarity index 81% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/CreateProject.java index 0f01b9bc452fc..44ee7e46a2a3e 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/CreateProject.java @@ -1,17 +1,22 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import static com.google.common.base.Preconditions.checkNotNull; -import static io.quarkus.generators.ProjectGenerator.CLASS_NAME; -import static io.quarkus.generators.ProjectGenerator.IS_SPRING; -import static io.quarkus.generators.ProjectGenerator.JAVA_TARGET; -import static io.quarkus.generators.ProjectGenerator.PROJECT_ARTIFACT_ID; -import static io.quarkus.generators.ProjectGenerator.PROJECT_GROUP_ID; -import static io.quarkus.generators.ProjectGenerator.PROJECT_VERSION; -import static io.quarkus.generators.ProjectGenerator.SOURCE_TYPE; - +import static io.quarkus.devtools.project.codegen.ProjectGenerator.CLASS_NAME; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.EXTENSIONS; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.IS_SPRING; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.JAVA_TARGET; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_ARTIFACT_ID; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_GROUP_ID; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_VERSION; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.SOURCE_TYPE; + +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.handlers.CreateProjectCommandHandler; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.SourceType; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.io.IOException; import java.nio.file.Path; @@ -81,14 +86,11 @@ public CreateProject className(String className) { return this; } - /** - * @deprecated in 1.3.0.CR - */ - @Deprecated public CreateProject extensions(Set extensions) { if (isSpringStyle(extensions)) { setValue(IS_SPRING, true); } + setValue(EXTENSIONS, extensions); return this; } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensions.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/ListExtensions.java similarity index 65% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensions.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/ListExtensions.java index 6f9fd3adbc438..05b6d85c9808f 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensions.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/ListExtensions.java @@ -1,11 +1,15 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.handlers.ListExtensionsCommandHandler; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.extensions.ExtensionsManager; import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.platform.tools.ToolsUtils; -import java.io.IOException; -import java.util.Map; -import org.apache.maven.model.Dependency; /** * Instances of this class are not thread-safe. They are created per single invocation. @@ -16,6 +20,7 @@ public class ListExtensions { public static final String ALL = ToolsUtils.dotJoin(PARAM_PREFIX, "all"); public static final String FORMAT = ToolsUtils.dotJoin(PARAM_PREFIX, "format"); public static final String SEARCH = ToolsUtils.dotJoin(PARAM_PREFIX, "search"); + public static final String EXTENSIONS_MANAGER = ToolsUtils.dotJoin(PARAM_PREFIX, "extensions-manager"); private final QuarkusCommandInvocation invocation; private final ListExtensionsCommandHandler handler = new ListExtensionsCommandHandler(); @@ -34,32 +39,17 @@ public ListExtensions format(String format) { return this; } - public ListExtensions search(String search) { - invocation.setValue(SEARCH, search); + public ListExtensions extensionsManager(ExtensionsManager extensionsManager) { + invocation.setValue(EXTENSIONS_MANAGER, checkNotNull(extensionsManager, "extensionsManager is required")); return this; } - /** - * @deprecated in 1.3.0.CR1 - * Please use {@link #all(boolean)}, {@link #format(String)} and {@link #search(String)} respectively. - */ - @Deprecated - public void listExtensions(boolean all, String format, String search) throws IOException { - all(all); - format(format); - search(search); - try { - execute(); - } catch (QuarkusCommandException e) { - throw new IOException("Failed to list extensions", e); - } + public ListExtensions search(String search) { + invocation.setValue(SEARCH, search); + return this; } public QuarkusCommandOutcome execute() throws QuarkusCommandException { return handler.execute(invocation); } - - public Map findInstalled() throws IOException { - return handler.findInstalled(invocation); - } } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensions.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/RemoveExtensions.java similarity index 57% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensions.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/RemoveExtensions.java index 5e6c2b6de8ed5..402eced8af3ff 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensions.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/RemoveExtensions.java @@ -1,6 +1,13 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.handlers.RemoveExtensionsCommandHandler; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.extensions.ExtensionsManager; import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.platform.tools.ToolsUtils; import java.util.Set; @@ -13,6 +20,7 @@ public class RemoveExtensions { public static final String NAME = "remove-extensions"; public static final String EXTENSIONS = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "extensions"); public static final String OUTCOME_UPDATED = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "outcome", "updated"); + public static final String EXTENSIONS_MANAGER = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "extensions-manager"); private final QuarkusCommandInvocation invocation; @@ -25,6 +33,11 @@ public RemoveExtensions extensions(Set extensions) { return this; } + public RemoveExtensions extensionsManager(ExtensionsManager extensionsManager) { + invocation.setValue(EXTENSIONS_MANAGER, checkNotNull(extensionsManager, "extensionsManager is required")); + return this; + } + public QuarkusCommandOutcome execute() throws QuarkusCommandException { return new RemoveExtensionsCommandHandler().execute(invocation); } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandException.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandException.java similarity index 87% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandException.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandException.java index e9545c28ae89f..eb13d45624e73 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandException.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandException.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; public class QuarkusCommandException extends Exception { diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandInvocation.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandInvocation.java similarity index 87% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandInvocation.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandInvocation.java index 3f2de476c5a9e..c2ff4b506364f 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandInvocation.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandInvocation.java @@ -1,8 +1,7 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; import static com.google.common.base.Preconditions.checkNotNull; -import io.quarkus.devtools.buildfile.BuildFile; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.tools.DefaultMessageWriter; @@ -43,11 +42,7 @@ public MessageWriter log() { } public QuarkusPlatformDescriptor getPlatformDescriptor() { - return quarkusProject.getDescriptor(); - } - - public BuildFile getBuildFile() { - return quarkusProject.getBuildFile(); + return quarkusProject.getPlatformDescriptor(); } } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandOutcome.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandOutcome.java similarity index 91% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandOutcome.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandOutcome.java index be8747ea3d9df..e04e6e2f6a283 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandOutcome.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandOutcome.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; public class QuarkusCommandOutcome extends ValueMap { diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/SelectionResult.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/SelectionResult.java similarity index 94% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/SelectionResult.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/SelectionResult.java index 3b51911a5794e..c97d6afcc0a31 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/SelectionResult.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/SelectionResult.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; import io.quarkus.dependencies.Extension; import java.util.Collections; diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ValueMap.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/ValueMap.java similarity index 83% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ValueMap.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/ValueMap.java index 7636d9a88b5c6..8d99fc794f6e1 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ValueMap.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/ValueMap.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; import java.util.HashMap; import java.util.Map; @@ -48,6 +48,18 @@ public String getStringValue(String name) { throw new IllegalStateException("value for '" + name + "' must be a String"); } + public boolean getBooleanValue(String name) { + final Object value = getValue(name, null); + if (value == null) { + throw new IllegalStateException("value for '" + name + "' must be defined"); + } + + if (Boolean.class.equals(value.getClass())) { + return ((Boolean) value).booleanValue(); + } + return Boolean.parseBoolean(value.toString()); + } + public boolean getValue(String name, boolean defaultValue) { final Object value = getValue(name, null); if (value == null) { diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/AddExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/AddExtensionsCommandHandler.java new file mode 100644 index 0000000000000..5cf56a7e0dc42 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/AddExtensionsCommandHandler.java @@ -0,0 +1,46 @@ +package io.quarkus.devtools.commands.handlers; + +import static io.quarkus.devtools.commands.AddExtensions.EXTENSIONS_MANAGER; +import static io.quarkus.devtools.commands.handlers.QuarkusCommandHandlers.computeExtensionsFromQuery; + +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.AddExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.project.extensions.ExtensionsManager; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * This class is thread-safe. It extracts extensions to be added to the project from an instance of + * {@link QuarkusCommandInvocation}. + */ +public class AddExtensionsCommandHandler implements QuarkusCommandHandler { + + @Override + public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + final Set extensionsQuery = invocation.getValue(AddExtensions.EXTENSIONS, Collections.emptySet()); + if (extensionsQuery.isEmpty()) { + return QuarkusCommandOutcome.success().setValue(AddExtensions.OUTCOME_UPDATED, false); + } + + final List extensionsToAdd = computeExtensionsFromQuery(invocation, extensionsQuery); + final ExtensionsManager extensionsManager = invocation.getValue(EXTENSIONS_MANAGER, + invocation.getQuarkusProject().getExtensionsManager()); + try { + if (extensionsToAdd != null) { + final int added = extensionsManager.add(extensionsToAdd); + return new QuarkusCommandOutcome(true).setValue(AddExtensions.OUTCOME_UPDATED, added > 0); + } + + } catch (IOException e) { + throw new QuarkusCommandException("Failed to add extensions", e); + } + + return new QuarkusCommandOutcome(false).setValue(AddExtensions.OUTCOME_UPDATED, false); + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java new file mode 100644 index 0000000000000..d6a4d1569c2ee --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java @@ -0,0 +1,82 @@ +package io.quarkus.devtools.commands.handlers; + +import static io.quarkus.devtools.commands.handlers.QuarkusCommandHandlers.computeExtensionsFromQuery; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.*; + +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.buildfile.GradleBuildFilesCreator; +import io.quarkus.devtools.project.codegen.ProjectGenerator; +import io.quarkus.devtools.project.codegen.ProjectGeneratorRegistry; +import io.quarkus.devtools.project.codegen.SourceType; +import io.quarkus.devtools.project.codegen.rest.BasicRestProjectGenerator; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import io.quarkus.platform.tools.ToolsUtils; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +/** + * Instances of this class are thread-safe. They create a new project extracting all the necessary properties from an instance + * of {@link QuarkusCommandInvocation}. + */ +public class CreateProjectCommandHandler implements QuarkusCommandHandler { + + @Override + public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + final QuarkusPlatformDescriptor platformDescr = invocation.getPlatformDescriptor(); + invocation.setValue(BOM_GROUP_ID, platformDescr.getBomGroupId()); + invocation.setValue(BOM_ARTIFACT_ID, platformDescr.getBomArtifactId()); + invocation.setValue(QUARKUS_VERSION, platformDescr.getQuarkusVersion()); + invocation.setValue(BOM_VERSION, platformDescr.getBomVersion()); + final Set extensionsQuery = invocation.getValue(ProjectGenerator.EXTENSIONS, Collections.emptySet()); + + final Properties quarkusProps = ToolsUtils.readQuarkusProperties(platformDescr); + quarkusProps.forEach((k, v) -> invocation.setValue(k.toString().replace("-", "_"), v.toString())); + + try { + String className = invocation.getStringValue(CLASS_NAME); + if (className != null) { + className = invocation.getValue(SOURCE_TYPE, SourceType.JAVA).stripExtensionFrom(className); + int idx = className.lastIndexOf('.'); + if (idx >= 0) { + String pkgName = invocation.getStringValue(PACKAGE_NAME); + if (pkgName == null) { + invocation.setValue(PACKAGE_NAME, className.substring(0, idx)); + } + className = className.substring(idx + 1); + } + invocation.setValue(CLASS_NAME, className); + } + + final List extensionsToAdd = computeExtensionsFromQuery(invocation, extensionsQuery); + + // extensionsToAdd is null when an error occurred while matching extensions + if (extensionsToAdd != null) { + ProjectGeneratorRegistry.get(BasicRestProjectGenerator.NAME).generate(invocation); + + //TODO ia3andy extensions should be added directly during the project generation + if (invocation.getQuarkusProject().getBuildTool().equals(BuildTool.GRADLE)) { + final GradleBuildFilesCreator creator = new GradleBuildFilesCreator(invocation.getQuarkusProject()); + creator.create( + invocation.getStringValue(PROJECT_GROUP_ID), + invocation.getStringValue(PROJECT_ARTIFACT_ID), + invocation.getStringValue(PROJECT_VERSION), + quarkusProps, + extensionsToAdd); + } else { + invocation.getQuarkusProject().getExtensionsManager().add(extensionsToAdd); + } + } + + } catch (IOException e) { + throw new QuarkusCommandException("Failed to create project", e); + } + return QuarkusCommandOutcome.success(); + } +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/ListExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/ListExtensionsCommandHandler.java new file mode 100644 index 0000000000000..e77e91c55b54f --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/ListExtensionsCommandHandler.java @@ -0,0 +1,154 @@ +package io.quarkus.devtools.commands.handlers; + +import static java.util.stream.Collectors.toMap; + +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.ListExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.extensions.ExtensionsManager; +import io.quarkus.platform.tools.MessageWriter; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Instances of this class are thread-safe. It lists extensions according to the options passed in as properties of + * {@link QuarkusCommandInvocation} + */ +public class ListExtensionsCommandHandler implements QuarkusCommandHandler { + + private static final String FULL_FORMAT = "%-8s %-50s %-50s %-25s%n%s"; + private static final String CONCISE_FORMAT = "%-50s %-50s"; + private static final String NAME_FORMAT = "%-50s"; + + @Override + public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + + final boolean all = invocation.getValue(ListExtensions.ALL, true); + final String format = invocation.getValue(ListExtensions.FORMAT, "concise"); + final String search = invocation.getValue(ListExtensions.SEARCH, "*"); + final ExtensionsManager extensionsManager = invocation.getValue(ListExtensions.EXTENSIONS_MANAGER, + invocation.getQuarkusProject().getExtensionsManager()); + + Map installedByGA; + try { + installedByGA = extensionsManager.read().stream() + .collect(toMap(Extension::managementKey, Function.identity())); + } catch (IOException e) { + throw new QuarkusCommandException("Failed to determine the list of installed extensions", e); + } + + Stream platformExtensionsStream = invocation.getPlatformDescriptor().getExtensions().stream(); + platformExtensionsStream = platformExtensionsStream.filter(this::filterUnlisted); + if (search != null && !"*".equalsIgnoreCase(search)) { + final Pattern searchPattern = Pattern.compile(".*" + search + ".*", Pattern.CASE_INSENSITIVE); + platformExtensionsStream = platformExtensionsStream.filter(e -> filterBySearch(searchPattern, e)); + } + List platformExtensions = platformExtensionsStream.collect(Collectors.toList()); + + if (platformExtensions.isEmpty()) { + invocation.log().info("No extension found with this pattern"); + } else { + String extensionStatus = all ? "available" : "installable"; + invocation.log().info(String.format("%nCurrent Quarkus extensions %s: ", extensionStatus)); + + BiConsumer currentFormatter; + switch (format.toLowerCase()) { + case "name": + currentFormatter = this::nameFormatter; + break; + case "full": + currentFormatter = this::fullFormatter; + currentFormatter.accept(invocation.log(), + new String[] { "Status", "Extension", "ArtifactId", "Updated Version", "Guide" }); + break; + case "concise": + default: + currentFormatter = this::conciseFormatter; + } + + platformExtensions.forEach(platformExtension -> display(invocation.log(), platformExtension, + installedByGA.get(platformExtension.managementKey()), all, currentFormatter)); + final BuildTool buildTool = invocation.getQuarkusProject().getBuildTool(); + if ("concise".equalsIgnoreCase(format)) { + if (BuildTool.GRADLE.equals(buildTool)) { + invocation.log().info("\nTo get more information, append --format=full to your command line."); + } else { + invocation.log().info( + "\nTo get more information, append -Dquarkus.extension.format=full to your command line."); + } + } + + if (BuildTool.GRADLE.equals(buildTool)) { + invocation.log().info("\nAdd an extension to your project by adding the dependency to your " + + "build.gradle or use `./gradlew addExtension --extensions=\"artifactId\"`"); + } else { + invocation.log().info("\nAdd an extension to your project by adding the dependency to your " + + "pom.xml or use `./mvnw quarkus:add-extension -Dextensions=\"artifactId\"`"); + } + + } + + return QuarkusCommandOutcome.success(); + } + + private boolean filterUnlisted(Extension e) { + return !e.getMetadata().containsKey("unlisted"); + } + + private boolean filterBySearch(final Pattern searchPattern, Extension e) { + return searchPattern.matcher(e.getName()).matches(); + } + + private void conciseFormatter(MessageWriter writer, String[] cols) { + writer.info(String.format(CONCISE_FORMAT, cols[1], cols[2], cols[4])); + } + + private void fullFormatter(MessageWriter writer, String[] cols) { + writer.info(String.format(FULL_FORMAT, cols[0], cols[1], cols[2], cols[3], cols[4])); + } + + private void nameFormatter(MessageWriter writer, String[] cols) { + writer.info(String.format(NAME_FORMAT, cols[2])); + } + + private void display(MessageWriter messageWriter, final Extension platformExtension, final Extension installed, boolean all, + BiConsumer formatter) { + if (!all && installed != null) { + return; + } + + String label = ""; + String version = ""; + + final String installedVersion = installed != null ? installed.getVersion() : null; + if (installedVersion != null) { + if (installedVersion.equalsIgnoreCase(platformExtension.getVersion())) { + label = "current"; + version = String.format("%s", installedVersion); + } else { + label = "update"; + version = String.format("%s <> %s", installedVersion, platformExtension.getVersion()); + } + } + + String[] result = new String[] { label, platformExtension.getName(), platformExtension.getArtifactId(), version, + platformExtension.getGuide() }; + + for (int i = 0; i < result.length; i++) { + result[i] = Objects.toString(result[i], ""); + } + + formatter.accept(messageWriter, result); + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandler.java new file mode 100644 index 0000000000000..dfc372a8b3ce3 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandler.java @@ -0,0 +1,10 @@ +package io.quarkus.devtools.commands.handlers; + +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; + +public interface QuarkusCommandHandler { + + QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException; +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlers.java similarity index 56% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensionsCommandHandler.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlers.java index 6541aa6992b7c..8ea62d9ea5b62 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensionsCommandHandler.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlers.java @@ -1,8 +1,13 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.handlers; +import static io.quarkus.platform.tools.ConsoleMessageFormat.NOK; +import static io.quarkus.platform.tools.ConsoleMessageFormat.nok; + +import com.google.common.collect.ImmutableList; import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.buildfile.BuildFile; -import java.io.IOException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.SelectionResult; +import io.quarkus.devtools.project.extensions.Extensions; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; @@ -11,101 +16,74 @@ import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; -/** - * This class is thread-safe. It extracts extensions to be added to the project from an instance of - * {@link QuarkusCommandInvocation}. - */ -public class AddExtensionsCommandHandler implements QuarkusCommand { - - final static Printer PRINTER = new Printer(); - - @Override - public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - - final Set extensions = invocation.getValue(AddExtensions.EXTENSIONS, Collections.emptySet()); - if (extensions.isEmpty()) { - return QuarkusCommandOutcome.success().setValue(AddExtensions.OUTCOME_UPDATED, false); - } - - boolean updated = false; - boolean success = true; +final class QuarkusCommandHandlers { - final List registry = invocation.getPlatformDescriptor().getExtensions(); + private QuarkusCommandHandlers() { + } - final BuildFile buildFile = invocation.getBuildFile(); - try { - for (String query : extensions) { - if (query.contains(":")) { - // GAV case. - updated = buildFile.addExtensionAsGAV(query) || updated; - } else { - SelectionResult result = select(query, registry, false); - if (!result.matches()) { - StringBuilder sb = new StringBuilder(); - // We have 3 cases, we can still have a single candidate, but the match is on label - // or we have several candidates, or none - Set candidates = result.getExtensions(); - if (candidates.isEmpty()) { - // No matches at all. - PRINTER.nok(" Cannot find a dependency matching '" + query + "', maybe a typo?"); - success = false; - } else { - sb.append(Printer.NOK).append(" Multiple extensions matching '").append(query).append("'"); - result.getExtensions() - .forEach(extension -> sb.append(System.lineSeparator()).append(" * ") - .append(extension.managementKey())); - sb.append(System.lineSeparator()) - .append(" Be more specific e.g using the exact name or the full GAV."); - PRINTER.print(sb.toString()); - success = false; - } - } else { // Matches. - for (Extension extension : result) { - // Don't set success to false even if the dependency is not added; as it's should be idempotent. - updated = buildFile.addDependency(invocation.getPlatformDescriptor(), extension) || updated; - } + static List computeExtensionsFromQuery(final QuarkusCommandInvocation invocation, + final Set extensionsQuery) { + final ImmutableList.Builder builder = ImmutableList. builder(); + for (String query : extensionsQuery) { + if (query.contains(":")) { + builder.add(Extensions.parse(query)); + } else { + SelectionResult result = select(invocation.getPlatformDescriptor().getExtensions(), query, false); + if (!result.matches()) { + StringBuilder sb = new StringBuilder(); + // We have 3 cases, we can still have a single candidate, but the match is on label + // or we have several candidates, or none + Set candidates = result.getExtensions(); + if (candidates.isEmpty()) { + // No matches at all. + invocation.log().info(nok(" Cannot find a dependency matching '" + query + "', maybe a typo?")); + return null; + } else { + sb.append(NOK).append(" Multiple extensions matching '").append(query).append("'"); + result.getExtensions() + .forEach(extension -> sb.append(System.lineSeparator()).append(" * ") + .append(extension.managementKey())); + sb.append(System.lineSeparator()) + .append(" Be more specific e.g using the exact name or the full GAV."); + invocation.log().info(sb.toString()); + return null; + } + } else { // Matches. + for (Extension extension : result) { + // Don't set success to false even if the dependency is not added; as it's should be idempotent. + builder.add(extension); } } } - } catch (IOException e) { - throw new QuarkusCommandException("Failed to add extensions", e); - } - - if (buildFile != null && updated) { - try { - buildFile.close(); - } catch (IOException e) { - throw new QuarkusCommandException("Failed to update the project", e); - } } - - return new QuarkusCommandOutcome(success).setValue(AddExtensions.OUTCOME_UPDATED, updated); + return builder.build(); } /** * Selection algorithm. * + * @param allPlatformExtensions the list of all platform extensions * @param query the query - * @param extensions the extension list * @param labelLookup whether or not the query must be tested against the labels of the extensions. Should * be {@code false} by default. * @return the list of matching candidates and whether or not a match has been found. */ - static SelectionResult select(String query, List extensions, boolean labelLookup) { + static SelectionResult select(final List allPlatformExtensions, final String query, final boolean labelLookup) { String q = query.trim().toLowerCase(); // Try exact matches - Set matchesNameOrArtifactId = extensions.stream() + Set matchesNameOrArtifactId = allPlatformExtensions.stream() .filter(extension -> extension.getName().equalsIgnoreCase(q) || matchesArtifactId(extension.getArtifactId(), q)) .collect(Collectors.toSet()); if (matchesNameOrArtifactId.size() == 1) { return new SelectionResult(matchesNameOrArtifactId, true); } - extensions = extensions.stream().filter(e -> !e.isUnlisted()).collect(Collectors.toList()); + final List listedPlatformExtensions = allPlatformExtensions.stream() + .filter(e -> !e.isUnlisted()).collect(Collectors.toList()); // Try short names - Set matchesShortName = extensions.stream().filter(extension -> matchesShortName(extension, q)) + Set matchesShortName = listedPlatformExtensions.stream().filter(extension -> matchesShortName(extension, q)) .collect(Collectors.toSet()); if (matchesShortName.size() == 1 && matchesNameOrArtifactId.isEmpty()) { @@ -113,9 +91,11 @@ static SelectionResult select(String query, List extensions, boolean } // Partial matches on name, artifactId and short names - Set partialMatches = extensions.stream().filter(extension -> extension.getName().toLowerCase().contains(q) - || extension.getArtifactId().toLowerCase().contains(q) - || extension.getShortName().toLowerCase().contains(q)).collect(Collectors.toSet()); + Set partialMatches = listedPlatformExtensions.stream() + .filter(extension -> extension.getName().toLowerCase().contains(q) + || extension.getArtifactId().toLowerCase().contains(q) + || extension.getShortName().toLowerCase().contains(q)) + .collect(Collectors.toSet()); // Even if we have a single partial match, if the name, artifactId and short names are ambiguous, so not // consider it as a match. if (partialMatches.size() == 1 && matchesNameOrArtifactId.isEmpty() && matchesShortName.isEmpty()) { @@ -125,7 +105,7 @@ static SelectionResult select(String query, List extensions, boolean // find by labels List matchesLabels; if (labelLookup) { - matchesLabels = extensions.stream() + matchesLabels = listedPlatformExtensions.stream() .filter(extension -> extension.labelsForMatching().contains(q)).collect(Collectors.toList()); } else { matchesLabels = Collections.emptyList(); @@ -135,7 +115,7 @@ static SelectionResult select(String query, List extensions, boolean Set matchesPatterns; Pattern pattern = toRegex(q); if (pattern != null) { - matchesPatterns = extensions.stream() + matchesPatterns = listedPlatformExtensions.stream() .filter(extension -> pattern.matcher(extension.getName().toLowerCase()).matches() || pattern.matcher(extension.getArtifactId().toLowerCase()).matches() || pattern.matcher(extension.getShortName().toLowerCase()).matches() @@ -229,4 +209,5 @@ private static boolean matchesArtifactId(String artifactId, String q) { return artifactId.equalsIgnoreCase(q) || artifactId.equalsIgnoreCase("quarkus-" + q); } + } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/RemoveExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/RemoveExtensionsCommandHandler.java new file mode 100644 index 0000000000000..d3f8421825c64 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/RemoveExtensionsCommandHandler.java @@ -0,0 +1,45 @@ +package io.quarkus.devtools.commands.handlers; + +import static io.quarkus.devtools.commands.RemoveExtensions.EXTENSIONS_MANAGER; +import static io.quarkus.devtools.commands.handlers.QuarkusCommandHandlers.computeExtensionsFromQuery; + +import com.google.common.collect.ImmutableSet; +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.RemoveExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.project.extensions.ExtensionsManager; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * This class is thread-safe. It extracts extensions to be removed from the project from an instance of + * {@link QuarkusCommandInvocation}. + */ +public class RemoveExtensionsCommandHandler implements QuarkusCommandHandler { + + @Override + public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + final Set extensionsQuery = invocation.getValue(RemoveExtensions.EXTENSIONS, Collections.emptySet()); + if (extensionsQuery.isEmpty()) { + return QuarkusCommandOutcome.success().setValue(RemoveExtensions.OUTCOME_UPDATED, false); + } + + final List extensionsToRemove = computeExtensionsFromQuery(invocation, extensionsQuery); + final ExtensionsManager extensionsManager = invocation.getValue(EXTENSIONS_MANAGER, + invocation.getQuarkusProject().getExtensionsManager()); + try { + if (extensionsToRemove != null) { + final int removed = extensionsManager.remove(ImmutableSet.copyOf(extensionsToRemove)); + return new QuarkusCommandOutcome(true).setValue(RemoveExtensions.OUTCOME_UPDATED, removed > 0); + } + } catch (IOException e) { + throw new QuarkusCommandException("Failed to add extensions", e); + } + + return new QuarkusCommandOutcome(true).setValue(RemoveExtensions.OUTCOME_UPDATED, false); + } +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/BuildTool.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/BuildTool.java index 0987465b3c1b3..78b5a641c834c 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/BuildTool.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/BuildTool.java @@ -1,11 +1,10 @@ package io.quarkus.devtools.project; -import static com.google.common.base.Preconditions.checkNotNull; - -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.buildfile.GradleBuildFile; -import io.quarkus.devtools.buildfile.MavenBuildFile; -import io.quarkus.devtools.writer.ProjectWriter; +import io.quarkus.devtools.project.buildfile.GenericGradleBuildFile; +import io.quarkus.devtools.project.buildfile.MavenBuildFile; +import io.quarkus.devtools.project.extensions.ExtensionsManager; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import java.nio.file.Path; /** * An enum of build tools, such as Maven and Gradle. @@ -53,14 +52,14 @@ public String getBuildDirectory() { return buildDirectory; } - public BuildFile createBuildFile(final ProjectWriter writer) { - checkNotNull(writer, "writer is required"); + public ExtensionsManager createExtensionManager(final Path projectFolderPath, + final QuarkusPlatformDescriptor platformDescriptor) { switch (this) { case GRADLE: - return new GradleBuildFile(writer); + return new GenericGradleBuildFile(); case MAVEN: default: - return new MavenBuildFile(writer); + return new MavenBuildFile(projectFolderPath, platformDescriptor); } } } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java index 8d6ef03e40831..a4c9eaa58b639 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java @@ -2,33 +2,38 @@ import static com.google.common.base.Preconditions.checkNotNull; -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.writer.FileProjectWriter; +import io.quarkus.devtools.project.buildfile.MavenBuildFile; +import io.quarkus.devtools.project.extensions.ExtensionsManager; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.nio.file.Path; public final class QuarkusProject { private final Path projectFolderPath; - private final BuildFile buildFile; - private final QuarkusPlatformDescriptor descriptor; + private final QuarkusPlatformDescriptor platformDescriptor; + private final ExtensionsManager extensionsManager; - private QuarkusProject(final Path projectFolderPath, final QuarkusPlatformDescriptor descriptor, - final BuildFile buildFile) { + private QuarkusProject(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor, + final ExtensionsManager extensionsManager) { this.projectFolderPath = checkNotNull(projectFolderPath, "projectFolderPath is required"); - this.descriptor = checkNotNull(descriptor, "descriptor is required"); - this.buildFile = checkNotNull(buildFile, "buildFile is required"); + this.platformDescriptor = checkNotNull(platformDescriptor, "platformDescriptor is required"); + this.extensionsManager = checkNotNull(extensionsManager, "extensionsManager is required"); } - public static QuarkusProject of(final Path projectFolderPath, final QuarkusPlatformDescriptor descriptor, + public static QuarkusProject of(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor, + final ExtensionsManager extensionsManager) { + return new QuarkusProject(projectFolderPath, platformDescriptor, extensionsManager); + } + + public static QuarkusProject of(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor, final BuildTool buildTool) { - final BuildFile buildFile = buildTool.createBuildFile(new FileProjectWriter(projectFolderPath.toFile())); - return of(projectFolderPath, descriptor, buildFile); + return new QuarkusProject(projectFolderPath, platformDescriptor, + buildTool.createExtensionManager(projectFolderPath, platformDescriptor)); } - public static QuarkusProject of(final Path projectFolderPath, final QuarkusPlatformDescriptor descriptor, - final BuildFile buildFile) { - return new QuarkusProject(projectFolderPath, descriptor, buildFile); + public static QuarkusProject maven(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + return new QuarkusProject(projectFolderPath, platformDescriptor, + new MavenBuildFile(projectFolderPath, platformDescriptor)); } public static QuarkusProject resolveExistingProject(final Path projectFolderPath, @@ -44,19 +49,19 @@ public Path getProjectFolderPath() { return projectFolderPath; } - public BuildFile getBuildFile() { - return buildFile; + public BuildTool getBuildTool() { + return extensionsManager.getBuildTool(); } - public BuildTool getBuildTool() { - return buildFile.getBuildTool(); + public ExtensionsManager getExtensionsManager() { + return extensionsManager; } - public QuarkusPlatformDescriptor getDescriptor() { - return descriptor; + public QuarkusPlatformDescriptor getPlatformDescriptor() { + return platformDescriptor; } - private static BuildTool resolveExistingProjectBuildTool(Path projectFolderPath) { + public static BuildTool resolveExistingProjectBuildTool(Path projectFolderPath) { if (projectFolderPath.resolve("pom.xml").toFile().exists()) { return BuildTool.MAVEN; } else if (projectFolderPath.resolve("build.gradle").toFile().exists() @@ -65,4 +70,5 @@ private static BuildTool resolveExistingProjectBuildTool(Path projectFolderPath) } return null; } + } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java new file mode 100644 index 0000000000000..bc5b04f511bdd --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java @@ -0,0 +1,242 @@ +package io.quarkus.devtools.project.buildfile; + +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import java.util.Properties; +import java.util.Scanner; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import org.apache.maven.model.Dependency; + +// We keep it here to take advantage of the abstract tests +public abstract class AbstractGradleBuildFile extends BuildFile { + + private static final String BUILD_GRADLE_PATH = "build.gradle"; + private static final String SETTINGS_GRADLE_PATH = "settings.gradle"; + private static final String GRADLE_PROPERTIES_PATH = "gradle.properties"; + + private final Optional rootProjectPath; + + private AtomicReference modelReference = new AtomicReference<>(); + + public AbstractGradleBuildFile(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + super(projectFolderPath, platformDescriptor); + this.rootProjectPath = Optional.empty(); + } + + public AbstractGradleBuildFile(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor, + Path rootProjectPath) { + super(projectFolderPath, platformDescriptor); + this.rootProjectPath = Optional.ofNullable(rootProjectPath); + } + + @Override + public void writeToDisk() throws IOException { + if (rootProjectPath.isPresent()) { + Files.write(rootProjectPath.get().resolve(SETTINGS_GRADLE_PATH), getModel().getRootSettingsContent().getBytes()); + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + getModel().getRootPropertiesContent().store(out, "Gradle properties"); + Files.write(rootProjectPath.get().resolve(GRADLE_PROPERTIES_PATH), + getModel().getRootSettingsContent().getBytes()); + } + } else { + writeToProjectFile(SETTINGS_GRADLE_PATH, getModel().getSettingsContent().getBytes()); + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + getModel().getPropertiesContent().store(out, "Gradle properties"); + writeToProjectFile(GRADLE_PROPERTIES_PATH, out.toByteArray()); + } + } + writeToProjectFile(BUILD_GRADLE_PATH, getModel().getBuildContent().getBytes()); + } + + @Override + protected void addDependencyInBuildFile(Dependency dependency) throws IOException { + StringBuilder newBuildContent = new StringBuilder(); + readLineByLine(getModel().getBuildContent(), currentLine -> { + newBuildContent.append(currentLine).append(System.lineSeparator()); + if (currentLine.startsWith("dependencies {")) { + newBuildContent.append(" implementation '") + .append(dependency.getGroupId()) + .append(":") + .append(dependency.getArtifactId()); + if (dependency.getVersion() != null && !dependency.getVersion().isEmpty()) { + newBuildContent.append(":") + .append(dependency.getVersion()); + } + newBuildContent.append("'") + .append(System.lineSeparator()); + } + }); + getModel().setBuildContent(newBuildContent.toString()); + } + + @Override + protected void removeDependencyFromBuildFile(Dependency dependency) throws IOException { + String depString = new StringBuilder("'").append(dependency.getGroupId()).append(":") + .append(dependency.getArtifactId()).toString(); + StringBuilder newBuildContent = new StringBuilder(); + Scanner scanner = new Scanner(getModel().getBuildContent()); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (!line.contains(depString)) { + newBuildContent.append(line).append(System.lineSeparator()); + } + } + scanner.close(); + getModel().setBuildContent(newBuildContent.toString()); + } + + @Override + protected boolean containsBOM(String groupId, String artifactId) throws IOException { + String buildContent = getModel().getBuildContent(); + return buildContent.contains("enforcedPlatform(\"${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:") + || buildContent.contains("enforcedPlatform(\"" + groupId + ":" + artifactId + ":"); + } + + @Override + public String getProperty(String propertyName) throws IOException { + final String property = getModel().getPropertiesContent().getProperty(propertyName); + if (property != null || getModel().getRootPropertiesContent() == null) { + return property; + } + return getModel().getRootPropertiesContent().getProperty(propertyName); + } + + @Override + public BuildTool getBuildTool() { + return BuildTool.GRADLE; + } + + private void readLineByLine(String content, Consumer lineConsumer) { + try (Scanner scanner = new Scanner(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8.name())) { + while (scanner.hasNextLine()) { + String currentLine = scanner.nextLine(); + lineConsumer.accept(currentLine); + } + } + } + + private Model getModel() throws IOException { + return modelReference.updateAndGet(model -> { + if (model == null) { + try { + return readModel(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return model; + }); + } + + @Override + protected void refreshData() { + this.modelReference.set(null); + } + + private boolean hasRootProjectFile(final String fileName) throws IOException { + if (!rootProjectPath.isPresent()) { + return false; + } + final Path filePath = rootProjectPath.get().resolve(fileName); + return Files.exists(filePath); + } + + private byte[] readRootProjectFile(final String fileName) throws IOException { + final Path filePath = rootProjectPath + .orElseThrow(() -> new IllegalStateException("There is no rootProject defined in this GradleBuildFile")) + .resolve(fileName); + return Files.readAllBytes(filePath); + } + + private Model readModel() throws IOException { + String settingsContent = ""; + String buildContent = ""; + Properties propertiesContent = new Properties(); + String rootSettingsContent = null; + Properties rootPropertiesContent = null; + if (hasProjectFile(SETTINGS_GRADLE_PATH)) { + final byte[] settings = readProjectFile(SETTINGS_GRADLE_PATH); + settingsContent = new String(settings, StandardCharsets.UTF_8); + } + if (hasRootProjectFile(SETTINGS_GRADLE_PATH)) { + final byte[] settings = readRootProjectFile(SETTINGS_GRADLE_PATH); + rootSettingsContent = new String(settings, StandardCharsets.UTF_8); + } + if (hasProjectFile(BUILD_GRADLE_PATH)) { + final byte[] build = readProjectFile(BUILD_GRADLE_PATH); + buildContent = new String(build, StandardCharsets.UTF_8); + } + if (hasProjectFile(GRADLE_PROPERTIES_PATH)) { + final byte[] properties = readProjectFile(GRADLE_PROPERTIES_PATH); + propertiesContent.load(new ByteArrayInputStream(properties)); + } + if (hasRootProjectFile(GRADLE_PROPERTIES_PATH)) { + final byte[] properties = readRootProjectFile(GRADLE_PROPERTIES_PATH); + rootPropertiesContent = new Properties(); + rootPropertiesContent.load(new ByteArrayInputStream(properties)); + } + return new Model(settingsContent, buildContent, propertiesContent, rootSettingsContent, rootPropertiesContent); + } + + protected String getBuildContent() throws IOException { + return getModel().getBuildContent(); + } + + private static class Model { + private String settingsContent; + private String buildContent; + private Properties propertiesContent; + + private String rootSettingsContent; + private Properties rootPropertiesContent; + + public Model(String settingsContent, String buildContent, Properties propertiesContent, String rootSettingsContent, + Properties rootPropertiesContent) { + this.settingsContent = settingsContent; + this.buildContent = buildContent; + this.propertiesContent = propertiesContent; + this.rootSettingsContent = rootSettingsContent; + this.rootPropertiesContent = rootPropertiesContent; + } + + public String getSettingsContent() { + return settingsContent; + } + + public String getBuildContent() { + return buildContent; + } + + public Properties getPropertiesContent() { + return propertiesContent; + } + + public String getRootSettingsContent() { + return rootSettingsContent; + } + + public Properties getRootPropertiesContent() { + return rootPropertiesContent; + } + + public void setSettingsContent(String settingsContent) { + this.settingsContent = settingsContent; + } + + public void setBuildContent(String buildContent) { + this.buildContent = buildContent; + } + + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java new file mode 100644 index 0000000000000..e1b98d35e3cdb --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java @@ -0,0 +1,163 @@ +package io.quarkus.devtools.project.buildfile; + +import static com.google.common.base.Preconditions.checkNotNull; +import static io.quarkus.devtools.project.extensions.Extensions.key; +import static java.util.stream.Collectors.toList; + +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.project.extensions.Extensions; +import io.quarkus.devtools.project.extensions.ExtensionsManager; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.LongAdder; +import java.util.stream.Collectors; +import org.apache.maven.model.Dependency; + +public abstract class BuildFile implements ExtensionsManager { + + private final Path projectFolderPath; + private final QuarkusPlatformDescriptor platformDescriptor; + + public BuildFile(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + this.projectFolderPath = checkNotNull(projectFolderPath, "projectPath is required"); + this.platformDescriptor = checkNotNull(platformDescriptor, "platformDescriptor is required"); + } + + @Override + public final boolean hasQuarkusPlatformBom() throws IOException { + return containsBOM(platformDescriptor.getBomGroupId(), platformDescriptor.getBomArtifactId()); + } + + @Override + public final int add(List extensions) throws IOException { + if (!hasQuarkusPlatformBom()) { + throw new IllegalStateException("The Quarkus BOM is required to add a Quarkus extension"); + } + this.refreshData(); + final Set existingKeys = getDependenciesManagementKeys(); + final LongAdder counter = new LongAdder(); + extensions.stream() + .filter(a -> !existingKeys.contains(a.managementKey())) + .forEach(e -> { + try { + final boolean isInPlatformBom = isDefinedInRegistry(platformDescriptor.getExtensions(), + e.managementKey()); + // We hardcode the version when it is already defined in the platform bom + addDependencyInBuildFile(e.toDependency(isInPlatformBom)); + counter.increment(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + this.writeToDisk(); + return counter.intValue(); + } + + @Override + public final List read() throws IOException { + this.refreshData(); + return this.getDependencies().stream().filter(d -> this.isRegisteredQuarkusExtension(key(d))) + .map(d -> new Extension(d.getGroupId(), d.getArtifactId(), extractVersion(d))) + .collect(toList()); + } + + @Override + public final int remove(Set extensions) throws IOException { + this.refreshData(); + final Set existingKeys = getDependenciesManagementKeys(); + final LongAdder counter = new LongAdder(); + extensions.stream() + .filter(a -> existingKeys.contains(a.managementKey())) + .forEach(e -> { + try { + removeDependencyFromBuildFile(e.toDependency(true)); + counter.increment(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + this.writeToDisk(); + return counter.intValue(); + } + + protected abstract void addDependencyInBuildFile(Dependency dependency) throws IOException; + + protected abstract void removeDependencyFromBuildFile(Dependency dependency) throws IOException; + + protected abstract List getDependencies() throws IOException; + + protected abstract void writeToDisk() throws IOException; + + protected abstract String getProperty(String propertyName) throws IOException; + + protected abstract boolean containsBOM(String groupId, String artifactId) throws IOException; + + protected abstract void refreshData(); + + protected Path getProjectFolderPath() { + return projectFolderPath; + } + + protected boolean hasProjectFile(final String fileName) throws IOException { + final Path filePath = projectFolderPath.resolve(fileName); + return Files.exists(filePath); + } + + protected byte[] readProjectFile(final String fileName) throws IOException { + final Path filePath = projectFolderPath.resolve(fileName); + return Files.readAllBytes(filePath); + } + + protected void writeToProjectFile(final String fileName, final byte[] content) throws IOException { + Files.write(projectFolderPath.resolve(fileName), content); + } + + private boolean isRegisteredQuarkusExtension(final String managementKey) { + // This will not always be true as the platform descriptor does not contain the list of all available extensions + return isDefinedInRegistry(platformDescriptor.getExtensions(), managementKey); + } + + private Set getDependenciesManagementKeys() throws IOException { + return getDependencies().stream().map(Extensions::key).collect(Collectors.toSet()); + } + + private String extractVersion(final Dependency d) { + String version = d != null ? d.getVersion() : null; + if (version != null && version.startsWith("$")) { + String value = null; + try { + value = (String) this.getProperty(propertyName(version)); + if (value != null) { + return value; + } + } catch (IOException e) { + // ignore this error. + } + } + if (version != null) { + return version; + } + return mapToExtensionFromRegistry(platformDescriptor.getExtensions(), key(d)) + .map(Extension::getVersion) + .orElse(null); + } + + private String propertyName(final String variable) { + return variable.substring(2, variable.length() - 1); + } + + public static boolean isDefinedInRegistry(List registry, final String managementKey) { + return mapToExtensionFromRegistry(registry, managementKey).isPresent(); + } + + public static Optional mapToExtensionFromRegistry(List registry, final String managementKey) { + return registry.stream().filter(k -> Objects.equals(k.managementKey(), managementKey)).findFirst(); + } +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GenericGradleBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GenericGradleBuildFile.java new file mode 100644 index 0000000000000..829b40465a7af --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GenericGradleBuildFile.java @@ -0,0 +1,39 @@ +package io.quarkus.devtools.project.buildfile; + +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.extensions.ExtensionsManager; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** + * TODO We need to find a way to use the gradle api outside of a gradle plugin + */ +public class GenericGradleBuildFile implements ExtensionsManager { + + @Override + public BuildTool getBuildTool() { + return BuildTool.GRADLE; + } + + @Override + public List read() throws IOException { + throw new IllegalStateException("This feature is not yet implemented outside of the Gradle Plugin."); + } + + @Override + public boolean hasQuarkusPlatformBom() throws IOException { + throw new IllegalStateException("This feature is not yet implemented outside of the Gradle Plugin."); + } + + @Override + public int add(List extensions) throws IOException { + throw new IllegalStateException("This feature is not yet implemented outside of the Gradle Plugin."); + } + + @Override + public int remove(Set extensions) throws IOException { + throw new IllegalStateException("This feature is not yet implemented outside of the Gradle Plugin."); + } +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/GradleBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GradleBuildFilesCreator.java similarity index 52% rename from independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/GradleBuildFile.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GradleBuildFilesCreator.java index e148e30bfe82e..aaf18e4b402ab 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/GradleBuildFile.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GradleBuildFilesCreator.java @@ -1,77 +1,153 @@ -package io.quarkus.devtools.buildfile; +package io.quarkus.devtools.project.buildfile; + +import static io.quarkus.devtools.project.buildfile.BuildFile.isDefinedInRegistry; import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.maven.utilities.MojoUtils; +import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.tools.ToolsUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; -import java.util.Collections; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Properties; import java.util.Scanner; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import org.apache.maven.model.Dependency; -public class GradleBuildFile extends BuildFile { +public final class GradleBuildFilesCreator { private static final String BUILD_GRADLE_PATH = "build.gradle"; private static final String SETTINGS_GRADLE_PATH = "settings.gradle"; private static final String GRADLE_PROPERTIES_PATH = "gradle.properties"; + private final QuarkusProject quarkusProject; + + private AtomicReference modelReference = new AtomicReference<>(); - private final ProjectWriter rootWriter; + public GradleBuildFilesCreator(QuarkusProject quarkusProject) { + this.quarkusProject = quarkusProject; + } - private Model model; + public void create(String groupId, String artifactId, String version, + Properties properties, List extensions) throws IOException { + createSettingsContent(artifactId); + createBuildContent(groupId, version); + createProperties(); + extensions.stream() + .forEach(e -> { + try { + final boolean isInPlatformBom = isDefinedInRegistry( + quarkusProject.getPlatformDescriptor().getExtensions(), e.managementKey()); + // We hardcode the version when it is already defined in the platform bom + addDependencyInBuildFile(e.toDependency(isInPlatformBom)); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + this.writeToDisk(); + } - public GradleBuildFile(ProjectWriter writer) { - super(writer, BuildTool.GRADLE); - rootWriter = writer; + private void writeToDisk() throws IOException { + writeToProjectFile(SETTINGS_GRADLE_PATH, getModel().getSettingsContent().getBytes()); + writeToProjectFile(BUILD_GRADLE_PATH, getModel().getBuildContent().getBytes()); + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + getModel().getPropertiesContent().store(out, "Gradle properties"); + writeToProjectFile(GRADLE_PROPERTIES_PATH, out.toByteArray()); + } } - public GradleBuildFile(ProjectWriter writer, ProjectWriter rootWriter) { - super(writer, BuildTool.GRADLE); - this.rootWriter = rootWriter; + private void addDependencyInBuildFile(Dependency dependency) throws IOException { + StringBuilder newBuildContent = new StringBuilder(); + readLineByLine(getModel().getBuildContent(), currentLine -> { + newBuildContent.append(currentLine).append(System.lineSeparator()); + if (currentLine.startsWith("dependencies {")) { + newBuildContent.append(" implementation '") + .append(dependency.getGroupId()) + .append(":") + .append(dependency.getArtifactId()); + if (dependency.getVersion() != null && !dependency.getVersion().isEmpty()) { + newBuildContent.append(":") + .append(dependency.getVersion()); + } + newBuildContent.append("'") + .append(System.lineSeparator()); + } + }); + getModel().setBuildContent(newBuildContent.toString()); } - protected void rootWrite(String fileName, String content) throws IOException { - rootWriter.write(fileName, content); + protected boolean containsBOM(String groupId, String artifactId) throws IOException { + String buildContent = getModel().getBuildContent(); + return buildContent.contains("enforcedPlatform(\"${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:") + || buildContent.contains("enforcedPlatform(\"" + groupId + ":" + artifactId + ":"); } - protected ProjectWriter getRootWriter() { - return rootWriter; + public String getProperty(String propertyName) throws IOException { + return getModel().getPropertiesContent().getProperty(propertyName); } - @Override - public void close() throws IOException { - if (getWriter() == getRootWriter()) { - write(SETTINGS_GRADLE_PATH, getModel().getSettingsContent()); - } else { - rootWrite(SETTINGS_GRADLE_PATH, getModel().getSettingsContent()); + private void readLineByLine(String content, Consumer lineConsumer) { + try (Scanner scanner = new Scanner(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8.name())) { + while (scanner.hasNextLine()) { + String currentLine = scanner.nextLine(); + lineConsumer.accept(currentLine); + } } - write(BUILD_GRADLE_PATH, getModel().getBuildContent()); - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - getModel().getPropertiesContent().store(out, "Gradle properties"); - if (getModel().isRootProperties()) { - rootWrite(GRADLE_PROPERTIES_PATH, out.toString(StandardCharsets.UTF_8.toString())); - } else { - write(GRADLE_PROPERTIES_PATH, out.toString(StandardCharsets.UTF_8.toString())); + } + + private Model getModel() throws IOException { + return modelReference.updateAndGet(model -> { + if (model == null) { + try { + return readModel(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } + return model; + }); + } + + private Model readModel() throws IOException { + String settingsContent = ""; + String buildContent = ""; + Properties propertiesContent = new Properties(); + if (hasProjectFile(SETTINGS_GRADLE_PATH)) { + final byte[] settings = readProjectFile(SETTINGS_GRADLE_PATH); + settingsContent = new String(settings, StandardCharsets.UTF_8); + } + if (hasProjectFile(BUILD_GRADLE_PATH)) { + final byte[] build = readProjectFile(BUILD_GRADLE_PATH); + buildContent = new String(build, StandardCharsets.UTF_8); } + if (hasProjectFile(GRADLE_PROPERTIES_PATH)) { + final byte[] properties = readProjectFile(GRADLE_PROPERTIES_PATH); + propertiesContent.load(new ByteArrayInputStream(properties)); + } + return new Model(settingsContent, buildContent, propertiesContent); + } + + protected boolean hasProjectFile(final String fileName) throws IOException { + final Path filePath = quarkusProject.getProjectFolderPath().resolve(fileName); + return Files.exists(filePath); } - @Override - public void completeFile(String groupId, String artifactId, String version, QuarkusPlatformDescriptor platform, - Properties props) throws IOException { - completeSettingsContent(artifactId); - completeBuildContent(groupId, version, platform, props); - completeProperties(platform); + protected byte[] readProjectFile(final String fileName) throws IOException { + final Path filePath = quarkusProject.getProjectFolderPath().resolve(fileName); + return Files.readAllBytes(filePath); } - private void completeBuildContent(String groupId, String version, QuarkusPlatformDescriptor platform, Properties props) + protected void writeToProjectFile(final String fileName, final byte[] content) throws IOException { + Files.write(quarkusProject.getProjectFolderPath().resolve(fileName), content); + } + + private void createBuildContent(String groupId, String version) throws IOException { final String buildContent = getModel().getBuildContent(); StringBuilder res = new StringBuilder(buildContent); @@ -81,7 +157,8 @@ private void completeBuildContent(String groupId, String version, QuarkusPlatfor res.append(System.lineSeparator()).append(" id 'io.quarkus'").append(System.lineSeparator()); res.append("}"); } - if (!containsBOM(platform.getBomGroupId(), platform.getBomArtifactId())) { + if (!containsBOM(quarkusProject.getPlatformDescriptor().getBomGroupId(), + quarkusProject.getPlatformDescriptor().getBomArtifactId())) { res.append(System.lineSeparator()); res.append("dependencies {").append(System.lineSeparator()); res.append( @@ -113,7 +190,7 @@ private void completeBuildContent(String groupId, String version, QuarkusPlatfor getModel().setBuildContent(res.toString()); } - private void completeSettingsContent(String artifactId) throws IOException { + private void createSettingsContent(String artifactId) throws IOException { final String settingsContent = getModel().getSettingsContent(); final StringBuilder res = new StringBuilder(); if (!settingsContent.contains("id 'io.quarkus'")) { @@ -137,7 +214,8 @@ private void completeSettingsContent(String artifactId) throws IOException { getModel().setSettingsContent(res.toString()); } - private void completeProperties(QuarkusPlatformDescriptor platform) throws IOException { + private void createProperties() throws IOException { + final QuarkusPlatformDescriptor platform = quarkusProject.getPlatformDescriptor(); Properties props = getModel().getPropertiesContent(); if (props.getProperty("quarkusPluginVersion") == null) { props.setProperty("quarkusPluginVersion", ToolsUtils.getPluginVersion(ToolsUtils.readQuarkusProperties(platform))); @@ -153,174 +231,15 @@ private void completeProperties(QuarkusPlatformDescriptor platform) throws IOExc } } - @Override - protected void addDependencyInBuildFile(Dependency dependency) throws IOException { - StringBuilder newBuildContent = new StringBuilder(); - readLineByLine(getModel().getBuildContent(), new AppendDependency(newBuildContent, dependency)); - getModel().setBuildContent(newBuildContent.toString()); - } - - @Override - public boolean removeDependency(QuarkusPlatformDescriptor platform, Extension extension) throws IOException { - PRINTER.ok(" Removing extension " + extension.managementKey()); - Dependency dep; - if (containsBOM(platform.getBomGroupId(), platform.getBomArtifactId()) - && isDefinedInBom(platform.getManagedDependencies(), extension)) { - dep = extension.toDependency(true); - } else { - dep = extension.toDependency(false); - if (getProperty(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_NAME) != null) { - dep.setVersion(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_VALUE); - } - } - removeDependencyFromBuildFile(dep); - return true; - } - - @Override - protected void removeDependencyFromBuildFile(Dependency dependency) throws IOException { - String depString = new StringBuilder("'").append(dependency.getGroupId()).append(":") - .append(dependency.getArtifactId()).toString(); - StringBuilder newBuildContent = new StringBuilder(); - Scanner scanner = new Scanner(getModel().getBuildContent()); - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (!line.contains(depString)) { - newBuildContent.append(line).append(System.lineSeparator()); - } - } - scanner.close(); - getModel().setBuildContent(newBuildContent.toString()); - } - - private void readLineByLine(String content, Consumer lineConsumer) { - try (Scanner scanner = new Scanner(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), - StandardCharsets.UTF_8.name())) { - while (scanner.hasNextLine()) { - String currentLine = scanner.nextLine(); - lineConsumer.accept(currentLine); - } - } - } - - private static class AppendDependency implements Consumer { - - private StringBuilder newContent; - private Dependency dependency; - - public AppendDependency(StringBuilder newContent, Dependency dependency) { - this.newContent = newContent; - this.dependency = dependency; - } - - @Override - public void accept(String currentLine) { - newContent.append(currentLine).append(System.lineSeparator()); - if (currentLine.startsWith("dependencies {")) { - newContent.append(" implementation '") - .append(dependency.getGroupId()) - .append(":") - .append(dependency.getArtifactId()); - if (dependency.getVersion() != null && !dependency.getVersion().isEmpty()) { - newContent.append(":") - .append(dependency.getVersion()); - } - newContent.append("'") - .append(System.lineSeparator()); - } - } - - } - - @Override - protected boolean hasDependency(Extension extension) throws IOException { - return getDependencies().stream() - .anyMatch(d -> extension.getGroupId().equals(d.getGroupId()) - && extension.getArtifactId().equals(d.getArtifactId())); - } - - @Override - protected boolean containsBOM(String groupId, String artifactId) throws IOException { - String buildContent = getModel().getBuildContent(); - return buildContent.contains("enforcedPlatform(\"${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:") - || buildContent.contains("enforcedPlatform(\"" + groupId + ":" + artifactId + ":"); - } - - @Override - public List getDependencies() throws IOException { - return Collections.emptyList(); - } - - @Override - public String getProperty(String propertyName) throws IOException { - return getModel().getPropertiesContent().getProperty(propertyName); - } - - @Override - protected List getManagedDependencies() { - // Gradle tooling API only provide resolved dependencies. - return Collections.emptyList(); - } - - private Model getModel() throws IOException { - if (model == null) { - initModel(); - } - return model; - } - - protected void initModel() throws IOException { - String settingsContent = ""; - String buildContent = ""; - Properties propertiesContent = new Properties(); - boolean isRootProperties = false; - if (getWriter() == getRootWriter()) { - if (getWriter().exists(SETTINGS_GRADLE_PATH)) { - final byte[] settings = getWriter().getContent(SETTINGS_GRADLE_PATH); - settingsContent = new String(settings, StandardCharsets.UTF_8); - } - } else { - if (getRootWriter().exists(SETTINGS_GRADLE_PATH)) { - final byte[] settings = getRootWriter().getContent(SETTINGS_GRADLE_PATH); - settingsContent = new String(settings, StandardCharsets.UTF_8); - } - } - if (getWriter().exists(BUILD_GRADLE_PATH)) { - final byte[] build = getWriter().getContent(BUILD_GRADLE_PATH); - buildContent = new String(build, StandardCharsets.UTF_8); - } - if (getWriter().exists(GRADLE_PROPERTIES_PATH)) { - final byte[] properties = getWriter().getContent(GRADLE_PROPERTIES_PATH); - propertiesContent.load(new ByteArrayInputStream(properties)); - } - if (!propertiesContent.containsKey("quarkusPluginVersion") && - !propertiesContent.containsKey("quarkusPlatformGroupId") && - !propertiesContent.containsKey("quarkusPlatformArtifactId") && - !propertiesContent.containsKey("quarkusPlatformVersion")) { - if (getRootWriter().exists(GRADLE_PROPERTIES_PATH)) { - final byte[] properties = getRootWriter().getContent(GRADLE_PROPERTIES_PATH); - propertiesContent.load(new ByteArrayInputStream(properties)); - } - isRootProperties = true; - } - this.model = new Model(settingsContent, buildContent, propertiesContent, isRootProperties); - } - - protected String getBuildContent() throws IOException { - return getModel().getBuildContent(); - } - private static class Model { private String settingsContent; private String buildContent; private Properties propertiesContent; - private boolean rootProperties; - public Model(String settingsContent, String buildContent, Properties propertiesContent, boolean rootProperties) { + public Model(String settingsContent, String buildContent, Properties propertiesContent) { this.settingsContent = settingsContent; this.buildContent = buildContent; this.propertiesContent = propertiesContent; - this.rootProperties = rootProperties; } public String getSettingsContent() { @@ -343,8 +262,5 @@ public void setBuildContent(String buildContent) { this.buildContent = buildContent; } - public boolean isRootProperties() { - return rootProperties; - } } } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java new file mode 100644 index 0000000000000..9a79a6017094e --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java @@ -0,0 +1,108 @@ +package io.quarkus.devtools.project.buildfile; + +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.maven.utilities.MojoUtils; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; + +public class MavenBuildFile extends BuildFile { + + private AtomicReference modelRef = new AtomicReference<>(); + + public MavenBuildFile(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + super(projectFolderPath, platformDescriptor); + } + + @Override + public void writeToDisk() throws IOException { + if (getModel() == null) { + return; + } + try (ByteArrayOutputStream pomOutputStream = new ByteArrayOutputStream()) { + MojoUtils.write(getModel(), pomOutputStream); + writeToProjectFile(BuildTool.MAVEN.getDependenciesFile(), pomOutputStream.toByteArray()); + } + } + + @Override + protected void addDependencyInBuildFile(Dependency dependency) throws IOException { + if (getModel() != null) { + getModel().addDependency(dependency); + } + } + + @Override + protected void removeDependencyFromBuildFile(Dependency dependency) throws IOException { + if (getModel() != null) { + getModel().getDependencies() + .removeIf(d -> d.getGroupId().equals(dependency.getGroupId()) + && d.getArtifactId().equals(dependency.getArtifactId())); + } + } + + @Override + public List getDependencies() throws IOException { + return getModel() == null ? Collections.emptyList() : getModel().getDependencies(); + } + + @Override + protected boolean containsBOM(String groupId, String artifactId) throws IOException { + if (getModel() == null || getModel().getDependencyManagement() == null) { + return false; + } + List dependencies = getModel().getDependencyManagement().getDependencies(); + return dependencies.stream() + // Find bom + .filter(dependency -> "import".equals(dependency.getScope())) + .filter(dependency -> "pom".equals(dependency.getType())) + // Does it matches the bom artifact name + .anyMatch(dependency -> dependency.getArtifactId() + .equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_VALUE) + && dependency.getGroupId().equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_GROUP_ID_VALUE)); + } + + @Override + protected void refreshData() { + this.modelRef.set(null); + } + + @Override + public String getProperty(String propertyName) throws IOException { + if (getModel() == null) { + return null; + } + return getModel().getProperties().getProperty(propertyName); + } + + @Override + public BuildTool getBuildTool() { + return BuildTool.MAVEN; + } + + private Model getModel() throws IOException { + return modelRef.updateAndGet(model -> { + if (model == null) { + try { + return initModel(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return model; + }); + } + + private Model initModel() throws IOException { + byte[] content = readProjectFile(BuildTool.MAVEN.getDependenciesFile()); + return MojoUtils.readPom(new ByteArrayInputStream(content)); + } +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGenerator.java similarity index 77% rename from independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGenerator.java index 518e86f0505c2..575ac806428bb 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGenerator.java @@ -1,7 +1,6 @@ -package io.quarkus.generators; +package io.quarkus.devtools.project.codegen; -import io.quarkus.cli.commands.QuarkusCommandInvocation; -import io.quarkus.devtools.writer.ProjectWriter; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import java.io.IOException; public interface ProjectGenerator { @@ -18,11 +17,12 @@ public interface ProjectGenerator { String BUILD_DIRECTORY = "build_dir"; String ADDITIONAL_GITIGNORE_ENTRIES = "additional_gitignore_entries"; String CLASS_NAME = "class_name"; + String EXTENSIONS = "extensions"; String IS_SPRING = "is_spring"; String RESOURCE_PATH = "path"; String JAVA_TARGET = "java_target"; String getName(); - void generate(ProjectWriter writer, QuarkusCommandInvocation invocation) throws IOException; + void generate(QuarkusCommandInvocation invocation) throws IOException; } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGeneratorRegistry.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGeneratorRegistry.java similarity index 96% rename from independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGeneratorRegistry.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGeneratorRegistry.java index 14e50d3f9b847..4090c600ef7b5 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGeneratorRegistry.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGeneratorRegistry.java @@ -1,4 +1,4 @@ -package io.quarkus.generators; +package io.quarkus.devtools.project.codegen; import java.util.Map; import java.util.NoSuchElementException; diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/generators/SourceType.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/SourceType.java similarity index 98% rename from independent-projects/tools/common/src/main/java/io/quarkus/generators/SourceType.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/SourceType.java index be92d2f2be6b2..3044f4992d372 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/generators/SourceType.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/SourceType.java @@ -1,4 +1,4 @@ -package io.quarkus.generators; +package io.quarkus.devtools.project.codegen; import io.quarkus.maven.utilities.MojoUtils; import java.util.Arrays; diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGenerator.java similarity index 92% rename from independent-projects/tools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGenerator.java index 9578e9be6f54f..06be3d4e82d46 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGenerator.java @@ -1,12 +1,13 @@ -package io.quarkus.generators.rest; +package io.quarkus.devtools.project.codegen.rest; import static java.lang.String.format; -import io.quarkus.cli.commands.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.generators.ProjectGenerator; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.ProjectGenerator; +import io.quarkus.devtools.project.codegen.SourceType; +import io.quarkus.devtools.project.codegen.writer.FileProjectWriter; +import io.quarkus.devtools.project.codegen.writer.ProjectWriter; import io.quarkus.maven.utilities.MojoUtils; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -25,7 +26,13 @@ public String getName() { } @Override - public void generate(final ProjectWriter writer, QuarkusCommandInvocation invocation) throws IOException { + public void generate(QuarkusCommandInvocation invocation) throws IOException { + final FileProjectWriter writer = new FileProjectWriter(invocation.getQuarkusProject().getProjectFolderPath().toFile()); + writer.init(); + generate(writer, invocation); + } + + void generate(ProjectWriter writer, QuarkusCommandInvocation invocation) throws IOException { final BasicRestProject project = new BasicRestProject(writer, invocation); project.initProject(); @@ -122,7 +129,7 @@ private boolean initBuildTool() throws IOException { } private BuildTool getBuildTool() { - return invocation.getBuildFile().getBuildTool(); + return invocation.getQuarkusProject().getBuildTool(); } private void generate(final String templateName, QuarkusCommandInvocation invocation, final String outputFilePath, @@ -141,7 +148,7 @@ private void generate(final String templateName, QuarkusCommandInvocation invoca // do some nasty replacements for Java target if we want to generate Java 11 projects if ("11".equals(invocation.getValue(JAVA_TARGET))) { - if (BuildTool.GRADLE.equals(invocation.getBuildFile().getBuildTool())) { + if (BuildTool.GRADLE.equals(invocation.getQuarkusProject().getBuildTool())) { template = template.replace("JavaVersion.VERSION_1_8", "JavaVersion.VERSION_11"); } else { template = template.replace("1.8", diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/FileProjectWriter.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/FileProjectWriter.java similarity index 73% rename from independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/FileProjectWriter.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/FileProjectWriter.java index 35e4a2d4e1e52..da284ca0f50c0 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/FileProjectWriter.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/FileProjectWriter.java @@ -1,7 +1,7 @@ /** * */ -package io.quarkus.devtools.writer; +package io.quarkus.devtools.project.codegen.writer; import java.io.File; import java.io.IOException; @@ -19,21 +19,21 @@ public FileProjectWriter(final File file) { } @Override - public boolean init() { - if (root.exists() && !root.isDirectory()) { - System.out.println("Project root needs to either not exist or be a directory"); - return false; - } else if (!root.exists()) { + public void init() throws IOException { + if (!root.exists()) { boolean mkdirStatus = root.mkdirs(); if (!mkdirStatus) { - System.out.println("Failed to create root directory"); - return false; + throw new IOException("Failed to create root directory"); } + return; + } + if (!root.isDirectory()) { + throw new IOException("Project root needs to either not exist or be a directory"); + } + final String[] files = root.list(); + if (files != null && files.length > 0) { + throw new IOException("You can't create a project when the folder is not empty."); } - - System.out.println("Creating a new project in " + root.getAbsolutePath()); - - return true; } @Override diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/ProjectWriter.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/ProjectWriter.java similarity index 84% rename from independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/ProjectWriter.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/ProjectWriter.java index 45e75607edac7..4b9ecdea8cf5d 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/ProjectWriter.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/ProjectWriter.java @@ -1,7 +1,7 @@ /** * */ -package io.quarkus.devtools.writer; +package io.quarkus.devtools.project.codegen.writer; import java.io.Closeable; import java.io.File; @@ -12,9 +12,7 @@ */ public interface ProjectWriter extends Closeable { - default boolean init() { - return true; - } + void init() throws IOException; void write(String path, String content) throws IOException; diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/Extensions.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/Extensions.java new file mode 100644 index 0000000000000..34af541b140b2 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/Extensions.java @@ -0,0 +1,40 @@ +package io.quarkus.devtools.project.extensions; + +import io.quarkus.dependencies.Extension; +import java.util.Objects; +import org.apache.maven.model.Dependency; + +public final class Extensions { + private Extensions() { + } + + public static String key(final Extension extension) { + return extension.managementKey(); + } + + public static String key(final Dependency dependency) { + return dependency.getGroupId() + ":" + dependency.getArtifactId(); + } + + public static boolean equalsIgnoringVersions(final Extension a, final Extension b) { + return Objects.equals(a.getGroupId(), b.getGroupId()) && Objects.equals(a.getArtifactId(), b.getArtifactId()); + } + + public static Extension parse(String gav) { + final Extension res = new Extension(); + String[] segments = gav.split(":"); + if (segments.length >= 2) { + res.setGroupId(segments[0].toLowerCase()); + res.setArtifactId(segments[1].toLowerCase()); + if (segments.length >= 3 && !segments[2].isEmpty()) { + res.setVersion(segments[2]); + } + if (segments.length >= 4) { + res.setClassifier(segments[3].toLowerCase()); + } + return res; + } else { + throw new IllegalArgumentException("Invalid GAV '" + gav + "'"); + } + } +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/ExtensionsManager.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/ExtensionsManager.java new file mode 100644 index 0000000000000..796823574e651 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/ExtensionsManager.java @@ -0,0 +1,71 @@ +package io.quarkus.devtools.project.extensions; + +import static io.quarkus.devtools.project.extensions.Extensions.equalsIgnoringVersions; + +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.project.BuildTool; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** + * This interface defines a high level way of managing (read/write) extensions in any QuarkusProject + */ +public interface ExtensionsManager { + + /** + * @return the {@link BuildTool} of this extension manager + */ + BuildTool getBuildTool(); + + /** + * Read the build file(s) to get the list of extensions installed in this Quarkus project. + * + * @return The list of extensions installed in the project build file(s). + * @throws IOException if a problem occurs while reading the project build file(s) + */ + List read() throws IOException; + + /** + * Read build file(s) to check if an extension is installed in this Quarkus project. + * + * @param e the extensions to check + * @return true if it's installed + * @throws IOException if a problem occurs while reading the project build file(s) + */ + default boolean hasExtension(Extension e) throws IOException { + return read().stream().anyMatch(i -> equalsIgnoringVersions(i, e)); + } + + /** + * Check that the Quarkus Platform bom is defined in the project. + * The Quarkus Platform Bom role is to define version to use for Quarkus extensions. + * + * @return true if it's defined, false else + * @throws IOException if a problem occurs while reading the project build file(s) + */ + boolean hasQuarkusPlatformBom() throws IOException; + + /** + * This is going to add all the specified extensions to the project build file(s). + * + * Extensions which are already installed are ignored. + * + * @param extensions the list of extensions to add + * @return the number of added extensions (excluding already installed) + * @throws IOException if a problem occurs while reading/writing the project build file(s) + */ + int add(List extensions) throws IOException; + + /** + * This is going to remove all the specified extensions from the project build file(s). + * + * This is ignoring the {@link Extension} version + * + * @param extensions the set of extensions to remove + * @return the number of removed extensions (excluding already not installed) + * @throws IOException if a problem occurs while reading/writing the project build file(s) + */ + int remove(Set extensions) throws IOException; + +} diff --git a/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.devtools.project.codegen.ProjectGenerator b/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.devtools.project.codegen.ProjectGenerator new file mode 100644 index 0000000000000..41ba05fc1a3e4 --- /dev/null +++ b/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.devtools.project.codegen.ProjectGenerator @@ -0,0 +1 @@ +io.quarkus.devtools.project.codegen.rest.BasicRestProjectGenerator diff --git a/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.generators.ProjectGenerator b/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.generators.ProjectGenerator deleted file mode 100644 index 481f77c230b6b..0000000000000 --- a/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.generators.ProjectGenerator +++ /dev/null @@ -1 +0,0 @@ -io.quarkus.generators.rest.BasicRestProjectGenerator diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java deleted file mode 100644 index 44cbd56da51ea..0000000000000 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java +++ /dev/null @@ -1,314 +0,0 @@ -package io.quarkus.cli.commands; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.contentOf; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.generators.ProjectGenerator; -import io.quarkus.maven.utilities.MojoUtils; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Comparator; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import org.apache.maven.model.Model; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -public class CreateProjectTest extends PlatformAwareTestBase { - @Test - public void create() throws Exception { - final File file = new File("target/basic-rest"); - delete(file); - createProject(file, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); - - final File gitignore = new File(file, ".gitignore"); - assertTrue(gitignore.exists()); - final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); - assertTrue(gitignoreContent.contains("\ntarget/\n")); - } - - @Test - public void createGradle() throws Exception { - final File file = new File("target/basic-rest-gradle"); - delete(file); - createProject(BuildTool.GRADLE, file, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); - - final File gitignore = new File(file, ".gitignore"); - assertTrue(gitignore.exists()); - final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); - Assertions.assertFalse(gitignoreContent.contains("\ntarget/\n")); - assertTrue(gitignoreContent.contains("\nbuild/")); - assertTrue(gitignoreContent.contains("\n.gradle/\n")); - - assertThat(new File(file, "README.md")).exists(); - assertThat(contentOf(new File(file, "README.md"), "UTF-8")).contains("./gradlew"); - } - - @Test - public void createGradleOnExisting() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - final File buildGradle = new File(testDir, "build.gradle"); - buildGradle.createNewFile(); - final File settingsGradle = new File(testDir, "settings.gradle"); - settingsGradle.createNewFile(); - - createProject(BuildTool.GRADLE, testDir, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); - - final File gitignore = new File(testDir, ".gitignore"); - assertTrue(gitignore.exists()); - final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); - Assertions.assertFalse(gitignoreContent.contains("\ntarget/\n")); - assertTrue(gitignoreContent.contains("\nbuild/")); - assertTrue(gitignoreContent.contains("\n.gradle/\n")); - - assertThat(contentOf(new File(testDir, "build.gradle"), "UTF-8")) - .contains("id 'io.quarkus'") - .contains("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"); - - final Properties props = new Properties(); - try (InputStream is = Files.newInputStream(testDir.toPath().resolve("gradle.properties"))) { - props.load(is); - } - Assertions.assertEquals(getBomGroupId(), props.get("quarkusPlatformGroupId")); - Assertions.assertEquals(getBomArtifactId(), props.get("quarkusPlatformArtifactId")); - Assertions.assertEquals(getBomVersion(), props.get("quarkusPlatformVersion")); - - assertThat(new File(testDir, "README.md")).exists(); - assertThat(contentOf(new File(testDir, "README.md"), "UTF-8")).contains("./gradlew"); - } - - @Test - public void createOnTopPomWithoutResource() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - Model model = new Model(); - model.setModelVersion("4.0.0"); - model.setGroupId("org.acme"); - model.setArtifactId("foobar"); - model.setVersion("10.1.2"); - final File pom = new File(testDir, "pom.xml"); - MojoUtils.write(model, pom); - createProject(testDir, "something.is", "wrong", "1.0.0-SNAPSHOT"); - - assertThat(contentOf(pom, "UTF-8")) - .contains(getPluginArtifactId(), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE, getPluginGroupId()); - - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - - assertThat(new File(testDir, "README.md")).exists(); - assertThat(contentOf(new File(testDir, "README.md"), "UTF-8")).contains("./mvnw"); - assertThat(new File(testDir, "src/main/resources/application.properties")).exists(); - assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).isFile(); - assertThat(new File(testDir, "src/main/java")).isDirectory().matches(f -> { - String[] list = f.list(); - return list != null && list.length == 0; - }); - assertThat(new File(testDir, "src/test/java")).isDirectory().matches(f -> { - String[] list = f.list(); - return list != null && list.length == 0; - }); - - assertThat(contentOf(new File(testDir, "pom.xml"), "UTF-8")) - .contains(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_VALUE); - - } - - @Test - public void createOnTopPomWithResource() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - Model model = new Model(); - model.setModelVersion("4.0.0"); - model.setGroupId("org.acme"); - model.setArtifactId("foobar"); - model.setVersion("10.1.2"); - final File pom = new File(testDir, "pom.xml"); - MojoUtils.write(model, pom); - - final QuarkusCommandOutcome result = new CreateProject(testDir.toPath(), getPlatformDescriptor()) - .groupId("something.is") - .artifactId("wrong") - .version("1.0.0-SNAPSHOT") - .className("org.foo.MyResource") - .execute(); - assertTrue(result.isSuccess()); - - assertThat(contentOf(pom, "UTF-8")) - .contains(getPluginArtifactId(), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE, getPluginGroupId()); - - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - - assertThat(new File(testDir, "README.md")).exists(); - assertThat(contentOf(new File(testDir, "README.md"), "UTF-8")).contains("./mvnw"); - assertThat(new File(testDir, "src/main/resources/application.properties")).exists(); - assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).exists(); - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/main/java/org/foo/MyResource.java")).isFile(); - assertThat(contentOf(new File(testDir, "src/main/java/org/foo/MyResource.java"), "UTF-8")) - .contains("@Path", "@GET", "@Produces"); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java/org/foo/MyResourceTest.java")).isFile(); - assertThat(new File(testDir, "src/test/java/org/foo/NativeMyResourceIT.java")).isFile(); - - assertThat(contentOf(new File(testDir, "pom.xml"))).contains(getBomArtifactId()); - - } - - @Test - public void createOnTopPomWithSpringController() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - Model model = new Model(); - model.setModelVersion("4.0.0"); - model.setGroupId("org.acme"); - model.setArtifactId("foobar"); - model.setVersion("10.1.2"); - final File pom = new File(testDir, "pom.xml"); - MojoUtils.write(model, pom); - - final QuarkusCommandOutcome result = new CreateProject(testDir.toPath(), getPlatformDescriptor()) - .groupId("something.is") - .artifactId("wrong") - .version("1.0.0-SNAPSHOT") - .className("org.foo.MyResource") - .setValue(ProjectGenerator.IS_SPRING, true) - .execute(); - assertTrue(result.isSuccess()); - - assertThat(contentOf(pom, "UTF-8")) - .contains(getPluginArtifactId(), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE, getPluginGroupId()); - - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - - assertThat(new File(testDir, "README.md")).exists(); - assertThat(contentOf(new File(testDir, "README.md"), "UTF-8")).contains("./mvnw"); - assertThat(new File(testDir, "src/main/resources/application.properties")).exists(); - assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).exists(); - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/main/java/org/foo/MyResource.java")).isFile(); - assertThat(contentOf(new File(testDir, "src/main/java/org/foo/MyResource.java"), "UTF-8")) - .contains("@RestController", "@RequestMapping", "@GetMapping"); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java/org/foo/MyResourceTest.java")).isFile(); - assertThat(new File(testDir, "src/test/java/org/foo/NativeMyResourceIT.java")).isFile(); - - assertThat(contentOf(new File(testDir, "pom.xml"))).contains(getBomArtifactId()); - - } - - @Test - public void createNewWithCustomizations() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - final QuarkusCommandOutcome result = new CreateProject(testDir.toPath(), getPlatformDescriptor()) - .groupId("org.acme") - .artifactId("acme") - .version("1.0.0-SNAPSHOT") - .className("org.acme.MyResource") - .execute(); - assertTrue(result.isSuccess()); - - assertThat(new File(testDir, "pom.xml")).isFile(); - assertThat(new File(testDir, "src/main/java/org/acme/MyResource.java")).isFile(); - assertThat(new File(testDir, "src/main/java/org/acme/MyApplication.java")).doesNotExist(); - - assertThat(contentOf(new File(testDir, "pom.xml"), "UTF-8")) - .contains(getPluginArtifactId(), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE, getPluginGroupId()); - - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - - assertThat(new File(testDir, "src/main/resources/application.properties")).exists(); - assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).exists(); - - assertThat(contentOf(new File(testDir, "pom.xml"), "UTF-8")) - .contains(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_VERSION_VALUE); - - } - - @Test - @Timeout(2) - @DisplayName("Should create correctly multiple times in parallel with multiple threads") - void createMultipleTimes() throws InterruptedException { - final ExecutorService executorService = Executors.newFixedThreadPool(4); - final CountDownLatch latch = new CountDownLatch(20); - - List> collect = IntStream.range(0, 20).boxed().map(i -> (Callable) () -> { - File tempDir = Files.createTempDirectory("test").toFile(); - FileProjectWriter write = new FileProjectWriter(tempDir); - final QuarkusCommandOutcome result = new CreateProject(tempDir.toPath(), getPlatformDescriptor()) - .groupId("org.acme") - .artifactId("acme") - .version("1.0.0-SNAPSHOT") - .className("org.acme.MyResource") - .execute(); - assertTrue(result.isSuccess()); - latch.countDown(); - write.close(); - tempDir.delete(); - return null; - }).collect(Collectors.toList()); - executorService.invokeAll(collect); - latch.await(); - } - - public static void delete(final File file) throws IOException { - - if (file.exists()) { - try (Stream stream = Files.walk(file.toPath())) { - stream.sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - } - } - - Assertions.assertFalse( - Files.exists(file.toPath()), "Directory still exists"); - } - - private void createProject(final File file, String groupId, String artifactId, String version) - throws QuarkusCommandException { - createProject(BuildTool.MAVEN, file, groupId, artifactId, version); - } - - private void createProject(BuildTool buildTool, File file, String groupId, String artifactId, String version) - throws QuarkusCommandException { - final QuarkusCommandOutcome result = new CreateProject(file.toPath(), getPlatformDescriptor()) - .buildTool(buildTool) - .groupId(groupId) - .artifactId(artifactId) - .version(version) - .execute(); - assertTrue(result.isSuccess()); - } -} diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractAddExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractAddExtensionsTest.java similarity index 93% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractAddExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractAddExtensionsTest.java index 2e38ffa5b54ca..a8aad92c648cd 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractAddExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractAddExtensionsTest.java @@ -1,7 +1,9 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import static java.util.Arrays.asList; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -135,25 +137,25 @@ void testPartialMatchConflict() throws Exception { } @Test - void addExistingAndMissingExtensions() throws Exception { + void addExistingAndMissingExtensionsWillFailForBoth() throws Exception { createProject(); final QuarkusCommandOutcome result = addExtensions(asList("missing", "agroal")); final T project = readProject(); doesNotHaveDependency(project, "quarkus-missing"); - hasDependency(project, "quarkus-agroal"); + doesNotHaveDependency(project, "quarkus-agroal"); Assertions.assertFalse(result.isSuccess()); - Assertions.assertTrue(result.valueIs(AddExtensions.OUTCOME_UPDATED, true)); + Assertions.assertFalse(result.valueIs(AddExtensions.OUTCOME_UPDATED, true)); } @Test - void addDuplicatedExtension() throws Exception { + void doNotAddExtensionWhenMultipleMatch() throws Exception { createProject(); - addExtensions(asList("agroal", "jdbc", "non-exist-ent")); + addExtensions(asList("agroal", "jdbc")); final T project = readProject(); - hasDependency(project, "quarkus-agroal"); + doesNotHaveDependency(project, "quarkus-agroal"); doesNotHaveDependency(project, "quarkus-jdbc-postgresql"); doesNotHaveDependency(project, "quarkus-jdbc-h2"); } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractRemoveExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractRemoveExtensionsTest.java similarity index 93% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractRemoveExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractRemoveExtensionsTest.java index 1acfd547df718..9be4524fa0508 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractRemoveExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractRemoveExtensionsTest.java @@ -1,7 +1,9 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import static java.util.Arrays.asList; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -64,12 +66,12 @@ void testRegexpMatches() throws Exception { final QuarkusCommandOutcome result = addExtensions(asList("Sm??lRye**")); Assertions.assertTrue(result.isSuccess()); - Assertions.assertTrue(result.valueIs(AddExtensions.OUTCOME_UPDATED, true)); + Assertions.assertTrue(result.getBooleanValue(AddExtensions.OUTCOME_UPDATED)); final QuarkusCommandOutcome result2 = removeExtensions(asList("Sm??lRye**")); Assertions.assertTrue(result2.isSuccess()); - Assertions.assertTrue(result2.valueIs(RemoveExtensions.OUTCOME_UPDATED, true)); + Assertions.assertTrue(result2.getBooleanValue(RemoveExtensions.OUTCOME_UPDATED)); final T project = readProject(); hasNoDependency(project, "quarkus-smallrye-reactive-messaging"); diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddGradleExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddGradleExtensionsTest.java similarity index 53% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddGradleExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddGradleExtensionsTest.java index f080c5fffff4a..553fcb373760b 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddGradleExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddGradleExtensionsTest.java @@ -1,20 +1,23 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import com.google.common.collect.ImmutableList; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.buildfile.AbstractGradleBuildFile; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashSet; import java.util.List; -import org.junit.jupiter.api.Disabled; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.maven.model.Dependency; class AddGradleExtensionsTest extends AbstractAddExtensionsTest> { - @Disabled - void addExtensionTwiceInTwoBatches() throws IOException { - //FIXME This is currently not working - } - @Override protected List createProject() throws IOException, QuarkusCommandException { CreateProjectTest.delete(getProjectPath().toFile()); @@ -53,6 +56,30 @@ private static String getBuildFileDependencyString(final String groupId, final S } private QuarkusProject getQuarkusProject() { - return QuarkusProject.of(getProjectPath(), getPlatformDescriptor(), BuildTool.GRADLE); + final Path projectPath = getProjectPath(); + final QuarkusPlatformDescriptor platformDescriptor = getPlatformDescriptor(); + return QuarkusProject.of(projectPath, platformDescriptor, new TestingGradleBuildFile(projectPath, platformDescriptor)); + } + + static class TestingGradleBuildFile extends AbstractGradleBuildFile { + + public TestingGradleBuildFile(Path projectFolderPath, QuarkusPlatformDescriptor platformDescriptor) { + super(projectFolderPath, platformDescriptor); + } + + @Override + protected List getDependencies() throws IOException { + final Matcher matcher = Pattern.compile("\\s*implementation\\s+'([^\\v:]+):([^\\v:]+)(:[^:\\v]+)?'") + .matcher(getBuildContent()); + final ImmutableList.Builder builder = ImmutableList.builder(); + while (matcher.find()) { + final Dependency dep = new Dependency(); + dep.setGroupId(matcher.group(1)); + dep.setArtifactId(matcher.group(2)); + dep.setVersion(matcher.group(3)); + builder.add(dep); + } + return builder.build(); + } } } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddMavenExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddMavenExtensionsTest.java similarity index 87% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddMavenExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddMavenExtensionsTest.java index 57ee06f705246..904777bf8917e 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddMavenExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddMavenExtensionsTest.java @@ -1,5 +1,7 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.maven.utilities.MojoUtils; @@ -46,6 +48,6 @@ protected long countDependencyOccurrences(final Model project, final String grou } private QuarkusProject getQuarkusProject() { - return QuarkusProject.of(getProjectPath(), getPlatformDescriptor(), BuildTool.MAVEN); + return QuarkusProject.maven(getProjectPath(), getPlatformDescriptor()); } } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/CreateProjectTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/CreateProjectTest.java new file mode 100644 index 0000000000000..8bc7c11aea806 --- /dev/null +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/CreateProjectTest.java @@ -0,0 +1,142 @@ +package io.quarkus.devtools.commands; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.contentOf; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.codegen.writer.FileProjectWriter; +import io.quarkus.maven.utilities.MojoUtils; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.apache.maven.model.Model; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +public class CreateProjectTest extends PlatformAwareTestBase { + @Test + public void create() throws Exception { + final File file = new File("target/basic-rest"); + delete(file); + createProject(file, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); + + final File gitignore = new File(file, ".gitignore"); + assertTrue(gitignore.exists()); + final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); + assertTrue(gitignoreContent.contains("\ntarget/\n")); + } + + @Test + public void createGradle() throws Exception { + final File file = new File("target/basic-rest-gradle"); + delete(file); + createProject(BuildTool.GRADLE, file, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); + + final File gitignore = new File(file, ".gitignore"); + assertTrue(gitignore.exists()); + final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); + Assertions.assertFalse(gitignoreContent.contains("\ntarget/\n")); + assertTrue(gitignoreContent.contains("\nbuild/")); + assertTrue(gitignoreContent.contains("\n.gradle/\n")); + + assertThat(new File(file, "README.md")).exists(); + assertThat(contentOf(new File(file, "README.md"), "UTF-8")).contains("./gradlew"); + } + + @Test + public void createOnTopOfExisting() throws Exception { + final File testDir = new File("target/existing"); + delete(testDir); + testDir.mkdirs(); + + Model model = new Model(); + model.setModelVersion("4.0.0"); + model.setGroupId("org.acme"); + model.setArtifactId("foobar"); + model.setVersion("10.1.2"); + final File pom = new File(testDir, "pom.xml"); + MojoUtils.write(model, pom); + assertThatExceptionOfType(QuarkusCommandException.class).isThrownBy(() -> { + new CreateProject(testDir.toPath(), getPlatformDescriptor()) + .groupId("something.is") + .artifactId("wrong") + .version("1.0.0-SNAPSHOT") + .className("org.foo.MyResource") + .execute(); + }).withRootCauseInstanceOf(IOException.class); + } + + @Test + @Timeout(2) + @DisplayName("Should create correctly multiple times in parallel with multiple threads") + void createMultipleTimes() throws InterruptedException { + final ExecutorService executorService = Executors.newFixedThreadPool(4); + final CountDownLatch latch = new CountDownLatch(20); + + List> collect = IntStream.range(0, 20).boxed().map(i -> (Callable) () -> { + File tempDir = Files.createTempDirectory("test").toFile(); + FileProjectWriter write = new FileProjectWriter(tempDir); + final QuarkusCommandOutcome result = new CreateProject(tempDir.toPath(), getPlatformDescriptor()) + .groupId("org.acme") + .artifactId("acme") + .version("1.0.0-SNAPSHOT") + .className("org.acme.MyResource") + .execute(); + assertTrue(result.isSuccess()); + latch.countDown(); + write.close(); + tempDir.delete(); + return null; + }).collect(Collectors.toList()); + executorService.invokeAll(collect); + latch.await(); + } + + public static void delete(final File file) throws IOException { + + if (file.exists()) { + try (Stream stream = Files.walk(file.toPath())) { + stream.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + } + + Assertions.assertFalse( + Files.exists(file.toPath()), "Directory still exists"); + } + + private void createProject(final File file, String groupId, String artifactId, String version) + throws QuarkusCommandException { + createProject(BuildTool.MAVEN, file, groupId, artifactId, version); + } + + private void createProject(BuildTool buildTool, File file, String groupId, String artifactId, String version) + throws QuarkusCommandException { + final QuarkusCommandOutcome result = new CreateProject(file.toPath(), getPlatformDescriptor()) + .buildTool(buildTool) + .groupId(groupId) + .artifactId(artifactId) + .version(version) + .execute(); + assertTrue(result.isSuccess()); + } +} diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/ListExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/ListExtensionsTest.java similarity index 86% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/ListExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/ListExtensionsTest.java index cd039e61c49a6..a0b797e8ac976 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/ListExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/ListExtensionsTest.java @@ -1,11 +1,13 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import static io.quarkus.maven.utilities.MojoUtils.readPom; import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; +import static java.util.stream.Collectors.toMap; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertTrue; -import io.quarkus.devtools.project.BuildTool; +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.maven.utilities.MojoUtils; import io.quarkus.maven.utilities.QuarkusDependencyPredicate; @@ -13,11 +15,13 @@ import java.io.File; import java.io.IOException; import java.io.PrintStream; +import java.io.UncheckedIOException; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.HashSet; import java.util.Map; -import org.apache.maven.model.Dependency; +import java.util.function.Function; import org.apache.maven.model.Model; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -31,7 +35,7 @@ public void listWithBom() throws Exception { final ListExtensions listExtensions = new ListExtensions(project); - final Map installed = listExtensions.findInstalled(); + final Map installed = readByManagementKey(project); Assertions.assertNotNull(installed.get(getPluginGroupId() + ":quarkus-agroal")); } @@ -49,7 +53,7 @@ public void listWithBomExtensionWithSpaces() throws Exception { final ListExtensions listExtensions = new ListExtensions(quarkusProject); - final Map installed = listExtensions.findInstalled(); + final Map installed = readByManagementKey(quarkusProject); Assertions.assertNotNull(installed.get(getPluginGroupId() + ":quarkus-resteasy")); Assertions.assertNotNull(installed.get(getPluginGroupId() + ":quarkus-hibernate-validator")); @@ -59,10 +63,8 @@ public void listWithBomExtensionWithSpaces() throws Exception { public void listWithoutBom() throws Exception { final File pom = new File("target/list-extensions-test", "pom.xml"); final QuarkusProject quarkusProject = createNewProject(pom); - Model model = readPom(pom); - model.setDependencyManagement(null); model.getDependencies().stream() .filter(new QuarkusDependencyPredicate()) .forEach(d -> d.setVersion("0.0.1")); @@ -162,9 +164,9 @@ public void searchRest() throws Exception { @Test void testListExtensionsWithoutAPomFile() throws IOException { final Path tempDirectory = Files.createTempDirectory("proj"); - ListExtensions listExtensions = new ListExtensions( - QuarkusProject.of(tempDirectory, getPlatformDescriptor(), BuildTool.MAVEN)); - assertThat(listExtensions.findInstalled()).isEmpty(); + final QuarkusProject project = QuarkusProject.maven(tempDirectory, getPlatformDescriptor()); + assertThatExceptionOfType(UncheckedIOException.class).isThrownBy(() -> readByManagementKey(project)) + .withRootCauseInstanceOf(NoSuchFileException.class); } private void addExtensions(QuarkusProject quarkusProject, String... extensions) throws Exception { @@ -181,6 +183,11 @@ private QuarkusProject createNewProject(final File pom) throws IOException, Quar .artifactId("add-extension-test") .version("0.0.1-SNAPSHOT") .execute(); - return QuarkusProject.of(projectFolderPath, getPlatformDescriptor(), BuildTool.MAVEN); + return QuarkusProject.maven(projectFolderPath, getPlatformDescriptor()); + } + + private static Map readByManagementKey(QuarkusProject project) throws IOException { + return project.getExtensionsManager().read().stream() + .collect(toMap(Extension::managementKey, Function.identity())); } } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/PlatformAwareTestBase.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/PlatformAwareTestBase.java similarity index 98% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/PlatformAwareTestBase.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/PlatformAwareTestBase.java index d9a9541f65528..33b1278cd484d 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/PlatformAwareTestBase.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/PlatformAwareTestBase.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.tools.config.QuarkusPlatformConfig; diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveGradleExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveGradleExtensionsTest.java similarity index 80% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveGradleExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveGradleExtensionsTest.java index 2348a72ba3361..963af786163e6 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveGradleExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveGradleExtensionsTest.java @@ -1,9 +1,13 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashSet; import java.util.List; import org.junit.jupiter.api.Disabled; @@ -56,7 +60,10 @@ protected long countDependencyOccurrences(final List buildFile, final St } private QuarkusProject getQuarkusProject() { - return QuarkusProject.of(getProjectPath(), getPlatformDescriptor(), BuildTool.GRADLE); + final Path projectPath = getProjectPath(); + final QuarkusPlatformDescriptor platformDescriptor = getPlatformDescriptor(); + return QuarkusProject.of(projectPath, platformDescriptor, + new AddGradleExtensionsTest.TestingGradleBuildFile(projectPath, platformDescriptor)); } private static String getBuildFileDependencyString(final String groupId, final String artifactId, final String version) { diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveMavenExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveMavenExtensionsTest.java similarity index 88% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveMavenExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveMavenExtensionsTest.java index 57971f118a4ed..febaba8565c34 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveMavenExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveMavenExtensionsTest.java @@ -1,6 +1,7 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; -import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.maven.utilities.MojoUtils; import java.io.File; @@ -52,6 +53,6 @@ protected long countDependencyOccurrences(final Model project, final String grou } private QuarkusProject getQuarkusProject() { - return QuarkusProject.of(getProjectPath(), getPlatformDescriptor(), BuildTool.MAVEN); + return QuarkusProject.maven(getProjectPath(), getPlatformDescriptor()); } } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddExtensionsSelectTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlersTest.java similarity index 86% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddExtensionsSelectTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlersTest.java index 633d796491b68..e87d46a1e4ef2 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddExtensionsSelectTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlersTest.java @@ -1,14 +1,16 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.handlers; +import static io.quarkus.devtools.commands.handlers.QuarkusCommandHandlers.select; import static java.util.Arrays.asList; import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.data.SelectionResult; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -class AddExtensionsSelectTest { +class QuarkusCommandHandlersTest { @Test void testMultiMatchByLabels() { @@ -24,11 +26,11 @@ void testMultiMatchByLabels() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, true); + SelectionResult matches = select(extensions, "foo", true); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(2, matches.getExtensions().size()); - matches = AddExtensionsCommandHandler.select("foo", extensions, false); + matches = select(extensions, "foo", false); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(0, matches.getExtensions().size()); } @@ -44,7 +46,7 @@ void testThatSingleLabelMatchIsNotAMatch() { List extensions = asList(e1, e2); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, true); + SelectionResult matches = select(extensions, "foo", true); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(1, matches.getExtensions().size()); } @@ -63,11 +65,11 @@ void testMultiMatchByArtifactIdsAndNames() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, false); + SelectionResult matches = select(extensions, "foo", false); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(2, matches.getExtensions().size()); - matches = AddExtensionsCommandHandler.select("foo", extensions, true); + matches = select(extensions, "foo", true); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(3, matches.getExtensions().size()); @@ -88,7 +90,7 @@ void testShortNameSelection() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, false); + SelectionResult matches = select(extensions, "foo", false); Assertions.assertTrue(matches.matches()); Assertions.assertEquals(1, matches.getExtensions().size()); Assertions.assertTrue(matches.iterator().hasNext()); @@ -111,7 +113,7 @@ void testArtifactIdSelectionWithQuarkusPrefix() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, false); + SelectionResult matches = select(extensions, "foo", false); Assertions.assertEquals(1, matches.getExtensions().size()); Assertions.assertTrue(matches.iterator().hasNext()); Assertions.assertTrue(matches.iterator().next().getArtifactId().equalsIgnoreCase("quarkus-foo")); @@ -133,12 +135,11 @@ void testListedVsUnlisted() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("quarkus-foo", extensions, true); + SelectionResult matches = select(extensions, "quarkus-foo", true); Assertions.assertEquals(2, matches.getExtensions().size()); - matches = AddExtensionsCommandHandler.select("quarkus-foo-unlisted", extensions, true); + matches = select(extensions, "quarkus-foo-unlisted", true); Assertions.assertEquals(1, matches.getExtensions().size()); } - } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGeneratorTest.java similarity index 84% rename from independent-projects/tools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGeneratorTest.java index 1ca8d1441dc64..2e3255c76aff8 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGeneratorTest.java @@ -1,13 +1,13 @@ -package io.quarkus.generators.rest; - -import static io.quarkus.generators.ProjectGenerator.BOM_VERSION; -import static io.quarkus.generators.ProjectGenerator.CLASS_NAME; -import static io.quarkus.generators.ProjectGenerator.IS_SPRING; -import static io.quarkus.generators.ProjectGenerator.PACKAGE_NAME; -import static io.quarkus.generators.ProjectGenerator.PROJECT_ARTIFACT_ID; -import static io.quarkus.generators.ProjectGenerator.PROJECT_GROUP_ID; -import static io.quarkus.generators.ProjectGenerator.PROJECT_VERSION; -import static io.quarkus.generators.ProjectGenerator.SOURCE_TYPE; +package io.quarkus.devtools.project.codegen.rest; + +import static io.quarkus.devtools.project.codegen.ProjectGenerator.BOM_VERSION; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.CLASS_NAME; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.IS_SPRING; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PACKAGE_NAME; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_ARTIFACT_ID; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_GROUP_ID; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_VERSION; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.SOURCE_TYPE; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; @@ -19,13 +19,11 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import io.quarkus.bootstrap.util.IoUtils; -import io.quarkus.cli.commands.PlatformAwareTestBase; -import io.quarkus.cli.commands.QuarkusCommandInvocation; -import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.commands.PlatformAwareTestBase; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.SourceType; +import io.quarkus.devtools.project.codegen.writer.ProjectWriter; import io.quarkus.maven.utilities.MojoUtils; import java.nio.file.Files; import java.nio.file.Path; @@ -63,9 +61,8 @@ void generateMultipleTimes() throws InterruptedException { final BasicRestProjectGenerator basicRestProjectGenerator = new BasicRestProjectGenerator(); List> collect = IntStream.range(0, 20).boxed().map(i -> (Callable) () -> { final Path path = Files.createTempDirectory("test"); - try (FileProjectWriter writer = new FileProjectWriter(path.toFile())) { - basicRestProjectGenerator.generate(writer, - createQuarkusCommandInvocation(path)); + try { + basicRestProjectGenerator.generate(createQuarkusCommandInvocation(path)); } finally { IoUtils.recursiveDelete(path); } @@ -138,7 +135,7 @@ void generateFilesWithSpringControllerResource() throws Exception { } private QuarkusCommandInvocation createQuarkusCommandInvocation(Path projectPath) { - return new QuarkusCommandInvocation(QuarkusProject.of(projectPath, getPlatformDescriptor(), BuildTool.MAVEN), + return new QuarkusCommandInvocation(QuarkusProject.maven(projectPath, getPlatformDescriptor()), BASIC_PROJECT_CONTEXT); } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/compress/QuarkusProjectCompressTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/compress/QuarkusProjectCompressTest.java index 33b014420a00c..4b522f93f335f 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/compress/QuarkusProjectCompressTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/compress/QuarkusProjectCompressTest.java @@ -6,11 +6,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import io.quarkus.cli.commands.CreateProject; -import io.quarkus.cli.commands.CreateProjectTest; -import io.quarkus.cli.commands.PlatformAwareTestBase; -import io.quarkus.cli.commands.QuarkusCommandException; -import io.quarkus.cli.commands.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.CreateProject; +import io.quarkus.devtools.commands.CreateProjectTest; +import io.quarkus.devtools.commands.PlatformAwareTestBase; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import java.io.File; import java.io.FileInputStream; diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/test/platform/descriptor/CombinedQuarkusPlatformDescriptorTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/test/platform/descriptor/CombinedQuarkusPlatformDescriptorTest.java index a0c449225fd1e..f9da268b93ccc 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/test/platform/descriptor/CombinedQuarkusPlatformDescriptorTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/test/platform/descriptor/CombinedQuarkusPlatformDescriptorTest.java @@ -4,9 +4,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import io.quarkus.cli.commands.PlatformAwareTestBase; import io.quarkus.dependencies.Category; import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.PlatformAwareTestBase; import io.quarkus.platform.descriptor.CombinedQuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.util.List; diff --git a/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/platform/tools/ConsoleMessageFormat.java b/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/platform/tools/ConsoleMessageFormat.java new file mode 100644 index 0000000000000..d4dc80af5c314 --- /dev/null +++ b/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/platform/tools/ConsoleMessageFormat.java @@ -0,0 +1,23 @@ +package io.quarkus.platform.tools; + +public final class ConsoleMessageFormat { + + public static final String OK = "\u2705"; + public static final String NOK = "\u274c"; + public static final String NOOP = "\uD83D\uDC4D"; + + private ConsoleMessageFormat() { + } + + public static String nok(String content) { + return NOK + content; + } + + public static String ok(String content) { + return OK + content; + } + + public static String noop(String content) { + return NOOP + content; + } +} diff --git a/integration-tests/maven/src/test/resources/projects/simple-pom-it/pom.xml b/integration-tests/maven/src/test/resources/projects/simple-pom-it/pom.xml index 1dc185b7a8940..5147ef2652777 100644 --- a/integration-tests/maven/src/test/resources/projects/simple-pom-it/pom.xml +++ b/integration-tests/maven/src/test/resources/projects/simple-pom-it/pom.xml @@ -5,4 +5,21 @@ io.acme.it acme-empty-pom 0.0.1.BUILD-SNAPSHOT + + io.quarkus + quarkus-bom + @project.version@ + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + +