package net.fusejna; import java.io.File; import java.nio.ByteBuffer; import net.fusejna.StructFlock.FlockWrapper; import net.fusejna.StructFuseFileInfo.FileInfoWrapper; import net.fusejna.StructStat.StatWrapper; import net.fusejna.StructStatvfs.StatvfsWrapper; import net.fusejna.StructTimeBuffer.TimeBufferWrapper; import net.fusejna.types.TypeMode.ModeWrapper; import org.apache.log4j.Level; import org.apache.log4j.LogSF; import org.apache.log4j.Logger; final class LoggedFuseFilesystem extends FuseFilesystem { private static interface LoggedMethod<T> { public T invoke(); } private static interface LoggedVoidMethod { public void invoke(); } private static final String methodSuccess = "Method succeeded."; private static final String methodFailure = "Exception thrown: "; private static final String methodResult = " Result: "; private final String className; private final Logger actualLogger; private final FuseFilesystem filesystem; LoggedFuseFilesystem(final FuseFilesystem filesystem, final Logger logger) { this.filesystem = filesystem; actualLogger = logger; className = filesystem.getClass().getName(); } @Override public int access(final String path, final int access) { return log("access", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.access(path, access); } }, path, access); } @Override public void afterUnmount(final File mountPoint) { log("afterUnmount", new LoggedVoidMethod() { @Override public void invoke() { filesystem.afterUnmount(mountPoint); } }, mountPoint.toString()); } @Override public void beforeMount(final File mountPoint) { log("beforeMount", new LoggedVoidMethod() { @Override public void invoke() { filesystem.beforeMount(mountPoint); } }, mountPoint.toString()); } @Override public void beforeUnmount(final File mountPoint) { log("beforeUnmount", new LoggedVoidMethod() { @Override public void invoke() { filesystem.beforeUnmount(mountPoint); } }, mountPoint.toString()); } @Override public int bmap(final String path, final FileInfoWrapper info) { return log("bmap", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.bmap(path, info); } }, path, info); } @Override public int chmod(final String path, final ModeWrapper mode) { return log("chmod", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { filesystem.chmod(path, mode); return 0; } }, path, mode); } @Override public int chown(final String path, final long uid, final long gid) { return log("chown", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.chown(path, uid, gid); } }, path, uid, gid); } @Override public int create(final String path, final ModeWrapper mode, final FileInfoWrapper info) { return log("create", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.create(path, mode, info); } }, path, mode, info); } @Override public void destroy() { log("destroy", new LoggedVoidMethod() { @Override public void invoke() { filesystem.destroy(); } }); } @Override public int fgetattr(final String path, final StatWrapper stat, final FileInfoWrapper info) { return log("fgetattr", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.fgetattr(path, stat, info); } }, path, stat); } @Override public int flush(final String path, final FileInfoWrapper info) { return log("flush", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.flush(path, info); } }, path, info); } @Override public int fsync(final String path, final int datasync, final FileInfoWrapper info) { return log("flush", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.fsync(path, datasync, info); } }, path, info); } @Override public int fsyncdir(final String path, final int datasync, final FileInfoWrapper info) { return log("fsyncdir", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.fsyncdir(path, datasync, info); } }, path, info); } @Override public int ftruncate(final String path, final long offset, final FileInfoWrapper info) { return log("getattr", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.ftruncate(path, offset, info); } }, path, offset, info); } @Override public int getattr(final String path, final StatWrapper stat) { return log("getattr", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.getattr(path, stat); } }, path, stat); } @Override protected String getName() { return log("getName", (String) null, new LoggedMethod<String>() { @Override public String invoke() { return filesystem.getName(); } }); } @Override protected String[] getOptions() { return log("getOptions", (String[]) null, new LoggedMethod<String[]>() { @Override public String[] invoke() { return filesystem.getOptions(); } }); } @Override public int getxattr(final String path, final String xattr, final ByteBuffer buf, final long size, final long position) { return log("getxattr", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.getxattr(path, xattr, buf, size, position); } }, path, xattr, buf, size, position); } @Override public void init() { log("init", new LoggedVoidMethod() { @Override public void invoke() { filesystem.init(); } }); } @Override public int link(final String path, final String target) { return log("link", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.link(path, target); } }, path, target); } @Override public int listxattr(final String path, final XattrListFiller filler) { return log("listxattr", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.listxattr(path, filler); } }, path, filler); } @Override public int lock(final String path, final FileInfoWrapper info, final FlockCommand command, final FlockWrapper flock) { return log("lock", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.lock(path, info, command, flock); } }, path, info, command, flock); } private void log(final String methodName, final LoggedVoidMethod method) { log(methodName, method, null, (Object[]) null); } private void log(final String methodName, final LoggedVoidMethod method, final String path, final Object... args) { try { LogSF.entering(actualLogger, className, methodName, args); method.invoke(); LogSF.log(actualLogger, Level.INFO, "{}.{}: {}", className, methodName, (path == null ? "" : "[" + path + "] ") + methodSuccess); LogSF.exiting(actualLogger, className, methodName); } catch (final Throwable e) { logException(e, methodName, null, args); } } private <T> T log(final String methodName, final T defaultValue, final LoggedMethod<T> method) { return log(methodName, defaultValue, method, null, (Object[]) null); } private <T> T log(final String methodName, final T defaultValue, final LoggedMethod<T> method, final String path, final Object... args) { try { LogSF.entering(actualLogger, className, methodName, args); final T result = method.invoke(); LogSF.log(actualLogger, Level.INFO, "{}.{}: {}", className, methodName, (path == null ? "" : "[" + path + "] ") + methodSuccess + methodResult + result); LogSF.exiting(actualLogger, className, methodName); return result; } catch (final Throwable e) { return logException(e, methodName, defaultValue, args); } } private <T> T logException(final Throwable e, final String methodName, final T defaultValue, final Object... args) { final StackTraceElement[] stack = e.getStackTrace(); final StringBuilder builder = new StringBuilder(); for (final StackTraceElement element : stack) { builder.append("\n" + element); } LogSF.log(actualLogger, Level.ERROR, "{}.{}: {}", className, methodName, methodFailure + e + builder.toString()); return defaultValue; } @Override public int mkdir(final String path, final ModeWrapper mode) { return log("mkdir", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.mkdir(path, mode); } }, path, mode); } @Override public int mknod(final String path, final ModeWrapper mode, final long dev) { return log("mknod", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.mknod(path, mode, dev); } }, path, mode, dev); } @Override public int open(final String path, final FileInfoWrapper info) { return log("open", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.open(path, info); } }, path, info); } @Override public int opendir(final String path, final FileInfoWrapper info) { return log("opendir", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.opendir(path, info); } }, path, info); } @Override public int read(final String path, final ByteBuffer buffer, final long size, final long offset, final FileInfoWrapper info) { return log("read", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.read(path, buffer, size, offset, info); } }, path, buffer, size, offset, info); } @Override public int readdir(final String path, final DirectoryFiller filler) { return log("readdir", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.readdir(path, filler); } }, path, filler); } @Override public int readlink(final String path, final ByteBuffer buffer, final long size) { return log("readlink", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.readlink(path, buffer, size); } }, path, buffer, size); } @Override public int release(final String path, final FileInfoWrapper info) { return log("release", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.release(path, info); } }, path, info); } @Override public int releasedir(final String path, final FileInfoWrapper info) { return log("releasedir", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.releasedir(path, info); } }, path, info); } @Override public int removexattr(final String path, final String xattr) { return log("remtoexattr", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.removexattr(path, xattr); } }, path, xattr); } @Override public int rename(final String path, final String newName) { return log("rename", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.rename(path, newName); } }); } @Override public int rmdir(final String path) { return log("rmdir", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.rmdir(path); } }, path); } @Override void setFinalMountPoint(final File mountPoint) { super.setFinalMountPoint(mountPoint); filesystem.setFinalMountPoint(mountPoint); } @Override public int setxattr(final String path, final ByteBuffer buf, final long size, final int flags, final long position) { return log("setxattr", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.setxattr(path, buf, size, flags, position); } }, path, buf, size, flags, position); } @Override public int statfs(final String path, final StatvfsWrapper wrapper) { return log("statfs", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.statfs(path, wrapper); } }, path, wrapper); } @Override public int symlink(final String path, final String target) { return log("symlink", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.symlink(path, target); } }, path, target); } @Override public int truncate(final String path, final long offset) { return log("truncate", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.truncate(path, offset); } }, path, offset); } @Override public int unlink(final String path) { return log("unlink", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.unlink(path); } }, path); } @Override public int utimens(final String path, final TimeBufferWrapper wrapper) { return log("utimens", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.utimens(path, wrapper); } }, path, wrapper); } @Override public int write(final String path, final ByteBuffer buf, final long bufSize, final long writeOffset, final FileInfoWrapper wrapper) { return log("write", 0, new LoggedMethod<Integer>() { @Override public Integer invoke() { return filesystem.write(path, buf, bufSize, writeOffset, wrapper); } }, path, buf, bufSize, writeOffset, wrapper); } }