Skip to content

Commit

Permalink
[FIXED JENKINS-17236] Introduce ArtifactManager
Browse files Browse the repository at this point in the history
Merge branch 'ArtifactManager-JENKINS-17236'
  • Loading branch information
kohsuke committed Sep 13, 2013
2 parents eea972e + f60b631 commit b698b67
Show file tree
Hide file tree
Showing 23 changed files with 1,243 additions and 326 deletions.
3 changes: 3 additions & 0 deletions changelog.html
Expand Up @@ -67,6 +67,9 @@
<li class=rfe>
Display the full display name in title for jobs and views.
(<a href="https://github.com/jenkinsci/jenkins/pull/884">pull request 884</a>)
<li class='major rfe'>
Added a new extension point to control where archived artifacts get stored.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-17236">issue 17236</a>)
</ul>
</div><!--=TRUNK-END=-->

Expand Down
120 changes: 82 additions & 38 deletions core/src/main/java/hudson/FilePath.java
Expand Up @@ -53,10 +53,9 @@
import hudson.org.apache.tools.tar.TarInputStream;
import hudson.util.io.Archiver;
import hudson.util.io.ArchiverFactory;
import org.apache.tools.ant.BuildException;
import jenkins.util.VirtualFile;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.tar.TarEntry;
import org.apache.commons.io.input.CountingInputStream;
Expand Down Expand Up @@ -98,8 +97,10 @@

import com.sun.jna.Native;
import hudson.os.PosixException;
import java.io.BufferedInputStream;
import hudson.util.FileVisitor;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import org.apache.tools.ant.taskdefs.Chmod;

Expand Down Expand Up @@ -197,7 +198,7 @@ public final class FilePath implements Serializable {
* that's connected to that machine. If null, that means the local file path.
*/
public FilePath(VirtualChannel channel, String remote) {
this.channel = channel;
this.channel = channel == Jenkins.MasterComputer.localChannel ? null : channel;
this.remote = normalize(remote);
}

Expand Down Expand Up @@ -1041,6 +1042,13 @@ public URI invoke(File f, VirtualChannel channel) {
});
}

/**
* Gets the {@link VirtualFile} representation of this {@link FilePath}
*/
public VirtualFile toVirtualFile() {
return VirtualFile.forFilePath(this);
}

/**
* Creates this directory.
*/
Expand Down Expand Up @@ -1880,44 +1888,49 @@ public int copyRecursiveTo(String fileMask, FilePath target) throws IOException,
* the number of files copied.
*/
public int copyRecursiveTo(final String fileMask, final String excludes, final FilePath target) throws IOException, InterruptedException {
return copyRecursiveTo(new DirScanner.Glob(fileMask, excludes), target, fileMask);
}

/**
* Copies files according to a specified scanner to a target node.
* @param scanner a way of enumerating some files (must be serializable for possible delivery to remote side)
* @param target the destination basedir
* @param description a description of the fileset, for logging purposes
* @return the number of files copied
* @since 1.531
*/
public int copyRecursiveTo(final DirScanner scanner, final FilePath target, final String description) throws IOException, InterruptedException {
if(this.channel==target.channel) {
// local to local copy.
return act(new FileCallable<Integer>() {
private static final long serialVersionUID = 1L;
public Integer invoke(File base, VirtualChannel channel) throws IOException {
if(!base.exists()) return 0;
assert target.channel==null;

try {
class CopyImpl extends Copy {
private int copySize;

public CopyImpl() {
setProject(new org.apache.tools.ant.Project());
}

@Override
protected void doFileOperations() {
copySize = super.fileCopyMap.size();
super.doFileOperations();
final File dest = new File(target.remote);
final AtomicInteger count = new AtomicInteger();
scanner.scan(base, new FileVisitor() {
@Override public void visit(File f, String relativePath) throws IOException {
if (f.isFile()) {
File target = new File(dest, relativePath);
target.getParentFile().mkdirs();
Util.copyFile(f, target);
count.incrementAndGet();
}

public int getNumCopied() {
return copySize;
}
@Override public boolean understandsSymlink() {
return true;
}
@Override public void visitSymlink(File link, String target, String relativePath) throws IOException {
try {
Util.createSymlink(dest, target, relativePath, TaskListener.NULL);
} catch (InterruptedException x) {
throw (IOException) new IOException(x.toString()).initCause(x);
}
count.incrementAndGet();
}

CopyImpl copyTask = new CopyImpl();
copyTask.setTodir(new File(target.remote));
copyTask.addFileset(Util.createFileSet(base,fileMask,excludes));
copyTask.setOverwrite(true);
copyTask.setIncludeEmptyDirs(false);

copyTask.execute();
return copyTask.getNumCopied();
} catch (BuildException e) {
throw new IOException2("Failed to copy "+base+"/"+fileMask+" to "+target,e);
}
});
return count.get();
}
});
} else
Expand All @@ -1929,7 +1942,7 @@ public int getNumCopied() {
private static final long serialVersionUID = 1L;
public Void invoke(File f, VirtualChannel channel) throws IOException {
try {
readFromTar(remote+'/'+fileMask, f,TarCompression.GZIP.extract(pipe.getIn()));
readFromTar(remote + '/' + description, f,TarCompression.GZIP.extract(pipe.getIn()));
return null;
} finally {
pipe.getIn().close();
Expand All @@ -1939,7 +1952,7 @@ public Void invoke(File f, VirtualChannel channel) throws IOException {
Future<Integer> future2 = actAsync(new FileCallable<Integer>() {
private static final long serialVersionUID = 1L;
@Override public Integer invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
return writeToTar(new File(remote),fileMask,excludes,TarCompression.GZIP.compress(pipe.getOut()));
return writeToTar(new File(remote), scanner, TarCompression.GZIP.compress(pipe.getOut()));
}
});
try {
Expand All @@ -1957,14 +1970,14 @@ public Void invoke(File f, VirtualChannel channel) throws IOException {
private static final long serialVersionUID = 1L;
public Integer invoke(File f, VirtualChannel channel) throws IOException {
try {
return writeToTar(f,fileMask,excludes,TarCompression.GZIP.compress(pipe.getOut()));
return writeToTar(f, scanner, TarCompression.GZIP.compress(pipe.getOut()));
} finally {
pipe.getOut().close();
}
}
});
try {
readFromTar(remote+'/'+fileMask,new File(target.remote),TarCompression.GZIP.extract(pipe.getIn()));
readFromTar(remote + '/' + description,new File(target.remote),TarCompression.GZIP.extract(pipe.getIn()));
} catch (IOException e) {// BuildException or IOException
try {
future.get(3,TimeUnit.SECONDS);
Expand Down Expand Up @@ -2013,10 +2026,10 @@ public int tar(OutputStream out, DirScanner scanner) throws IOException, Interru
* @return
* number of files/directories that are written.
*/
private static Integer writeToTar(File baseDir, String fileMask, String excludes, OutputStream out) throws IOException {
private static Integer writeToTar(File baseDir, DirScanner scanner, OutputStream out) throws IOException {
Archiver tw = ArchiverFactory.TAR.create(out);
try {
new DirScanner.Glob(fileMask,excludes).scan(baseDir,tw);
scanner.scan(baseDir,tw);
} finally {
tw.close();
}
Expand Down Expand Up @@ -2479,4 +2492,35 @@ public FilePath call() throws IOException {
}
});
}

/**
* Helper class to make it easy to send an explicit list of files using {@link FilePath} methods.
* @since 1.531
*/
public static final class ExplicitlySpecifiedDirScanner extends DirScanner {

private static final long serialVersionUID = 1;

private final Map<String,String> files;

/**
* Create a “scanner” (it actually does no scanning).
* @param files a map from logical relative paths as per {@link FileVisitor#visit}, to actual relative paths within the scanned directory
*/
public ExplicitlySpecifiedDirScanner(Map<String,String> files) {
this.files = files;
}

@Override public void scan(File dir, FileVisitor visitor) throws IOException {
for (Map.Entry<String,String> entry : files.entrySet()) {
String archivedPath = entry.getKey();
assert archivedPath.indexOf('\\') == -1;
String workspacePath = entry.getValue();
assert workspacePath.indexOf('\\') == -1;
scanSingle(new File(dir, workspacePath), archivedPath, visitor);
}
}

}

}

0 comments on commit b698b67

Please sign in to comment.