package org.jfrog.hudson.generic;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import hudson.FilePath;
import hudson.remoting.VirtualChannel;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.jfrog.build.api.Dependency;
import org.jfrog.build.api.dependency.DownloadableArtifact;
import org.jfrog.build.api.util.FileChecksumCalculator;
import org.jfrog.build.api.util.Log;
import org.jfrog.build.extractor.clientConfiguration.client.ArtifactoryDependenciesClient;
import org.jfrog.build.extractor.clientConfiguration.util.DependenciesDownloader;
import org.jfrog.build.extractor.clientConfiguration.util.DependenciesDownloaderHelper;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Primary implementation of dependencies downloader,
* handles Jenkins slaves and re-use a client for HTTP communication.
*
* @author Shay Yaakov
*/
public class DependenciesDownloaderImpl implements DependenciesDownloader {
private ArtifactoryDependenciesClient client;
private FilePath workspace;
private Log log;
private boolean flatDownload = false;
public DependenciesDownloaderImpl(ArtifactoryDependenciesClient client, FilePath workspace, Log log) {
this.client = client;
this.workspace = workspace;
this.log = log;
}
public ArtifactoryDependenciesClient getClient() {
return client;
}
public List<Dependency> download(Set<DownloadableArtifact> downloadableArtifacts) throws IOException {
DependenciesDownloaderHelper helper = new DependenciesDownloaderHelper(this, log);
return helper.downloadDependencies(downloadableArtifacts);
}
public String getTargetDir(String targetDir, String relativeDir) throws IOException {
try {
String downloadFileRelativePath = this.flatDownload && relativeDir.contains("/") ?
StringUtils.substringAfterLast(relativeDir, "/") : relativeDir;
FilePath targetDirFile = new FilePath(workspace, targetDir).child(downloadFileRelativePath);
return targetDirFile.absolutize().getRemote();
} catch (InterruptedException e) {
log.warn("Caught interrupted exception: " + e.getLocalizedMessage());
}
return null;
}
public Map<String, String> saveDownloadedFile(InputStream is, String filePath) throws IOException {
try {
FilePath child = workspace.child(filePath);
child.copyFrom(is);
return child.act(new DownloadFileCallable(log));
} catch (InterruptedException e) {
log.warn("Caught interrupted exception: " + e.getLocalizedMessage());
} finally {
IOUtils.closeQuietly(is);
}
return null;
}
public boolean isFileExistsLocally(String filePath, String md5, String sha1) throws IOException {
try {
FilePath child = workspace.child(filePath);
if (!child.exists()) {
return false;
}
if (child.isDirectory()) {
return false;
}
Map<String, String> checksumsMap = child.act(new DownloadFileCallable(log));
boolean isExists = checksumsMap != null &&
StringUtils.isNotBlank(md5) && StringUtils.equals(md5, checksumsMap.get("md5")) &&
StringUtils.isNotBlank(sha1) && StringUtils.equals(sha1, checksumsMap.get("sha1"));
if (isExists) {
return true;
} else {
log.info(String.format("Overriding existing in destination file: %s", child));
return false;
}
} catch (InterruptedException e) {
log.warn("Caught interrupted exception: " + e.getLocalizedMessage());
}
return false;
}
public void removeUnusedArtifactsFromLocal(Set<String> allResolvesFiles, Set<String> forDeletionFiles)
throws IOException {
try {
for (String resolvedFile : forDeletionFiles) {
FilePath resolvedFileParent = workspace.child(resolvedFile).getParent();
if (!resolvedFileParent.exists()) {
continue;
}
List<FilePath> fileSiblings = resolvedFileParent.list();
if (fileSiblings == null || fileSiblings.isEmpty()) {
continue;
}
for (FilePath sibling : fileSiblings) {
String siblingPath = sibling.absolutize().getRemote();
if (!isResolvedOrParentOfResolvedFile(allResolvesFiles, siblingPath)) {
sibling.deleteRecursive();
log.info("Deleted unresolved file '" + siblingPath + "'");
}
}
}
} catch (InterruptedException e) {
log.warn("Caught interrupted exception: " + e.getLocalizedMessage());
}
}
public void setFlatDownload(boolean flat){
this.flatDownload = flat;
}
private boolean isResolvedOrParentOfResolvedFile(Set<String> resolvedFiles, final String path) {
return Iterables.any(resolvedFiles, new Predicate<String>() {
public boolean apply(String filePath) {
return StringUtils.equals(filePath, path) || StringUtils.startsWith(filePath, path);
}
});
}
private static class DownloadFileCallable implements FilePath.FileCallable<Map<String, String>> {
private Log log;
public DownloadFileCallable(Log log) {
this.log = log;
}
public Map<String, String> invoke(File f, VirtualChannel channel) throws IOException {
try {
return FileChecksumCalculator.calculateChecksums(f, "md5", "sha1");
} catch (NoSuchAlgorithmException e) {
log.warn("Could not find checksum algorithm: " + e.getLocalizedMessage());
}
return null;
}
}
}