Skip to content

Commit

Permalink
Merge branch 'main' into handle-erroneous-nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
timtebeek authored Sep 24, 2024
2 parents 1dbf818 + 25eb0f6 commit d6a680a
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 111 deletions.
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionSha256Sum=1541fa36599e12857140465f3c91a97409b4512501c26f9631fb113e392c5bd1
distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.openrewrite.java.isolated;


import com.sun.source.doctree.DocCommentTree;
import com.sun.source.tree.*;
import com.sun.source.util.TreePathScanner;
import com.sun.tools.javac.code.Flags;
Expand Down Expand Up @@ -2221,7 +2222,7 @@ private List<J.Annotation> collectAnnotations(Map<Integer, JCAnnotation> annotat
return annotations;
}

Space formatWithCommentTree(String prefix, JCTree tree, DCTree.@Nullable DCDocComment commentTree) {
Space formatWithCommentTree(String prefix, JCTree tree, @Nullable DocCommentTree commentTree) {
Space fmt = format(prefix);
if (commentTree != null) {
List<Comment> comments = fmt.getComments();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,10 @@ public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) {
if (versionTag.isPresent()) {
String version = versionTag.get().getValue().orElse(null);
if (version != null) {
if (version.trim().startsWith("${") && !newVersion.equals(getResolutionResult().getPom().getValue(version.trim()))) {
doAfterVisit(new ChangePropertyValue(version, newVersion, false, false).getVisitor());
if (version.trim().startsWith("${")) {
if (!newVersion.equals(getResolutionResult().getPom().getValue(version.trim()))) {
doAfterVisit(new ChangePropertyValue(version, newVersion, false, false).getVisitor());
}
} else if (!newVersion.equals(version)) {
doAfterVisit(new ChangeTagValueVisitor<>(versionTag.get(), newVersion));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Timer;
import lombok.Getter;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.HttpSenderExecutionContextView;
Expand All @@ -42,6 +43,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -256,23 +258,22 @@ public MavenMetadata downloadMetadata(GroupArtifactVersion gav, @Nullable Resolv
boolean cacheEmptyResult = false;
try {
String scheme = URI.create(repo.getUri()).getScheme();
String uri = repo.getUri() + (repo.getUri().endsWith("/") ? "" : "/") +
String baseUri = repo.getUri() + (repo.getUri().endsWith("/") ? "" : "/") +
requireNonNull(gav.getGroupId()).replace('.', '/') + '/' +
gav.getArtifactId() + '/' +
(gav.getVersion() == null ? "" : gav.getVersion() + '/') +
"maven-metadata.xml";
(gav.getVersion() == null ? "" : gav.getVersion() + '/');

if ("file".equals(scheme)) {
// A maven repository can be expressed as a URI with a file scheme
Path path = Paths.get(URI.create(uri));
Path path = Paths.get(URI.create(baseUri + "maven-metadata-local.xml"));
if (Files.exists(path)) {
MavenMetadata parsed = MavenMetadata.parse(Files.readAllBytes(path));
if (parsed != null) {
result = Optional.of(parsed);
}
}
} else {
byte[] responseBody = requestAsAuthenticatedOrAnonymous(repo, uri);
byte[] responseBody = requestAsAuthenticatedOrAnonymous(repo, baseUri + "maven-metadata.xml");
MavenMetadata parsed = MavenMetadata.parse(responseBody);
if (parsed != null) {
result = Optional.of(parsed);
Expand Down Expand Up @@ -392,7 +393,7 @@ public MavenMetadata downloadMetadata(GroupArtifactVersion gav, @Nullable Resolv
versions.add(path.getFileName().toString());
}
}
return new MavenMetadata.Versioning(versions, null, null);
return new MavenMetadata.Versioning(versions, null, null, null);
} catch (IOException e) {
throw new MavenDownloadingException("Unable to derive metadata from file repository. " + e.getMessage(), null, gav);
}
Expand Down Expand Up @@ -422,7 +423,7 @@ public MavenMetadata downloadMetadata(GroupArtifactVersion gav, @Nullable Resolv
return null;
}

return new MavenMetadata.Versioning(versions, null, null);
return new MavenMetadata.Versioning(versions, null, null, null);
}

String hrefToVersion(String href, String rootUri) {
Expand All @@ -446,10 +447,15 @@ protected MavenMetadata mergeMetadata(MavenMetadata m1, MavenMetadata m2) {
mergeVersions(m1.getVersioning().getVersions(), m2.getVersioning().getVersions()),
Stream.concat(m1.getVersioning().getSnapshotVersions() == null ? Stream.empty() : m1.getVersioning().getSnapshotVersions().stream(),
m2.getVersioning().getSnapshotVersions() == null ? Stream.empty() : m2.getVersioning().getSnapshotVersions().stream()).collect(toList()),
maxSnapshot(m1.getVersioning().getSnapshot(), m2.getVersioning().getSnapshot())
maxSnapshot(m1.getVersioning().getSnapshot(), m2.getVersioning().getSnapshot()),
maxLastUpdated(m1.getVersioning().getLastUpdated(), m2.getVersioning().getLastUpdated())
));
}

private @Nullable ZonedDateTime maxLastUpdated(@Nullable ZonedDateTime left, @Nullable ZonedDateTime right) {
return left == null ? right : right == null ? left : left.compareTo(right) >= 0 ? left : right;
}

private List<String> mergeVersions(List<String> versions1, List<String> versions2) {
Set<String> merged = new HashSet<>(versions1);
merged.addAll(versions2);
Expand Down Expand Up @@ -728,8 +734,7 @@ Collection<MavenRepository> distinctNormalizedRepositories(
return null;
}

String originalUrl = repository.getUri();
if ("file".equals(URI.create(originalUrl).getScheme())) {
if ("file".equals(URI.create(repository.getUri()).getScheme())) {
return repository;
}
result = mavenCache.getNormalizedRepository(repository);
Expand All @@ -739,63 +744,13 @@ Collection<MavenRepository> distinctNormalizedRepositories(
ctx.getResolutionListener().repositoryAccessFailed(repository.getUri(), new IllegalArgumentException("Repository " + repository.getUri() + " is not HTTP(S)."));
return null;
}

// Always prefer to use https, fallback to http only if https isn't available
// URLs are case-sensitive after the domain name, so it can be incorrect to lowerCase() a whole URL
// This regex accepts any capitalization of the letters in "http"
String httpsUri = repository.getUri().toLowerCase().startsWith("http:") ?
repository.getUri().replaceFirst("[hH][tT][tT][pP]://", "https://") :
repository.getUri();
if (!httpsUri.endsWith("/")) {
httpsUri += "/";
}

HttpSender.Request.Builder request = applyAuthenticationToRequest(repository, httpSender.head(httpsUri));
MavenRepository normalized = null;
try {
sendRequest(request.build());
normalized = repository.withUri(httpsUri).withKnownToExist(true);
} catch (Throwable t) {
if (t instanceof HttpSenderResponseException) {
HttpSenderResponseException e = (HttpSenderResponseException) t;
// response was returned from the server, but it was not a 200 OK. The server therefore exists.
if (e.isServerReached()) {
normalized = repository.withUri(httpsUri);
}
}
if (normalized == null) {
if (!httpsUri.equals(originalUrl)) {
try {
sendRequest(request.url(originalUrl).build());
normalized = new MavenRepository(
repository.getId(),
originalUrl,
repository.getReleases(),
repository.getSnapshots(),
repository.getUsername(),
repository.getPassword(),
repository.getTimeout());
} catch (HttpSenderResponseException e) {
//Response was returned from the server, but it was not a 200 OK. The server therefore exists.
if (e.isServerReached()) {
normalized = new MavenRepository(
repository.getId(),
originalUrl,
repository.getReleases(),
repository.getSnapshots(),
repository.getUsername(),
repository.getPassword(),
repository.getTimeout());
}
} catch (Throwable e) {
// ok to fall through here and cache a null
}
}
}
if (normalized == null) {
ctx.getResolutionListener().repositoryAccessFailed(repository.getUri(), t);
}
normalized = normalizeRepository(repository);
} catch (Throwable e) {
ctx.getResolutionListener().repositoryAccessFailed(repository.getUri(), e);
}

mavenCache.putNormalizedRepository(repository, normalized);
result = Optional.ofNullable(normalized);
}
Expand All @@ -808,6 +763,89 @@ Collection<MavenRepository> distinctNormalizedRepositories(
return result == null || !result.isPresent() ? null : applyAuthenticationToRepository(result.get());
}

@Nullable
MavenRepository normalizeRepository(MavenRepository repository) throws Throwable {
// Always prefer to use https, fallback to http only if https isn't available
// URLs are case-sensitive after the domain name, so it can be incorrect to lowerCase() a whole URL
// This regex accepts any capitalization of the letters in "http"
String originalUrl = repository.getUri();
String httpsUri = originalUrl.toLowerCase().startsWith("http:") ?
repository.getUri().replaceFirst("[hH][tT][tT][pP]://", "https://") :
repository.getUri();
if (!httpsUri.endsWith("/")) {
httpsUri += "/";
}

HttpSender.Request.Builder request = httpSender.options(httpsUri);
if (repository.getTimeout() != null) {
request = request.withConnectTimeout(repository.getTimeout())
.withReadTimeout(repository.getTimeout());
}

ReachabilityResult reachability = reachable(applyAuthenticationToRequest(repository, request));
if (reachability.isSuccess()) {
return repository.withUri(httpsUri);
}
reachability = reachable(applyAuthenticationToRequest(repository, request.withMethod(HttpSender.Method.HEAD).url(httpsUri)));
if (reachability.isReachable()) {
return repository.withUri(httpsUri);
}
if (!originalUrl.equals(httpsUri)) {
reachability = reachable(applyAuthenticationToRequest(repository, request.withMethod(HttpSender.Method.OPTIONS).url(originalUrl)));
if (reachability.isSuccess()) {
return repository.withUri(originalUrl);
}
reachability = reachable(applyAuthenticationToRequest(repository, request.withMethod(HttpSender.Method.HEAD).url(originalUrl)));
if (reachability.isReachable()) {
return repository.withUri(originalUrl);
}
}
// Won't be null if server is unreachable
throw Objects.requireNonNull(reachability.throwable);
}

@Value
private static class ReachabilityResult {
Reachability reachability;
@Nullable
Throwable throwable;

private static ReachabilityResult success() {
return new ReachabilityResult(Reachability.SUCCESS, null);
}

public boolean isReachable() {
return reachability == Reachability.SUCCESS || reachability == Reachability.ERROR;
}

public boolean isSuccess() {
return reachability == Reachability.SUCCESS;
}
}

enum Reachability {
SUCCESS,
ERROR,
UNREACHABLE;
}

private ReachabilityResult reachable(HttpSender.Request.Builder request) {
try {
sendRequest(request.build());
return ReachabilityResult.success();
} catch (Throwable t) {
if (t instanceof HttpSenderResponseException) {
HttpSenderResponseException e = (HttpSenderResponseException) t;
// response was returned from the server, but it was not a 200 OK. The server therefore exists.
if (e.isServerReached()) {
return new ReachabilityResult(Reachability.ERROR, t);
}
}
return new ReachabilityResult(Reachability.UNREACHABLE, t);
}
}


/**
* Replicates Apache Maven's behavior to attempt anonymous download if repository credentials prove invalid
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.fasterxml.jackson.dataformat.xml.XmlFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

Expand Down Expand Up @@ -63,6 +64,7 @@ public class MavenXmlMapper {
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.PUBLIC_ONLY))
.registerModule(new JavaTimeModule())
.registerModule(new StringTrimModule());

writeMapper = XmlMapper.builder(xmlFactory)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,27 @@
*/
package org.openrewrite.maven.tree;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Value;
import lombok.experimental.FieldDefaults;
import org.jspecify.annotations.Nullable;
import org.openrewrite.maven.internal.MavenXmlMapper;
import org.openrewrite.xml.XmlParser;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.util.List;

import static java.util.Collections.emptyList;

@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@Getter
public class MavenMetadata {
private static final XmlParser xmlParser = new XmlParser() {
@Override
public boolean accept(Path path) {
return super.accept(path) || path.toString().endsWith(".pom");
}
};

public static final MavenMetadata EMPTY = new MavenMetadata(new MavenMetadata.Versioning(emptyList(), emptyList(), null));
public static final MavenMetadata EMPTY = new MavenMetadata(new MavenMetadata.Versioning(emptyList(), emptyList(), null, null));

Versioning versioning;

Expand All @@ -62,10 +55,15 @@ public static class Versioning {
@Nullable
Snapshot snapshot;

@Nullable
ZonedDateTime lastUpdated;

public Versioning(
@JacksonXmlElementWrapper(localName = "versions") List<String> versions,
@JacksonXmlElementWrapper(localName = "snapshotVersions") @Nullable List<SnapshotVersion> snapshotVersions,
@Nullable Snapshot snapshot) {
@Nullable Snapshot snapshot,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyyMMddHHmmss", timezone = "UTC") @Nullable ZonedDateTime lastUpdated) {
this.lastUpdated = lastUpdated;
this.versions = versions;
this.snapshotVersions = snapshotVersions;
this.snapshot = snapshot;
Expand All @@ -83,7 +81,11 @@ public static MavenMetadata parse(InputStream document) {
public static @Nullable MavenMetadata parse(byte[] document) throws IOException {
MavenMetadata metadata = MavenXmlMapper.readMapper().readValue(document, MavenMetadata.class);
if (metadata != null && metadata.getVersioning() != null && metadata.getVersioning().getVersions() == null) {
return new MavenMetadata(new Versioning(emptyList(), metadata.getVersioning().getSnapshotVersions(), metadata.getVersioning().getSnapshot()));
return new MavenMetadata(new Versioning(
emptyList(),
metadata.getVersioning().getSnapshotVersions(),
metadata.getVersioning().getSnapshot(),
metadata.getVersioning().getLastUpdated()));
}
return metadata;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import org.openrewrite.maven.tree.MavenMetadata;

import java.io.IOException;
import java.time.ZonedDateTime;

import static java.time.ZoneOffset.UTC;
import static org.assertj.core.api.Assertions.assertThat;

class MavenMetadataTest {
Expand All @@ -46,6 +48,7 @@ void deserializeMetadata() throws IOException {

MavenMetadata parsed = MavenMetadata.parse(metadata.getBytes());
assertThat(parsed.getVersioning().getVersions()).hasSize(2);
assertThat(parsed.getVersioning().getLastUpdated()).isEqualTo(ZonedDateTime.of(2021, 1, 15, 4, 27, 54, 0, UTC));
}

@SuppressWarnings("ConstantConditions")
Expand Down Expand Up @@ -80,6 +83,7 @@ void deserializeSnapshotMetadata() throws IOException {
MavenMetadata parsed = MavenMetadata.parse(metadata.getBytes());
MavenMetadata.Versioning versioning = parsed.getVersioning();

assertThat(versioning.getLastUpdated()).isNull();
assertThat(versioning.getSnapshot().getTimestamp()).isEqualTo("20220927.033510");
assertThat(versioning.getSnapshot().getBuildNumber()).isEqualTo("223");
assertThat(versioning.getVersions()).isNotNull();
Expand Down
Loading

0 comments on commit d6a680a

Please sign in to comment.