Skip to content

Commit 75d062b

Browse files
committed
Lay down validation
1 parent 77472fa commit 75d062b

28 files changed

+797
-39
lines changed

core/src/main/java/eu/maveniverse/maven/njord/shared/NjordSession.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@ public interface NjordSession extends Closeable {
4949
/**
5050
* Drops all session-bound artifact stores.
5151
*/
52-
void dropSessionArtifactStores();
52+
boolean dropSessionArtifactStores();
5353
}

core/src/main/java/eu/maveniverse/maven/njord/shared/impl/DefaultNjordSession.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Map;
2929
import java.util.Optional;
3030
import java.util.concurrent.ConcurrentHashMap;
31+
import java.util.concurrent.atomic.AtomicBoolean;
3132
import org.eclipse.aether.RepositorySystemSession;
3233

3334
public class DefaultNjordSession extends CloseableConfigSupport<Config> implements NjordSession {
@@ -133,19 +134,22 @@ public ArtifactStore getOrCreateSessionArtifactStore(String uri) {
133134
}
134135

135136
@Override
136-
public void dropSessionArtifactStores() {
137+
public boolean dropSessionArtifactStores() {
137138
ConcurrentHashMap<String, String> sessionBoundStore = (ConcurrentHashMap<String, String>)
138139
session.getData().computeIfAbsent(SESSION_BOUND_STORES_KEY, () -> new ConcurrentHashMap<>());
140+
AtomicBoolean result = new AtomicBoolean(false);
139141
sessionBoundStore.values().forEach(n -> {
140142
try {
141143
Optional<ArtifactStore> artifactStore = internalArtifactStoreManager.selectArtifactStore(n);
142144
if (artifactStore.isPresent()) {
143145
internalArtifactStoreManager.dropArtifactStore(artifactStore.orElseThrow());
146+
result.set(true);
144147
}
145148
} catch (IOException e) {
146149
logger.warn("Could not select ArtifactStore with name {}", n, e);
147150
}
148151
});
152+
return result.get();
149153
}
150154

151155
@Override

core/src/main/java/eu/maveniverse/maven/njord/shared/impl/publisher/ArtifactStorePublisherSupport.java

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public abstract class ArtifactStorePublisherSupport extends CloseableSupport imp
3030
protected final RemoteRepository targetSnapshotRepository;
3131
protected final RemoteRepository serviceReleaseRepository;
3232
protected final RemoteRepository serviceSnapshotRepository;
33+
protected final ArtifactStoreValidator releaseValidator;
34+
protected final ArtifactStoreValidator snapshotValidator;
3335

3436
protected ArtifactStorePublisherSupport(
3537
RepositorySystem repositorySystem,
@@ -39,7 +41,9 @@ protected ArtifactStorePublisherSupport(
3941
RemoteRepository targetReleaseRepository,
4042
RemoteRepository targetSnapshotRepository,
4143
RemoteRepository serviceReleaseRepository,
42-
RemoteRepository serviceSnapshotRepository) {
44+
RemoteRepository serviceSnapshotRepository,
45+
ArtifactStoreValidator releaseValidator,
46+
ArtifactStoreValidator snapshotValidator) {
4347
this.repositorySystem = requireNonNull(repositorySystem);
4448
this.session = requireNonNull(session);
4549
this.name = requireNonNull(name);
@@ -48,6 +52,8 @@ protected ArtifactStorePublisherSupport(
4852
this.targetSnapshotRepository = targetSnapshotRepository;
4953
this.serviceReleaseRepository = serviceReleaseRepository;
5054
this.serviceSnapshotRepository = serviceSnapshotRepository;
55+
this.releaseValidator = releaseValidator;
56+
this.snapshotValidator = snapshotValidator;
5157
}
5258

5359
@Override
@@ -82,23 +88,34 @@ public Optional<RemoteRepository> serviceSnapshotRepository() {
8288

8389
@Override
8490
public Optional<ArtifactStoreValidator> releaseValidator() {
85-
return Optional.empty();
91+
return Optional.ofNullable(releaseValidator);
8692
}
8793

8894
@Override
8995
public Optional<ArtifactStoreValidator> snapshotValidator() {
90-
return Optional.empty();
96+
return Optional.ofNullable(snapshotValidator);
9197
}
9298

9399
@Override
94-
public abstract void publish(ArtifactStore artifactStore) throws IOException;
95-
96-
protected void validateArtifactStore(ArtifactStore artifactStore) throws IOException {
100+
public Optional<ArtifactStoreValidator.ValidationResult> validate(ArtifactStore artifactStore) throws IOException {
101+
requireNonNull(artifactStore);
97102
Optional<ArtifactStoreValidator> vo =
98103
artifactStore.repositoryMode() == RepositoryMode.RELEASE ? releaseValidator() : snapshotValidator();
99104
if (vo.isPresent()) {
100-
ArtifactStoreValidator.ValidationResult vr = vo.orElseThrow().validate(artifactStore);
101-
logger.error("ArtifactStore {} validation result:", artifactStore);
105+
return Optional.of(vo.orElseThrow().validate(artifactStore));
106+
} else {
107+
return Optional.empty();
108+
}
109+
}
110+
111+
@Override
112+
public abstract void publish(ArtifactStore artifactStore) throws IOException;
113+
114+
protected void validateArtifactStore(ArtifactStore artifactStore) throws IOException {
115+
Optional<ArtifactStoreValidator.ValidationResult> vro = validate(artifactStore);
116+
if (vro.isPresent()) {
117+
ArtifactStoreValidator.ValidationResult vr = vro.orElseThrow();
118+
logger.info("Validation results:");
102119
AtomicInteger counter = new AtomicInteger(0);
103120
for (String msg : vr.error()) {
104121
logger.error(" {}. {}", counter.incrementAndGet(), msg);
@@ -112,7 +129,11 @@ protected void validateArtifactStore(ArtifactStore artifactStore) throws IOExcep
112129
if (!vr.isValid()) {
113130
logger.error("ArtifactStore {} failed validation", artifactStore);
114131
throw new IOException("Validation failed");
132+
} else {
133+
logger.info("ArtifactStore {} passed validation", artifactStore);
115134
}
135+
} else {
136+
logger.info("Not validated artifact store, no validator set for publisher {}", name());
116137
}
117138
}
118139
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2023-2024 Maveniverse Org.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v20.html
7+
*/
8+
package eu.maveniverse.maven.njord.shared.impl.publisher;
9+
10+
import static java.util.Objects.requireNonNull;
11+
12+
import eu.maveniverse.maven.njord.shared.Config;
13+
import eu.maveniverse.maven.njord.shared.publisher.ArtifactStoreValidator;
14+
import eu.maveniverse.maven.njord.shared.publisher.ArtifactStoreValidatorFactory;
15+
import eu.maveniverse.maven.njord.shared.publisher.spi.Validator;
16+
import eu.maveniverse.maven.njord.shared.publisher.spi.ValidatorFactory;
17+
import java.util.ArrayList;
18+
import java.util.List;
19+
import javax.inject.Inject;
20+
import javax.inject.Named;
21+
import javax.inject.Singleton;
22+
import org.eclipse.aether.RepositorySystemSession;
23+
24+
@Singleton
25+
@Named(CentralArtifactStoreValidatorFactory.NAME)
26+
public class CentralArtifactStoreValidatorFactory implements ArtifactStoreValidatorFactory {
27+
public static final String NAME = "central";
28+
29+
private final List<ValidatorFactory> validatorFactories;
30+
31+
@Inject
32+
public CentralArtifactStoreValidatorFactory(List<ValidatorFactory> validatorFactories) {
33+
this.validatorFactories = requireNonNull(validatorFactories);
34+
}
35+
36+
@Override
37+
public ArtifactStoreValidator create(RepositorySystemSession session, Config config) {
38+
ArrayList<Validator> validators = new ArrayList<>();
39+
for (ValidatorFactory factory : this.validatorFactories) {
40+
validators.add(factory.create(session, config));
41+
}
42+
return new DefaultArtifactStoreValidator(NAME, "Central Validator", validators);
43+
}
44+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright (c) 2023-2024 Maveniverse Org.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v20.html
7+
*/
8+
package eu.maveniverse.maven.njord.shared.impl.publisher;
9+
10+
import static java.util.Objects.requireNonNull;
11+
12+
import eu.maveniverse.maven.njord.shared.publisher.ArtifactStoreValidator;
13+
import eu.maveniverse.maven.njord.shared.publisher.spi.Validator;
14+
import eu.maveniverse.maven.njord.shared.store.ArtifactStore;
15+
import java.io.IOException;
16+
import java.util.Collection;
17+
import java.util.List;
18+
import java.util.concurrent.ConcurrentHashMap;
19+
import java.util.concurrent.CopyOnWriteArrayList;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
public class DefaultArtifactStoreValidator implements ArtifactStoreValidator {
24+
protected final Logger logger = LoggerFactory.getLogger(getClass());
25+
26+
private final String name;
27+
private final String description;
28+
private final Collection<Validator> validators;
29+
30+
public DefaultArtifactStoreValidator(String name, String description, Collection<Validator> validators) {
31+
this.name = requireNonNull(name);
32+
this.description = requireNonNull(description);
33+
this.validators = requireNonNull(validators);
34+
}
35+
36+
@Override
37+
public String name() {
38+
return name;
39+
}
40+
41+
@Override
42+
public String description() {
43+
return description;
44+
}
45+
46+
@Override
47+
public ValidationResult validate(ArtifactStore artifactStore) throws IOException {
48+
VR vr = new VR(description());
49+
for (Validator validator : validators) {
50+
validator.validate(artifactStore, vr.child(validator.description()));
51+
}
52+
return vr;
53+
}
54+
55+
private static final class VR implements ValidationResult, Validator.ValidationResultCollector {
56+
private final String name;
57+
private final CopyOnWriteArrayList<String> info = new CopyOnWriteArrayList<>();
58+
private final CopyOnWriteArrayList<String> warnings = new CopyOnWriteArrayList<>();
59+
private final CopyOnWriteArrayList<String> errors = new CopyOnWriteArrayList<>();
60+
private final ConcurrentHashMap<String, VR> children = new ConcurrentHashMap<>();
61+
62+
private VR(String name) {
63+
this.name = requireNonNull(name);
64+
}
65+
66+
@Override
67+
public String name() {
68+
return name;
69+
}
70+
71+
@Override
72+
public Collection<String> info() {
73+
return List.copyOf(info);
74+
}
75+
76+
@Override
77+
public Collection<String> warning() {
78+
return List.copyOf(warnings);
79+
}
80+
81+
@Override
82+
public Collection<String> error() {
83+
return List.copyOf(errors);
84+
}
85+
86+
@Override
87+
public Collection<ValidationResult> children() {
88+
return List.copyOf(children.values());
89+
}
90+
91+
@Override
92+
public Validator.ValidationResultCollector addInfo(String msg) {
93+
info.add(msg);
94+
return this;
95+
}
96+
97+
@Override
98+
public Validator.ValidationResultCollector addWarning(String msg) {
99+
warnings.add(msg);
100+
return this;
101+
}
102+
103+
@Override
104+
public Validator.ValidationResultCollector addError(String msg) {
105+
errors.add(msg);
106+
return this;
107+
}
108+
109+
@Override
110+
public Validator.ValidationResultCollector child(String name) {
111+
VR child = new VR(name);
112+
children.put(name, child);
113+
return child;
114+
}
115+
}
116+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) 2023-2024 Maveniverse Org.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v20.html
7+
*/
8+
package eu.maveniverse.maven.njord.shared.impl.publisher.spi;
9+
10+
import eu.maveniverse.maven.njord.shared.Config;
11+
import eu.maveniverse.maven.njord.shared.publisher.spi.Validator;
12+
import eu.maveniverse.maven.njord.shared.publisher.spi.ValidatorFactory;
13+
import eu.maveniverse.maven.njord.shared.store.ArtifactStore;
14+
import java.io.ByteArrayOutputStream;
15+
import java.io.IOException;
16+
import java.io.InputStream;
17+
import java.nio.charset.StandardCharsets;
18+
import java.util.HashSet;
19+
import java.util.Map;
20+
import java.util.Objects;
21+
import java.util.Optional;
22+
import javax.inject.Named;
23+
import javax.inject.Singleton;
24+
import org.eclipse.aether.RepositorySystemSession;
25+
import org.eclipse.aether.artifact.Artifact;
26+
import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
27+
import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmHelper;
28+
import org.eclipse.aether.util.artifact.ArtifactIdUtils;
29+
import org.eclipse.aether.util.artifact.SubArtifact;
30+
31+
/**
32+
* Verifies checksum for every artifact.
33+
*/
34+
@Singleton
35+
@Named(ArtifactChecksumValidatorFactory.NAME)
36+
public class ArtifactChecksumValidatorFactory extends ValidatorSupport implements ValidatorFactory {
37+
public static final String NAME = "artifact-checksum";
38+
39+
public ArtifactChecksumValidatorFactory() {
40+
super(NAME, "Artifact Checksum Validator");
41+
}
42+
43+
@Override
44+
public Validator create(RepositorySystemSession session, Config config) {
45+
return this;
46+
}
47+
48+
@Override
49+
public void validate(ArtifactStore artifactStore, ValidationResultCollector collector) throws IOException {
50+
for (Artifact artifact : artifactStore.artifacts()) {
51+
if (artifactStore.omitChecksumsForExtensions().stream()
52+
.noneMatch(e -> artifact.getExtension().endsWith(e))) {
53+
Map<String, String> checksums = ChecksumAlgorithmHelper.calculate(
54+
artifact.getFile(), artifactStore.checksumAlgorithmFactories());
55+
ValidationResultCollector chkCollector = collector.child(ArtifactIdUtils.toId(artifact));
56+
HashSet<String> algOk = new HashSet<>();
57+
HashSet<String> algMissing = new HashSet<>();
58+
HashSet<String> algMismatch = new HashSet<>();
59+
for (ChecksumAlgorithmFactory algorithmFactory : artifactStore.checksumAlgorithmFactories()) {
60+
String calculated = checksums.get(algorithmFactory.getName());
61+
String deployed = null;
62+
Artifact checksumArtifact = new SubArtifact(
63+
artifact, "*", artifact.getExtension() + "." + algorithmFactory.getFileExtension());
64+
Optional<InputStream> co = artifactStore.artifactContent(checksumArtifact);
65+
if (co.isPresent()) {
66+
try (InputStream in = co.orElseThrow()) {
67+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
68+
in.transferTo(bos);
69+
deployed = bos.toString(StandardCharsets.UTF_8);
70+
}
71+
if (Objects.equals(calculated, deployed)) {
72+
algOk.add(algorithmFactory.getName());
73+
} else {
74+
algMismatch.add(algorithmFactory.getName());
75+
}
76+
} else {
77+
algMissing.add(algorithmFactory.getName());
78+
}
79+
}
80+
if (!algOk.isEmpty()) {
81+
chkCollector.addInfo("OK: " + String.join(", ", algOk));
82+
}
83+
if (!algMissing.isEmpty()) {
84+
chkCollector.addError("MISSING: " + String.join(", ", algMissing));
85+
}
86+
if (!algMismatch.isEmpty()) {
87+
chkCollector.addError("MISMATCH: " + String.join(", ", algMismatch));
88+
}
89+
}
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)