diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java index 0af143e11..5184eeb85 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/LanguageServiceAccessor.java @@ -13,7 +13,6 @@ import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -41,7 +40,7 @@ public class LanguageServiceAccessor { private final Project project; public static LanguageServiceAccessor getInstance(Project project) { - return ServiceManager.getService(project, LanguageServiceAccessor.class); + return project.getService(LanguageServiceAccessor.class); } private LanguageServiceAccessor(Project project) { diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleManager.java b/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleManager.java index 32b532747..cde5aaa08 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleManager.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4ij/lifecycle/LanguageServerLifecycleManager.java @@ -13,7 +13,6 @@ *******************************************************************************/ package com.redhat.devtools.intellij.lsp4ij.lifecycle; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.redhat.devtools.intellij.lsp4ij.LanguageServerWrapper; import org.eclipse.lsp4j.jsonrpc.MessageConsumer; @@ -30,7 +29,7 @@ public class LanguageServerLifecycleManager { public static LanguageServerLifecycleManager getInstance(Project project) { - return ServiceManager.getService(project, LanguageServerLifecycleManager.class); + return project.getService(LanguageServerLifecycleManager.class); } private static final Logger LOGGER = LoggerFactory.getLogger(LanguageServerLifecycleManager.class);//$NON-NLS-1$ diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedManager.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedManager.java index e840d5a07..ad3b3b346 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedManager.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/classpath/ClasspathResourceChangedManager.java @@ -16,7 +16,6 @@ import com.intellij.ProjectTopics; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; @@ -27,8 +26,6 @@ import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.messages.Topic; -import java.util.ArrayList; -import java.util.List; import java.util.Set; /** @@ -57,7 +54,7 @@ public class ClasspathResourceChangedManager implements Disposable { private final ClasspathResourceChangedListener listener; public static ClasspathResourceChangedManager getInstance(Project project) { - return ServiceManager.getService(project, ClasspathResourceChangedManager.class); + return project.getService(ClasspathResourceChangedManager.class); } public interface Listener { diff --git a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProjectManager.java b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProjectManager.java index 86566d2f6..2acef6115 100644 --- a/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProjectManager.java +++ b/src/main/java/com/redhat/devtools/intellij/lsp4mp4ij/psi/core/project/PsiMicroProfileProjectManager.java @@ -11,7 +11,6 @@ import com.intellij.ProjectTopics; import com.intellij.openapi.Disposable; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.ModuleListener; @@ -38,7 +37,7 @@ public final class PsiMicroProfileProjectManager implements Disposable { private static final String JAVA_FILE_EXTENSION = "java"; public static PsiMicroProfileProjectManager getInstance(Project project) { - return ServiceManager.getService(project, PsiMicroProfileProjectManager.class); + return project.getService(PsiMicroProfileProjectManager.class); } private final MessageBusConnection connection; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusConstants.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusConstants.java index 168be827c..5ce514de5 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusConstants.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusConstants.java @@ -13,10 +13,10 @@ import com.intellij.openapi.util.Key; import com.redhat.devtools.intellij.quarkus.projectWizard.QuarkusExtensionsModel; import com.redhat.devtools.intellij.quarkus.projectWizard.QuarkusModel; -import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate; +import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate; public class QuarkusConstants { - public final static Key WIZARD_TOOL_KEY = Key.create(QuarkusConstants.class.getPackage().getName() + ".tool"); + public final static Key WIZARD_TOOL_KEY = Key.create(QuarkusConstants.class.getPackage().getName() + ".tool"); public final static Key WIZARD_EXAMPLE_KEY = Key.create(QuarkusConstants.class.getPackage().getName() + ".example"); public final static Key WIZARD_GROUPID_KEY = Key.create(QuarkusConstants.class.getPackage().getName() + ".groupId"); public final static Key WIZARD_ARTIFACTID_KEY = Key.create(QuarkusConstants.class.getPackage().getName() + ".artifactId"); diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusDeploymentSupport.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusDeploymentSupport.java index 6da5a944c..3f0d26018 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusDeploymentSupport.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusDeploymentSupport.java @@ -13,7 +13,6 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.WriteAction; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProcessCanceledException; @@ -33,8 +32,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.messages.MessageBusConnection; import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager; +import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate; import com.redhat.devtools.intellij.quarkus.search.QuarkusModuleComponent; -import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -60,7 +59,7 @@ public class QuarkusDeploymentSupport implements ClasspathResourceChangedManager private final Project project; public static QuarkusDeploymentSupport getInstance(Project project) { - return ServiceManager.getService(project, QuarkusDeploymentSupport.class); + return project.getService(QuarkusDeploymentSupport.class); } public QuarkusDeploymentSupport(Project project) { @@ -94,7 +93,7 @@ public static void updateClasspathWithQuarkusDeployment(Module module, ProgressI return; LOGGER.info("Ensuring library to " + module.getName()); long start = System.currentTimeMillis(); - ToolDelegate toolDelegate = ToolDelegate.getDelegate(module); + BuildToolDelegate toolDelegate = BuildToolDelegate.getDelegate(module); if (toolDelegate != null) { LOGGER.info("Tool delegate found for " + module.getName()); if (isQuarkusModule(module)) { @@ -190,11 +189,11 @@ private static void addLibrary(ModifiableRootModel model, List[] qu LibraryEx.ModifiableModelEx libraryModel = library.getModifiableModel(); // Add quarkus deployment dependencies binaries - for (VirtualFile rootFile : quarkusDeploymentDependencies[ToolDelegate.BINARY]) { + for (VirtualFile rootFile : quarkusDeploymentDependencies[BuildToolDelegate.BINARY]) { libraryModel.addRoot(rootFile, OrderRootType.CLASSES); } // Add quarkus deployment dependencies sources - for (VirtualFile rootFile : quarkusDeploymentDependencies[ToolDelegate.SOURCES]) { + for (VirtualFile rootFile : quarkusDeploymentDependencies[BuildToolDelegate.SOURCES]) { libraryModel.addRoot(rootFile, OrderRootType.SOURCES); } @@ -235,7 +234,7 @@ private static boolean isQuarkusDeploymentLibrary(@NotNull LibraryOrderEntry lib private static boolean isQuarkusExtensionWithDeploymentArtifact(@Nullable Library library) { if (library != null) { for (VirtualFile vFile : library.getFiles(OrderRootType.CLASSES)) { - if (vFile.isDirectory() && ToolDelegate.hasExtensionProperties(VfsUtilCore.virtualToIoFile(vFile))) { + if (vFile.isDirectory() && BuildToolDelegate.hasExtensionProperties(VfsUtilCore.virtualToIoFile(vFile))) { return true; } } diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java index ebf4d4ca4..2700dfe65 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusModuleUtil.java @@ -11,42 +11,25 @@ package com.redhat.devtools.intellij.quarkus; import com.intellij.facet.FacetManager; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtilCore; -import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.DependencyScope; import com.intellij.openapi.roots.LibraryOrderEntry; -import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.OrderEnumerator; -import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.RootPolicy; -import com.intellij.openapi.roots.impl.OrderEntryUtil; -import com.intellij.openapi.roots.impl.libraries.LibraryEx; -import com.intellij.openapi.roots.libraries.Library; -import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.core.ls.PsiUtilsLSImpl; import com.redhat.devtools.intellij.quarkus.facet.QuarkusFacet; -import com.redhat.devtools.intellij.quarkus.search.QuarkusModuleComponent; -import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; -import java.text.MessageFormat; import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusPostStartupActivity.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusPostStartupActivity.java index e4719bace..1c2d20794 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusPostStartupActivity.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusPostStartupActivity.java @@ -15,11 +15,13 @@ import com.intellij.openapi.startup.StartupActivity; import com.redhat.devtools.intellij.lsp4mp4ij.classpath.ClasspathResourceChangedManager; import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.project.PsiMicroProfileProjectManager; +import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfigurationManager; import org.jetbrains.annotations.NotNull; public class QuarkusPostStartupActivity implements StartupActivity, DumbAware { @Override public void runActivity(@NotNull Project project) { + QuarkusRunConfigurationManager.getInstance(project); ClasspathResourceChangedManager.getInstance(project); // Force the instantiation of the manager to be sure that classpath listener // are registered before QuarkusLanguageClient classpath listener diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java index 32a8649a5..5090af413 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/QuarkusProjectService.java @@ -13,7 +13,6 @@ import com.intellij.json.JsonFileType; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProcessCanceledException; @@ -46,7 +45,7 @@ public class QuarkusProjectService implements ClasspathResourceChangedManager.Li private final Map> schemas = new ConcurrentHashMap<>(); public static QuarkusProjectService getInstance(Project project) { - return ServiceManager.getService(project, QuarkusProjectService.class); + return project.getService(QuarkusProjectService.class); } private final MessageBusConnection connection; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/tool/ToolDelegate.java b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/BuildToolDelegate.java similarity index 86% rename from src/main/java/com/redhat/devtools/intellij/quarkus/tool/ToolDelegate.java rename to src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/BuildToolDelegate.java index a3d977e9d..8679e0440 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/tool/ToolDelegate.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/BuildToolDelegate.java @@ -8,15 +8,17 @@ * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ -package com.redhat.devtools.intellij.quarkus.tool; +package com.redhat.devtools.intellij.quarkus.buildtool; import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.messages.MessageBusConnection; import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.model.MavenId; @@ -36,7 +38,7 @@ import static com.redhat.devtools.intellij.quarkus.QuarkusConstants.QUARKUS_DEPLOYMENT_PROPERTY_NAME; import static com.redhat.devtools.intellij.quarkus.QuarkusConstants.QUARKUS_EXTENSION_PROPERTIES; -public interface ToolDelegate { +public interface BuildToolDelegate { static boolean shouldResolveArtifactTransitively(MavenId deploymentId) { // The kubernetes support is only available if quarkus-kubernetes artifact is @@ -88,8 +90,8 @@ static String getQuarkusExtension(Reader r) throws IOException { return p.getProperty(QUARKUS_DEPLOYMENT_PROPERTY_NAME); } - public static ToolDelegate getDelegate(Module module) { - for(ToolDelegate toolDelegate : getDelegates()) { + public static BuildToolDelegate getDelegate(Module module) { + for(BuildToolDelegate toolDelegate : getDelegates()) { if (toolDelegate.isValid(module)) { return toolDelegate; } @@ -161,7 +163,7 @@ default VirtualFile getJarFile(File file) { return virtualFile != null? JarFileSystem.getInstance().getJarRootForLocalFile(virtualFile):null; } - static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.toolDelegate"); + static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.toolDelegate"); @NotNull static List[] initDeploymentFiles() { @@ -171,11 +173,20 @@ static List[] initDeploymentFiles() { return result; } - public static ToolDelegate[] getDelegates() { - ToolDelegate[] delegates = EP_NAME.getExtensions(); + public static BuildToolDelegate[] getDelegates() { + BuildToolDelegate[] delegates = EP_NAME.getExtensions(); Arrays.sort(delegates, (a,b) -> a.getOrder() - b.getOrder()); return delegates; } RunnerAndConfigurationSettings getConfigurationDelegate(Module module, QuarkusRunConfiguration configuration); + + /** + * Add project import listener. + * + * @param project the project. + * @param connection the project connection used to subscribe maven, gradle listener which tracks project import. + * @param listener the project import listener. + */ + void addProjectImportListener(@NotNull Project project, @NotNull MessageBusConnection connection, @NotNull ProjectImportListener listener); } diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/ProjectImportListener.java b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/ProjectImportListener.java new file mode 100644 index 000000000..600f1c5ec --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/ProjectImportListener.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, + * and is available at https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.intellij.quarkus.buildtool; + +import com.intellij.openapi.module.Module; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Project import listener API. + * + */ +public interface ProjectImportListener { + + /** + * On import finished. + * + * @param modules imported modules. + */ + void importFinished(@NotNull List modules); + +} diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/gradle/AbstractGradleToolDelegate.java b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/AbstractGradleToolDelegate.java similarity index 81% rename from src/main/java/com/redhat/devtools/intellij/quarkus/gradle/AbstractGradleToolDelegate.java rename to src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/AbstractGradleToolDelegate.java index e91fa59f0..1ee726f16 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/gradle/AbstractGradleToolDelegate.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/AbstractGradleToolDelegate.java @@ -8,18 +8,23 @@ * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ -package com.redhat.devtools.intellij.quarkus.gradle; +package com.redhat.devtools.intellij.quarkus.buildtool.gradle; import com.intellij.execution.RunManager; import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.ide.util.newProjectWizard.AddModuleWizard; import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.externalSystem.ExternalSystemModulePropertyManager; import com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType; import com.intellij.openapi.externalSystem.service.ExternalSystemFacadeManager; import com.intellij.openapi.externalSystem.service.RemoteExternalSystemFacade; +import com.intellij.openapi.externalSystem.service.project.IdeModelsProvider; +import com.intellij.openapi.externalSystem.service.project.IdeModelsProviderImpl; import com.intellij.openapi.externalSystem.service.project.ProjectDataManager; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManagerImpl; import com.intellij.openapi.externalSystem.service.remote.RemoteExternalSystemTaskManager; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.module.ModifiableModuleModel; @@ -28,6 +33,7 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.roots.LibraryOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderRootType; @@ -37,35 +43,32 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.projectImport.ProjectImportBuilder; import com.intellij.projectImport.ProjectImportProvider; +import com.intellij.util.messages.MessageBusConnection; import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; +import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate; +import com.redhat.devtools.intellij.quarkus.buildtool.ProjectImportListener; import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration; -import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.execution.GradleRunnerUtil; import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType; import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration; import org.jetbrains.plugins.gradle.util.GradleConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; +import java.io.*; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Stream; -public abstract class AbstractGradleToolDelegate implements ToolDelegate { +public abstract class AbstractGradleToolDelegate implements BuildToolDelegate { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGradleToolDelegate.class); private static final String M2_REPO = System.getProperty("user.home") + File.separator + ".m2" + File.separator + "repository"; @@ -87,7 +90,7 @@ public boolean isValid(Module module) { @Override public List[] getDeploymentFiles(Module module, ProgressIndicator progressIndicator) { - List[] result = ToolDelegate.initDeploymentFiles(); + List[] result = BuildToolDelegate.initDeploymentFiles(); ModuleRootManager manager = ModuleRootManager.getInstance(module); Set deploymentIds = new HashSet<>(); try { @@ -107,9 +110,9 @@ public List[] getDeploymentFiles(Module module, ProgressIndicator p /** * Collect all deployment JARs and dependencies including the sources JARs. Will run a specific Gradle task. * - * @param module the module + * @param module the module * @param deploymentIds the Maven coordinates of the module deployment JARs - * @param result the list where to place results to + * @param result the list where to place results to * @throws IOException if an error occurs running Gradle */ private void processDownload(Module module, Set deploymentIds, List[] result) throws IOException { @@ -125,12 +128,12 @@ private void processDownload(Module module, Set deploymentIds, List[] result) throws IOException { @@ -181,7 +184,7 @@ private void collectDependencies(Module module, Path customBuildFile, Path custo if (file != null) { VirtualFile jarFile = getJarFile(file); if (jarFile != null) { - result[file.endsWith("sources.jar")?SOURCES:BINARY].add(jarFile); + result[file.endsWith("sources.jar") ? SOURCES : BINARY].add(jarFile); } } } @@ -209,8 +212,8 @@ private void collectDependencies(Module module, Path customBuildFile, Path custo /** * Generate the custom build file from the module build file and adding the specific task. * - * @param basePath the base path where the module build file is in - * @param outputPath the path to the task result file + * @param basePath the base path where the module build file is in + * @param outputPath the path to the task result file * @param deploymentIds the Maven coordinates of the module deployment JARs * @return the path to the custom build file * @throws IOException if an error occurs generating the file @@ -235,7 +238,8 @@ private Path generateCustomGradleSettings(String basePath, Path customBuildFile) String content = ""; try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { content = IOUtils.toString(reader); - } catch (IOException e) {} + } catch (IOException e) { + } content += System.lineSeparator() + "rootProject.buildFileName =\"" + customBuildFile.getFileName().toFile().getName() + "\""; Path customPath = Files.createTempFile(base, null, getScriptExtension()); try (Writer writer = Files.newBufferedWriter(customPath, StandardCharsets.UTF_8)) { @@ -245,12 +249,11 @@ private Path generateCustomGradleSettings(String basePath, Path customBuildFile) } - /** * Append the specific task definition. * - * @param content the content of the module build file - * @param outputPath the path to the task result file + * @param content the content of the module build file + * @param outputPath the path to the task result file * @param deploymentIds the Maven coordinates of the module deployment JARs * @return the content of the custom build file */ @@ -269,7 +272,7 @@ private void processLibrary(Library library, ModuleRootManager manager, Set entry instanceof LibraryOrderEntry && (GRADLE_LIBRARY_PREFIX + deploymentIdStr).equals(((LibraryOrderEntry)entry).getLibraryName())).findFirst().isPresent(); + return Stream.of(manager.getOrderEntries()).filter(entry -> entry instanceof LibraryOrderEntry && (GRADLE_LIBRARY_PREFIX + deploymentIdStr).equals(((LibraryOrderEntry) entry).getLibraryName())).findFirst().isPresent(); } private boolean isDependency(ModuleRootManager manager, String groupId, String artifactId) { @@ -314,7 +317,7 @@ public void processImport(Module module) { Project project = module.getProject(); File gradleFile = null; - for(VirtualFile virtualFile : ModuleRootManager.getInstance(module).getContentRoots()) { + for (VirtualFile virtualFile : ModuleRootManager.getInstance(module).getContentRoots()) { File baseDir = VfsUtilCore.virtualToIoFile(virtualFile); File file = new File(baseDir, getScriptName()); if (file.exists()) { @@ -328,7 +331,7 @@ public void processImport(Module module) { ProjectImportBuilder gradleProjectImportBuilder = gradleProjectImportProvider.getBuilder(); AddModuleWizard wizard = new AddModuleWizard(project, gradleFile.getPath(), new ProjectImportProvider[]{gradleProjectImportProvider}); if (wizard.getStepCount() == 0 || wizard.showAndGet()) { - gradleProjectImportBuilder.commit(project, (ModifiableModuleModel)null, (ModulesProvider)null); + gradleProjectImportBuilder.commit(project, (ModifiableModuleModel) null, (ModulesProvider) null); } } } @@ -337,6 +340,7 @@ public void processImport(Module module) { private static final String LEGACY_IMPORT_BUILDER_CLASS_NAME = "org.jetbrains.plugins.gradle.service.project.wizard.GradleProjectImportBuilder"; private static final String NEW_IMPORT_PROVIDER_CLASS_NAME = "org.jetbrains.plugins.gradle.service.project.wizard.JavaGradleProjectImportProvider"; + @NotNull private ProjectImportProvider getGradleProjectImportProvider() { try { @@ -346,7 +350,8 @@ private ProjectImportProvider getGradleProjectImportProvider() { Class clazz = (Class) Class.forName(LEGACY_IMPORT_BUILDER_CLASS_NAME); ProjectImportBuilder builder = clazz.getConstructor(ProjectDataManager.class).newInstance(ProjectDataManager.getInstance()); return (ProjectImportProvider) Class.forName(LEGACY_IMPORT_PROVIDER_CLASS_NAME).getConstructor(clazz).newInstance(builder); - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e1) { + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | + InvocationTargetException e1) { throw new RuntimeException(e1); } } catch (IllegalAccessException | InstantiationException e) { @@ -368,4 +373,35 @@ public RunnerAndConfigurationSettings getConfigurationDelegate(Module module, Qu gradleConfiguration.setBeforeRunTasks(configuration.getBeforeRunTasks()); return settings; } + + @Override + public void addProjectImportListener(@NotNull Project project, @NotNull MessageBusConnection connection, @NotNull ProjectImportListener listener) { + connection.subscribe(ProjectDataImportListener.TOPIC, new ProjectDataImportListener() { + @Override + public void onImportFinished(@Nullable String projectPath) { + if (!ProjectUtil.guessProjectDir(project).getPath().contains(projectPath)) { + // The imported project doesn't belong to the input project, ignore it. + return; + } + List modules = new ArrayList<>(); + Module[] existingModules = ModuleManager.getInstance(project).getModules(); + for (Module module : existingModules) { + // Check if the module is a Gradle project + if (GradleRunnerUtil.isGradleModule(module) && isValidGradleModule(module)) { + modules.add(module); + } + } + listener.importFinished(modules); + } + }); + } + + private static boolean isValidGradleModule(Module module) { + // Remove Quarkus Gradle modules: + // - $projectName.integrationTest + // - $projectName.native-test + // - $projectName.test + String name = module.getName(); + return !(name.endsWith(".integrationTest") || name.endsWith(".native-test") || name.endsWith(".test")); + } } diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/gradle/GradleGroovyToolDelegate.java b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/GradleGroovyToolDelegate.java similarity index 98% rename from src/main/java/com/redhat/devtools/intellij/quarkus/gradle/GradleGroovyToolDelegate.java rename to src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/GradleGroovyToolDelegate.java index c4d868400..53aa9444b 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/gradle/GradleGroovyToolDelegate.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/GradleGroovyToolDelegate.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ -package com.redhat.devtools.intellij.quarkus.gradle; +package com.redhat.devtools.intellij.quarkus.buildtool.gradle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/gradle/GradleKotlinToolDelegate.java b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/GradleKotlinToolDelegate.java similarity index 98% rename from src/main/java/com/redhat/devtools/intellij/quarkus/gradle/GradleKotlinToolDelegate.java rename to src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/GradleKotlinToolDelegate.java index 3e84633e0..e69db0e1a 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/gradle/GradleKotlinToolDelegate.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/GradleKotlinToolDelegate.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ -package com.redhat.devtools.intellij.quarkus.gradle; +package com.redhat.devtools.intellij.quarkus.buildtool.gradle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/gradle/QuarkusProjectDataService.java b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/QuarkusProjectDataService.java similarity index 98% rename from src/main/java/com/redhat/devtools/intellij/quarkus/gradle/QuarkusProjectDataService.java rename to src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/QuarkusProjectDataService.java index 9996e7170..407cb470a 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/gradle/QuarkusProjectDataService.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/gradle/QuarkusProjectDataService.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ -package com.redhat.devtools.intellij.quarkus.gradle; +package com.redhat.devtools.intellij.quarkus.buildtool.gradle; import com.intellij.facet.Facet; import com.intellij.facet.FacetType; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/maven/MavenToolDelegate.java b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/maven/MavenToolDelegate.java similarity index 88% rename from src/main/java/com/redhat/devtools/intellij/quarkus/maven/MavenToolDelegate.java rename to src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/maven/MavenToolDelegate.java index d35d1f79f..c6cd1c040 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/maven/MavenToolDelegate.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/maven/MavenToolDelegate.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ -package com.redhat.devtools.intellij.quarkus.maven; +package com.redhat.devtools.intellij.quarkus.buildtool.maven; import com.intellij.execution.RunManager; import com.intellij.execution.RunnerAndConfigurationSettings; @@ -18,16 +18,21 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.messages.MessageBusConnection; import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; +import com.redhat.devtools.intellij.quarkus.buildtool.ProjectImportListener; import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration; -import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate; +import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate; +import com.redhat.devtools.intellij.quarkus.settings.UserDefinedQuarkusSettings; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.execution.MavenRunConfiguration; import org.jetbrains.idea.maven.execution.MavenRunConfigurationType; import org.jetbrains.idea.maven.execution.MavenRunnerSettings; import org.jetbrains.idea.maven.model.MavenArtifact; import org.jetbrains.idea.maven.model.MavenArtifactInfo; import org.jetbrains.idea.maven.model.MavenId; +import org.jetbrains.idea.maven.project.MavenImportListener; import org.jetbrains.idea.maven.project.MavenProject; import org.jetbrains.idea.maven.project.MavenProjectsManager; import org.jetbrains.idea.maven.server.MavenEmbedderWrapper; @@ -40,7 +45,7 @@ import java.lang.reflect.Method; import java.util.*; -public class MavenToolDelegate implements ToolDelegate { +public class MavenToolDelegate implements BuildToolDelegate { private static final Logger LOGGER = LoggerFactory.getLogger(MavenToolDelegate.class); @Override @@ -51,7 +56,7 @@ public boolean isValid(Module module) { @Override public List[] getDeploymentFiles(Module module, ProgressIndicator progressIndicator) { MavenProject mavenProject = MavenProjectsManager.getInstance(module.getProject()).findProject(module); - List[] result = ToolDelegate.initDeploymentFiles(); + List[] result = BuildToolDelegate.initDeploymentFiles(); if (mavenProject != null) { getDeploymentFiles(module, mavenProject, result, progressIndicator); } @@ -97,7 +102,7 @@ private void getDeploymentFiles(Module module, MavenProject mavenProject, List resolveDeploymentArtifacts(Module module, MavenProjec } } else { for (var deploymentId : deploymentIds) { - boolean shouldResolveArtifactTransitively = ToolDelegate.shouldResolveArtifactTransitively(deploymentId); + boolean shouldResolveArtifactTransitively = BuildToolDelegate.shouldResolveArtifactTransitively(deploymentId); progressIndicator.checkCanceled(); progressIndicator.setText2("Resolving " + (shouldResolveArtifactTransitively ? " (Transitevely) " : "") + "'" + deploymentId + "'"); if (shouldResolveArtifactTransitively) { @@ -256,6 +261,25 @@ public RunnerAndConfigurationSettings getConfigurationDelegate(Module module, Qu return settings; } + @Override + public void addProjectImportListener(@NotNull Project project, @NotNull MessageBusConnection connection, @NotNull ProjectImportListener listener) { + connection.subscribe(MavenImportListener.TOPIC, new MavenImportListener() { + @Override + public void importFinished(@NotNull Collection importedProjects, @NotNull List newModules) { + List modules = new ArrayList<>(); + for (MavenProject mavenProject : importedProjects) { + MavenProjectsManager projectsManager = MavenProjectsManager.getInstance(project); + Module module = projectsManager.findModule(mavenProject); + if (module != null) { + modules.add(module); + } + } + listener.importFinished(modules); + } + }); + + } + private void ensureRunnerSettings(MavenRunConfiguration mavenConfiguration) { if (mavenConfiguration.getRunnerSettings() == null) { mavenConfiguration.setRunnerSettings(new MavenRunnerSettings()); diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/maven/QuarkusFacetMavenImporter.java b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/maven/QuarkusFacetMavenImporter.java similarity index 97% rename from src/main/java/com/redhat/devtools/intellij/quarkus/maven/QuarkusFacetMavenImporter.java rename to src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/maven/QuarkusFacetMavenImporter.java index 869887acc..b2c596d7e 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/maven/QuarkusFacetMavenImporter.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/maven/QuarkusFacetMavenImporter.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ -package com.redhat.devtools.intellij.quarkus.maven; +package com.redhat.devtools.intellij.quarkus.buildtool.maven; import com.intellij.facet.FacetType; import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/maven/QuarkusMavenLibraryImporter.java b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/maven/QuarkusMavenLibraryImporter.java similarity index 97% rename from src/main/java/com/redhat/devtools/intellij/quarkus/maven/QuarkusMavenLibraryImporter.java rename to src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/maven/QuarkusMavenLibraryImporter.java index 27b2f3e13..a00dd80d7 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/maven/QuarkusMavenLibraryImporter.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/buildtool/maven/QuarkusMavenLibraryImporter.java @@ -8,7 +8,7 @@ * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ -package com.redhat.devtools.intellij.quarkus.maven; +package com.redhat.devtools.intellij.quarkus.buildtool.maven; import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; import com.intellij.openapi.module.Module; diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/projectWizard/QuarkusModuleInfoStep.java b/src/main/java/com/redhat/devtools/intellij/quarkus/projectWizard/QuarkusModuleInfoStep.java index ef8d124ca..78fefb678 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/projectWizard/QuarkusModuleInfoStep.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/projectWizard/QuarkusModuleInfoStep.java @@ -33,7 +33,7 @@ import com.intellij.util.ui.JBUI; import com.redhat.devtools.intellij.quarkus.QuarkusBundle; import com.redhat.devtools.intellij.quarkus.QuarkusConstants; -import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate; +import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,7 +60,7 @@ public class QuarkusModuleInfoStep extends ModuleWizardStep implements Disposabl private ComboBox streamComboBox; - private ComboBox toolComboBox; + private ComboBox toolComboBox; private JBCheckBox exampleField; @@ -98,7 +98,7 @@ public JComponent getComponent() { @Override public void updateDataModel() { - context.putUserData(QuarkusConstants.WIZARD_TOOL_KEY, (ToolDelegate)toolComboBox.getModel().getSelectedItem()); + context.putUserData(QuarkusConstants.WIZARD_TOOL_KEY, (BuildToolDelegate)toolComboBox.getModel().getSelectedItem()); context.putUserData(QuarkusConstants.WIZARD_EXAMPLE_KEY, exampleField.isSelected()); context.putUserData(QuarkusConstants.WIZARD_GROUPID_KEY, groupIdField.getText()); context.putUserData(QuarkusConstants.WIZARD_ARTIFACTID_KEY, artifactIdField.getText()); @@ -171,11 +171,11 @@ protected void customizeCellRenderer(@NotNull JList lis formBuilder.addLabeledComponent("Quarkus stream:", streamComponent); - final CollectionComboBoxModel toolModel = new CollectionComboBoxModel<>(Arrays.asList(ToolDelegate.getDelegates())); + final CollectionComboBoxModel toolModel = new CollectionComboBoxModel<>(Arrays.asList(BuildToolDelegate.getDelegates())); toolComboBox = new ComboBox<>(toolModel); - toolComboBox.setRenderer(new ColoredListCellRenderer() { + toolComboBox.setRenderer(new ColoredListCellRenderer() { @Override - protected void customizeCellRenderer(@NotNull JList list, ToolDelegate toolDelegate, int index, boolean selected, boolean hasFocus) { + protected void customizeCellRenderer(@NotNull JList list, BuildToolDelegate toolDelegate, int index, boolean selected, boolean hasFocus) { this.append(toolDelegate.getDisplay()); } }); diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfiguration.java b/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfiguration.java index 17e39a556..37955c1e5 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfiguration.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfiguration.java @@ -42,7 +42,7 @@ import com.redhat.devtools.intellij.quarkus.QuarkusConstants; import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; import com.redhat.devtools.intellij.quarkus.TelemetryService; -import com.redhat.devtools.intellij.quarkus.tool.ToolDelegate; +import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate; import com.redhat.devtools.intellij.telemetry.core.service.TelemetryMessageBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -98,7 +98,7 @@ public void checkConfiguration() throws RuntimeConfigurationException { if (!QuarkusModuleUtil.isQuarkusModule(module)) { throw new RuntimeConfigurationException("Not a Quarkus module", QUARKUS_CONFIGURATION); } - ToolDelegate delegate = ToolDelegate.getDelegate(module); + BuildToolDelegate delegate = BuildToolDelegate.getDelegate(module); if (delegate == null) { throw new RuntimeConfigurationException("Can't find a tool to process the module", QUARKUS_CONFIGURATION); } @@ -131,7 +131,7 @@ private void allocateLocalPort() { public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException { TelemetryMessageBuilder.ActionMessage telemetry = TelemetryService.instance().action(TelemetryService.RUN_PREFIX + "run"); telemetry.property("kind", executor.getId()); - ToolDelegate toolDelegate = ToolDelegate.getDelegate(getModule()); + BuildToolDelegate toolDelegate = BuildToolDelegate.getDelegate(getModule()); allocateLocalPort(); if (toolDelegate != null) { telemetry.property("tool", toolDelegate.getDisplay()); diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfigurationManager.java b/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfigurationManager.java new file mode 100644 index 000000000..414ad47f9 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfigurationManager.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.quarkus.run; + +import com.intellij.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.dashboard.RunDashboardManager; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.util.concurrency.NonUrgentExecutor; +import com.intellij.util.messages.MessageBusConnection; +import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil; +import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate; +import com.redhat.devtools.intellij.quarkus.buildtool.ProjectImportListener; +import com.redhat.devtools.intellij.quarkus.settings.UserDefinedQuarkusSettings; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Quarkus run configuration manager used to: + * + *
    + *
  • auto create Quarkus run configuration when a Quarkus Maven/Gradle is imported
  • + *
  • provides methods to find existing/ create Quarkus run configuration from a given module
  • + *
+ */ +public class QuarkusRunConfigurationManager implements Disposable { + + private static final Logger LOGGER = LoggerFactory.getLogger(QuarkusRunConfigurationManager.class); + + public static QuarkusRunConfigurationManager getInstance(Project project) { + return project.getService(QuarkusRunConfigurationManager.class); + } + + private final Project project; + private final MessageBusConnection connection; + + public QuarkusRunConfigurationManager(Project project) { + this.project = project; + connection = addProjectImportListener(project); + } + + public @Nullable RunnerAndConfigurationSettings findExistingConfigurationFor(@NotNull Module module) { + List configurations = RunManager.getInstance(project).getConfigurationSettingsList(QuarkusRunConfigurationType.class); + if (!configurations.isEmpty()) { + for (RunnerAndConfigurationSettings settings : configurations) { + QuarkusRunConfiguration configuration = (QuarkusRunConfiguration) settings.getConfiguration(); + if (module.equals(configuration.getModule())) { + return settings; + } + } + } + return null; + } + + public @NotNull RunnerAndConfigurationSettings createConfiguration(@NotNull Module module, boolean save) { + var runManager = RunManager.getInstance(module.getProject()); + RunnerAndConfigurationSettings quarkusSettings = runManager.createConfiguration(module.getName(), QuarkusRunConfigurationType.class); + ((QuarkusRunConfiguration) quarkusSettings.getConfiguration()).setModule(module); + // Ensure that configuration name is unique + runManager.setUniqueNameIfNeeded(quarkusSettings.getConfiguration()); + if (save) { + quarkusSettings.storeInLocalWorkspace(); + // Save the configuration + runManager.addConfiguration(quarkusSettings); + } + if (runManager.getAllSettings().size() == 1) { + // Select the Quarkus configuration on the top of the Run configuration list + runManager.setSelectedConfiguration(quarkusSettings); + } + return quarkusSettings; + } + + @NotNull + private static String generateConfigurationName(@NotNull Module module, RunManager runManager) { + String configurationName = module.getName(); + RunnerAndConfigurationSettings existingConfiguration = runManager.findConfigurationByTypeAndName(QuarkusRunConfigurationType.ID, configurationName); + int index = 0; + while (existingConfiguration != null) { + configurationName = module.getName() + " (" + index++ + ")"; + existingConfiguration = runManager.findConfigurationByTypeAndName(QuarkusRunConfigurationType.ID, configurationName); + } + return configurationName; + } + + @NotNull + private MessageBusConnection addProjectImportListener(Project project) { + MessageBusConnection connection = project.getMessageBus().connect(); + ProjectImportListener listener = new ProjectImportListener() { + + @Override + public void importFinished(@NotNull List modules) { + if (!UserDefinedQuarkusSettings.getInstance(project).isCreateQuarkusRunConfigurationOnProjectImport()) { + return; + } + tryToCreateRunConfigurations(modules); + } + }; + BuildToolDelegate[] delegates = BuildToolDelegate.getDelegates(); + for (BuildToolDelegate delegate : delegates) { + delegate.addProjectImportListener(project, connection, listener); + } + return connection; + } + + private void tryToCreateRunConfigurations(List modules) { + if (modules.isEmpty()) { + return; + } + ReadAction.nonBlocking(() -> doTryToCreateRunConfigurations(modules)) + .inSmartMode(project) + .submit(NonUrgentExecutor.getInstance()); + } + + private void doTryToCreateRunConfigurations(List modules) { + boolean runConfigurationCreated = false; + for (Module module : modules) { + if (tryToCreateRunConfiguration(module)) { + runConfigurationCreated = true; + } + } + if (runConfigurationCreated) { + if (!addQuarkusRunConfigurationTypeInServicesViewIfNeeded(true)) { + // The Services view is not updated correctly (when Ultimate is used and it tries to update the services view insame time) because of this error + // java.util.ConcurrentModificationException + // at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1597) + // at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1620) + // at com.google.common.collect.Sets$3$1.computeNext(Sets.java:907) + // at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:145) + // at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:140) + // at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:335) + // at java.base/java.util.HashSet.(HashSet.java:121) + // at com.intellij.execution.dashboard.RunDashboardManagerImpl.setTypes(RunDashboardManagerImpl.java:295) + // at + + // We retry 5 times to update it + for (int i = 0; i < 5; i++) { + if (addQuarkusRunConfigurationTypeInServicesViewIfNeeded(i == 4)) { + // The update is done correctly + break; + } + } + } + } + } + + private boolean tryToCreateRunConfiguration(Module module) { + if (!QuarkusModuleUtil.isQuarkusModule(module)) { + return false; + } + // Find existing Quarkus run configuration + RunnerAndConfigurationSettings quarkusSettings = findExistingConfigurationFor(module); + if (quarkusSettings == null) { + // No Quarkus run configuration for the module, create it and save it in the .idea/workspace.xml file + createConfiguration(module, true); + return true; + } + return false; + } + + /** + * Add "QuarkusRunConfigurationType" in the Services view settings to show "Quarkus Dev Mode" and their Quarkus run configuration in the Services view. + * @param logError true if error must be logged while updating Services view settings. + * @return true if the update is done correctly and false otherwise. + */ + private boolean addQuarkusRunConfigurationTypeInServicesViewIfNeeded(boolean logError) { + try { + RunDashboardManager runDashboardManager = RunDashboardManager.getInstance(project); + Set types = new HashSet<>(runDashboardManager.getTypes()); + if (!types.contains(QuarkusRunConfigurationType.ID)) { + types.add(QuarkusRunConfigurationType.ID); + runDashboardManager.setTypes(types); + } + return true; + } + catch(Exception e) { + // This case comes from when Ultimate is used and Ultimate Quarkus support update in same time their Quarkus Configuration Type. + // java.util.ConcurrentModificationException + // at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1597) + // at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1620) + // at com.google.common.collect.Sets$3$1.computeNext(Sets.java:907) + // at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:145) + // at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:140) + // at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:335) + // at java.base/java.util.HashSet.(HashSet.java:121) + // at com.intellij.execution.dashboard.RunDashboardManagerImpl.setTypes(RunDashboardManagerImpl.java:295) + // at + if (logError) { + LOGGER.error("Error while adding QuarkusRunConfigurationType in Services view settings", e); + } + return false; + } + } + + @Override + public void dispose() { + connection.disconnect(); + } +} diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfigurationType.java b/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfigurationType.java index 843f5a4f0..d011b42e3 100644 --- a/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfigurationType.java +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/run/QuarkusRunConfigurationType.java @@ -19,7 +19,7 @@ import javax.swing.Icon; public class QuarkusRunConfigurationType implements ConfigurationType { - private static final String ID = "QuarkusRunConfiguration"; + public static final String ID = "QuarkusRunConfiguration"; @NotNull @Override diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/settings/QuarkusConfigurable.java b/src/main/java/com/redhat/devtools/intellij/quarkus/settings/QuarkusConfigurable.java new file mode 100644 index 000000000..369e54167 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/settings/QuarkusConfigurable.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.quarkus.settings; + +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.NamedConfigurable; +import com.intellij.openapi.util.NlsContexts; +import com.redhat.devtools.intellij.quarkus.QuarkusBundle; + +import javax.swing.*; + +/** + * Quarkus configuration. + */ +public class QuarkusConfigurable extends NamedConfigurable { + + private final Project project; + private QuarkusView myView; + + public QuarkusConfigurable(Project project) { + this.project = project; + } + + @Override + public UserDefinedQuarkusSettings getEditableObject() { + return null; + } + + @Override + public @NlsContexts.DetailedDescription String getBannerSlogan() { + return null; + } + + @Override + public JComponent createOptionsPanel() { + if (myView == null) { + myView = new QuarkusView(); + } + return myView.getComponent(); + } + + @Override + public void setDisplayName(String name) { + } + + @Override + public @NlsContexts.ConfigurableName String getDisplayName() { + return QuarkusBundle.message("quarkus"); + } + + + @Override + public void reset() { + if (myView == null) return; + UserDefinedQuarkusSettings settings = UserDefinedQuarkusSettings.getInstance(project); + myView.setCreateQuarkusRunConfigurationOnProjectImport(settings.isCreateQuarkusRunConfigurationOnProjectImport()); + } + + @Override + public boolean isModified() { + if (myView == null) return false; + UserDefinedQuarkusSettings settings = UserDefinedQuarkusSettings.getInstance(project); + return !(myView.isCreateQuarkusRunConfigurationOnProjectImport() == settings.isCreateQuarkusRunConfigurationOnProjectImport()); + } + + @Override + public void apply() throws ConfigurationException { + if (myView == null) return; + UserDefinedQuarkusSettings settings = UserDefinedQuarkusSettings.getInstance(project); + settings.setCreateQuarkusRunConfigurationOnProjectImport(myView.isCreateQuarkusRunConfigurationOnProjectImport()); + settings.fireStateChanged(); + } +} diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/settings/QuarkusView.java b/src/main/java/com/redhat/devtools/intellij/quarkus/settings/QuarkusView.java new file mode 100644 index 000000000..9517ec3dc --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/settings/QuarkusView.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.quarkus.settings; + +import com.intellij.openapi.Disposable; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.util.ui.FormBuilder; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UI; +import com.redhat.devtools.intellij.quarkus.QuarkusBundle; + +import javax.swing.*; +import javax.swing.border.TitledBorder; + +/** + * Quarkus view. + */ +public class QuarkusView implements Disposable { + + private final JPanel myMainPanel; + + private JBCheckBox createQuarkusRunConfigurationOnProjectImportCheckBox = new JBCheckBox(QuarkusBundle.message("quarkus.create.quarkus.run.configuration.on.project.import")); + + public QuarkusView() { + JComponent descriptionPanel = createDescription(null); + JPanel settingsPanel = createSettings(descriptionPanel); + TitledBorder title = IdeBorderFactory.createTitledBorder(QuarkusBundle.message("quarkus.title")); + settingsPanel.setBorder(title); + this.myMainPanel = JBUI.Panels.simplePanel(10, 10) + .addToLeft(JBUI.Panels.simplePanel()) + .addToCenter(settingsPanel); + } + + private JPanel createSettings(JComponent description) { + return FormBuilder.createFormBuilder() + .addComponent(description, 0) + .addComponent(createQuarkusRunConfigurationOnProjectImportCheckBox, 5) + .addComponentFillVertically(new JPanel(), 0) + .getPanel(); + } + + private JComponent createDescription(String description) { + /** + * Normally comments are below the controls. + * Here we want the comments to precede the controls, we therefore create an empty, 0-sized panel. + */ + JPanel titledComponent = UI.PanelFactory.grid().createPanel(); + titledComponent.setMinimumSize(JBUI.emptySize()); + titledComponent.setPreferredSize(JBUI.emptySize()); + if (description != null && !description.isBlank()) { + titledComponent = UI.PanelFactory.panel(titledComponent) + .withComment(description) + .resizeX(true) + .resizeY(true) + .createPanel(); + } + return titledComponent; + } + + public JComponent getComponent() { + return myMainPanel; + } + + + public boolean isCreateQuarkusRunConfigurationOnProjectImport() { + return createQuarkusRunConfigurationOnProjectImportCheckBox.isSelected(); + } + + public void setCreateQuarkusRunConfigurationOnProjectImport(boolean createQuarkusRunConfigurationOnProjectImport) { + createQuarkusRunConfigurationOnProjectImportCheckBox.setSelected(createQuarkusRunConfigurationOnProjectImport); + } + + @Override + public void dispose() { + + } +} diff --git a/src/main/java/com/redhat/devtools/intellij/quarkus/settings/UserDefinedQuarkusSettings.java b/src/main/java/com/redhat/devtools/intellij/quarkus/settings/UserDefinedQuarkusSettings.java new file mode 100644 index 000000000..019c6ffc1 --- /dev/null +++ b/src/main/java/com/redhat/devtools/intellij/quarkus/settings/UserDefinedQuarkusSettings.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package com.redhat.devtools.intellij.quarkus.settings; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.project.Project; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * User defined Quarkus settings for: + * + *
    + *
  • auto create Quarkus run configuration
  • + *
+ */ +@State( + name = "QuarkusSettingsState", + storages = {@Storage("quarkusSettings.xml")} +) +public class UserDefinedQuarkusSettings implements PersistentStateComponent { + + private volatile MyState myState = new MyState(); + + private final Project project; + + public UserDefinedQuarkusSettings(Project project) { + this.project = project; + } + + private final List myChangeHandlers = ContainerUtil.createConcurrentList(); + + public static @NotNull UserDefinedQuarkusSettings getInstance(@NotNull Project project) { + return project.getService(UserDefinedQuarkusSettings.class); + } + + public void addChangeHandler(Runnable runnable) { + myChangeHandlers.add(runnable); + } + + public void removeChangeHandler(Runnable runnable) { + myChangeHandlers.remove(runnable); + } + + public void fireStateChanged() { + for (Runnable handler : myChangeHandlers) { + handler.run(); + } + } + public boolean isCreateQuarkusRunConfigurationOnProjectImport() { + return myState.myCreateQuarkusRunConfigurationOnProjectImport; + } + + public void setCreateQuarkusRunConfigurationOnProjectImport(boolean createQuarkusRunConfigurationOnProjectImport) { + myState.myCreateQuarkusRunConfigurationOnProjectImport = createQuarkusRunConfigurationOnProjectImport; + } + + @Nullable + @Override + public MyState getState() { + return myState; + } + + @Override + public void loadState(@NotNull MyState state) { + myState = state; + for (Runnable handler : myChangeHandlers) { + handler.run(); + } + } + + public static class MyState { + + @Tag("createQuarkusRunConfigurationOnProjectImport") + public boolean myCreateQuarkusRunConfigurationOnProjectImport = true; + + MyState() { + } + + } + +} diff --git a/src/main/resources/META-INF/lsp4ij-quarkus.xml b/src/main/resources/META-INF/lsp4ij-quarkus.xml index a2892bf07..0bf0fe817 100644 --- a/src/main/resources/META-INF/lsp4ij-quarkus.xml +++ b/src/main/resources/META-INF/lsp4ij-quarkus.xml @@ -47,6 +47,15 @@ + + + + diff --git a/src/main/resources/META-INF/plugin-gradle.xml b/src/main/resources/META-INF/plugin-gradle.xml index 305a68dd3..e367547bb 100644 --- a/src/main/resources/META-INF/plugin-gradle.xml +++ b/src/main/resources/META-INF/plugin-gradle.xml @@ -1,12 +1,12 @@ - - + + - + diff --git a/src/main/resources/META-INF/plugin-maven.xml b/src/main/resources/META-INF/plugin-maven.xml index dc4d8a6d6..54dc3c5f2 100644 --- a/src/main/resources/META-INF/plugin-maven.xml +++ b/src/main/resources/META-INF/plugin-maven.xml @@ -1,13 +1,13 @@ - + - - + + diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index d62a9349d..da049d7f1 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -338,6 +338,7 @@ + @@ -371,7 +372,7 @@ - + diff --git a/src/main/resources/messages/QuarkusBundle.properties b/src/main/resources/messages/QuarkusBundle.properties index 0aa090119..e6b477648 100644 --- a/src/main/resources/messages/QuarkusBundle.properties +++ b/src/main/resources/messages/QuarkusBundle.properties @@ -11,6 +11,11 @@ # Red Hat Inc. - initial API and implementation ############################################################################### +## Quarkus UI settings page +quarkus=Quarkus +quarkus.title=Quarkus configuration +quarkus.create.quarkus.run.configuration.on.project.import=Create "Quarkus Dev Mode" run configuration on project import + ## Quarkus explorer quarkus.tool.window.display.name=Projects @@ -22,4 +27,4 @@ quarkus.wizard.loading.streams=Loading Quarkus platform streams... quarkus.wizard.error.extensions.loading=Failed to load Quarkus extensions quarkus.wizard.error.extensions.loading.message=Failed to load extensions for Quarkus {0}:\n{1} -quarkus.wizard.loading.extensions=Loading Quarkus extensions... \ No newline at end of file +quarkus.wizard.loading.extensions=Loading Quarkus extensions... diff --git a/src/test/java/com/redhat/devtools/intellij/GradleTestCase.java b/src/test/java/com/redhat/devtools/intellij/GradleTestCase.java index 9d7b2781c..6299e9c41 100644 --- a/src/test/java/com/redhat/devtools/intellij/GradleTestCase.java +++ b/src/test/java/com/redhat/devtools/intellij/GradleTestCase.java @@ -12,7 +12,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; -import com.redhat.devtools.intellij.quarkus.gradle.AbstractGradleToolDelegate; +import com.redhat.devtools.intellij.quarkus.buildtool.gradle.AbstractGradleToolDelegate; import org.gradle.tooling.GradleConnector; import org.gradle.tooling.ProjectConnection; import org.gradle.tooling.model.idea.IdeaProject;