Skip to content

Commit

Permalink
Maven Central search results should be cached #263
Browse files Browse the repository at this point in the history
Fixes: #263
  • Loading branch information
vrubezhny committed Jul 26, 2023
1 parent d20ffcf commit 71b92d7
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -29,6 +31,8 @@
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.lemminx.extensions.maven.MavenLemminxExtension;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

Expand Down Expand Up @@ -56,8 +60,35 @@ public class RemoteCentralRepositorySearcher {
public static boolean disableCentralSearch = Boolean
.parseBoolean(System.getProperty(RemoteCentralRepositorySearcher.class.getName() + ".disableCentralSearch"));

public static int cacheExpirationTimeoutMinutes = Integer
.parseInt(System.getProperty(RemoteCentralRepositorySearcher.class.getName() + ".cacheExpirationTimeoutMinutes", "-1"), 10);

private OkHttpClient client = new OkHttpClient();

private static class CacheManager<K,V> {
private static long DEFAULT_EXPIRATION_TIMEOUT = 30;

private Cache <K, V> cache;
private long cacheExpirationTimeout = DEFAULT_EXPIRATION_TIMEOUT;

public CacheManager(long expirationTimeoutMinutes) {
cacheExpirationTimeout = expirationTimeoutMinutes != -1 ?
expirationTimeoutMinutes : DEFAULT_EXPIRATION_TIMEOUT;
cache = CacheBuilder.newBuilder()
.expireAfterWrite(cacheExpirationTimeout, TimeUnit.MINUTES)
.build();
}

V get(K key, Callable<? extends V> loader) {
try {
return cache.get(key, loader);
} catch (ExecutionException e) {
LOGGER.log(Level.SEVERE, "Maven Central Repo search failed: " + e.getMessage(), e);
}
return null;
}
}

public RemoteCentralRepositorySearcher(MavenLemminxExtension lemminxMavenPlugin) {
}

Expand All @@ -68,74 +99,95 @@ public Collection<Artifact> getArtifacts(Dependency artifactToSearch) {
return disableCentralSearch ? Collections.emptySet() : internalGetArtifacts(artifactToSearch, PACKAGING_TYPE_JAR);
}

public Set<ArtifactVersion> getArtifactVersions(Dependency artifactToSearch) {
public Collection<ArtifactVersion> getArtifactVersions(Dependency artifactToSearch) {
return disableCentralSearch ? Collections.emptySet() : internalGetArtifactVersions(artifactToSearch, PACKAGING_TYPE_JAR);
}

public Set<String> getGroupIds(Dependency artifactToSearch) {
public Collection<String> getGroupIds(Dependency artifactToSearch) {
return disableCentralSearch ? Collections.emptySet() : internalGetGroupIds(artifactToSearch, PACKAGING_TYPE_JAR);
}

public Collection<Artifact> getPluginArtifacts(Dependency artifactToSearch) {
return disableCentralSearch ? Collections.emptySet() : internalGetArtifacts(artifactToSearch, PACKAGING_TYPE_MAVEN_PLUGIN);
}

public Set<ArtifactVersion> getPluginArtifactVersions(Dependency artifactToSearch) {
public Collection<ArtifactVersion> getPluginArtifactVersions(Dependency artifactToSearch) {
return disableCentralSearch ? Collections.emptySet() : internalGetArtifactVersions(artifactToSearch, PACKAGING_TYPE_MAVEN_PLUGIN);
}

public Set<String> getPluginGroupIds(Dependency artifactToSearch) {
public Collection<String> getPluginGroupIds(Dependency artifactToSearch) {
return disableCentralSearch ? Collections.emptySet() : internalGetGroupIds(artifactToSearch, PACKAGING_TYPE_MAVEN_PLUGIN);
}

private static CacheManager<Request, Collection<Artifact>> artifactsCache = new CacheManager<>(cacheExpirationTimeoutMinutes);
private Collection<Artifact> internalGetArtifacts(Dependency artifactToSearch, String packaging) {
Request request = createArtifactIdsRequesty(artifactToSearch, packaging);
JsonObject responseBody = getResponseBody(request, artifactToSearch);
if (responseBody == null || responseBody.get("numFound").getAsInt() <= 0 ) {
return Collections.emptyList();
}
return artifactsCache.get(request, new Callable<Collection<Artifact>>() {
@Override
public Collection<Artifact> call() throws Exception {
JsonObject responseBody = getResponseBody(request, artifactToSearch);
if (responseBody == null || responseBody.get("numFound").getAsInt() <= 0 ) {
return Collections.emptyList();
}

List<Artifact> artifactInfos = new ArrayList<>();
responseBody.get("docs").getAsJsonArray().forEach(d -> {
artifactInfos.add(toArtifactInfo(d.getAsJsonObject()));
List<Artifact> artifactInfos = new ArrayList<>();
responseBody.get("docs").getAsJsonArray().forEach(d -> {
artifactInfos.add(toArtifactInfo(d.getAsJsonObject()));
});

return artifactInfos;
}
});
return artifactInfos;
}

private Set<ArtifactVersion> internalGetArtifactVersions(Dependency artifactToSearch, String packaging) {

private static CacheManager<Request, Collection<ArtifactVersion>> artifactVersionsCache = new CacheManager<>(cacheExpirationTimeoutMinutes);
private Collection<ArtifactVersion> internalGetArtifactVersions(Dependency artifactToSearch, String packaging) {
if (isEmpty(artifactToSearch.getArtifactId())) {
return Collections.emptySet();
}

Request request = createArtifactVersionsRequest(artifactToSearch, packaging);
JsonObject responseBody = getResponseBody(request, artifactToSearch);
if (responseBody == null || responseBody.get("numFound").getAsInt() <= 0 ) {
return Collections.emptySet();
}

Set<ArtifactVersion> artifactVersions = new HashSet<ArtifactVersion>();
responseBody.get("docs").getAsJsonArray().forEach(d -> {
artifactVersions.add(new DefaultArtifactVersion(d.getAsJsonObject().get(VERSION).getAsString()));
return artifactVersionsCache.get(request, new Callable<Collection<ArtifactVersion>>() {
@Override
public Collection<ArtifactVersion> call() throws Exception {
JsonObject responseBody = getResponseBody(request, artifactToSearch);
if (responseBody == null || responseBody.get("numFound").getAsInt() <= 0 ) {
return Collections.emptySet();
}

Set<ArtifactVersion> artifactVersions = new HashSet<ArtifactVersion>();
responseBody.get("docs").getAsJsonArray().forEach(d -> {
artifactVersions.add(new DefaultArtifactVersion(d.getAsJsonObject().get(VERSION).getAsString()));
});

return artifactVersions;
}
});
return artifactVersions;
}

private Set<String> internalGetGroupIds(Dependency artifactToSearch, String packaging) {
private static CacheManager<Request, Collection<String>> groupIdsCache = new CacheManager<>(cacheExpirationTimeoutMinutes);
private Collection<String> internalGetGroupIds(Dependency artifactToSearch, String packaging) {
if (isEmpty(artifactToSearch.getGroupId())) {
return Collections.emptySet();
}

Request request = createGroupIdsRequest(artifactToSearch, packaging);
JsonObject responseBody = getResponseBody(request, artifactToSearch);
if (responseBody == null || responseBody.get("numFound").getAsInt() <= 0 ) {
return Collections.emptySet();
}

Set<String> artifactGroupIds = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
responseBody.get("docs").getAsJsonArray().forEach(d -> {
artifactGroupIds.add(d.getAsJsonObject().get(GROUP_ID).getAsString());
return groupIdsCache.get(request, new Callable<Collection<String>>() {
@Override
public Collection<String> call() throws Exception {
JsonObject responseBody = getResponseBody(request, artifactToSearch);
if (responseBody == null || responseBody.get("numFound").getAsInt() <= 0 ) {
return Collections.emptySet();
}

Collection<String> artifactGroupIds = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
responseBody.get("docs").getAsJsonArray().forEach(d -> {
artifactGroupIds.add(d.getAsJsonObject().get(GROUP_ID).getAsString());
});

return artifactGroupIds;
}
});
return artifactGroupIds;
}

private static Request createGroupIdsRequest(Dependency artifactToSearch, String packaging) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Collection;
import java.util.Set;

import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.model.Dependency;
Expand Down Expand Up @@ -78,7 +77,7 @@ void testGetArtifactVersionss() {
dep.setVersion(V);
System.out.println("\nDep: " + dep.toString());

Set<ArtifactVersion> artifactVersions = repoSearcher.getArtifactVersions(dep);
Collection<ArtifactVersion> artifactVersions = repoSearcher.getArtifactVersions(dep);
assertNotNull(artifactVersions);
assertFalse(artifactVersions.isEmpty());

Expand All @@ -95,7 +94,7 @@ void testGetGroupIdss() {
dep.setGroupId(G);
System.out.println("\nDep: " + dep.toString());

Set<String> artifactGroups = repoSearcher.getGroupIds(dep);
Collection<String> artifactGroups = repoSearcher.getGroupIds(dep);
assertNotNull(artifactGroups);
assertFalse(artifactGroups.isEmpty());

Expand Down Expand Up @@ -164,7 +163,7 @@ void testGetPluginArtifactVersionss() {

System.out.println("\nDep: " + dep.toString());

Set<ArtifactVersion> artifactVersions = repoSearcher.getPluginArtifactVersions(dep);
Collection<ArtifactVersion> artifactVersions = repoSearcher.getPluginArtifactVersions(dep);
assertNotNull(artifactVersions);
assertFalse(artifactVersions.isEmpty());

Expand All @@ -180,7 +179,7 @@ void testGetPluginGroupIdss() {
dep.setGroupId(G);
System.out.println("\nDep: " + dep.toString());

Set<String> artifactGroups = repoSearcher.getPluginGroupIds(dep);
Collection<String> artifactGroups = repoSearcher.getPluginGroupIds(dep);
assertNotNull(artifactGroups);
assertFalse(artifactGroups.isEmpty());

Expand Down

0 comments on commit 71b92d7

Please sign in to comment.