
com.simpligility.maven.provisioner.MavenRepositoryDeployer Maven / Gradle / Ivy
/**
* Copyright simpligility technologies inc. http://www.simpligility.com
* Licensed under Eclipse Public License - v 1.0 http://www.eclipse.org/legal/epl-v10.html
*/
package com.simpligility.maven.provisioner;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
import com.simpligility.maven.Gav;
import com.simpligility.maven.GavPattern;
import com.simpligility.maven.GavUtil;
import com.simpligility.maven.MavenConstants;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.AndFileFilter;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.NotFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.deployment.DeployRequest;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MavenRepositoryDeployer {
private static Logger logger = LoggerFactory.getLogger("MavenRepositoryHelper");
private File repositoryPath;
private RepositorySystem system;
private DefaultRepositorySystemSession session;
private final Configuration config;
private final TreeSet successfulDeploys = new TreeSet();
private final TreeSet failedDeploys = new TreeSet();
private final TreeSet skippedDeploys = new TreeSet();
private final TreeSet potentialDeploys = new TreeSet();
private final Set gavPatterns;
public MavenRepositoryDeployer(File repositoryPath, Configuration configuration) {
this.repositoryPath = repositoryPath;
this.config = configuration;
initialize();
gavPatterns = loadGavPatternsFromFilterFile(config.getDeployFilterFile());
}
/**
* Default constructor to make unit testing easier
*/
public MavenRepositoryDeployer(Configuration configuration) {
gavPatterns = new HashSet<>();
this.config = configuration;
}
private void initialize() {
system = RepositoryHandler.getRepositorySystem();
session = RepositoryHandler.getRepositorySystemSession(system, repositoryPath);
if (config.getParallelDeploy()) {
session.setConfigProperty("aether.connector.basic.parallelPut", "true");
session.setConfigProperty("aether.connector.basic.threads", config.getDeployThreads());
}
}
public static Collection getLeafDirectories(File repoPath) {
// Using commons-io, if performance or so is a problem it might be worth looking at the Java 8 streams API
// e.g. http://blog.jooq.org/2014/01/24/java-8-friday-goodies-the-new-new-io-apis/
// not yet though..
Collection subDirectories = FileUtils.listFilesAndDirs(
repoPath, DirectoryFileFilter.DIRECTORY, VisibleDirectoryFileFilter.DIRECTORY);
Collection leafDirectories = new ArrayList();
for (File subDirectory : subDirectories) {
if (isLeafVersionDirectory(subDirectory) && subDirectory != repoPath) {
leafDirectories.add(subDirectory);
}
}
return leafDirectories;
}
/**
* Determine if it is a leaf directory with artifacts in it. Criteria used is that there is no subdirectory.
*
* @param subDirectory
* @return
*/
private static boolean isLeafVersionDirectory(File subDirectory) {
boolean isLeafVersionDirectory;
Collection subDirectories = FileUtils.listFilesAndDirs(
subDirectory, (IOFileFilter) VisibleDirectoryFileFilter.DIRECTORY, (IOFileFilter)
VisibleDirectoryFileFilter.DIRECTORY);
// it finds at least itself so have to check for > 1
isLeafVersionDirectory = subDirectories.size() > 1 ? false : true;
return isLeafVersionDirectory;
}
public static Collection getPomFiles(File repoPath) {
Collection pomFiles = new ArrayList();
Collection leafDirectories = getLeafDirectories(repoPath);
for (File leafDirectory : leafDirectories) {
IOFileFilter fileFilter =
new AndFileFilter(new WildcardFileFilter("*.pom"), new NotFileFilter(new SuffixFileFilter("sha1")));
pomFiles.addAll(FileUtils.listFiles(leafDirectory, fileFilter, null));
}
return pomFiles;
}
public void deployToRemote() {
Collection leafDirectories = getLeafDirectories(repositoryPath);
for (File leafDirectory : leafDirectories) {
String leafAbsolutePath = leafDirectory.getAbsoluteFile().toString();
int repoAbsolutePathLength =
repositoryPath.getAbsoluteFile().toString().length();
String leafRepoPath = leafAbsolutePath.substring(repoAbsolutePathLength + 1, leafAbsolutePath.length());
Gav gav = GavUtil.getGavFromRepositoryPath(leafRepoPath);
if (!canDeployGav(gav, 10)) {
logger.info("Skipping deployment of " + gav + " as it is not in the deploy filter file.");
skippedDeploys.add(gav.toString());
continue;
}
boolean pomInTarget = false;
if (config.getCheckTarget()) {
pomInTarget = checkIfPomInTarget(gav);
}
if (pomInTarget) {
logger.info("Found POM for " + gav + " already in target. Skipping deployment.");
skippedDeploys.add(gav.toString());
} else {
// only interested in files using the artifactId-version* pattern
// don't bother with .sha1 files
IOFileFilter fileFilter = new AndFileFilter(
new WildcardFileFilter(gav.getArtifactId() + "-" + gav.getVersion() + "*"),
new NotFileFilter(new SuffixFileFilter("sha1")));
Collection artifacts = FileUtils.listFiles(leafDirectory, fileFilter, null);
Authentication auth = new AuthenticationBuilder()
.addUsername(config.getUsername())
.addPassword(config.getPassword())
.build();
RemoteRepository distRepo = new RemoteRepository.Builder(
"repositoryIdentifier", "default", config.getTargetUrl())
.setProxy(ProxyHelper.getProxy(config.getTargetUrl()))
.setAuthentication(auth)
.build();
DeployRequest deployRequest = new DeployRequest();
deployRequest.setRepository(distRepo);
for (File file : artifacts) {
String extension;
if (file.getName().endsWith("tar.gz")) {
extension = "tar.gz";
} else {
extension = FilenameUtils.getExtension(file.getName());
}
String baseFileName = gav.getFilenameStart() + "." + extension;
String fileName = file.getName();
String g = gav.getGroupId();
String a = gav.getArtifactId();
String v = gav.getVersion();
Artifact artifact = null;
if (gav.getPomFilename().equals(fileName)) {
artifact = new DefaultArtifact(g, a, MavenConstants.POM, v);
} else if (gav.getJarFilename().equals(fileName)) {
artifact = new DefaultArtifact(g, a, MavenConstants.JAR, v);
} else if (gav.getSourceFilename().equals(fileName)) {
artifact = new DefaultArtifact(g, a, MavenConstants.SOURCES, MavenConstants.JAR, v);
} else if (gav.getJavadocFilename().equals(fileName)) {
artifact = new DefaultArtifact(g, a, MavenConstants.JAVADOC, MavenConstants.JAR, v);
} else if (baseFileName.equals(fileName)) {
artifact = new DefaultArtifact(g, a, extension, v);
} else {
String classifier = file.getName()
.substring(
gav.getFilenameStart().length() + 1,
file.getName().length() - ("." + extension).length());
artifact = new DefaultArtifact(g, a, classifier, extension, v);
}
if (artifact != null) {
artifact = artifact.setFile(file);
deployRequest.addArtifact(artifact);
}
}
try {
if (config.getVerifyOnly()) {
for (Artifact artifact : deployRequest.getArtifacts()) {
potentialDeploys.add(artifact.toString());
}
} else {
system.deploy(session, deployRequest);
for (Artifact artifact : deployRequest.getArtifacts()) {
successfulDeploys.add(artifact.toString());
}
}
} catch (Exception e) {
logger.info("Deployment failed with " + e.getMessage() + ", artifact might be deployed already.");
for (Artifact artifact : deployRequest.getArtifacts()) {
failedDeploys.add(artifact.toString());
}
}
}
}
summarize();
}
public void summarize() {
logger.info("Deployed {} artifacts.", successfulDeploys.size());
logger.info("Failed to deploy {} artifacts.", failedDeploys.size());
logger.info("Skipped {} artifacts.", skippedDeploys.size());
logger.info("Potentially deploy {} artifacts.", potentialDeploys.size());
}
/**
* Check if POM file for provided gav can be found in target. Just does
* a HTTP get of the header and verifies http status OK 200.
* @param gav group artifact version string
* @return {@code true} if the pom.xml already exists in the target repository
*/
private boolean checkIfPomInTarget(Gav gav) {
boolean alreadyInTarget = false;
String artifactUrl = config.getTargetUrl() + gav.getRepositoryURLPath() + gav.getPomFilename();
logger.debug("Headers for {}", artifactUrl);
HttpHead httphead;
try {
httphead = new HttpHead(artifactUrl);
} catch (Exception e) {
logger.error("Skipped : {}", artifactUrl, e);
return true;
}
if (!StringUtils.isEmpty(config.getUsername()) && !StringUtils.isEmpty(config.getUsername())) {
String encoding = java.util.Base64.getEncoder()
.encodeToString((config.getUsername() + ":" + config.getPassword()).getBytes());
httphead.setHeader("Authorization", "Basic " + encoding);
}
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpResponse response = httpClient.execute(httphead);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpURLConnection.HTTP_OK) {
alreadyInTarget = true;
} else {
logger.debug("Headers not found HTTP: {}", statusCode);
}
} catch (IOException ioe) {
logger.warn("Could not check target repository for already existing pom.xml.", ioe);
}
return alreadyInTarget;
}
public String listSucessfulDeployments() {
StringBuilder builder = new StringBuilder();
builder.append("Sucessful Deployments:\n\n");
for (String artifact : successfulDeploys) {
builder.append(artifact + "\n");
}
return builder.toString();
}
public String listFailedDeployments() {
StringBuilder builder = new StringBuilder();
builder.append("Failed Deployments:\n\n");
for (String artifact : failedDeploys) {
builder.append(artifact + "\n");
}
return builder.toString();
}
public String listSkippedDeployment() {
StringBuilder builder = new StringBuilder();
builder.append("Skipped Deployments (POM already in target):\n\n");
for (String artifact : skippedDeploys) {
builder.append(artifact + "\n");
}
return builder.toString();
}
public String listPotentialDeployment() {
StringBuilder builder = new StringBuilder();
builder.append("Potential Deployments :\n\n");
for (String artifact : potentialDeploys) {
builder.append(artifact + "\n");
}
return builder.toString();
}
public static Gav getCoordinates(File pomFile) throws Exception {
BufferedReader in = new BufferedReader(new FileReader(pomFile));
MavenXpp3Reader reader = new MavenXpp3Reader();
Model model = reader.read(in);
// get coordinates and take care of inheritance and default
String g = model.getGroupId();
if (StringUtils.isEmpty(g)) {
g = model.getParent().getGroupId();
}
String a = model.getArtifactId();
if (StringUtils.isEmpty(a)) {
a = model.getParent().getArtifactId();
}
String v = model.getVersion();
if (StringUtils.isEmpty(v)) {
v = model.getParent().getVersion();
}
String p = model.getPackaging();
if (StringUtils.isEmpty(p)) {
p = MavenConstants.JAR;
}
Gav gav = new Gav(g, a, v, p);
return gav;
}
public boolean hasFailure() {
return failedDeploys.size() > 0;
}
public String getFailureMessage() {
return "Failed to deploy some artifacts.";
}
public Set loadGavPatternsFromFilterFile(String deployFilterFile) {
Set gavPatterns = new HashSet<>();
if (deployFilterFile == null) {
return gavPatterns;
}
try {
File file = new File(deployFilterFile);
if (!file.exists()) {
return gavPatterns;
}
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
// If the line starts with a "!" then it is an inverse pattern,
// and we want to ignore this GAV specifically
boolean inverse = false;
if (line.startsWith("!")) {
line = line.substring(1);
inverse = true;
}
String[] parts = line.split(":");
if (parts.length != 4) {
logger.warn("Invalid GAV filter line: {}", line);
continue;
}
String groupId = parts[0];
String artifactId = parts[1];
String version = parts[2];
if (!patternIsValid(groupId, artifactId, version)) {
logger.warn("Invalid GAV filter line: {}", line);
continue;
}
String packagingPattern = parts[3].replace("*", ".*");
Pattern pattern = Pattern.compile(groupId + ":" + artifactId + ":" + version + ":" + packagingPattern);
gavPatterns.add(new GavPattern(pattern, inverse));
}
} catch (IOException e) {
logger.error("Failed to load GAVs from filter file", e);
}
return gavPatterns;
}
public boolean patternIsValid(String groupId, String artifactId, String version) {
return !groupId.contains("*") && !artifactId.contains("*") && !version.contains("*");
}
public boolean canDeployGav(Gav gav, int threadPoolSize) {
if (gavPatterns == null || gavPatterns.isEmpty()) {
return true;
}
try (GavMatcherExecutor gavMatcherExecutor = new GavMatcherExecutor(threadPoolSize)) {
List> futures = gavMatcherExecutor.evaluateGav(gav, gavPatterns);
for (Future future : futures) {
if (future.get()) {
return true;
}
}
} catch (Exception e) {
logger.error("Error evaluating GAV {} against patterns", gav, e);
}
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy