mirror of
https://github.com/apache/maven-mvnd.git
synced 2025-09-27 07:55:25 +00:00
Support -r / --resume option, fixes #351
This commit is contained in:
@@ -25,6 +25,8 @@ import java.io.FileOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -100,6 +102,9 @@ import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginArtifactsCache;
|
||||
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingPluginRealmCache;
|
||||
import org.mvndaemon.mvnd.cache.invalidating.InvalidatingProjectArtifactsCache;
|
||||
import org.mvndaemon.mvnd.common.Environment;
|
||||
import org.mvndaemon.mvnd.execution.BuildResumptionPersistenceException;
|
||||
import org.mvndaemon.mvnd.execution.DefaultBuildResumptionAnalyzer;
|
||||
import org.mvndaemon.mvnd.execution.DefaultBuildResumptionDataRepository;
|
||||
import org.mvndaemon.mvnd.logging.internal.Slf4jLoggerManager;
|
||||
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
|
||||
import org.mvndaemon.mvnd.logging.smart.LoggingExecutionListener;
|
||||
@@ -113,6 +118,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
|
||||
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
|
||||
|
||||
/**
|
||||
@@ -142,6 +148,8 @@ public class DaemonMavenCli {
|
||||
|
||||
public static final String STYLE_COLOR_PROPERTY = "style.color";
|
||||
|
||||
public static final String RESUME = "r";
|
||||
|
||||
private final Slf4jLoggerManager plexusLoggerManager;
|
||||
|
||||
private final ILoggerFactory slf4jLoggerFactory;
|
||||
@@ -260,7 +268,7 @@ public class DaemonMavenCli {
|
||||
|
||||
void cli(CliRequest cliRequest)
|
||||
throws Exception {
|
||||
CLIManager cliManager = new CLIManager();
|
||||
CLIManager cliManager = newCLIManager();
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
CommandLine mavenConfig = null;
|
||||
@@ -301,10 +309,16 @@ public class DaemonMavenCli {
|
||||
|
||||
private void help(CliRequest cliRequest) throws Exception {
|
||||
if (cliRequest.commandLine.hasOption(CLIManager.HELP)) {
|
||||
buildEventListener.log(MvndHelpFormatter.displayHelp(new CLIManager()));
|
||||
buildEventListener.log(MvndHelpFormatter.displayHelp(newCLIManager()));
|
||||
throw new ExitException(0);
|
||||
}
|
||||
}
|
||||
|
||||
private CLIManager newCLIManager() {
|
||||
CLIManager cliManager = new CLIManager();
|
||||
cliManager.options.addOption(Option.builder(RESUME).longOpt("resume").desc("Resume reactor from " +
|
||||
"the last failed project, using the resume.properties file in the build directory").build());
|
||||
return cliManager;
|
||||
}
|
||||
|
||||
private CommandLine cliMerge(CommandLine mavenArgs, CommandLine mavenConfig) {
|
||||
@@ -739,15 +753,15 @@ public class DaemonMavenCli {
|
||||
|
||||
Map<String, String> references = new LinkedHashMap<>();
|
||||
|
||||
MavenProject project = null;
|
||||
List<MavenProject> failedProjects = new ArrayList<>();
|
||||
|
||||
for (Throwable exception : result.getExceptions()) {
|
||||
ExceptionSummary summary = handler.handleException(exception);
|
||||
|
||||
logSummary(summary, references, "", cliRequest.showErrors);
|
||||
|
||||
if (project == null && exception instanceof LifecycleExecutionException) {
|
||||
project = ((LifecycleExecutionException) exception).getProject();
|
||||
if (exception instanceof LifecycleExecutionException) {
|
||||
failedProjects.add(((LifecycleExecutionException) exception).getProject());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,11 +786,30 @@ public class DaemonMavenCli {
|
||||
}
|
||||
}
|
||||
|
||||
if (project != null && !project.equals(result.getTopologicallySortedProjects().get(0))) {
|
||||
slf4jLogger.error("");
|
||||
slf4jLogger.error("After correcting the problems, you can resume the build with the command");
|
||||
slf4jLogger.error(buffer().a(" ").strong("mvn <args> -rf "
|
||||
+ getResumeFrom(result.getTopologicallySortedProjects(), project)).toString());
|
||||
boolean canResume = new DefaultBuildResumptionAnalyzer().determineBuildResumptionData(result).map(resumption -> {
|
||||
try {
|
||||
Path directory = Paths.get(request.getBaseDirectory()).resolve("target");
|
||||
new DefaultBuildResumptionDataRepository().persistResumptionData(directory, resumption);
|
||||
return true;
|
||||
} catch (BuildResumptionPersistenceException e) {
|
||||
slf4jLogger.warn("Could not persist build resumption data", e);
|
||||
}
|
||||
return false;
|
||||
}).orElse(false);
|
||||
|
||||
if (canResume) {
|
||||
logBuildResumeHint("mvn <args> -r");
|
||||
} else if (!failedProjects.isEmpty()) {
|
||||
List<MavenProject> sortedProjects = result.getTopologicallySortedProjects();
|
||||
|
||||
// Sort the failedProjects list in the topologically sorted order.
|
||||
failedProjects.sort(comparing(sortedProjects::indexOf));
|
||||
|
||||
MavenProject firstFailedProject = failedProjects.get(0);
|
||||
if (!firstFailedProject.equals(sortedProjects.get(0))) {
|
||||
String resumeFromSelector = getResumeFromSelector(sortedProjects, firstFailedProject);
|
||||
logBuildResumeHint("mvn <args> -rf " + resumeFromSelector);
|
||||
}
|
||||
}
|
||||
|
||||
if (MavenExecutionRequest.REACTOR_FAIL_NEVER.equals(cliRequest.request.getReactorFailureBehavior())) {
|
||||
@@ -787,10 +820,18 @@ public class DaemonMavenCli {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
Path directory = Paths.get(request.getBaseDirectory()).resolve("target");
|
||||
new DefaultBuildResumptionDataRepository().removeResumptionData(directory);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void logBuildResumeHint(String resumeBuildHint) {
|
||||
slf4jLogger.error("");
|
||||
slf4jLogger.error("After correcting the problems, you can resume the build with the command");
|
||||
slf4jLogger.error(buffer().a(" ").strong(resumeBuildHint).toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to determine the value to resume the build with {@code -rf} taking into account the
|
||||
* edge case where multiple modules in the reactor have the same artifactId.
|
||||
@@ -808,7 +849,7 @@ public class DaemonMavenCli {
|
||||
* @return Value for -rf flag to resume build exactly from place where it failed ({@code :artifactId} in
|
||||
* general and {@code groupId:artifactId} when there is a name clash).
|
||||
*/
|
||||
private String getResumeFrom(List<MavenProject> mavenProjects, MavenProject failedProject) {
|
||||
private String getResumeFromSelector(List<MavenProject> mavenProjects, MavenProject failedProject) {
|
||||
for (MavenProject buildProject : mavenProjects) {
|
||||
if (failedProject.getArtifactId().equals(buildProject.getArtifactId()) && !failedProject.equals(
|
||||
buildProject)) {
|
||||
@@ -1172,6 +1213,11 @@ public class DaemonMavenCli {
|
||||
request.setBaseDirectory(request.getPom().getParentFile());
|
||||
}
|
||||
|
||||
if (commandLine.hasOption(RESUME)) {
|
||||
new DefaultBuildResumptionDataRepository()
|
||||
.applyResumptionData(request, Paths.get(request.getBaseDirectory()).resolve("target"));
|
||||
}
|
||||
|
||||
if (commandLine.hasOption(CLIManager.RESUME_FROM)) {
|
||||
request.setResumeFrom(commandLine.getOptionValue(CLIManager.RESUME_FROM));
|
||||
}
|
||||
|
@@ -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.execution;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import java.util.Optional;
|
||||
import org.apache.maven.execution.MavenExecutionResult;
|
||||
|
||||
/**
|
||||
* Instances of this class are responsible for determining whether it makes sense to "resume" a build (i.e., using
|
||||
* the {@code --resume} flag.
|
||||
*/
|
||||
public interface BuildResumptionAnalyzer {
|
||||
/**
|
||||
* Construct an instance of {@link BuildResumptionData} based on the outcome of the current Maven build.
|
||||
*
|
||||
* @param result Outcome of the current Maven build.
|
||||
* @return A {@link BuildResumptionData} instance or {@link Optional#empty()} if resuming the build is not
|
||||
* possible.
|
||||
*/
|
||||
Optional<BuildResumptionData> determineBuildResumptionData(final MavenExecutionResult result);
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.execution;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class holds the information required to enable resuming a Maven build with {@code --resume}.
|
||||
*/
|
||||
public class BuildResumptionData {
|
||||
/**
|
||||
* The list of projects that remain to be built.
|
||||
*/
|
||||
private final List<String> remainingProjects;
|
||||
|
||||
public BuildResumptionData(final List<String> remainingProjects) {
|
||||
this.remainingProjects = remainingProjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the projects that still need to be built when resuming.
|
||||
*
|
||||
* @return A list containing the group and artifact id of the projects.
|
||||
*/
|
||||
public List<String> getRemainingProjects() {
|
||||
return this.remainingProjects;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.execution;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import org.apache.maven.execution.MavenExecutionRequest;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
|
||||
/**
|
||||
* Instances of this interface retrieve and store data for the --resume / -r feature. This data is used to ensure newer
|
||||
* builds of the same project, that have the -r command-line flag, skip successfully built projects during earlier
|
||||
* invocations of Maven.
|
||||
*/
|
||||
public interface BuildResumptionDataRepository {
|
||||
/**
|
||||
* Persists any data needed to resume the build at a later point in time, using a new Maven invocation. This method
|
||||
* may also decide it is not needed or meaningful to persist such data, and return <code>false</code> to indicate
|
||||
* so.
|
||||
*
|
||||
* @param rootProject The root project that is being built.
|
||||
* @param buildResumptionData Information needed to resume the build.
|
||||
* @throws BuildResumptionPersistenceException When an error occurs while persisting data.
|
||||
*/
|
||||
void persistResumptionData(final MavenProject rootProject, final BuildResumptionData buildResumptionData)
|
||||
throws BuildResumptionPersistenceException;
|
||||
|
||||
/**
|
||||
* Uses previously stored resumption data to enrich an existing execution request.
|
||||
*
|
||||
* @param request The execution request that will be enriched.
|
||||
* @param rootProject The root project that is being built.
|
||||
*/
|
||||
void applyResumptionData(final MavenExecutionRequest request, final MavenProject rootProject);
|
||||
|
||||
/**
|
||||
* Removes previously stored resumption data.
|
||||
*
|
||||
* @param rootProject The root project that is being built.
|
||||
*/
|
||||
void removeResumptionData(final MavenProject rootProject);
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.execution;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This exception will be thrown when something fails while persisting build resumption data.
|
||||
*
|
||||
* @see BuildResumptionDataRepository#persistResumptionData
|
||||
*/
|
||||
public class BuildResumptionPersistenceException extends Exception {
|
||||
public BuildResumptionPersistenceException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.execution;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.maven.execution.BuildFailure;
|
||||
import org.apache.maven.execution.BuildSuccess;
|
||||
import org.apache.maven.execution.MavenExecutionResult;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link BuildResumptionAnalyzer}.
|
||||
*/
|
||||
@Named
|
||||
@Singleton
|
||||
public class DefaultBuildResumptionAnalyzer implements BuildResumptionAnalyzer {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultBuildResumptionAnalyzer.class);
|
||||
|
||||
@Override
|
||||
public Optional<BuildResumptionData> determineBuildResumptionData(final MavenExecutionResult result) {
|
||||
if (!result.hasExceptions()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<MavenProject> sortedProjects = result.getTopologicallySortedProjects();
|
||||
|
||||
boolean hasNoSuccess = sortedProjects.stream()
|
||||
.noneMatch(project -> result.getBuildSummary(project) instanceof BuildSuccess);
|
||||
|
||||
if (hasNoSuccess) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<String> remainingProjects = sortedProjects.stream()
|
||||
.filter(project -> result.getBuildSummary(project) == null
|
||||
|| result.getBuildSummary(project) instanceof BuildFailure)
|
||||
.map(project -> project.getGroupId() + ":" + project.getArtifactId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (remainingProjects.isEmpty()) {
|
||||
LOGGER.info("No remaining projects found, resuming the build would not make sense.");
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new BuildResumptionData(remainingProjects));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.execution;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.maven.execution.MavenExecutionRequest;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This implementation of {@link BuildResumptionDataRepository} persists information in a properties file. The file is
|
||||
* stored in the build output directory under the Maven execution root.
|
||||
*/
|
||||
@Named
|
||||
@Singleton
|
||||
public class DefaultBuildResumptionDataRepository implements BuildResumptionDataRepository {
|
||||
private static final String RESUME_PROPERTIES_FILENAME = "resume.properties";
|
||||
private static final String REMAINING_PROJECTS = "remainingProjects";
|
||||
private static final String PROPERTY_DELIMITER = ", ";
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultBuildResumptionDataRepository.class);
|
||||
|
||||
@Override
|
||||
public void persistResumptionData(MavenProject rootProject, BuildResumptionData buildResumptionData)
|
||||
throws BuildResumptionPersistenceException {
|
||||
Path directory = Paths.get(rootProject.getBuild().getDirectory());
|
||||
persistResumptionData(directory, buildResumptionData);
|
||||
}
|
||||
|
||||
public void persistResumptionData(Path directory, BuildResumptionData buildResumptionData)
|
||||
throws BuildResumptionPersistenceException {
|
||||
Properties properties = convertToProperties(buildResumptionData);
|
||||
|
||||
Path resumeProperties = directory.resolve(RESUME_PROPERTIES_FILENAME);
|
||||
try {
|
||||
Files.createDirectories(resumeProperties.getParent());
|
||||
try (Writer writer = Files.newBufferedWriter(resumeProperties)) {
|
||||
properties.store(writer, null);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
String message = "Could not create " + RESUME_PROPERTIES_FILENAME + " file.";
|
||||
throw new BuildResumptionPersistenceException(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties convertToProperties(final BuildResumptionData buildResumptionData) {
|
||||
Properties properties = new Properties();
|
||||
|
||||
String value = String.join(PROPERTY_DELIMITER, buildResumptionData.getRemainingProjects());
|
||||
properties.setProperty(REMAINING_PROJECTS, value);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyResumptionData(MavenExecutionRequest request, MavenProject rootProject) {
|
||||
Path directory = Paths.get(rootProject.getBuild().getDirectory());
|
||||
applyResumptionData(request, directory);
|
||||
}
|
||||
|
||||
public void applyResumptionData(MavenExecutionRequest request, Path directory) {
|
||||
Properties properties = loadResumptionFile(directory);
|
||||
applyResumptionProperties(request, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResumptionData(MavenProject rootProject) {
|
||||
Path directory = Paths.get(rootProject.getBuild().getDirectory());
|
||||
removeResumptionData(directory);
|
||||
}
|
||||
|
||||
public void removeResumptionData(Path directory) {
|
||||
Path resumeProperties = directory.resolve(RESUME_PROPERTIES_FILENAME);
|
||||
try {
|
||||
Files.deleteIfExists(resumeProperties);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not delete {} file. ", RESUME_PROPERTIES_FILENAME, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties loadResumptionFile(Path rootBuildDirectory) {
|
||||
Properties properties = new Properties();
|
||||
Path path = rootBuildDirectory.resolve(RESUME_PROPERTIES_FILENAME);
|
||||
if (!Files.exists(path)) {
|
||||
LOGGER.warn("The {} file does not exist. The --resume / -r feature will not work.", path);
|
||||
return properties;
|
||||
}
|
||||
|
||||
try (Reader reader = Files.newBufferedReader(path)) {
|
||||
properties.load(reader);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Unable to read {}. The --resume / -r feature will not work.", path);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
// This method is made package-private for testing purposes
|
||||
void applyResumptionProperties(MavenExecutionRequest request, Properties properties) {
|
||||
if (properties.containsKey(REMAINING_PROJECTS)
|
||||
&& StringUtils.isEmpty(request.getResumeFrom())) {
|
||||
String propertyValue = properties.getProperty(REMAINING_PROJECTS);
|
||||
Stream.of(propertyValue.split(PROPERTY_DELIMITER))
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.forEach(request.getSelectedProjects()::add);
|
||||
LOGGER.info("Resuming from {} due to the --resume / -r feature.", propertyValue);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user