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
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@

import java.io.File;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.SessionScoped;
import org.apache.maven.buildcache.artifact.ArtifactRestorationReport;
import org.apache.maven.buildcache.checksum.MavenProjectInput;
import org.apache.maven.buildcache.xml.Build;
import org.apache.maven.buildcache.xml.CacheConfig;
Expand Down Expand Up @@ -109,8 +112,9 @@ public void execute(
// Forked execution should be thought as a part of originating mojo internal implementation
// If forkedExecution is detected, it means that originating mojo is not cached so forks should rerun too
boolean forkedExecution = lifecyclePhasesHelper.isForkedProject(project);
List<MojoExecution> cleanPhase = null;
if (source == Source.LIFECYCLE && !forkedExecution) {
List<MojoExecution> cleanPhase = lifecyclePhasesHelper.getCleanSegment(project, mojoExecutions);
cleanPhase = lifecyclePhasesHelper.getCleanSegment(project, mojoExecutions);
for (MojoExecution mojoExecution : cleanPhase) {
mojoExecutionRunner.run(mojoExecution);
}
Expand All @@ -128,8 +132,12 @@ public void execute(
boolean restorable = result.isSuccess() || result.isPartialSuccess();
boolean restored = result.isSuccess(); // if partially restored need to save increment
if (restorable) {
restored &= restoreProject(result, mojoExecutions, mojoExecutionRunner, cacheConfig);
} else {
CacheRestorationStatus cacheRestorationStatus =
restoreProject(result, mojoExecutions, mojoExecutionRunner, cacheConfig);
restored &= CacheRestorationStatus.SUCCESS == cacheRestorationStatus;
executeExtraCleanPhaseIfNeeded(cacheRestorationStatus, cleanPhase, mojoExecutionRunner);
}
if (!restored) {
Copy link
Contributor

@maximilian-novikov-db maximilian-novikov-db Aug 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps we need to execute all mojos in this case including for Clean phase to remove any "garbage" which could be left after an unsuccessful restore

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Restoring in the tmp + mv seems like a more robust approach.

Copy link
Contributor Author

@kbuntrock kbuntrock Aug 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea @maximilian-novikov-db.

@AlexanderAshitkin : do you mean we should change the way we restore artefacts from remote caches? I'm more inclined to the "second clean phase" idea since it covers any restoration problem (in case "clean" was asked in the build). For further changes, I guess it could be done in another PR.

Copy link
Contributor

@AlexanderAshitkin AlexanderAshitkin Aug 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is discussable:

  • clean phase has been completed by this time, and repeating it sounds slightly off to me.
  • Keeping partially restored artifacts is error-prone, and clean might not even be requested.
  • invoking ad-hoc clean might wipe out other cached data and might be undesirable

I see 2 options here: leave it as is (and rely on the user requested clean), or implement mv logic to minimize the risk of corrupted cache data in the target dir.

Copy link
Contributor

@maximilian-novikov-db maximilian-novikov-db Aug 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Keeping partially restored artifacts is error-prone, and clean might not even be requested.

all mojos except clean are executed again, if it falls back to the "normal" build, i'd say keeping any partially restored data is harmful and may lead to unpredictable result

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suggest to remove the filter here

|| lifecyclePhasesHelper.isLaterPhaseThanClean(mojoExecution.getLifecyclePhase())) {

and not to add Clean if it's not requested by user

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that seeing a second clean phase seems a bit "off" but I could be in piece with it since it is a abnormal edge case (and we will write a beautiful log to explain it. :p)

Trying to summarize the exchange in order to check if we have a consensus in a situation where the cache fails the restoration

  • the build was requested without clean phase : we do not change the actual behaviour.
  • the build was requested with a clean phase : an extra clean phase will be executed before the actual behaviour (= the whole build is executed).

We change generically the log "Cannot restore project artifacts, continuing with non cached build" by :
"Cannot restore project artifacts, continuing with non cached build (clean phase will be re-executed if requested)".

And in another PR, the remote download could be enhanced by doing it in two steps : downloading in a temp directory + moving it
Reducing the risk of leftovers files.

Copy link
Contributor

@AlexanderAshitkin AlexanderAshitkin Aug 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some clarifications about the current implementation:

  • Logic of rerunning on failures is already within the BuildCacheMojosExecutionStrategy#restoreProject:
if (!restored) {
    // cleanup partial state
    project.getArtifact().setFile(null);
    project.getArtifact().setResolved(false);
    mojoListener.remove(project);
    // build as usual
    for (MojoExecution mojoExecution : cachedSegment) { <<< rebuilding everything
        mojoExecutionRunner.run(mojoExecution);
    }
}
  • !restored condition should be read as "at least part of the project were rebuilt, not restored."
  • It doesn't mean an error automatically. It means something was rebuilt (at least in the design). 100% successful completion of the restoreProject should return false if it was a non-cached path and the build state differs from the cached one.

So, in all, it feels like we might need fixes within the restoreProject:

  1. to implement proper clean phase
  2. Add a verification step to the "forced" steps - to force cache update on the detected inconsistencies
  3. Make sure that in case of additional executions ("post-cached" or similar), we return false to update the cache.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlexanderAshitkin : Sorry, did not had time to investigate this the past week, hope I'll have a bit more time on this one.

Copy link
Contributor Author

@kbuntrock kbuntrock Aug 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, finally had some time to work on what you pointed out @AlexanderAshitkin : regular build after a failed try to use the cache was handled differently depending the situation.
I will add comments on some lines of this commit to improve your ability to review it effectively. But here are some global points :

  • I moved the part where we check cached mojos consistency before artifact restoration. There are two benefits : this steps is faster than restoration over the network. So if we can't use the cache because of mojos parameters, better to know it before downloading artifacts. And secondly: there are no more "things" to rollback since setting artifacts of the project is done at the very end of the restoration.
  • I kept the regular build execution outside the "restoreProject" function.
  • I execute an extra clean phase only if two conditions are met : clean phase was requested in the build + there are potentially files restored in the project directory (it does not happen if we don't restore generated resources, "outputArtifacts" or if an error occurs before)

I'm removing the draft on this PR since I don't want anymore to link it with my other PR. The duplicated 3 line function used in IT test can be remove in a very small 3rd PR.

for (MojoExecution mojoExecution : mojoExecutions) {
if (source == Source.CLI
|| mojoExecution.getLifecyclePhase() == null
Expand All @@ -155,6 +163,31 @@ public void execute(
}
}

/**
* Cache configuration could demand to restore some files in the project directory (generated sources or even arbitrary content)
* If an error occurs during or after this kind of restoration AND a clean phase was required in the build :
* we execute an extra clean phase to remove any potential partially restored files
*
* @param cacheRestorationStatus the restoration status
* @param cleanPhase clean phase mojos
* @param mojoExecutionRunner mojo runner
* @throws LifecycleExecutionException
*/
private void executeExtraCleanPhaseIfNeeded(
final CacheRestorationStatus cacheRestorationStatus,
List<MojoExecution> cleanPhase,
MojoExecutionRunner mojoExecutionRunner)
throws LifecycleExecutionException {
if (CacheRestorationStatus.FAILURE_NEEDS_CLEAN == cacheRestorationStatus
&& cleanPhase != null
&& !cleanPhase.isEmpty()) {
LOGGER.info("Extra clean phase is executed as cache could be partially restored.");
for (MojoExecution mojoExecution : cleanPhase) {
mojoExecutionRunner.run(mojoExecution);
}
}
}

private Source getSource(List<MojoExecution> mojoExecutions) {
if (mojoExecutions == null || mojoExecutions.isEmpty()) {
return null;
Expand All @@ -167,7 +200,7 @@ private Source getSource(List<MojoExecution> mojoExecutions) {
return Source.LIFECYCLE;
}

private boolean restoreProject(
private CacheRestorationStatus restoreProject(
CacheResult cacheResult,
List<MojoExecution> mojoExecutions,
MojoExecutionRunner mojoExecutionRunner,
Expand All @@ -183,14 +216,34 @@ private boolean restoreProject(
final List<MojoExecution> cachedSegment =
lifecyclePhasesHelper.getCachedSegment(project, mojoExecutions, build);

boolean restored = cacheController.restoreProjectArtifacts(cacheResult);
if (!restored) {
// Verify cache consistency for cached mojos
LOGGER.debug("Verify consistency on cached mojos");
Set<MojoExecution> forcedExecutionMojos = new HashSet<>();
for (MojoExecution cacheCandidate : cachedSegment) {
if (cacheController.isForcedExecution(project, cacheCandidate)) {
forcedExecutionMojos.add(cacheCandidate);
} else {
if (!verifyCacheConsistency(
cacheCandidate, build, project, session, mojoExecutionRunner, cacheConfig)) {
LOGGER.info("A cached mojo is not consistent, continuing with non cached build");
return CacheRestorationStatus.FAILURE;
}
}
}

// Restore project artifacts
ArtifactRestorationReport restorationReport = cacheController.restoreProjectArtifacts(cacheResult);
if (!restorationReport.isSuccess()) {
LOGGER.info("Cannot restore project artifacts, continuing with non cached build");
return false;
return restorationReport.isRestoredFilesInProjectDirectory()
? CacheRestorationStatus.FAILURE_NEEDS_CLEAN
: CacheRestorationStatus.FAILURE;
}

// Execute mandatory mojos (forced by configuration)
LOGGER.debug("Execute mandatory mojos in the cache segment");
for (MojoExecution cacheCandidate : cachedSegment) {
if (cacheController.isForcedExecution(project, cacheCandidate)) {
if (forcedExecutionMojos.contains(cacheCandidate)) {
LOGGER.info(
"Mojo execution is forced by project property: {}",
cacheCandidate.getMojoDescriptor().getFullGoalName());
Expand All @@ -206,31 +259,20 @@ private boolean restoreProject(
// org.apache.maven.api.MojoExecution.class, new DefaultMojoExecution(cacheCandidate));
mojoExecutionRunner.run(cacheCandidate);
} else {
restored = verifyCacheConsistency(
cacheCandidate, build, project, session, mojoExecutionRunner, cacheConfig);
if (!restored) {
break;
}
}
}

if (!restored) {
// cleanup partial state
project.getArtifact().setFile(null);
project.getArtifact().setResolved(false);
mojoListener.remove(project);
// build as usual
for (MojoExecution mojoExecution : cachedSegment) {
mojoExecutionRunner.run(mojoExecution);
LOGGER.info(
"Skipping plugin execution (cached): {}",
cacheCandidate.getMojoDescriptor().getFullGoalName());
}
}

// Execute mojos after the cache segment
LOGGER.debug("Execute mojos post cache segment");
List<MojoExecution> postCachedSegment =
lifecyclePhasesHelper.getPostCachedSegment(project, mojoExecutions, build);
for (MojoExecution mojoExecution : postCachedSegment) {
Copy link
Contributor

@AlexanderAshitkin AlexanderAshitkin Aug 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kbuntrock, it is out of scope, but please notice two edge cases here:

  • A post-cached segment is possible if initially the build was cached at, say package phase and the current build runs a later phase, say install. In that case, the best behavior is to update the cached build with the more complete version, which won't happen in the SUCCESS case - maybe we need a separate status.
  • The same for forced executions - even if the execution is forced, the mojo configuration could be not consistent with the cached one. If it is not consistent, it could be desirable to update the cache. We shouldn't update the cache on discrepancies only if configs prohibit it.

mojoExecutionRunner.run(mojoExecution);
}
return restored;
return CacheRestorationStatus.SUCCESS;
} finally {
mojoExecutionScope.exit();
}
Expand Down Expand Up @@ -263,13 +305,14 @@ private boolean verifyCacheConsistency(

if (consistent) {
long elapsed = System.currentTimeMillis() - createdTimestamp;
LOGGER.info("Skipping plugin execution (reconciled in {} millis): {}", elapsed, fullGoalName);
}

if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"Checked {}, resolved mojo: {}, cached params: {}", fullGoalName, mojo, completedExecution);
"Plugin execution will be skipped ({} : reconciled in {} millis)", elapsed, fullGoalName);
}

LOGGER.debug(
"Checked {}, resolved mojo: {}, cached params: {}", fullGoalName, mojo, completedExecution);

} catch (PluginContainerException | PluginConfigurationException e) {
throw new LifecycleExecutionException("Cannot get configured mojo", e);
} finally {
Expand All @@ -278,8 +321,8 @@ private boolean verifyCacheConsistency(
}
}
} else {
LOGGER.info(
"Skipping plugin execution (cached): {}",
LOGGER.debug(
"Plugin execution will be skipped ({} : cached)",
cacheCandidate.getMojoDescriptor().getFullGoalName());
}

Expand Down Expand Up @@ -364,4 +407,10 @@ private static String normalizedPath(Path path, Path baseDirPath) {
}
return normalizedPath;
}

private enum CacheRestorationStatus {
SUCCESS,
FAILURE,
FAILURE_NEEDS_CLEAN;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.List;
import java.util.Map;

import org.apache.maven.buildcache.artifact.ArtifactRestorationReport;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.MojoExecutionEvent;
import org.apache.maven.plugin.MojoExecution;
Expand All @@ -34,7 +35,7 @@ public interface CacheController {
CacheResult findCachedBuild(
MavenSession session, MavenProject project, List<MojoExecution> mojoExecutions, boolean skipCache);

boolean restoreProjectArtifacts(CacheResult cacheResult);
ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult);

void save(
CacheResult cacheResult,
Expand Down
34 changes: 31 additions & 3 deletions src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.regex.Pattern;
Expand All @@ -55,8 +56,10 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.maven.SessionScoped;
import org.apache.maven.artifact.InvalidArtifactRTException;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import org.apache.maven.buildcache.artifact.ArtifactRestorationReport;
import org.apache.maven.buildcache.artifact.RestoredArtifact;
import org.apache.maven.buildcache.checksum.MavenProjectInput;
import org.apache.maven.buildcache.hash.HashAlgorithm;
Expand Down Expand Up @@ -295,10 +298,13 @@ private boolean canIgnoreMissingSegment(MavenProject project, Build info, List<M
}

@Override
public boolean restoreProjectArtifacts(CacheResult cacheResult) {
public ArtifactRestorationReport restoreProjectArtifacts(CacheResult cacheResult) {

LOGGER.debug("Restore project artifacts");
final Build build = cacheResult.getBuildInfo();
final CacheContext context = cacheResult.getContext();
final MavenProject project = context.getProject();
ArtifactRestorationReport restorationReport = new ArtifactRestorationReport();

try {
RestoredArtifact restoredProjectArtifact = null;
Expand All @@ -325,6 +331,8 @@ public boolean restoreProjectArtifacts(CacheResult cacheResult) {
// it may also be disabled on a per-project level (defaults to true - enable)
if (cacheConfig.isRestoreGeneratedSources()
&& MavenProjectInput.isRestoreGeneratedSources(project)) {
// Set this value before trying the restoration, to keep a trace of the attempt if it fails
restorationReport.setRestoredFilesInProjectDirectory(true);
// generated sources artifact
final Path attachedArtifactFile =
localCache.getArtifactFile(context, cacheResult.getSource(), attachedArtifactInfo);
Expand All @@ -348,11 +356,11 @@ public boolean restoreProjectArtifacts(CacheResult cacheResult) {
project.setArtifact(restoredProjectArtifact);
}
restoredAttachedArtifacts.forEach(project::addAttachedArtifact);
return true;
restorationReport.setSuccess(true);
} catch (Exception e) {
LOGGER.debug("Cannot restore cache, continuing with normal build.", e);
return false;
}
return restorationReport;
}

/**
Expand Down Expand Up @@ -401,6 +409,26 @@ private Future<File> createDownloadTask(
});
if (!cacheConfig.isLazyRestore()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a more relevant name for the original intent is lazyDownload.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"LazyRestore" seems fine to me. I would instead replace "download" by "restore" since there is no downloading involved when working with a local cache. But it's not something I will do in this PR. :P

downloadTask.run();
try {
downloadTask.get();
Copy link
Contributor

@AlexanderAshitkin AlexanderAshitkin Aug 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Kevin. Please elaborate, what are the benefits of implementing this. Because drawbacks are apparent - cached artifacts not used in the actual build might fail the cache now (exotic classifiers, etc.). If that starts happening, it is a more serious issue than a delayed error. The change has its own merits, but the default behavior should be the most reasonable considering all the cases.

Copy link
Contributor Author

@kbuntrock kbuntrock Aug 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello Alexander, nice to meet you. :)

As I apprehend the "lazy restore" functionality, there are two approach, each one with their pros and cons.

Non lazy restore (the default) : immediate restoration of artefact and other build outputs.
Build stability is the main concern. If the restoration fails, the build is executed normally. (I'm interrogative about the part where you say it can fail the build. For me it's the exact opposite intend). Con: we might restore stuff not used further in another module build.

Lazy restore : restoration of artefacts is done only when used. It can save some bandwidth and speedup the build when used with a remote cache. (Pros with a local cache are not so clear to me.) Con: If the cache is corrupted/not accessible and an artefact must be used as a dependency in another module : the build is doomed and will fail.

Actually, the non lazy restore does not really work as expected since the restore is done (download is immediately launched when using a remote cache), but we don't check the result straight away. It might be far too late when we'll discover the artefact did not restored well.

Am I missing something in the picture?

Copy link
Contributor

@AlexanderAshitkin AlexanderAshitkin Aug 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi. The main question is whether or not to fail the cache when unused (in reactor) artifacts cannot be downloaded. I’m trying to understand what are the benefits of failing the cache. Does it solve any practical issues? Because in terms of the cache hit rate, this change brings regression.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kbuntrock could you please move this change to a separate pr

Copy link
Contributor Author

@kbuntrock kbuntrock Aug 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maximilian-novikov-db : I would rather not since I consider this as part of the bug I am willing to fix.

Here are some other arguments.

The documentation state the following in the "performance" page :

Use a lazy restore

By default, the cache tries to restore all artifacts for a project preemptively. Lazy restore could give significant time by avoiding requesting and downloading unnecessary artifacts from the cache. It is beneficial when small changes are a dominating build pattern. Use command line flag:

-Dmaven.build.cache.lazyRestore=true";

In cache corruption situations, the lazy cache cannot support fallback to normal execution. It will fail instead. To heal the corrupted cache, manually remove corrupted cache entries or force cache rewrite.

Implied by this piece of documentation : Contrary to the lazy restore, the pre-emptive default restore option supports fallback. This is actually not the case for artifacts, the most important build outputs.
Alongside, restoration of generated source and other attached outputs works as expected : any cache corruption will trigger a regular build.
I have a strong feeling we are just in presence of a simple implementation oversight I'm fixing here : calling "run" on a FutureTask does the restoration but do no throw any exception upon failure. It has to be checked via a get method.

@AlexanderAshitkin
To complete a bit more this question :

Does it solve any practical issues?

Yes. Actually, if a corrupted/non downloadable artefact is used in a dependant module while we have not used the "lazy restore" functionality : the build will fail.
Worst case before this fix : the build fails
Worst case after this fix : we do a regular build.

Seems pretty important that one should be able to choose stability over rapidity.

Copy link
Contributor

@AlexanderAshitkin AlexanderAshitkin Aug 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that keeping the fallback is important. Thanks for the clarifications! We can add such optimization in the future by tracking artifact usage downstream.

} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new InvalidArtifactRTException(
artifact.getGroupId(),
artifact.getArtifactId(),
artifact.getVersion(),
artifact.getType(),
RestoredArtifact.MSG_INTERRUPTED_WHILE_RETRIEVING_ARTIFACT_FILE,
e);
} catch (ExecutionException e) {
throw new InvalidArtifactRTException(
artifact.getGroupId(),
artifact.getArtifactId(),
artifact.getVersion(),
artifact.getType(),
RestoredArtifact.MSG_ERROR_RETRIEVING_ARTIFACT_FILE,
e.getCause());
}
}
return downloadTask;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,4 @@ public void afterExecutionFailure(MojoExecutionEvent event) {
public Map<String, MojoExecutionEvent> getProjectExecutions(MavenProject project) {
return projectExecutions.get(project);
}

public void remove(MavenProject project) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understood well, this was used to "reset" the project mojo's execution spying after a "rollback" from using the cache to a regular build execution.
I'm not anymore in a position where I need this function so I deleted it. But I could use your careful reviewing on it @maximilian-novikov-db (I see you name on the oldest commit of the class :p )

projectExecutions.remove(project);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.maven.buildcache.artifact;

public class ArtifactRestorationReport {

/**
* Success restoration indicator
*/
private boolean success;

/**
* True if some files have been restored (or attempted in case of error) in the project directory
*/
private boolean restoredFilesInProjectDirectory;

public boolean isSuccess() {
return success;
}

public void setSuccess(boolean success) {
this.success = success;
}

public boolean isRestoredFilesInProjectDirectory() {
return restoredFilesInProjectDirectory;
}

public void setRestoredFilesInProjectDirectory(boolean restoredFilesInProjectDirectory) {
this.restoredFilesInProjectDirectory = restoredFilesInProjectDirectory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
public class RestoredArtifact extends DefaultArtifact {

private static final Logger LOGGER = LoggerFactory.getLogger(RestoredArtifact.class);
public static final String MSG_INTERRUPTED_WHILE_RETRIEVING_ARTIFACT_FILE =
"Interrupted while retrieving artifact file";
public static final String MSG_ERROR_RETRIEVING_ARTIFACT_FILE = "Error retrieving artifact file";

private volatile Future<File> fileFuture;

Expand Down Expand Up @@ -75,7 +78,7 @@ public File getFile() {
getArtifactId(),
getVersion(),
getType(),
"Error retrieving artifact file",
MSG_ERROR_RETRIEVING_ARTIFACT_FILE,
e);
}
} else {
Expand All @@ -94,15 +97,15 @@ public File getFile() {
getArtifactId(),
getVersion(),
getType(),
"Interrupted while retrieving artifact file",
MSG_INTERRUPTED_WHILE_RETRIEVING_ARTIFACT_FILE,
e);
} catch (ExecutionException e) {
throw new InvalidArtifactRTException(
getGroupId(),
getArtifactId(),
getVersion(),
getType(),
"Error retrieving artifact file",
MSG_ERROR_RETRIEVING_ARTIFACT_FILE,
e.getCause());
}
}
Expand Down
Loading