/*
* Copyright 2014 OCTO Technology
*
* 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 com.octo.reactive.audit;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
@SuppressWarnings({"FinalClass", "UtilityClass"})
public final class FileTools
{
public static final int NO_ERROR = 0;
public static final int NET_ERROR = -1;
public static final int FILE_ERROR = 1;
static final Field fieldOutFilterOutputStream;
static final Field fieldBoutObjectOutputStream;
static final Field fieldOutObjectOutputStream;
static
{
try
{
fieldOutFilterOutputStream = FilterOutputStream.class.getDeclaredField("out");
fieldOutFilterOutputStream.setAccessible(true);
fieldBoutObjectOutputStream = ObjectOutputStream.class.getDeclaredField("bout");
fieldBoutObjectOutputStream.setAccessible(true);
fieldOutObjectOutputStream = fieldBoutObjectOutputStream.getType().getDeclaredField("out");
fieldOutObjectOutputStream.setAccessible(true);
}
catch (NoSuchFieldException e)
{
throw new Error(e);
}
}
static final FilenameDumpClosure FileTools_filenameDump = new FilenameDumpClosure()
{
@Override
public void dump(StringBuilder buf, Class cl, String filename)
{
filenameDump(buf, cl, filename);
}
};
static final FilenameDumpClosure FileTools_chainFilenameDump = new FilenameDumpClosure()
{
@Override
public void dump(StringBuilder buf, Class cl, String filename)
{
chainFilenameDump(buf, cl, filename);
}
};
private static final Field fieldInFilterInputStream;
private static final Field fieldBinObjectInputStream;
private static final Field fieldInObjectInputStream;
private static final Field fieldPeekObjectInputStream;
private static final Field fieldLockReader;
private static final Field fieldInFilterReader;
private static final Field fieldInBufferedReader;
private static final Field fieldInFileDescriptor;
static
{
try
{
fieldInFileDescriptor = FileDescriptor.class.getDeclaredField("fd");
fieldInFileDescriptor.setAccessible(true);
fieldLockReader = Reader.class.getDeclaredField("lock");
fieldLockReader.setAccessible(true);
fieldInFilterReader = FilterReader.class.getDeclaredField("in");
fieldInFilterReader.setAccessible(true);
fieldInBufferedReader = BufferedReader.class.getDeclaredField("in");
fieldInBufferedReader.setAccessible(true);
}
catch (NoSuchFieldException e)
{
throw new Error(e);
}
}
private static final Field fieldLockWriter;
private static final Field fieldOutFilterWriter;
private static final Field fieldOutBufferedWriter;
private static final Field fieldOutPrintWriter;
static
{
try
{
fieldLockWriter = Writer.class.getDeclaredField("lock");
fieldLockWriter.setAccessible(true);
fieldOutFilterWriter = FilterWriter.class.getDeclaredField("out");
fieldOutFilterWriter.setAccessible(true);
fieldOutBufferedWriter = BufferedWriter.class.getDeclaredField("out");
fieldOutBufferedWriter.setAccessible(true);
fieldOutPrintWriter = PrintWriter.class.getDeclaredField("out");
fieldOutPrintWriter.setAccessible(true);
}
catch (NoSuchFieldException e)
{
throw new Error(e);
}
}
private static Field fieldPathOutputStream = null;
private static Field fieldPathInputStream = null;
static
{
try
{
fieldInFilterInputStream = FilterInputStream.class.getDeclaredField("in");
fieldInFilterInputStream.setAccessible(true);
fieldBinObjectInputStream = ObjectInputStream.class.getDeclaredField("bin");
fieldBinObjectInputStream.setAccessible(true);
fieldInObjectInputStream = fieldBinObjectInputStream.getType().getDeclaredField("in");
fieldInObjectInputStream.setAccessible(true);
fieldPeekObjectInputStream = fieldInObjectInputStream.getType().getDeclaredField("in");
fieldPeekObjectInputStream.setAccessible(true);
try
{
fieldPathOutputStream = FileOutputStream.class.getDeclaredField("path");
fieldPathOutputStream.setAccessible(true);
}
catch (Exception e)
{
// Ignore
}
try
{
fieldPathInputStream = FileInputStream.class.getDeclaredField("path");
fieldPathInputStream.setAccessible(true);
}
catch (Exception e)
{
// Ignore
}
}
catch (NoSuchFieldException e)
{
throw new Error(e);
}
}
private FileTools()
{
}
static public int isLastInputStreamWithLatency(InputStream in)
{
if (in == null)
return NO_ERROR;
while (in instanceof FilterInputStream
|| in instanceof ObjectInputStream)
{
try
{
if (in instanceof FilterInputStream)
{
FilterInputStream filter = (FilterInputStream) in;
in = (InputStream) fieldInFilterInputStream.get(filter);
}
else
{
ObjectInputStream objIn = (ObjectInputStream) in;
// Ok for Java8
in = (InputStream) fieldPeekObjectInputStream.get(
fieldInObjectInputStream.get(
fieldBinObjectInputStream.get(objIn)
)
);
}
}
catch (IllegalAccessException e)
{
throw new Error(e);
}
}
if (in != null)
{
if (in.getClass().getName().equals("java.net.SocketInputStream")) return NET_ERROR;
if (in instanceof FileInputStream) return FILE_ERROR;
if (in.getClass().getName().startsWith("sun.net.www.")) return NET_ERROR;
}
return NO_ERROR;
}
public static int isLastReaderWithLatency(Reader reader)
{
try
{
while (reader instanceof FilterReader
|| reader instanceof BufferedReader)
{
if (reader instanceof FilterReader)
{
reader = (Reader) fieldInFilterReader.get(reader);
}
else
{
reader = (Reader) fieldInBufferedReader.get(reader);
}
}
if (reader instanceof InputStreamReader)
{
InputStream in = (InputStream) fieldLockReader.get(reader);
return isLastInputStreamWithLatency(in);
}
else
return NO_ERROR;
}
catch (IllegalAccessException e)
{
throw new Error(e);
}
}
public static int isLastOutputStreamWithLatency(OutputStream out)
{
while (out instanceof FilterOutputStream
|| out instanceof ObjectOutputStream)
{
try
{
if (out instanceof FilterOutputStream)
{
FilterOutputStream filter = (FilterOutputStream) out;
out = (OutputStream) fieldOutFilterOutputStream.get(filter);
}
else
{
ObjectOutputStream objIn = (ObjectOutputStream) out;
// Ok for Java8
out = (OutputStream) fieldOutObjectOutputStream.get(
fieldBoutObjectOutputStream.get(objIn)
);
}
}
catch (IllegalAccessException e)
{
throw new Error(e);
}
}
if (out.getClass().getName().equals("java.net.SocketOutputStream")) return NET_ERROR;
if (out instanceof FileOutputStream)
{
try
{
int i = (Integer) fieldInFileDescriptor.get(((FileOutputStream) out).getFD());
if ((i == 1) || (i == 2))
{
return NO_ERROR; // Console
}
}
catch (Exception e)
{
// Ignore
}
return FILE_ERROR;
}
if (out.getClass().getName().startsWith("sun.net.www.")) return NET_ERROR;
return NO_ERROR;
}
@SuppressWarnings("ChainOfInstanceofChecks")
public static int isLastOutputStreamFromWriterWithLatency(Writer writer)
{
try
{
while (writer instanceof FilterWriter
|| writer instanceof BufferedWriter
|| writer instanceof PrintWriter)
{
if (writer instanceof FilterWriter)
{
writer = (Writer) fieldOutFilterWriter.get(writer);
}
else if (writer instanceof PrintWriter)
{
writer = (Writer) fieldOutPrintWriter.get(writer);
}
else
{
writer = (Writer) fieldOutBufferedWriter.get(writer);
}
}
if (writer instanceof OutputStreamWriter)
{
OutputStream out = (OutputStream) fieldLockWriter.get(writer);
return isLastOutputStreamWithLatency(out);
}
else
{
return NO_ERROR;
}
}
catch (IllegalAccessException e)
{
throw new Error(e);
}
}
public static CharSequence printFilename(OutputStream out)
{
// Java8 return dumpChain(out, new StringBuilder(), FileTools::filenameDump);
return dumpChain(out, new StringBuilder(), FileTools_filenameDump);
}
public static CharSequence printFilename(Writer writer)
{
// Java8 return dumpChain(writer, FileTools::filenameDump);
return dumpChain(writer, FileTools_filenameDump);
}
static public CharSequence printFilename(InputStream in)
{
// Java8 return dumpChain(in, new StringBuilder(), FileTools::filenameDump);
return dumpChain(in, new StringBuilder(), FileTools_filenameDump);
}
public static CharSequence printFilename(Reader reader)
{
// Java8 return dumpChain(reader, FileTools::filenameDump);
return dumpChain(reader, FileTools_filenameDump);
}
static public CharSequence dumpFilename(InputStream in)
{
// Java8 return dumpChain(in, new StringBuilder(), FileTools::chainFilenameDump);
return dumpChain(in, new StringBuilder(), FileTools_chainFilenameDump);
}
public static CharSequence dumpChain(OutputStream out)
{
// Java8 return dumpChain(out, new StringBuilder(), FileTools::chainFilenameDump);
return dumpChain(out, new StringBuilder(), FileTools_chainFilenameDump);
}
public static CharSequence dumpChain(Writer writer)
{
// Java8 return dumpChain(writer, FileTools::chainFilenameDump);
return dumpChain(writer, FileTools_chainFilenameDump);
}
static public CharSequence dumpChain(InputStream in)
{
// Java8 return dumpChain(in, new StringBuilder(), FileTools::chainFilenameDump);
return dumpChain(in, new StringBuilder(), FileTools_chainFilenameDump);
}
public static CharSequence dumpChain(Reader reader)
{
// Java8 return dumpChain(reader, FileTools::chainFilenameDump);
return dumpChain(reader, FileTools_chainFilenameDump);
}
private static CharSequence dumpChain(OutputStream out, StringBuilder buf, FilenameDumpClosure dump)
{
if (out == null) return buf;
dump.dump(buf, out.getClass(), null);
while (out instanceof FilterOutputStream
|| out instanceof ObjectOutputStream)
{
try
{
if (out instanceof FilterOutputStream)
{
FilterOutputStream filter = (FilterOutputStream) out;
out = (OutputStream) fieldOutFilterOutputStream.get(filter);
}
else
{
ObjectOutputStream objIn = (ObjectOutputStream) out;
// Ok for Java8
out = (OutputStream) fieldOutObjectOutputStream.get(
fieldBoutObjectOutputStream.get(objIn)
);
}
dump.dump(buf, out.getClass(), null);
}
catch (IllegalAccessException e)
{
throw new Error(e);
}
}
if (out instanceof FileOutputStream)
{
String path;
if (fieldPathOutputStream != null)
{
try
{
path = (String) fieldPathOutputStream.get(out);
dump.dump(buf, null, path);
}
catch (IllegalAccessException e)
{
// Ignore
}
}
}
else
{
buf.append(" -> ").append(out.getClass().getSimpleName());
}
return buf;
}
@SuppressWarnings("ChainOfInstanceofChecks")
public static CharSequence dumpChain(Writer writer, FilenameDumpClosure dump)
{
StringBuilder buf = new StringBuilder();
if (writer == null) return buf;
try
{
dump.dump(buf, writer.getClass(), null);
while (writer instanceof FilterWriter
|| writer instanceof BufferedWriter
|| writer instanceof PrintWriter)
{
if (writer instanceof FilterWriter)
{
writer = (Writer) fieldOutFilterWriter.get(writer);
}
else if (writer instanceof PrintWriter)
{
writer = (Writer) fieldOutPrintWriter.get(writer);
}
else
{
writer = (Writer) fieldOutBufferedWriter.get(writer);
}
dump.dump(buf, writer.getClass(), null);
}
if (writer instanceof OutputStreamWriter)
{
OutputStream out = (OutputStream) fieldLockWriter.get(writer);
dump.dump(buf, Void.class, null);
return dumpChain(out, buf, dump);
}
else
{
//if (out!=null) ConfigReactiveAudit.config.debug("Without delegate to output stream");
return buf;
}
}
catch (IllegalAccessException e)
{
throw new Error(e);
}
}
static private CharSequence dumpChain(InputStream in,
StringBuilder buf,
FilenameDumpClosure dump)
{
if (in == null) return buf;
dump.dump(buf, in.getClass(), null);
while (in instanceof FilterInputStream
|| in instanceof ObjectInputStream)
{
try
{
if (in instanceof FilterInputStream)
{
FilterInputStream filter = (FilterInputStream) in;
in = (InputStream) fieldInFilterInputStream.get(filter);
}
else
{
ObjectInputStream objIn = (ObjectInputStream) in;
// Ok for Java8
in = (InputStream) fieldPeekObjectInputStream.get(
fieldInObjectInputStream.get(
fieldBinObjectInputStream.get(objIn)
)
);
}
dump.dump(buf, in.getClass(), null);
}
catch (IllegalAccessException e)
{
throw new Error(e);
}
}
if (in instanceof FileInputStream)
{
String path;
if (fieldPathInputStream != null)
{
try
{
path = (String) fieldPathInputStream.get(in);
chainFilenameDump(buf, null, path);
}
catch (IllegalAccessException e)
{
// Ignore
}
}
}
else
{
dump.dump(buf, in.getClass(), null);
}
return buf;
}
private static CharSequence dumpChain(Reader reader, FilenameDumpClosure dump)
{
StringBuilder buf = new StringBuilder();
if (reader == null) return buf;
dump.dump(buf, reader.getClass(), null);
try
{
while (reader instanceof FilterReader
|| reader instanceof BufferedReader)
{
if (reader instanceof FilterReader)
{
reader = (Reader) fieldInFilterReader.get(reader);
}
else
{
reader = (Reader) fieldInBufferedReader.get(reader);
}
dump.dump(buf, reader.getClass(), null);
}
if (reader instanceof InputStreamReader)
{
InputStream in = (InputStream) fieldLockReader.get(reader);
dump.dump(buf, Void.class, null);
return dumpChain(in, buf, dump);
}
return buf;
}
catch (IllegalAccessException e)
{
throw new Error(e);
}
}
static private void chainFilenameDump(StringBuilder buf, Class cl, String filename)
{
if (cl != null)
{
buf.append(" -> ");
if (cl != Void.class) buf.append(cl.getClass().getSimpleName());
}
if (filename != null)
{
buf.append(" -> ").append(filename);
}
}
static private void filenameDump(StringBuilder buf, Class cl, String filename)
{
if (filename != null)
{
buf.append("with ").append(filename);
}
}
static String homeFile(String filename)
{
if (filename.startsWith("file:"))
{
try
{
filename = new File(new URL(filename).toURI()).getPath();
}
catch (Exception e)
{
// Ignore
}
}
String home = System.getProperty("user.home");
String homeFilename;
String homeRef = "~";
if (File.separator.equals("\\")) // Windows
homeRef = "%HOME%";
if (filename.startsWith(home))
{
homeFilename = filename.substring(home.length());
if (homeFilename.startsWith(File.separator))
homeFilename = homeRef + homeFilename;
else
homeFilename = homeRef + File.separator + homeFilename;
}
else
homeFilename = filename;
return homeFilename;
}
interface FilenameDumpClosure
{
void dump(StringBuilder buf, Class cl, String filename);
}
}