From a54e98fd72c8c7980b022a0f202a249c7059c07a Mon Sep 17 00:00:00 2001 From: ribeaudc <ribeaudc> Date: Fri, 26 Oct 2007 19:18:03 +0000 Subject: [PATCH] add: - 'IChecksum', 'IChecksumWriter' and 'ChecksumWriter'. - Unit test for 'IChecksumWriter'. change: - '.classpath' which used 'fast-md5' library. SVN: 2266 --- bds/.classpath | 1 + .../ch/systemsx/cisd/bds/ChecksumWriter.java | 159 ++++++++++++++++++ .../java/ch/systemsx/cisd/bds/IChecksum.java | 37 ++++ .../ch/systemsx/cisd/bds/IChecksumWriter.java | 42 +++++ .../systemsx/cisd/bds/ChecksumWriterTest.java | 90 ++++++++++ 5 files changed, 329 insertions(+) create mode 100644 bds/source/java/ch/systemsx/cisd/bds/ChecksumWriter.java create mode 100644 bds/source/java/ch/systemsx/cisd/bds/IChecksum.java create mode 100644 bds/source/java/ch/systemsx/cisd/bds/IChecksumWriter.java create mode 100644 bds/sourceTest/java/ch/systemsx/cisd/bds/ChecksumWriterTest.java diff --git a/bds/.classpath b/bds/.classpath index f0cc47d3a9b..71f7dd90f65 100644 --- a/bds/.classpath +++ b/bds/.classpath @@ -13,5 +13,6 @@ <classpathentry kind="lib" path="/libraries/jmock/third-party-libs/hamcrest-library-1.0.jar"/> <classpathentry kind="lib" path="/libraries/jmock/third-party-libs/objenesis-1.0.jar"/> <classpathentry kind="lib" path="/libraries/log4j/log4j.jar" sourcepath="/libraries/log4j/src.zip"/> + <classpathentry kind="lib" path="/libraries/fast-md5/fast-md5.jar" sourcepath="/libraries/fast-md5/src.zip"/> <classpathentry kind="output" path="targets/classes"/> </classpath> diff --git a/bds/source/java/ch/systemsx/cisd/bds/ChecksumWriter.java b/bds/source/java/ch/systemsx/cisd/bds/ChecksumWriter.java new file mode 100644 index 00000000000..8c8441b400a --- /dev/null +++ b/bds/source/java/ch/systemsx/cisd/bds/ChecksumWriter.java @@ -0,0 +1,159 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.bds; + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; +import java.util.Comparator; + +import com.twmacinta.util.MD5; + +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.utilities.CompositeComparator; +import ch.systemsx.cisd.common.utilities.FileComparator; +import ch.systemsx.cisd.common.utilities.FileUtilities; + +/** + * A default <code>IChecksumWriter</code> implementation. + * + * @author Christian Ribeaud + */ +public final class ChecksumWriter implements IChecksumWriter +{ + + private final static Comparator<File> BY_TYPE_AND_NAME = createFileComparator(); + + private static final String LINE_SEPARATOR = "\n"; + + private static final String COLUMN_SEPARATOR = " "; + + private final IChecksum checksumImpl; + + /** We use only one instance of <code>StringBuilder</code> and recycle it. */ + private final StringBuilder builder; + + protected File root; + + public ChecksumWriter() + { + this.checksumImpl = new MD5Checksum(); + builder = new StringBuilder(); + } + + @SuppressWarnings("unchecked") + private final static Comparator<File> createFileComparator() + { + return new CompositeComparator<File>(FileComparator.BY_TYPE, FileComparator.BY_NAME); + } + + private final static void checkFile(final File file) + { + assert file != null && file.exists(); + } + + private final void appendToWriter(final File file, final Writer writer) + { + try + { + writer.append(createChecksumLine(file, checksumImpl.getChecksum(file))); + } catch (IOException ex) + { + throw EnvironmentFailureException.fromTemplate(ex, "An I/O exception has been thrown regarding file '%s'.", + file.getAbsolutePath()); + } + } + + private final void internalWriteChecksum(final File file, final Writer writer) + { + if (file.isFile()) + { + appendToWriter(file, writer); + } else if (file.isDirectory()) + { + final File[] files = FileUtilities.listFiles(file); + Arrays.sort(files, BY_TYPE_AND_NAME); + for (int i = 0; i < files.length; i++) + { + internalWriteChecksum(files[i], writer); + } + } + } + + protected String getColumnSeparator() + { + return COLUMN_SEPARATOR; + } + + protected String getLineSeparator() + { + return LINE_SEPARATOR; + } + + protected String createChecksumLine(final File file, final String checksum) + { + builder.setLength(0); + builder.append(checksum); + builder.append(getColumnSeparator()); + builder.append(FileUtilities.getRelativeFile(root.getAbsolutePath(), file.getAbsolutePath())); + builder.append(getLineSeparator()); + return builder.toString(); + } + + // + // IChecksumHandler + // + + public final void writeChecksum(final File file, final Writer writer) + { + checkFile(file); + root = file; + internalWriteChecksum(file, writer); + } + + // + // Helper classes + // + + /** + * A <code>IChecksum</code> implementation based on <i>MD5</i>. + * + * @author Christian Ribeaud + */ + private final static class MD5Checksum implements IChecksum + { + + // + // IChecksum + // + + public final String getChecksum(final File file) throws EnvironmentFailureException + { + checkFile(file); + try + { + return MD5.asHex(MD5.getHash(file)); + } catch (IOException ex) + { + throw EnvironmentFailureException.fromTemplate(ex, + "An I/O exception has been thrown regarding file '%s'.", file.getAbsolutePath()); + } + } + + } +} diff --git a/bds/source/java/ch/systemsx/cisd/bds/IChecksum.java b/bds/source/java/ch/systemsx/cisd/bds/IChecksum.java new file mode 100644 index 00000000000..5655b5f63bd --- /dev/null +++ b/bds/source/java/ch/systemsx/cisd/bds/IChecksum.java @@ -0,0 +1,37 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.bds; + +import java.io.File; + +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; + +/** + * Implementations know how to compute a <a href="http://en.wikipedia.org/wiki/Checksum">checksum</a> from a file. + * + * @author Christian Ribeaud + */ +public interface IChecksum +{ + + /** + * Returns the checksum for given <var>file</var>. + * + * @param file could not be <code>null</code> and must exist. + */ + public String getChecksum(final File file) throws EnvironmentFailureException; +} diff --git a/bds/source/java/ch/systemsx/cisd/bds/IChecksumWriter.java b/bds/source/java/ch/systemsx/cisd/bds/IChecksumWriter.java new file mode 100644 index 00000000000..0c959386d28 --- /dev/null +++ b/bds/source/java/ch/systemsx/cisd/bds/IChecksumWriter.java @@ -0,0 +1,42 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.bds; + +import java.io.File; +import java.io.Writer; + +/** + * Implementations write computed checksums to some specified {@link Writer}. + * + * @author Christian Ribeaud + */ +public interface IChecksumWriter +{ + + /** + * Appends computed checksum for given <var>file</var> to given <var>writer</var>. + * <p> + * If given <var>file</var> is a directory, then recursively calls this method for each file composing the + * directory. + * </p> + * + * @param file can not be <code>null</code> and must exist. + * @param writer can not be <code>null</code>. + */ + public void writeChecksum(final File file, final Writer writer); + +} \ No newline at end of file diff --git a/bds/sourceTest/java/ch/systemsx/cisd/bds/ChecksumWriterTest.java b/bds/sourceTest/java/ch/systemsx/cisd/bds/ChecksumWriterTest.java new file mode 100644 index 00000000000..86f90ce94d2 --- /dev/null +++ b/bds/sourceTest/java/ch/systemsx/cisd/bds/ChecksumWriterTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.bds; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.fail; + +import java.io.File; +import java.io.StringWriter; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import ch.systemsx.cisd.common.logging.LogInitializer; +import ch.systemsx.cisd.common.utilities.FileUtilities; + +/** + * Test cases for corresponding {@link ChecksumWriter} class. + * + * @author Christian Ribeaud + */ +public class ChecksumWriterTest +{ + private static final File UNIT_TEST_ROOT_DIRECTORY = new File("targets" + File.separator + "unit-test-wd"); + + private static final File WORKING_DIRECTORY = + new File(UNIT_TEST_ROOT_DIRECTORY, ChecksumWriterTest.class.getSimpleName()); + + private IChecksumWriter checksumWriter = new ChecksumWriter(); + + @BeforeMethod + public final void setUp() + { + LogInitializer.init(); + WORKING_DIRECTORY.mkdirs(); + assert WORKING_DIRECTORY.isDirectory(); + WORKING_DIRECTORY.deleteOnExit(); + } + + @Test + public final void testWriteChecksum() + { + FileUtilities.writeToFile(new File(WORKING_DIRECTORY, "file1"), "This is my first file."); + FileUtilities.writeToFile(new File(WORKING_DIRECTORY, "abracadabra"), "This is my second file."); + FileUtilities.writeToFile(new File(WORKING_DIRECTORY, "zoro.txt"), "This is my third file."); + File dir = new File(WORKING_DIRECTORY, "dir"); + dir.mkdir(); + FileUtilities.writeToFile(new File(dir, "file2"), "This is my fourth file."); + FileUtilities.writeToFile(new File(dir, "escargot.png"), "This is my fifth file."); + FileUtilities.writeToFile(new File(dir, "barbapapa"), "This is my sixth file."); + try + { + checksumWriter.writeChecksum(null, null); + fail("Null value not allowed here."); + } catch (AssertionError e) + { + // Nothing to do here + } + final StringWriter writer = new StringWriter(); + try + { + checksumWriter.writeChecksum(new File("choubidou"), writer); + fail("Given file must exist."); + } catch (AssertionError e) + { + // Nothing to do here + } + checksumWriter.writeChecksum(WORKING_DIRECTORY, writer); + assertEquals("31b98e3c4af8d09595dcef2ce8232fbd abracadabra\n" + + "23b447be20a6ddfe875a8b59ceae83ff file1\n" + + "0ba14d9315ba1c59449eb2fe1dbcbb61 zoro.txt\n" + + "23b541115335bbe719c447bb9c942b1a dir/barbapapa\n" + + "b63f5226ef2d2fc6d38342d90869412c dir/escargot.png\n" + + "0f782d8d89620f2a5600eba6565f6bfd dir/file2\n", writer.toString()); + } +} \ No newline at end of file -- GitLab