All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jmxtrans.agent.util.io.IoUtils Maven / Gradle / Ivy

There is a newer version: 1.2.11
Show newest version
/*
 * Copyright (c) 2010-2016 the original author or authors
 *
 * 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 org.jmxtrans.agent.util.io;

import org.jmxtrans.agent.util.logging.Logger;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.util.logging.Level;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 * @author Cyrille Le Clerc
 */
public class IoUtils {
    protected final static Logger logger = Logger.getLogger(IoUtils.class.getName());

    private IoUtils(){}

    /**
     * @param filePath can be prefixed by "{@code http://}", "{@code https://}", "{@code file://}". If given value is not prefixed, "{@code file://}" is assumed.
     * @return @return A long value representing the time the file was
     * last modified, measured in milliseconds since the epoch
     * (00:00:00 GMT, January 1, 1970), or {@code 0L} if the
     * file does not exist, if an I/O error occurs or if the given filePath is {@code null}
     * @throws IoRuntimeException
     */
    public static long getFileLastModificationDate(@Nullable String filePath) throws IoRuntimeException {
        if (filePath == null)
            return 0;

        if (filePath.toLowerCase().startsWith("classpath:")) {
            String classpathResourcePath = filePath.substring("classpath:".length());
            URL configurationFileUrl = Thread.currentThread().getContextClassLoader().getResource(classpathResourcePath);
            if (configurationFileUrl == null) {
                if (logger.isLoggable(Level.FINER))
                    logger.fine("File '" + filePath + "' not found in classpath");
                return 0L;
            } else {
                File configurationFile;
                try {
                    configurationFile = new File(configurationFileUrl.toURI());
                } catch (URISyntaxException e) {
                    throw new IoRuntimeException("Exception parsing '" + filePath + "'", e);
                }
                if (!configurationFile.exists())
                    throw new IllegalStateException("File path=" + filePath + ", url=" + configurationFileUrl + " not found");

                if (logger.isLoggable(Level.FINER))
                    logger.fine("Classpath file '" + filePath + "' last modified at " + new Timestamp(configurationFile.lastModified()));
                return configurationFile.lastModified();
            }
        } else if (
                filePath.toLowerCase().startsWith("http://") ||
                        filePath.toLowerCase().startsWith("https://")
                ) {
            if (logger.isLoggable(Level.FINER))
                logger.fine("Http files not supported: '" + filePath + "' is seen as never modified");
            return 0L;
        } else if (filePath.toLowerCase().startsWith("file://")){
            File file;
            try {
                file = new File(new URI(filePath));
            } catch (URISyntaxException|RuntimeException e) {
                throw new IoRuntimeException("Exception parsing '" + filePath + "'", e);
            }

            if (file.exists()) {
                if (logger.isLoggable(Level.FINER))
                    logger.fine("File '" + filePath + "' last modified at " + new Timestamp(file.lastModified()));
                return file.lastModified();
            } else {
                if (logger.isLoggable(Level.FINER))
                    logger.fine("File '" + filePath + "' not found ");
                return 0L;
            }
        } else {
            File file;
            try {
                file = new File(filePath);
            } catch (RuntimeException e) {
                throw new IoRuntimeException("Exception parsing '" + filePath + "'", e);
            }

            if (file.exists()) {
                if (logger.isLoggable(Level.FINER))
                    logger.fine("File '" + filePath + "' last modified at " + new Timestamp(file.lastModified()));
                return file.lastModified();
            } else {
                if (logger.isLoggable(Level.FINER))
                    logger.fine("File '" + filePath + "' not found ");
                return 0L;
            }
        }
    }

    @Nonnull
    public static Document getFileAsDocument(@Nonnull Resource resource) throws IoRuntimeException {
        if (resource == null)
            throw new IoRuntimeException(new NullPointerException("resource cannot be null"));

        DocumentBuilder dBuilder;
        try {
            dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

            try {
                File configurationFile = resource.getFile();
                return dBuilder.parse(configurationFile);
            } catch(IoRuntimeException e) {
                try (InputStream in = resource.getInputStream()) {
                	return dBuilder.parse(in);
                }
            }
        } catch (ParserConfigurationException | SAXException e) {
            throw new IoRuntimeException(e);
        } catch (IOException e) {
            throw IoRuntimeException.propagate(e);
        }
    }

    /**
     * Simple implementation without chunking if the source file is big.
     *
     * @param source
     * @param destination
     * @throws java.io.IOException
     */
    private static void doCopySmallFile(File source, File destination) throws IOException {
        if (destination.exists() && destination.isDirectory()) {
            throw new IOException("Can not copy file, destination is a directory: " + destination.getAbsolutePath());
        }

        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel input = null;
        FileChannel output = null;
        try {
            fis = new FileInputStream(source);
            fos = new FileOutputStream(destination, false);
            input = fis.getChannel();
            output = fos.getChannel();
            output.transferFrom(input, 0, input.size());
        } finally {
            closeQuietly(output);
            closeQuietly(input);
            closeQuietly(fis);
            closeQuietly(fos);
        }
        if (destination.length() != source.length()) {
            throw new IOException("Failed to copy content from '" +
                    source + "' (" + source.length() + "bytes) to '" + destination + "' (" + destination.length() + ")");
        }

    }

    public static boolean isFileUrl(URL url){
        if (url.getProtocol().equals("file")) {
            return true;
        } else {
            return false;
        }

    }
    public static void closeQuietly (URLConnection cnn) {
        if (cnn == null) {
            return;
        } else if (cnn instanceof HttpURLConnection) {
            ((HttpURLConnection) cnn).disconnect();
        } else {
            // do nothing
        }
    }
    public static void closeQuietly(Closeable closeable) {
        if (closeable == null)
            return;
        try {
            closeable.close();
        } catch (Exception e) {
            // ignore silently
        }
    }

    public static void closeQuietly(Writer writer) {
        if (writer == null)
            return;
        try {
            writer.close();
        } catch (Exception e) {
            // ignore silently
        }
    }

    /**
     * Needed for old JVMs where {@link java.io.InputStream} does not implement {@link java.io.Closeable}.
     */
    public static void closeQuietly(InputStream inputStream) {
        if (inputStream == null)
            return;
        try {
            inputStream.close();
        } catch (Exception e) {
            // ignore silently
        }
    }

    public static void replaceFile(File source, File destination) throws IOException {
        boolean destinationExists;
        if (destination.exists()) {
            boolean deleted = destination.delete();
            if (deleted) {
                destinationExists = false;
            } else {
                destinationExists = true;
            }
        } else {
            destinationExists = false;
        }
        if (destinationExists) {
            doCopySmallFile(source, destination);
        } else {
            boolean renamed = source.renameTo(destination);
            if (!renamed) {
                doCopySmallFile(source, destination);
            }
        }
    }

    /**
     * Simple implementation without chunking if the source file is big.
     *
     * @param source
     * @param destination
     * @throws java.io.IOException
     */
    private static void doCopySmallFile(File source, File destination, boolean append) throws IOException {
        if (destination.exists() && destination.isDirectory()) {
            throw new IOException("Can not copy file, destination is a directory: " + destination.getAbsolutePath());
        } else if (!destination.exists()) {
            boolean renamed = source.renameTo(destination);
            if (renamed) return;
        }

        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel input = null;
        FileChannel output = null;
        long initialSize = destination.length();
        try {
            fos = new FileOutputStream(destination, append);
            if (append) {
                fos.write(("\n").getBytes(StandardCharsets.UTF_8));
            }
            fos.write(Files.readAllBytes(Paths.get(source.getAbsolutePath())));
        } finally {
            closeQuietly(output);
            closeQuietly(input);
            closeQuietly(fis);
            closeQuietly(fos);
        }
        if (!append && destination.length() != source.length()) {
            throw new IOException("Failed to copy content from '" +
                    source + "' (" + source.length() + "bytes) to '" + destination + "' (" + destination.length() + "). isAppend? " + append );
        }
        else if (append && destination.length() <= initialSize ) {
            throw new IOException("Failed to append content from '" +
                    source + "' (" + source.length() + "bytes) to '" + destination + "' (" + destination.length() + "). isAppend? " + append );
        }

    }

    public static void appendToFile(File source, File destination, long maxFileSize, int maxBackupIndex) throws IOException {
        boolean destinationExists = validateDestinationFile(source, destination, maxFileSize, maxBackupIndex);
        if (destinationExists) {
            doCopySmallFile(source, destination, true);
        } else {
            boolean renamed = source.renameTo(destination);
            if (!renamed) {
                doCopySmallFile(source, destination, false);
            }
        }
    }

    public static boolean validateDestinationFile(File source, File destination, long maxFileSize, int maxBackupIndex) throws IOException {
        if (!destination.exists() || destination.isDirectory()) return false;
        long totalLengthAfterAppending = destination.length() + source.length();
        if (totalLengthAfterAppending > maxFileSize) {
            rollFiles(destination, maxBackupIndex);
            return false; // File no longer exists because it was move to filename.1
        }

        return true;
    }

    public static void rollFiles(File destination, int maxBackupIndex) throws IOException {

        // if maxBackup index == 10 then we will have file
        // outputFile, outpuFile.1 outputFile.2 ... outputFile.10
        // we only care if 9 and lower exists to move them up a number
        for (int i = maxBackupIndex - 1; i >= 0; i--) {
            String path = destination.getAbsolutePath();
            path=(i==0)?path:path + "." + i;
            File f = new File(path);
            if (!f.exists()) continue;

            File fNext = new File(destination + "." + (i + 1));
            doCopySmallFile(f, fNext, false);
        }

        boolean deleted = destination.delete();
        if (!deleted) {
            logger.warning("Failure to delete file " + destination);
        }
    }

    public static void copy(InputStream in, OutputStream out) throws IOException{
        byte[] buffer = new byte[1024];
        int length;
        while ((length = in.read(buffer)) >= 0) {
            out.write(buffer, 0,length);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy