diff --git a/changelog/@unreleased/pr-45.v2.yml b/changelog/@unreleased/pr-45.v2.yml new file mode 100644 index 0000000..9272d3c --- /dev/null +++ b/changelog/@unreleased/pr-45.v2.yml @@ -0,0 +1,5 @@ +type: fix +fix: + description: Cache is loaded on first instance and on project refresh + links: + - https://github.com/palantir/gradle-consistent-versions-idea-plugin/pull/45 diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/FolderCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/FolderCompletionContributor.java index 8b8b81a..0a830b4 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/FolderCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/FolderCompletionContributor.java @@ -22,7 +22,6 @@ import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.lookup.LookupElementBuilder; -import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager; import com.intellij.openapi.project.Project; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiElement; @@ -32,14 +31,9 @@ public class FolderCompletionContributor extends CompletionContributor { - private final GradleCacheExplorer gradleCacheExplorer = new GradleCacheExplorer(); - private final RepositoryExplorer repositoryExplorer = new RepositoryExplorer(); public FolderCompletionContributor() { - // We add listener at this stage so that we can invalidate the cache when the gradle project refreshed - ExternalSystemProgressNotificationManager.getInstance() - .addNotificationListener(new InvalidateCacheOnGradleProjectRefresh(gradleCacheExplorer)); cacheCompletion(VersionPropsTypes.GROUP_PART); cacheCompletion(VersionPropsTypes.NAME_KEY); remoteCompletion(VersionPropsTypes.GROUP_PART); @@ -74,7 +68,9 @@ protected void addCompletions( Project project = parameters.getOriginalFile().getProject(); - gradleCacheExplorer.getCompletions(RepositoryLoader.loadRepositories(project), group).stream() + GradleCacheExplorer.getInstance() + .getCompletions(RepositoryLoader.loadRepositories(project), group) + .stream() .map(suggestion -> LookupElementBuilder.create(GroupPartOrPackageName.of(suggestion))) .forEach(resultSet::addElement); } diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GradleCacheExplorer.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GradleCacheExplorer.java index 83e5b6b..a2f1845 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GradleCacheExplorer.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GradleCacheExplorer.java @@ -16,12 +16,8 @@ package com.palantir.gradle.versions.intellij; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; +import com.google.common.base.Stopwatch; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; import java.io.BufferedInputStream; import java.io.IOException; import java.nio.file.Files; @@ -31,9 +27,7 @@ import java.util.HashSet; import java.util.Optional; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; import org.slf4j.Logger; @@ -43,78 +37,59 @@ public class GradleCacheExplorer { private static final Logger log = LoggerFactory.getLogger(GradleCacheExplorer.class); private static final String GRADLE_CACHE_PATH = System.getProperty("user.home") + "/.gradle/caches/modules-2/"; - private final Cache> cache = - Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build(); + private final AtomicReference> cache = new AtomicReference<>(Collections.emptySet()); - public final void invalidateCache() { - cache.invalidateAll(); + GradleCacheExplorer() { + loadCache(); + } + + public final void loadCache() { + cache.set(extractStrings()); } public final Set getCompletions(Set repoUrls, DependencyGroup input) { + Stopwatch stopWatch = Stopwatch.createStarted(); + String parsedInput = String.join(".", input.parts()); - ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - if (indicator == null) { - return Collections.emptySet(); + + Set results = cache.get().stream() + .map(url -> extractGroupAndArtifactFromUrl(repoUrls, url)) + .flatMap(Optional::stream) + .collect(Collectors.toSet()); + + if (parsedInput.isEmpty()) { + return results; } - try { - Callable> task = () -> extractStrings(repoUrls, indicator); - Future> future = ApplicationManager.getApplication().executeOnPooledThread(task); - Set results = - com.intellij.openapi.application.ex.ApplicationUtil.runWithCheckCanceled(future::get, indicator); + stopWatch.stop(); + log.debug("Completion matching time: {} ms", stopWatch.elapsed().toMillis()); - if (parsedInput.isEmpty()) { - return results; - } + return results.stream() + .filter(result -> result.startsWith(parsedInput)) + .map(result -> result.substring(parsedInput.length() + 1)) + .collect(Collectors.toSet()); + } + + private Set extractStrings() { + try (Stream allFolders = Files.list(Paths.get(GRADLE_CACHE_PATH))) { + + Stream metadataFolders = + allFolders.filter(path -> path.getFileName().toString().startsWith("metadata-")); - return results.stream() - .filter(result -> result.startsWith(parsedInput)) - .map(result -> result.substring(parsedInput.length() + 1)) + return metadataFolders + .map(metadataFolder -> metadataFolder.resolve("resource-at-url.bin")) + .filter(Files::exists) + .flatMap(this::extractStringsFromBinFile) + .filter(this::isValidResourceUrl) .collect(Collectors.toSet()); - } catch (ProcessCanceledException e) { - log.debug("Operation was cancelled", e); - } catch (Exception e) { - log.warn("Failed to get completions", e); + } catch (IOException e) { + log.error("Failed to list metadata folders", e); + return Collections.emptySet(); } - return Collections.emptySet(); } - private Set extractStrings(Set repoUrls, ProgressIndicator indicator) { - return cache.get("metadata", key -> { - try (Stream allFolders = Files.list(Paths.get(GRADLE_CACHE_PATH))) { - - Stream metadataFolders = - allFolders.filter(path -> path.getFileName().toString().startsWith("metadata-")); - - return metadataFolders - .peek(metadataFolder -> { - if (indicator.isCanceled()) { - throw new RuntimeException( - new InterruptedException("Operation was canceled by the user.")); - } - }) - .map(metadataFolder -> metadataFolder.resolve("resource-at-url.bin")) - .filter(Files::exists) - .flatMap(this::extractStringsFromBinFile) - .filter(url -> isValidResourceUrl(repoUrls, url)) - .map(url -> extractGroupAndArtifactFromUrl(repoUrls, url)) - .flatMap(Optional::stream) - .collect(Collectors.toSet()); - } catch (IOException e) { - log.error("Failed to list metadata folders", e); - return Collections.emptySet(); - } catch (RuntimeException e) { - if (e.getCause() instanceof InterruptedException) { - log.debug("Operation was cancelled", e); - return Collections.emptySet(); - } - throw e; - } - }); - } - - final boolean isValidResourceUrl(Set repoUrls, String url) { - return repoUrls.stream().anyMatch(url::startsWith) && (url.endsWith(".pom") || url.endsWith(".jar")); + final boolean isValidResourceUrl(String url) { + return (url.startsWith("https://")) && (url.endsWith(".pom") || url.endsWith(".jar")); } final Stream extractStringsFromBinFile(Path binFile) { @@ -182,4 +157,8 @@ Optional extractGroupAndArtifactFromUrl(Set repoUrls, String url return Optional.of(String.format("%s:%s", group, artifact)); }); } + + static GradleCacheExplorer getInstance() { + return ApplicationManager.getApplication().getService(GradleCacheExplorer.class); + } } diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/InvalidateCacheOnGradleProjectRefresh.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/LoadCacheOnGradleProjectRefresh.java similarity index 78% rename from gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/InvalidateCacheOnGradleProjectRefresh.java rename to gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/LoadCacheOnGradleProjectRefresh.java index 93f3a2c..f2390b4 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/InvalidateCacheOnGradleProjectRefresh.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/LoadCacheOnGradleProjectRefresh.java @@ -24,21 +24,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class InvalidateCacheOnGradleProjectRefresh implements ExternalSystemTaskNotificationListener { - private static final Logger log = LoggerFactory.getLogger(InvalidateCacheOnGradleProjectRefresh.class); - - private final GradleCacheExplorer gradleCacheExplorer; - - public InvalidateCacheOnGradleProjectRefresh(GradleCacheExplorer gradleCacheExplorer) { - this.gradleCacheExplorer = gradleCacheExplorer; - } +public class LoadCacheOnGradleProjectRefresh implements ExternalSystemTaskNotificationListener { + private static final Logger log = LoggerFactory.getLogger(LoadCacheOnGradleProjectRefresh.class); @Override public final void onSuccess(ExternalSystemTaskId id) { if (GradleConstants.SYSTEM_ID.equals(id.getProjectSystemId()) && id.getType() == ExternalSystemTaskType.RESOLVE_PROJECT) { - log.info("Gradle project refresh finished"); - gradleCacheExplorer.invalidateCache(); + log.debug("Gradle project refresh finished"); + GradleCacheExplorer.getInstance().loadCache(); } } diff --git a/gradle-consistent-versions-idea-plugin/src/main/resources/META-INF/plugin.xml b/gradle-consistent-versions-idea-plugin/src/main/resources/META-INF/plugin.xml index 10cad32..2e0331f 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/resources/META-INF/plugin.xml +++ b/gradle-consistent-versions-idea-plugin/src/main/resources/META-INF/plugin.xml @@ -52,5 +52,7 @@ + + - \ No newline at end of file + diff --git a/gradle-consistent-versions-idea-plugin/src/test/java/com/palantir/gradle/versions/intellij/GradleCacheExplorerTest.java b/gradle-consistent-versions-idea-plugin/src/test/java/com/palantir/gradle/versions/intellij/GradleCacheExplorerTest.java index 0dbe0c1..69374e2 100644 --- a/gradle-consistent-versions-idea-plugin/src/test/java/com/palantir/gradle/versions/intellij/GradleCacheExplorerTest.java +++ b/gradle-consistent-versions-idea-plugin/src/test/java/com/palantir/gradle/versions/intellij/GradleCacheExplorerTest.java @@ -42,25 +42,21 @@ void beforeEach() { @Test void test_gets_valid_urls_only() { - Set projectUrls = Set.of("https://repo.maven.apache.org/maven2/", "https://jcenter.bintray.com/"); - assertThat(explorer.isValidResourceUrl( - projectUrls, "https://repo.maven.apache.org/maven2/com/example/artifact/1.0/artifact-1.0.pom")) + "https://repo.maven.apache.org/maven2/com/example/artifact/1.0/artifact-1.0.pom")) .as("because the URL is from a known valid repository and ends with .pom") .isTrue(); - assertThat(explorer.isValidResourceUrl( - projectUrls, "https://jcenter.bintray.com/com/example/artifact/1.0/artifact-1.0.jar")) + assertThat(explorer.isValidResourceUrl("https://jcenter.bintray.com/com/example/artifact/1.0/artifact-1.0.jar")) .as("because the URL is from a known valid repository and ends with .jar") .isTrue(); - assertThat(explorer.isValidResourceUrl( - projectUrls, "https://example.com/com/example/artifact/1.0/artifact-1.0.pom")) - .as("because the URL is not from a known valid repository") + assertThat(explorer.isValidResourceUrl("example.com/com/example/artifact/1.0/artifact-1.0.pom")) + .as("because the URL is not a valid URL") .isFalse(); assertThat(explorer.isValidResourceUrl( - projectUrls, "https://repo.maven.apache.org/maven2/com/example/artifact/1.0/artifact-1.0.txt")) + "https://repo.maven.apache.org/maven2/com/example/artifact/1.0/artifact-1.0.txt")) .as("because the URL ends with an invalid extension") .isFalse(); }