From 297d7f617d3329e1da8ab9b5a75db6037eb3d452 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 23 Dec 2010 18:03:47 +0100 Subject: [PATCH] added UnArchiver api and SystemUtil --- .../java/sonia/scm/io/AbstractUnArchiver.java | 261 ++++++++++++++++++ .../main/java/sonia/scm/io/UnArchiver.java | 71 +++++ .../main/java/sonia/scm/io/ZipUnArchiver.java | 199 +++++++++++++ .../src/main/java/sonia/scm/util/IOUtil.java | 26 ++ .../main/java/sonia/scm/util/SystemUtil.java | 63 +++++ 5 files changed, 620 insertions(+) create mode 100644 scm-core/src/main/java/sonia/scm/io/AbstractUnArchiver.java create mode 100644 scm-core/src/main/java/sonia/scm/io/UnArchiver.java create mode 100644 scm-core/src/main/java/sonia/scm/io/ZipUnArchiver.java create mode 100644 scm-core/src/main/java/sonia/scm/util/SystemUtil.java diff --git a/scm-core/src/main/java/sonia/scm/io/AbstractUnArchiver.java b/scm-core/src/main/java/sonia/scm/io/AbstractUnArchiver.java new file mode 100644 index 0000000000..f940012be5 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/io/AbstractUnArchiver.java @@ -0,0 +1,261 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.io; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.util.ChecksumUtil; +import sonia.scm.util.IOUtil; +import sonia.scm.util.Util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import java.util.Properties; + +/** + * + * @author Sebastian Sdorra + */ +public abstract class AbstractUnArchiver implements UnArchiver +{ + + /** Field description */ + public static final String FILE_SOURCE_PROPERTIES = "scm-source.properties"; + + /** Field description */ + public static final String PROPERTY_CHECKSUM = "scm.unarchiver.checksum"; + + /** Field description */ + public static final String PROPERTY_SOURCEFILE = "scm.unarchiver.source"; + + /** the logger for AbstractUnArchiver */ + private static final Logger logger = + LoggerFactory.getLogger(AbstractUnArchiver.class); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param archive + * @param outputDirectory + * + * @throws IOException + */ + protected abstract void extractArchive(File archive, File outputDirectory) + throws IOException; + + /** + * Method description + * + * + * @param archive + * @param outputDirectory + * + * @throws IOException + */ + @Override + public void extract(File archive, File outputDirectory) throws IOException + { + extract(archive, outputDirectory, false); + } + + /** + * Method description + * + * + * @param archive + * @param outputDirectory + * @param force + * + * @throws IOException + */ + @Override + public void extract(File archive, File outputDirectory, boolean force) + throws IOException + { + if (!outputDirectory.exists()) + { + IOUtil.mkdirs(outputDirectory); + extractAndCreateProperties(archive, outputDirectory); + } + else if (force || isModified(archive, outputDirectory)) + { + IOUtil.delete(outputDirectory); + IOUtil.mkdirs(outputDirectory); + extractAndCreateProperties(archive, outputDirectory); + } + } + + /** + * Method description + * + * + * @param archive + * @param outputDirectory + * + * @throws IOException + */ + private void extractAndCreateProperties(File archive, File outputDirectory) + throws IOException + { + extractArchive(archive, outputDirectory); + + String checksum = ChecksumUtil.createChecksum(archive); + + writeProperties(outputDirectory, checksum); + } + + /** + * Method description + * + * + * @param outputDirectory + * @param checksum + * + * @throws IOException + */ + private void writeProperties(File outputDirectory, String checksum) + throws IOException + { + Properties properties = new Properties(); + + properties.setProperty(PROPERTY_CHECKSUM, checksum); + properties.setProperty(PROPERTY_SOURCEFILE, + outputDirectory.getAbsolutePath()); + + FileOutputStream output = null; + + try + { + output = new FileOutputStream(new File(outputDirectory, + FILE_SOURCE_PROPERTIES)); + properties.store( + output, AbstractUnArchiver.class.getName().concat(" properties")); + } + finally + { + IOUtil.close(output); + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param sourcePropsFile + * + * @return + */ + private String getChecksumProperty(File sourcePropsFile) + { + Properties properties = new Properties(); + FileInputStream input = null; + + try + { + input = new FileInputStream(sourcePropsFile); + properties.load(input); + } + catch (IOException ex) + { + logger.error(ex.getMessage(), ex); + } + finally + { + IOUtil.close(input); + } + + return properties.getProperty(PROPERTY_CHECKSUM); + } + + /** + * Method description + * + * + * @param archive + * @param outputDirectory + * + * @return + */ + private boolean isModified(File archive, File outputDirectory) + { + boolean modified = true; + File sourcePropsFile = new File(outputDirectory, FILE_SOURCE_PROPERTIES); + + if (sourcePropsFile.exists()) + { + String checksumProperty = getChecksumProperty(sourcePropsFile); + + if (Util.isNotEmpty(checksumProperty)) + { + try + { + String checksum = ChecksumUtil.createChecksum(archive); + + if (checksumProperty.equals(checksum)) + { + if (logger.isDebugEnabled()) + { + logger.debug("file {} is not modified", archive.getPath()); + } + + modified = false; + } + else if (logger.isDebugEnabled()) + { + logger.debug("file {} is modified", archive.getPath()); + } + } + catch (IOException ex) + { + logger.error(ex.getMessage(), ex); + } + } + } + + return modified; + } +} diff --git a/scm-core/src/main/java/sonia/scm/io/UnArchiver.java b/scm-core/src/main/java/sonia/scm/io/UnArchiver.java new file mode 100644 index 0000000000..6973dd4e84 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/io/UnArchiver.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.io; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.IOException; + +/** + * + * @author Sebastian Sdorra + */ +public interface UnArchiver +{ + + /** + * Method description + * + * + * @param archive + * @param outputDirectory + * @param force + * + * @throws IOException + */ + public void extract(File archive, File outputDirectory, boolean force) + throws IOException; + + /** + * Method description + * + * + * @param archive + * @param outputDirectory + * + * @throws IOException + */ + public void extract(File archive, File outputDirectory) throws IOException; +} diff --git a/scm-core/src/main/java/sonia/scm/io/ZipUnArchiver.java b/scm-core/src/main/java/sonia/scm/io/ZipUnArchiver.java new file mode 100644 index 0000000000..c7e1c33f40 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/io/ZipUnArchiver.java @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.io; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.util.IOUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * + * @author Sebastian Sdorra + */ +public class ZipUnArchiver extends AbstractUnArchiver +{ + + /** Field description */ + public static final String EXTENSION = ".zip"; + + /** the logger for ZipUnArchiver */ + private static final Logger logger = + LoggerFactory.getLogger(ZipUnArchiver.class); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param archive + * @param outputDirectory + * + * @throws IOException + */ + @Override + protected void extractArchive(File archive, File outputDirectory) + throws IOException + { + if (logger.isDebugEnabled()) + { + logger.debug("extract zip \"{}\" to \"{}\"", archive.getPath(), + outputDirectory.getAbsolutePath()); + } + + ZipInputStream input = null; + + try + { + input = new ZipInputStream(new FileInputStream(archive)); + + ZipEntry entry = input.getNextEntry(); + + while (entry != null) + { + extractEntry(outputDirectory, input, entry); + entry = input.getNextEntry(); + } + } + finally + { + IOUtil.close(input); + } + } + + /** + * Method description + * + * + * @param outputDirectory + * @param name + * + * @return + */ + private File createFile(File outputDirectory, String name) + { + if (name.contains("..")) + { + throw new IllegalArgumentException("name is invalid"); + } + + return new File(outputDirectory, name); + } + + /** + * Method description + * + * + * @param outputDirectory + * @param input + * @param entry + * + * @throws IOException + */ + private void extractEntry(File outputDirectory, ZipInputStream input, + ZipEntry entry) + throws IOException + { + try + { + File file = createFile(outputDirectory, entry.getName()); + + if (!entry.isDirectory()) + { + File parent = file.getParentFile(); + + if (parent != null) + { + IOUtil.mkdirs(parent); + extractFile(input, file); + } + else + { + logger.warn("file {} has no parent", file.getPath()); + } + } + else + { + IOUtil.mkdirs(file); + } + } + finally + { + input.closeEntry(); + } + } + + /** + * Method description + * + * + * @param input + * @param outputFile + * + * @throws IOException + */ + private void extractFile(ZipInputStream input, File outputFile) + throws IOException + { + if (logger.isDebugEnabled()) + { + logger.debug("extract file {}", outputFile.getPath()); + } + + FileOutputStream output = null; + + try + { + output = new FileOutputStream(outputFile); + IOUtil.copy(input, output); + } + finally + { + IOUtil.close(output); + } + } +} diff --git a/scm-core/src/main/java/sonia/scm/util/IOUtil.java b/scm-core/src/main/java/sonia/scm/util/IOUtil.java index 62ed1d88fd..2b8f5556e3 100644 --- a/scm-core/src/main/java/sonia/scm/util/IOUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/IOUtil.java @@ -38,6 +38,8 @@ package sonia.scm.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.io.ZipUnArchiver; + //~--- JDK imports ------------------------------------------------------------ import java.io.Closeable; @@ -161,6 +163,30 @@ public class IOUtil } } + /** + * Method description + * + * + * @param archive + * @param outputDirectory + * + * @throws IOException + */ + public static void extract(File archive, File outputDirectory) + throws IOException + { + String name = archive.getName().toLowerCase(); + + if (name.endsWith(ZipUnArchiver.EXTENSION)) + { + new ZipUnArchiver().extract(archive, outputDirectory); + } + else + { + throw new IOException("no unarchiver found for ".concat(name)); + } + } + /** * Method description * diff --git a/scm-core/src/main/java/sonia/scm/util/SystemUtil.java b/scm-core/src/main/java/sonia/scm/util/SystemUtil.java new file mode 100644 index 0000000000..6d0d06dd3c --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/util/SystemUtil.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.util; + +/** + * + * @author Sebastian Sdorra + */ +public class SystemUtil +{ + + /** Field description */ + public static final String PROPERTY_OSNAME = "os.name"; + + /** Field description */ + public static final String PROPERTY_VALUE_WINDOWS = "Windows"; + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public static boolean isWindows() + { + String os = System.getProperty(PROPERTY_OSNAME); + + return (os != null) && os.startsWith(PROPERTY_VALUE_WINDOWS); + } +}