Skip to content

Commit

Permalink
Negative local Maven repo lookup persists after installing the artifa…
Browse files Browse the repository at this point in the history
…ct, fixes #482
  • Loading branch information
gnodet committed Oct 7, 2021
1 parent dff50ac commit dae96da
Show file tree
Hide file tree
Showing 13 changed files with 560 additions and 1 deletion.
Expand Up @@ -17,16 +17,27 @@

import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.artifact.DefaultProjectArtifactsCache;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.WorkspaceRepository;
import org.eclipse.sisu.Priority;
import org.mvndaemon.mvnd.cache.Cache;
import org.mvndaemon.mvnd.cache.CacheFactory;
Expand All @@ -36,6 +47,119 @@
@Priority(10)
public class InvalidatingProjectArtifactsCache extends DefaultProjectArtifactsCache {

protected static class CacheKey
implements Key {

private final String groupId;

private final String artifactId;

private final String version;

private final Set<String> dependencyArtifacts;

private final WorkspaceRepository workspace;

private final LocalRepository localRepo;

private final List<RemoteRepository> repositories;

private final Set<String> collect;

private final Set<String> resolve;

private boolean aggregating;

private final int hashCode;

public CacheKey(MavenProject project, List<RemoteRepository> repositories,
Collection<String> scopesToCollect, Collection<String> scopesToResolve, boolean aggregating,
RepositorySystemSession session) {

groupId = project.getGroupId();
artifactId = project.getArtifactId();
version = project.getVersion();

Set<String> deps = new LinkedHashSet<>();
if (project.getDependencyArtifacts() != null) {
for (Artifact dep : project.getDependencyArtifacts()) {
deps.add(dep.toString());
}
}
dependencyArtifacts = Collections.unmodifiableSet(deps);

workspace = RepositoryUtils.getWorkspace(session);
this.localRepo = session.getLocalRepository();
this.repositories = new ArrayList<>(repositories.size());
for (RemoteRepository repository : repositories) {
if (repository.isRepositoryManager()) {
this.repositories.addAll(repository.getMirroredRepositories());
} else {
this.repositories.add(repository);
}
}
collect = scopesToCollect == null
? Collections.<String> emptySet()
: Collections.unmodifiableSet(new HashSet<>(scopesToCollect));
resolve = scopesToResolve == null
? Collections.<String> emptySet()
: Collections.unmodifiableSet(new HashSet<>(scopesToResolve));
this.aggregating = aggregating;

int hash = 17;
hash = hash * 31 + Objects.hashCode(groupId);
hash = hash * 31 + Objects.hashCode(artifactId);
hash = hash * 31 + Objects.hashCode(version);
hash = hash * 31 + Objects.hashCode(dependencyArtifacts);
hash = hash * 31 + Objects.hashCode(workspace);
hash = hash * 31 + Objects.hashCode(localRepo);
hash = hash * 31 + RepositoryUtils.repositoriesHashCode(repositories);
hash = hash * 31 + Objects.hashCode(collect);
hash = hash * 31 + Objects.hashCode(resolve);
hash = hash * 31 + Objects.hashCode(aggregating);
this.hashCode = hash;
}

public boolean matches(String groupId, String artifactId, String version) {
return Objects.equals(this.groupId, groupId)
&& Objects.equals(this.artifactId, artifactId)
&& Objects.equals(this.version, version);
}

@Override
public String toString() {
return groupId + ":" + artifactId + ":" + version;
}

@Override
public int hashCode() {
return hashCode;
}

@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}

if (!(o instanceof CacheKey)) {
return false;
}

CacheKey that = (CacheKey) o;

return Objects.equals(groupId, that.groupId) && Objects.equals(artifactId, that.artifactId)
&& Objects.equals(version, that.version)
&& Objects.equals(dependencyArtifacts, that.dependencyArtifacts)
&& Objects.equals(workspace, that.workspace)
&& Objects.equals(localRepo, that.localRepo)
&& RepositoryUtils.repositoriesEquals(repositories, that.repositories)
&& Objects.equals(collect, that.collect)
&& Objects.equals(resolve, that.resolve)
&& aggregating == that.aggregating;
}
}

static class Record implements org.mvndaemon.mvnd.cache.CacheRecord {
private final CacheRecord record;

Expand Down Expand Up @@ -66,6 +190,13 @@ public InvalidatingProjectArtifactsCache(CacheFactory cacheFactory) {
this.cache = cacheFactory.newCache();
}

@Override
public Key createKey(MavenProject project, Collection<String> scopesToCollect, Collection<String> scopesToResolve,
boolean aggregating, RepositorySystemSession session) {
return new CacheKey(project, project.getRemoteProjectRepositories(), scopesToCollect, scopesToResolve,
aggregating, session);
}

@Override
public CacheRecord get(Key key) throws LifecycleExecutionException {
Record r = cache.get(key);
Expand Down
Expand Up @@ -30,6 +30,7 @@
import org.apache.maven.eventspy.EventSpy;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.project.MavenProject;
import org.eclipse.sisu.Typed;
import org.mvndaemon.mvnd.common.Environment;
import org.slf4j.Logger;
Expand All @@ -44,15 +45,19 @@ public class InvalidatingRealmCacheEventSpy extends AbstractEventSpy {

private final InvalidatingPluginRealmCache pluginCache;
private final InvalidatingExtensionRealmCache extensionCache;
private final InvalidatingProjectArtifactsCache projectArtifactsCache;
private Path multiModuleProjectDirectory;
private String pattern;
private PathMatcher matcher;

@Inject
public InvalidatingRealmCacheEventSpy(
InvalidatingPluginRealmCache cache, InvalidatingExtensionRealmCache extensionCache) {
InvalidatingPluginRealmCache cache,
InvalidatingExtensionRealmCache extensionCache,
InvalidatingProjectArtifactsCache projectArtifactsCache) {
this.pluginCache = cache;
this.extensionCache = extensionCache;
this.projectArtifactsCache = projectArtifactsCache;
}

@Override
Expand Down Expand Up @@ -97,12 +102,21 @@ public void onEvent(Object event) throws Exception {
/* Evict the entries referring to jars under multiModuleProjectDirectory */
pluginCache.cache.removeIf(this::shouldEvict);
extensionCache.cache.removeIf(this::shouldEvict);
MavenExecutionResult mer = (MavenExecutionResult) event;
List<MavenProject> projects = mer.getTopologicallySortedProjects();
projectArtifactsCache.cache
.removeIf((k, r) -> shouldEvict(projects, (InvalidatingProjectArtifactsCache.CacheKey) k, r));
}
} catch (Exception e) {
LOG.warn("Could not notify CliPluginRealmCache", e);
}
}

private boolean shouldEvict(List<MavenProject> projects, InvalidatingProjectArtifactsCache.CacheKey k,
InvalidatingProjectArtifactsCache.Record v) {
return projects.stream().anyMatch(p -> k.matches(p.getGroupId(), p.getArtifactId(), p.getVersion()));
}

private boolean shouldEvict(InvalidatingPluginRealmCache.Key k, InvalidatingPluginRealmCache.Record v) {
try {
for (URL url : v.record.getRealm().getURLs()) {
Expand Down
@@ -0,0 +1,52 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mvndaemon.mvnd.it;

import java.io.IOException;
import javax.inject.Inject;
import org.junit.jupiter.api.Test;
import org.mvndaemon.mvnd.assertj.TestClientOutput;
import org.mvndaemon.mvnd.junit.ClientFactory;
import org.mvndaemon.mvnd.junit.MvndTest;
import org.mvndaemon.mvnd.junit.TestParameters;

@MvndTest(projectDir = "src/test/projects/multi-lookup")
public class MultiLookupTest {

@Inject
ClientFactory clientFactory;

@Inject
TestParameters parameters;

@Test
void cleanInstall() throws IOException, InterruptedException {

final TestClientOutput output = new TestClientOutput();

clientFactory
.newClient(parameters.cd(parameters.getTestDir().resolve("project/hello")))
.execute(output, "clean", "install", "-e").assertFailure();

clientFactory
.newClient(parameters)
.execute(output, "clean", "install", "-e").assertSuccess();

clientFactory
.newClient(parameters.cd(parameters.getTestDir().resolve("project/hello")))
.execute(output, "clean", "install", "-e").assertSuccess();
}
}
@@ -0,0 +1,3 @@
-Dmaven.wagon.httpconnectionManager.ttlSeconds=120
-Dmaven.wagon.http.retryHandler.requestSentEnabled=true
-Dmaven.wagon.http.retryHandler.count=10
30 changes: 30 additions & 0 deletions integration-tests/src/test/projects/multi-lookup/api/pom.xml
@@ -0,0 +1,30 @@
<!--
Copyright 2019 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mvndaemon.mvnd.test.multi-lookup</groupId>
<artifactId>multi-lookup</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>multi-lookup-api</artifactId>

</project>
@@ -0,0 +1,22 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mvndaemon.mvnd.test.multi.module.api;

public interface Greeting {

public String greet();

}
44 changes: 44 additions & 0 deletions integration-tests/src/test/projects/multi-lookup/hello/pom.xml
@@ -0,0 +1,44 @@
<!--
Copyright 2019 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mvndaemon.mvnd.test.multi-lookup</groupId>
<artifactId>multi-lookup</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>multi-lookup-hello</artifactId>

<dependencies>
<dependency>
<groupId>org.mvndaemon.mvnd.test.multi-lookup</groupId>
<artifactId>multi-lookup-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.2</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
@@ -0,0 +1,26 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mvndaemon.mvnd.test.multi.module.hello;

import org.mvndaemon.mvnd.test.multi.module.api.Greeting;

public class Hello implements Greeting {

public String greet() {
return "Hello";
}

}

0 comments on commit dae96da

Please sign in to comment.