Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions maven-external-dependency-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,25 @@
[![Build Status](https://travis-ci.org/openam-org-ru/maven-external-dependency-plugin.svg)](https://travis-ci.org/openam-org-ru/maven-external-dependency-plugin)

Forked from https://code.google.com/archive/p/maven-external-dependency-plugin/

## Download retry configuration

To make external artifact downloads resilient to transient network errors
(e.g. `java.net.ConnectException: Connection timed out`), the
`resolve-external` goal retries failed downloads.

Mojo-level configuration parameters (apply to every `<artifactItem>` unless
overridden on the artifact itself):

| Parameter | Default | Description |
|-------------------------|---------|--------------------------------------------------------------|
| `downloadRetryAttempts` | `5` | Number of attempts before giving up on a single download. |
| `downloadTimeout` | `10000` | Per-attempt connection/transfer timeout in milliseconds. |
| `downloadRetryDelay` | `2000` | Delay in milliseconds between retry attempts. |

Per-artifact overrides (defined inside `<artifactItem>`): `timeout`,
`retryAttempts`, `retryDelay`. When set, they take precedence over the
Mojo-level defaults.

Authorization failures are not retried; the build fails immediately on
`AuthorizationException`.
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,24 @@ public class ArtifactItem
*/
private Integer timeout;

/**
* Per-artifact override for the number of attempts to download this
* artifact in case of transient network failures. When unset, the
* Mojo-level {@code downloadRetryAttempts} parameter (default 5) is used.
*
* @parameter
*/
private Integer retryAttempts;

/**
* Per-artifact override for the delay in millis between download retry
* attempts. When unset, the Mojo-level {@code downloadRetryDelay}
* parameter (default 2000 ms) is used.
*
* @parameter
*/
private Integer retryDelay;

/**
* Packaging type of the artifact to be installed.
*
Expand Down Expand Up @@ -387,7 +405,17 @@ public final void setDownloadUrl(final String downloadUrl)
*/
public final Integer getTimeout()
{
return (timeout==null||timeout<=0)?5000:timeout;
return (timeout==null||timeout<=0)?10000:timeout;
}

/**
* @return Raw timeout value as configured (may be null) so callers can
* distinguish an explicitly set per-artifact timeout from the
* default fallback returned by {@link #getTimeout()}.
*/
public final Integer getTimeoutRaw()
{
return timeout;
}

/**
Expand All @@ -399,6 +427,41 @@ public final void setTimeout(final Integer timeout)
this.timeout = timeout;
}

/**
* @return Raw retry attempts value as configured (may be null).
*/
public final Integer getRetryAttempts()
{
return retryAttempts;
}

/**
* @param retryAttempts
* Number of attempts to download the artifact in case of
* transient network failures.
*/
public final void setRetryAttempts(final Integer retryAttempts)
{
this.retryAttempts = retryAttempts;
}

/**
* @return Raw retry delay value (in millis) as configured (may be null).
*/
public final Integer getRetryDelay()
{
return retryDelay;
}

/**
* @param retryDelay
* Delay in millis between download retry attempts.
*/
public final void setRetryDelay(final Integer retryDelay)
{
this.retryDelay = retryDelay;
}

/**
* @return Packaging.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,31 @@ public class ResolveExternalDependencyMojo extends
* @readonly
*/
private MavenSettingsBuilder mavenSettingsBuilder;

/**
* Default number of attempts to download an external artifact in case
* of transient network failures (e.g. connection timeouts). Used when
* an {@code <artifactItem>} does not define its own {@code retryAttempts}.
*
* @parameter default-value="5"
*/
private Integer downloadRetryAttempts;

/**
* Default timeout in milliseconds for artifact download attempts. Used
* when an {@code <artifactItem>} does not define its own {@code timeout}.
*
* @parameter default-value="10000"
*/
private Integer downloadTimeout;

/**
* Default delay in milliseconds between download retry attempts. Used
* when an {@code <artifactItem>} does not define its own {@code retryDelay}.
*
* @parameter default-value="2000"
*/
private Integer downloadRetryDelay;

public void execute() throws MojoExecutionException, MojoFailureException
{
Expand Down Expand Up @@ -183,14 +208,6 @@ public void execute() throws MojoExecutionException, MojoFailureException
URL downloadUrl = new URL(artifactItem.getDownloadUrl());
String endPointUrl = downloadUrl.getProtocol() + "://"+ downloadUrl.getAuthority();
Repository repository = new Repository("additonal-configs", endPointUrl);
Wagon wagon = wagonManager.getWagon(downloadUrl.getProtocol());
if (getLog().isDebugEnabled())
{
Debug debug = new Debug();
wagon.addSessionListener(debug);
wagon.addTransferListener(debug);
}
wagon.setTimeout(artifactItem.getTimeout());
Settings settings = mavenSettingsBuilder.buildSettings();
ProxyInfo proxyInfo = null;
if (settings != null&& settings.getActiveProxy() != null)
Expand All @@ -204,13 +221,93 @@ public void execute() throws MojoExecutionException, MojoFailureException
proxyInfo.setUserName(settingsProxy.getUsername());
proxyInfo.setPassword(settingsProxy.getPassword());
}

if (proxyInfo != null)
wagon.connect(repository, wagonManager.getAuthenticationInfo(repository.getId()),proxyInfo);
else
wagon.connect(repository, wagonManager.getAuthenticationInfo(repository.getId()));

wagon.get(downloadUrl.getPath().substring(1), tempDownloadFile);

// resolve effective retry / timeout settings:
// per-artifact value (if set) wins over the
// Mojo-level default.
int effectiveAttempts = resolveRetryAttempts(artifactItem);
int effectiveTimeout = resolveTimeout(artifactItem);
long effectiveRetryDelay = resolveRetryDelay(artifactItem);

Exception lastFailure = null;
for (int attempt = 1; attempt <= effectiveAttempts; attempt++)
{
Wagon wagon = wagonManager.getWagon(downloadUrl.getProtocol());
if (getLog().isDebugEnabled())
{
Debug debug = new Debug();
wagon.addSessionListener(debug);
wagon.addTransferListener(debug);
}
wagon.setTimeout(effectiveTimeout);
try
{
if (proxyInfo != null)
wagon.connect(repository, wagonManager.getAuthenticationInfo(repository.getId()),proxyInfo);
else
wagon.connect(repository, wagonManager.getAuthenticationInfo(repository.getId()));

wagon.get(downloadUrl.getPath().substring(1), tempDownloadFile);
// success: stop retrying
lastFailure = null;
break;
}
catch (org.apache.maven.wagon.authorization.AuthorizationException ae)
{
// authorization issues are not transient, fail fast
throw ae;
}
catch (Exception ex)
{
lastFailure = ex;
getLog().warn(
"download attempt " + attempt + "/" + effectiveAttempts
+ " failed for URL: " + artifactItem.getDownloadUrl()
+ " - " + ex.getClass().getName() + ": " + ex.getMessage());

// discard partial download before retrying
if (tempDownloadFile.exists() && !tempDownloadFile.delete())
{
getLog().debug("could not delete partial temp file: "
+ tempDownloadFile.getAbsolutePath());
}

if (attempt < effectiveAttempts && effectiveRetryDelay > 0)
{
try
{
Thread.sleep(effectiveRetryDelay);
}
catch (InterruptedException ie)
{
Thread.currentThread().interrupt();
throw new MojoExecutionException(
"Interrupted while waiting to retry download of "
+ artifactItem.getDownloadUrl(), ie);
}
}
}
finally
{
try
{
wagon.disconnect();
}
catch (Exception ignored)
{
getLog().debug("error while disconnecting wagon: "
+ ignored.getMessage());
}
}
}

if (lastFailure != null)
{
throw new MojoExecutionException(
"Failed to download artifact " + artifactItem.getDownloadUrl()
+ " after " + effectiveAttempts + " attempt(s)",
lastFailure);
}
}else {
FileUtils.copyFile(new File(artifactItem.getDownloadUrl()), tempDownloadFile);
}
Expand Down Expand Up @@ -440,4 +537,61 @@ protected boolean resolveArtifactItem(Artifact artifact)

return artifactResolved;
}

/**
* Resolve effective number of download attempts for the given artifact.
* Per-artifact value (if set and positive) takes precedence over the
* Mojo-level {@code downloadRetryAttempts} parameter. Falls back to 5.
*/
private int resolveRetryAttempts(ArtifactItem artifactItem)
{
Integer perArtifact = artifactItem.getRetryAttempts();
if (perArtifact != null && perArtifact > 0)
{
return perArtifact;
}
if (downloadRetryAttempts != null && downloadRetryAttempts > 0)
{
return downloadRetryAttempts;
}
return 5;
}

/**
* Resolve effective download timeout (in millis) for the given artifact.
* Per-artifact value (if explicitly set and positive) takes precedence
* over the Mojo-level {@code downloadTimeout} parameter.
*/
private int resolveTimeout(ArtifactItem artifactItem)
{
Integer perArtifact = artifactItem.getTimeoutRaw();
if (perArtifact != null && perArtifact > 0)
{
return perArtifact;
}
if (downloadTimeout != null && downloadTimeout > 0)
{
return downloadTimeout;
}
return 10000;
}

/**
* Resolve effective delay (in millis) between retry attempts for the
* given artifact. Per-artifact value (if set and non-negative) takes
* precedence over the Mojo-level {@code downloadRetryDelay} parameter.
*/
private long resolveRetryDelay(ArtifactItem artifactItem)
{
Integer perArtifact = artifactItem.getRetryDelay();
if (perArtifact != null && perArtifact >= 0)
{
return perArtifact;
}
if (downloadRetryDelay != null && downloadRetryDelay >= 0)
{
return downloadRetryDelay;
}
return 2000L;
}
}
Loading