LoggingChannelFactory.java
package emissary.core.channels;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
/**
* This class creates a SeekableByteChannelFactory for logging accesses to every method on the SeekableByteChannel. It
* is intended only for development/debugging purposes.
*/
public final class LoggingChannelFactory {
private LoggingChannelFactory() {}
/**
* Creates a SeekableByteChannelFactory for logging accesses to every method on the SeekableByteChannel.
*
* @param seekableByteChannelFactory that creates the SeekableByteChannel to wrap.
* @param identifier that is added to the log message.
* @param logger to use for logging.
* @param logStackTrace specifies whether a stack trace should be added to the log message.
* @return the logging SeekableByteChannelFactory
*/
public static SeekableByteChannelFactory create(final SeekableByteChannelFactory seekableByteChannelFactory,
final String identifier, final Logger logger, final boolean logStackTrace) {
return new LoggingChannelFactoryImpl(seekableByteChannelFactory, identifier, logger, logStackTrace);
}
/**
* The SeekableByteChannelFactory for creating logging SeekableByteChannels.
*/
private static class LoggingChannelFactoryImpl implements SeekableByteChannelFactory {
/**
* The SeekableByteChannel that is being wrapped.
*/
private final SeekableByteChannelFactory seekableByteChannelFactory;
/**
* An identifier add to the log message.
*/
private final String identifier;
/**
* The logger to be used for logging.
*/
private final Logger logger;
/**
* Determines whether a stack trace should also be logged.
*/
private final boolean logStackTrace;
/**
* A one-up counter for identifying each instance.
*/
private final AtomicLong currentInstance = new AtomicLong(0);
private LoggingChannelFactoryImpl(final SeekableByteChannelFactory seekableByteChannelFactory,
final String identifier, final Logger logger, final boolean logStackTrace) {
Validate.notNull(seekableByteChannelFactory, "Required: seekableByteChannelFactory not null!");
Validate.notNull(identifier, "Required: identifier not null!");
Validate.notNull(logger, "Required: logger not null!");
this.seekableByteChannelFactory = seekableByteChannelFactory;
this.identifier = identifier;
this.logger = logger;
this.logStackTrace = logStackTrace;
}
@Override
public SeekableByteChannel create() {
return new LoggingSeekableByteChannel(seekableByteChannelFactory.create(),
identifier + " : " + currentInstance.getAndIncrement(), logger, logStackTrace);
}
}
/**
* This SeekableByteChannel wraps another SeekableByteChannel and logs all methods calls.
*/
private static class LoggingSeekableByteChannel implements SeekableByteChannel {
/**
* The SeekableByteChannel that is being wrapped.
*/
private final SeekableByteChannel seekableByteChannel;
/**
* The prefix added to the log statement.
*/
private final String prefix;
/**
* The logger to be used for logging.
*/
private final Logger logger;
/**
* Determines whether a stack trace should also be logged.
*/
private final boolean logStackTrace;
/**
* Constructs a SeekableByteChannel that wraps another SeekableByteChannel and logs all methods calls.
*
* @param seekableByteChannel to be wrapped.
* @param prefix added to the log statement.
* @param logger to be used for logging.
* @param logStackTrace determines whether a stack trace should also be logged.
*/
private LoggingSeekableByteChannel(final SeekableByteChannel seekableByteChannel, final String prefix,
final Logger logger, final boolean logStackTrace) {
this.seekableByteChannel = seekableByteChannel;
this.prefix = prefix;
this.logger = logger;
this.logStackTrace = logStackTrace;
log(logger, logStackTrace, "{} : created : lST={}", prefix, logStackTrace);
}
@Override
public boolean isOpen() {
final boolean open = seekableByteChannel.isOpen();
log(logger, logStackTrace, "{} : isOpen : o={}", prefix, open);
return open;
}
@Override
public void close() throws IOException {
log(logger, logStackTrace, "{} : close", prefix);
seekableByteChannel.close();
}
@Override
public int read(final ByteBuffer byteBuffer) throws IOException {
final int bbP = byteBuffer.position();
final int bbC = byteBuffer.capacity();
final long sbcP = seekableByteChannel.position();
final int bytesRead = seekableByteChannel.read(byteBuffer);
log(logger, logStackTrace, "{} : read : bbP={} bbC={} sbcP={} : r={}", prefix, bbP, bbC, sbcP, bytesRead);
return bytesRead;
}
@Override
public int write(final ByteBuffer byteBuffer) throws IOException {
final int bbP = byteBuffer.position();
final int bbC = byteBuffer.capacity();
final long sbcP = seekableByteChannel.position();
final int bytesWritten = seekableByteChannel.write(byteBuffer);
log(logger, logStackTrace, "{} : write : bbP={} bbC={} sbcP={} : bW={}", prefix, bbP, bbC, sbcP,
bytesWritten);
return bytesWritten;
}
@Override
public long position() throws IOException {
final long position = seekableByteChannel.position();
log(logger, logStackTrace, "{} : position : p={}", prefix, position);
return position;
}
@Override
public SeekableByteChannel position(final long newPosition) throws IOException {
log(logger, logStackTrace, "{} : position : nP={}", prefix, newPosition);
return seekableByteChannel.position(newPosition);
}
@Override
public long size() throws IOException {
final long size = seekableByteChannel.size();
log(logger, logStackTrace, "{} : size : s={}", prefix, size);
return size;
}
@Override
public SeekableByteChannel truncate(final long size) throws IOException {
log(logger, logStackTrace, "{} : truncate : s={}", prefix, size);
return seekableByteChannel.truncate(size);
}
private static void log(final Logger logger, final boolean logStackTrace, final String format,
final Object... arguments) {
if (logStackTrace) {
final Object[] newArguments = Arrays.copyOf(arguments, arguments.length + 1);
// "Throwable" is used since this class should only be used during development.
newArguments[newArguments.length - 1] = new Throwable("DEBUG");
logger.info(format, newArguments);
} else {
logger.info(format, arguments);
}
}
}
}