org.jmxtrans.agent.util.io.IoUtils Maven / Gradle / Ivy
/*
* 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