From 4e72f92bc792817e92a5bf766a0544113ca9ef33 Mon Sep 17 00:00:00 2001 From: kohsuke Date: Sat, 15 May 2010 13:40:29 +0000 Subject: [PATCH] Refactored FilePath to expose the archive handling code. git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@31064 71c3de6d-444a-0410-be80-ed276b4c234a --- core/src/main/java/hudson/FilePath.java | 151 ++---------------- .../main/java/hudson/util/FileVisitor.java | 2 +- .../main/java/hudson/util/io/Archiver.java | 46 ++++++ .../java/hudson/util/io/ArchiverFactory.java | 85 ++++++++++ .../main/java/hudson/util/io/TarArchiver.java | 91 +++++++++++ .../main/java/hudson/util/io/ZipArchiver.java | 75 +++++++++ 6 files changed, 309 insertions(+), 141 deletions(-) create mode 100644 core/src/main/java/hudson/util/io/Archiver.java create mode 100644 core/src/main/java/hudson/util/io/ArchiverFactory.java create mode 100644 core/src/main/java/hudson/util/io/TarArchiver.java create mode 100644 core/src/main/java/hudson/util/io/ZipArchiver.java diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 6ccf6fcb1f..4535d129e5 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -39,7 +39,6 @@ import hudson.remoting.RemoteOutputStream; import hudson.remoting.VirtualChannel; import hudson.remoting.RemoteInputStream; import hudson.util.DirScanner; -import hudson.util.FileVisitor; import hudson.util.IOException2; import hudson.util.HeadBufferingStream; import hudson.util.FormValidation; @@ -48,16 +47,15 @@ import static hudson.util.jna.GNUCLibrary.LIBC; import static hudson.Util.fixEmpty; import static hudson.FilePath.TarCompression.GZIP; import hudson.os.PosixAPI; -import hudson.org.apache.tools.tar.TarOutputStream; import hudson.org.apache.tools.tar.TarInputStream; +import hudson.util.io.Archiver; +import hudson.util.io.ArchiverFactory; import org.apache.tools.ant.BuildException; 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.tools.zip.ZipOutputStream; -import org.apache.tools.zip.ZipEntry; import org.apache.commons.io.input.CountingInputStream; import org.apache.commons.fileupload.FileItem; import org.kohsuke.stapler.Stapler; @@ -78,7 +76,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; @@ -335,7 +332,7 @@ public final class FilePath implements Serializable { * @since 1.315 */ public void zip(OutputStream os, FileFilter filter) throws IOException, InterruptedException { - archive(new ZipArchiverFactory(),os,filter); + archive(ArchiverFactory.ZIP,os,filter); } /** @@ -350,7 +347,7 @@ public final class FilePath implements Serializable { * Use {@link #zip(OutputStream,String)} that has more consistent name. */ public void createZipArchive(OutputStream os, final String glob) throws IOException, InterruptedException { - archive(new ZipArchiverFactory(),os,glob); + archive(ArchiverFactory.ZIP,os,glob); } /** @@ -363,14 +360,14 @@ public final class FilePath implements Serializable { * @since 1.315 */ public void zip(OutputStream os, final String glob) throws IOException, InterruptedException { - archive(new ZipArchiverFactory(),os,glob); + archive(ArchiverFactory.ZIP,os,glob); } /** * Uses the given scanner on 'this' directory to list up files and then archive it to a zip stream. */ public int zip(OutputStream out, DirScanner scanner) throws IOException, InterruptedException { - return archive(new ZipArchiverFactory(), out, scanner); + return archive(ArchiverFactory.ZIP, out, scanner); } /** @@ -1512,132 +1509,6 @@ public final class FilePath implements Serializable { } } - private static interface ArchiverFactory extends Serializable { - Archiver create(OutputStream out); - } - - private static final class TarArchiverFactory implements ArchiverFactory, Serializable { - public Archiver create(OutputStream out) { - return new TarWriter(out); - } - - private static final long serialVersionUID = 1L; - } - - private static final class ZipArchiverFactory implements ArchiverFactory, Serializable { - public Archiver create(OutputStream out) { - return new ZipWriter(out); - } - - private static final long serialVersionUID = 1L; - } - - - /** - * Base for {@link TarWriter} and {@link ZipWriter}. - */ - private static abstract class Archiver extends FileVisitor implements Closeable { - protected int entriesWritten =0; - - /** - * Number of files/directories archived. - */ - public int countEntries() { - return entriesWritten; - } - } - - /** - * {@link FileVisitor} that creates a tar archive. - */ - private static final class TarWriter extends Archiver { - private final byte[] buf = new byte[8192]; - private final TarOutputStream tar; - - 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. - @Override - public void flush() throws IOException { - // so don't do anything in flush - } - }); - tar.setLongFileMode(TarOutputStream.LONGFILE_GNU); - } - - public void visit(File file, String relativePath) throws IOException { - if(Functions.isWindows()) - relativePath = relativePath.replace('\\','/'); - - if(file.isDirectory()) - relativePath+='/'; - TarEntry te = new TarEntry(relativePath); - te.setModTime(file.lastModified()); - if(!file.isDirectory()) - te.setSize(file.length()); - - tar.putNextEntry(te); - - if (!file.isDirectory()) { - FileInputStream in = new FileInputStream(file); - try { - int len; - while((len=in.read(buf))>=0) - tar.write(buf,0,len); - } finally { - in.close(); - } - } - - tar.closeEntry(); - entriesWritten++; - } - - public void close() throws IOException { - tar.close(); - } - } - - /** - * {@link FileVisitor} that creates a zip archive. - */ - private static final class ZipWriter extends Archiver { - 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(); - } - entriesWritten++; - } - - 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; - } /** * Writes files in 'this' directory to a tar stream. @@ -1646,18 +1517,18 @@ public final class FilePath implements Serializable { * Ant file pattern mask, like "**/*.java". */ public int tar(OutputStream out, final String glob) throws IOException, InterruptedException { - return archive(new TarArchiverFactory(), out, glob); + return archive(ArchiverFactory.TAR, out, glob); } public int tar(OutputStream out, FileFilter filter) throws IOException, InterruptedException { - return archive(new TarArchiverFactory(), out, filter); + return archive(ArchiverFactory.TAR, out, filter); } /** * Uses the given scanner on 'this' directory to list up files and then archive it to a tar stream. */ public int tar(OutputStream out, DirScanner scanner) throws IOException, InterruptedException { - return archive(new TarArchiverFactory(), out, scanner); + return archive(ArchiverFactory.TAR, out, scanner); } /** @@ -1667,13 +1538,13 @@ public final class FilePath implements Serializable { * 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); + Archiver tw = ArchiverFactory.TAR.create(out); try { new DirScanner.Glob(fileMask,excludes).scan(baseDir,tw); } finally { tw.close(); } - return tw.entriesWritten; + return tw.countEntries(); } /** diff --git a/core/src/main/java/hudson/util/FileVisitor.java b/core/src/main/java/hudson/util/FileVisitor.java index e9ee7c8b25..9b60e84d1d 100644 --- a/core/src/main/java/hudson/util/FileVisitor.java +++ b/core/src/main/java/hudson/util/FileVisitor.java @@ -7,7 +7,7 @@ import java.io.Serializable; /** * Visits files in a directory recursively. - + * * @since 1.343 * @see DirScanner */ diff --git a/core/src/main/java/hudson/util/io/Archiver.java b/core/src/main/java/hudson/util/io/Archiver.java new file mode 100644 index 0000000000..2e1faae11b --- /dev/null +++ b/core/src/main/java/hudson/util/io/Archiver.java @@ -0,0 +1,46 @@ +/* + * The MIT License + * + * Copyright (c) 2010, InfraDNA, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.util.io; + +import hudson.util.FileVisitor; + +import java.io.Closeable; + +/** + * {@link FileVisitor} that creates archive files. + * + * @since 1.359 + * @see ArchiverFactory + */ +public abstract class Archiver extends FileVisitor implements Closeable { + protected int entriesWritten =0; + + /** + * Number of files/directories archived. + */ + public int countEntries() { + return entriesWritten; + } +} diff --git a/core/src/main/java/hudson/util/io/ArchiverFactory.java b/core/src/main/java/hudson/util/io/ArchiverFactory.java new file mode 100644 index 0000000000..9a4fde46c3 --- /dev/null +++ b/core/src/main/java/hudson/util/io/ArchiverFactory.java @@ -0,0 +1,85 @@ +/* + * The MIT License + * + * Copyright (c) 2010, InfraDNA, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.util.io; + +import hudson.FilePath.TarCompression; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; + +/** + * Creates {@link Archiver} on top of a stream. + * + * @author Kohsuke Kawaguchi + * @since 1.359 +*/ +public abstract class ArchiverFactory implements Serializable { + /** + * Creates an archiver on top of the given stream. + */ + public abstract Archiver create(OutputStream out) throws IOException; + + /** + * Uncompressed tar format. + */ + public static ArchiverFactory TAR = new TarArchiverFactory(TarCompression.NONE); + + /** + * tar+gz + */ + public static ArchiverFactory TARGZ = new TarArchiverFactory(TarCompression.GZIP); + + /** + * Zip format. + */ + public static ArchiverFactory ZIP = new ZipArchiverFactory(); + + + + private static final class TarArchiverFactory extends ArchiverFactory { + private final TarCompression method; + + private TarArchiverFactory(TarCompression method) { + this.method = method; + } + + public Archiver create(OutputStream out) throws IOException { + return new TarArchiver(method.compress(out)); + } + + private static final long serialVersionUID = 1L; + } + + private static final class ZipArchiverFactory extends ArchiverFactory { + public Archiver create(OutputStream out) { + return new ZipArchiver(out); + } + + private static final long serialVersionUID = 1L; + } + + private static final long serialVersionUID = 1L; +} diff --git a/core/src/main/java/hudson/util/io/TarArchiver.java b/core/src/main/java/hudson/util/io/TarArchiver.java new file mode 100644 index 0000000000..de55b9d825 --- /dev/null +++ b/core/src/main/java/hudson/util/io/TarArchiver.java @@ -0,0 +1,91 @@ +/* + * The MIT License + * + * Copyright (c) 2010, InfraDNA, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.util.io; + +import hudson.Functions; +import hudson.org.apache.tools.tar.TarOutputStream; +import hudson.util.FileVisitor; +import org.apache.tools.tar.TarEntry; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * {@link FileVisitor} that creates a tar archive. + * + * @see ArchiverFactory#TAR + */ +final class TarArchiver extends Archiver { + private final byte[] buf = new byte[8192]; + private final TarOutputStream tar; + + TarArchiver(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. + @Override + public void flush() throws IOException { + // so don't do anything in flush + } + }); + tar.setLongFileMode(TarOutputStream.LONGFILE_GNU); + } + + public void visit(File file, String relativePath) throws IOException { + if(Functions.isWindows()) + relativePath = relativePath.replace('\\','/'); + + if(file.isDirectory()) + relativePath+='/'; + TarEntry te = new TarEntry(relativePath); + te.setModTime(file.lastModified()); + if(!file.isDirectory()) + te.setSize(file.length()); + + tar.putNextEntry(te); + + if (!file.isDirectory()) { + FileInputStream in = new FileInputStream(file); + try { + int len; + while((len=in.read(buf))>=0) + tar.write(buf,0,len); + } finally { + in.close(); + } + } + + tar.closeEntry(); + entriesWritten++; + } + + public void close() throws IOException { + tar.close(); + } +} diff --git a/core/src/main/java/hudson/util/io/ZipArchiver.java b/core/src/main/java/hudson/util/io/ZipArchiver.java new file mode 100644 index 0000000000..19e9b678fc --- /dev/null +++ b/core/src/main/java/hudson/util/io/ZipArchiver.java @@ -0,0 +1,75 @@ +/* + * The MIT License + * + * Copyright (c) 2010, InfraDNA, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package hudson.util.io; + +import hudson.util.FileVisitor; +import org.apache.tools.zip.ZipEntry; +import org.apache.tools.zip.ZipOutputStream; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * {@link FileVisitor} that creates a zip archive. + * + * @see ArchiverFactory#ZIP + */ +final class ZipArchiver extends Archiver { + private final byte[] buf = new byte[8192]; + private final ZipOutputStream zip; + + ZipArchiver(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(); + } + entriesWritten++; + } + + 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; +} -- GitLab