Java tutorial
/* Copyright 2009-2015 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * The MOEA Framework is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package iDynoOptimizer.MOEAFramework26.src.org.moeaframework.util.io; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.Reader; import java.io.Writer; import java.security.DigestInputStream; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.MessageFormat; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.FrameworkException; import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.Settings; import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.util.CommandLineUtility; /** * Detects corrupted files using the MD5 message digest in a format compatible * with the Unix command line utility {@code md5sum}. * * <ol> * <li>Strict mode - All files must be validated, otherwise exceptions are * thrown * <li>Safe mode - Files with associated digest files are validated, but only * warnings are printed if no digest file exists * </ol> */ public class FileProtection extends CommandLineUtility { /** * The property value for strict mode. */ public static final String STRICT_MODE = "STRICT"; /** * The property value for safe mode. */ public static final String SAFE_MODE = "SAFE"; /** * Private constructor to prevent instantiation. */ private FileProtection() { super(); } /** * Creates and returns a new instance of {@link MessageDigest}. * * @return a new instance of {@code MessageDigest} * @throws FrameworkException if the message digest could not be created */ private static MessageDigest createMessageDigest() { try { return MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new FrameworkException(e); } } /** * Returns the digest file for the specified file. * * @param file the file to be validated * @return the digest file for the specified file */ public static File getDigestFile(File file) { return new File(file.getParentFile(), MessageFormat.format(Settings.getFileProtectionFormat(), file.getName())); } /** * Saves the message digest file in a format compatible with {@code md5sum}. * * @param file the file to be validated * @param digest the message digest * @throws IOException if an I/O error occurred */ private static void saveDigest(File file, byte[] digest) throws IOException { PrintStream ps = null; try { ps = new PrintStream(getDigestFile(file)); ps.print(Hex.encodeHex(digest)); ps.print(" "); ps.print(file.getPath()); } finally { if (ps != null) { ps.close(); } } } /** * Returns the digest value stored in the message digest file. * * @param file the file to be validated * @return the message digest * @throws IOException if an I/O error occurred */ private static byte[] loadDigest(File file) throws IOException { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(getDigestFile(file))); String line = reader.readLine(); if (line == null) { throw new ValidationException(file, "invalid digest file"); } int split = line.indexOf(' '); String digestHex = line.substring(0, split); String fileName = line.substring(split + 2); if (!file.getPath().equals(fileName)) { throw new ValidationException(file, "invalid digest file"); } return Hex.decodeHex(digestHex.toCharArray()); } catch (Exception e) { throw new ValidationException(file, "invalid digest file"); } finally { if (reader != null) { reader.close(); } } } /** * Computes and returns the message digest for the specified file. * * @param file the file to be validated * @return the message digest for the specified file * @throws IOException if an I/O error occurred */ private static byte[] computeDigest(File file) throws IOException { InputStream is = null; try { is = new FileInputStream(file); return DigestUtils.md5(is); } finally { if (is != null) { is.close(); } } } /** * Returns a {@link Reader} wrapping the result from calling * {@code openInputStream} with the specified file. * * @param file the file to be opened * @return a {@code Reader} wrapping the result from calling * {@code openInputStream} with the specified file * @throws FileNotFoundException if the specified file does not exist */ public static Reader openReader(final File file) throws FileNotFoundException { return new InputStreamReader(openInputStream(file)); } /** * Returns an {@link InputStream} for reading from the specified file and * performs validation on the file when the {@code close} method is invoked * on the stream. * * @param file the file to the opened and validated * @return an {@code InputStream} for reading from the specified file * @throws FileNotFoundException if the specified file does not exist */ public static InputStream openInputStream(final File file) throws FileNotFoundException { return new DigestInputStream(new FileInputStream(file), createMessageDigest()) { @Override public void close() throws IOException { //finish reading the file contents byte[] buffer = new byte[Settings.BUFFER_SIZE]; while (read(buffer) != -1) { //reading the data calculates the checksum, nothing else to //do here } super.close(); //check digest validate(file, getMessageDigest().digest()); } }; } /** * Returns a {@link Writer} wrapping the result from calling * {@code openOutputStream} with the specified file. * * @param file the file to be opened * @return a {@code Writer} wrapping the result from calling * {@code openOutputStream} with the specified file * @throws FileNotFoundException if the specified file does not exist */ public static Writer openWriter(final File file) throws FileNotFoundException { return new OutputStreamWriter(openOutputStream(file)); } /** * Validates the file. * * @param file the file being validated * @param actual the actual message digest of the file * @throws IOException if an I/O error occurred */ private static void validate(File file, byte[] actual) throws IOException { String mode = Settings.getFileProtectionMode(); File digestFile = getDigestFile(file); if (digestFile.exists()) { byte[] expected = loadDigest(file); if (!MessageDigest.isEqual(actual, expected)) { throw new ValidationException(file, "digest does not match"); } } else { if (mode.equalsIgnoreCase(STRICT_MODE)) { throw new ValidationException(file, "no digest file"); } else { System.err.println("no digest file exists to validate " + file); } } } /** * Validates the file. * * @param file the file to be validated * @throws IOException if an I/O error occurred */ public static void validate(File file) throws IOException { validate(file, computeDigest(file)); } /** * Returns an {@link OutputStream} for writing to the specified file and * saves a digest file for validating its contents when the {@code close} * method is invoked. * * @param file the file to be opened * @return an {@code OutputStream} for writing to the specified file * @throws FileNotFoundException if the specified file does not exist */ public static OutputStream openOutputStream(final File file) throws FileNotFoundException { return new DigestOutputStream(new FileOutputStream(file), createMessageDigest()) { @Override public void close() throws IOException { super.close(); saveDigest(file, getMessageDigest().digest()); } }; } @SuppressWarnings("static-access") @Override public Options getOptions() { Options options = super.getOptions(); options.addOption( OptionBuilder.withLongOpt("check").withDescription("Validates the listed files").create('c')); return options; } @Override public void run(CommandLine commandLine) throws Exception { if (commandLine.hasOption("check")) { int validCount = 0; int invalidCount = 0; String mode = Settings.getFileProtectionMode(); for (String filename : commandLine.getArgs()) { File file = new File(filename); System.out.print(file); System.out.print(": "); if (getDigestFile(file).exists()) { byte[] actual = computeDigest(file); byte[] expected = loadDigest(file); boolean valid = MessageDigest.isEqual(actual, expected); if (valid) { validCount++; System.out.println("OK"); } else { invalidCount++; System.out.println("FAILED"); } } else { if (mode.equalsIgnoreCase(STRICT_MODE)) { invalidCount++; System.out.println("FAILED"); } else { validCount++; System.out.println("OK (NO DIGEST FILE)"); } } } if (invalidCount > 0) { System.err.print("WARNING: "); System.err.print(invalidCount); System.err.print(" of "); System.err.print(validCount + invalidCount); System.err.println(" computed checksums did NOT match"); } } else { for (String filename : commandLine.getArgs()) { File file = new File(filename); byte[] digest = computeDigest(file); saveDigest(file, digest); } } } /** * Starts the command line utility for validating files using message * digests. * * @param args the command line arguments * @throws Exception if an error occurred */ public static void main(String[] args) throws Exception { new FileProtection().start(args); } }