diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 1969c19315..5a8097f055 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -73,7 +73,6 @@ import java.io.OutputStream; import java.io.Serializable; import java.io.Writer; import java.io.OutputStreamWriter; -import java.io.Closeable; import java.net.URI; import java.net.URL; import java.net.URLConnection; @@ -247,31 +246,44 @@ public final class FilePath implements Serializable { * Creates a zip file from this directory or a file and sends that to the given output stream. */ public void createZipArchive(OutputStream os) throws IOException, InterruptedException { - createZipArchive(os,(FileFilter)null); - } - - /** - * Creates a zip file from this directory by using the specified filter, - * and sends the result to the given output stream. - * - * @param filter - * Must be serializable since it may be executed remotely. Can be null to add all files. - * - * @since 1.314 - */ - public void createZipArchive(OutputStream os, final FileFilter filter) throws IOException, InterruptedException { final OutputStream out = (channel!=null)?new RemoteOutputStream(os):os; act(new FileCallable() { + private transient byte[] buf; public Void invoke(File f, VirtualChannel channel) throws IOException { - ZipWriter zw = new ZipWriter(out); - try { - accept(f,zw.with(filter)); - } finally { - zw.close(); - } + buf = new byte[8192]; + + ZipOutputStream zip = new ZipOutputStream(out); + zip.setEncoding(System.getProperty("file.encoding")); + scan(f,zip,""); + zip.close(); return null; } + private void scan(File f, ZipOutputStream zip, String path) throws IOException { + // Bitmask indicating directories in 'external attributes' of a ZIP archive entry. + final long BITMASK_IS_DIRECTORY = 1<<4; + + if (f.canRead()) { + if(f.isDirectory()) { + ZipEntry dirZipEntry = new ZipEntry(path+f.getName()+'/'); + // Setting this bit explicitly is needed by some unzipping applications (see HUDSON-3294). + dirZipEntry.setExternalAttributes(BITMASK_IS_DIRECTORY); + zip.putNextEntry(dirZipEntry); + zip.closeEntry(); + for( File child : f.listFiles() ) + scan(child,zip,path+f.getName()+'/'); + } else { + zip.putNextEntry(new ZipEntry(path+f.getName())); + FileInputStream in = new FileInputStream(f); + int len; + while((len=in.read(buf))>0) + zip.write(buf,0,len); + in.close(); + zip.closeEntry(); + } + } + } + private static final long serialVersionUID = 1L; }); } @@ -294,12 +306,24 @@ public final class FilePath implements Serializable { final OutputStream out = (channel!=null)?new RemoteOutputStream(os):os; act(new FileCallable() { public Void invoke(File dir, VirtualChannel channel) throws IOException { - ZipWriter zw = new ZipWriter(out); - try { - accept(dir,glob,null,zw); - } finally { - zw.close(); + byte[] buf = new byte[8192]; + + ZipOutputStream zip = new ZipOutputStream(out); + zip.setEncoding(System.getProperty("file.encoding")); + for( String entry : glob(dir,glob) ) { + File file = new File(dir,entry); + if (file.canRead()) { + zip.putNextEntry(new ZipEntry(dir.getName()+'/'+entry)); + FileInputStream in = new FileInputStream(file); + int len; + while((len=in.read(buf))>0) + zip.write(buf,0,len); + in.close(); + zip.closeEntry(); + } } + + zip.close(); return null; } @@ -1351,69 +1375,39 @@ public final class FilePath implements Serializable { } /** - * Visits files in a directory recursively. - * Primarily used for building an archive with various filtering. + * Writes to a tar stream and stores obtained files to the base dir. + * + * @return + * number of files/directories that are written. */ - private static abstract class FileVisitor { - abstract void visit(File f, String relativePath) throws IOException; + private Integer writeToTar(File baseDir, String fileMask, String excludes, OutputStream out) throws IOException { + FileSet fs = Util.createFileSet(baseDir,fileMask,excludes); - /** - * Decorates by a given filter. - */ - FileVisitor with(FileFilter f) { - if(f==null) return this; - return new FilterFileVisitor(f,this); - } - } + byte[] buf = new byte[8192]; - private static final class FilterFileVisitor extends FileVisitor implements Serializable { - private final FileFilter filter; - private final FileVisitor visitor; - - private FilterFileVisitor(FileFilter filter, FileVisitor visitor) { - this.filter = filter!=null ? filter : PASS_THROUGH; - this.visitor = visitor; - } - - public void visit(File f, String relativePath) throws IOException { - if(f.isDirectory() || filter.accept(f)) - visitor.visit(f,relativePath); - } - - private static final FileFilter PASS_THROUGH = new FileFilter() { - public boolean accept(File pathname) { - return true; + TarOutputStream tar = new TarOutputStream(new BufferedOutputStream(out) { + // TarOutputStream uses TarBuffer internally, + // which flushes the stream for each block. this creates unnecessary + // data stream fragmentation, and flush request to a remote, which slows things down. + public void flush() throws IOException { + // so don't do anything in flush } - }; - - private static final long serialVersionUID = 1L; - } - - /** - * {@link FileVisitor} that creates a tar archive. - */ - private static final class TarWriter extends FileVisitor implements Closeable { - private final byte[] buf = new byte[8192]; - private final TarOutputStream tar; - int filesWritten=0; - - private TarWriter(OutputStream out) { - tar = new TarOutputStream(new BufferedOutputStream(out) { - // TarOutputStream uses TarBuffer internally, - // which flushes the stream for each block. this creates unnecessary - // data stream fragmentation, and flush request to a remote, which slows things down. - public void flush() throws IOException { - // so don't do anything in flush - } - }); - tar.setLongFileMode(TarOutputStream.LONGFILE_GNU); + }); + tar.setLongFileMode(TarOutputStream.LONGFILE_GNU); + String[] files; + if(baseDir.exists()) { + DirectoryScanner ds = fs.getDirectoryScanner(new org.apache.tools.ant.Project()); + files = ds.getIncludedFiles(); + } else { + files = new String[0]; } - - public void visit(File file, String relativePath) throws IOException { + for( String f : files) { if(Functions.isWindows()) - relativePath = relativePath.replace('\\','/'); + f = f.replace('\\','/'); - TarEntry te = new TarEntry(relativePath); + File file = new File(baseDir, f); + + TarEntry te = new TarEntry(f); te.setModTime(file.lastModified()); if(!file.isDirectory()) te.setSize(file.length()); @@ -1429,129 +1423,11 @@ public final class FilePath implements Serializable { } tar.closeEntry(); - filesWritten++; } - public void close() throws IOException { - tar.close(); - } - } + tar.close(); - /** - * {@link FileVisitor} that creates a zip archive. - */ - private static final class ZipWriter extends FileVisitor implements Closeable { - private final byte[] buf = new byte[8192]; - private final ZipOutputStream zip; - - private ZipWriter(OutputStream out) { - zip = new ZipOutputStream(out); - zip.setEncoding(System.getProperty("file.encoding")); - } - - public void visit(File f, String relativePath) throws IOException { - if(f.isDirectory()) { - ZipEntry dirZipEntry = new ZipEntry(relativePath+'/'); - // Setting this bit explicitly is needed by some unzipping applications (see HUDSON-3294). - dirZipEntry.setExternalAttributes(BITMASK_IS_DIRECTORY); - zip.putNextEntry(dirZipEntry); - zip.closeEntry(); - } else { - zip.putNextEntry(new ZipEntry(relativePath)); - FileInputStream in = new FileInputStream(f); - int len; - while((len=in.read(buf))>0) - zip.write(buf,0,len); - in.close(); - zip.closeEntry(); - } - } - - public void close() throws IOException { - zip.close(); - } - - // Bitmask indicating directories in 'external attributes' of a ZIP archive entry. - private static final long BITMASK_IS_DIRECTORY = 1<<4; - } - - /** - * Scans the given base directory with a GLOB and exclusion mask, and calls visitor. - */ - private void accept(File baseDir, String fileMask, String excludes, FileVisitor visitor) throws IOException { - if(fileMask==null || excludes==null) { - // optimization - accept(baseDir,visitor); - return; - } - - FileSet fs = Util.createFileSet(baseDir,fileMask,excludes); - - if(baseDir.exists()) { - DirectoryScanner ds = fs.getDirectoryScanner(new org.apache.tools.ant.Project()); - for( String f : ds.getIncludedFiles()) { - File file = new File(baseDir, f); - visitor.visit(file,f); - } - } - } - - /** - * Scans the given directory tree recursively and calls visitor. - */ - private void accept(final File baseDir, final FileVisitor visitor) throws IOException { - class Scanner { - public void run() throws IOException { - scan(baseDir,""); - } - - private void scan(File f, String path) throws IOException { - if (f.canRead()) { - visitor.visit(f,path+f.getName()); - if(f.isDirectory()) { - for( File child : f.listFiles() ) - scan(child,path+f.getName()+'/'); - } - } - } - } - new Scanner().run(); - } - - - /** - * Writes files in 'this' directory to a tar stream. - * - * @param glob - * Ant file pattern mask, like "**/*.java". - */ - public int tar(OutputStream out, final String glob) throws IOException, InterruptedException { - final OutputStream o = new RemoteOutputStream(out); - try { - return act(new FileCallable() { - public Integer invoke(File f, VirtualChannel channel) throws IOException { - return writeToTar(f,glob,null,o); - } - }); - } finally { - out.close(); - } - } - - /** - * Writes to a tar stream and stores obtained files to the base dir. - * - * @return - * number of files/directories that are written. - */ - private Integer writeToTar(File baseDir, String fileMask, String excludes, OutputStream out) throws IOException { - TarWriter tw = new TarWriter(out); - try { - accept(baseDir,fileMask,excludes,tw); - } finally { - tw.close(); - } - return tw.filesWritten; + return files.length; } /** diff --git a/core/src/main/java/hudson/scm/CVSSCM.java b/core/src/main/java/hudson/scm/CVSSCM.java index 4a38af704d..05f8e114c0 100644 --- a/core/src/main/java/hudson/scm/CVSSCM.java +++ b/core/src/main/java/hudson/scm/CVSSCM.java @@ -49,6 +49,7 @@ import hudson.util.ArgumentListBuilder; import hudson.util.ForkOutputStream; import hudson.util.IOException2; import hudson.util.FormValidation; +import hudson.util.AtomicFileWriter; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.tools.ant.BuildException; @@ -1010,6 +1011,13 @@ public class CVSSCM extends SCM implements Serializable { if(modified) { // write it back + AtomicFileWriter w = new AtomicFileWriter(entries); + try { + w.write(newContents.toString()); + w.commit(); + } finally { + w.abort(); + } File tmp = new File(f, "CVS/Entries.tmp"); FileUtils.writeStringToFile(tmp,newContents.toString()); entries.delete();