package rocks.inspectit.shared.cs.storage.nio.write; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.file.Path; import java.util.List; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import rocks.inspectit.shared.all.spring.logger.Log; import rocks.inspectit.shared.all.storage.nio.stream.ExtendedByteBufferOutputStream; import rocks.inspectit.shared.cs.storage.nio.AbstractChannelManager; import rocks.inspectit.shared.cs.storage.nio.CustomAsyncChannel; import rocks.inspectit.shared.cs.storage.nio.WriteReadAttachment; import rocks.inspectit.shared.cs.storage.nio.WriteReadCompletionRunnable; /** * Channel manager for writing the data. * * @author Ivan Senic * */ @Component public class WritingChannelManager extends AbstractChannelManager { /** * The log of this class. */ @Log Logger log; /** * Max opened channels. */ @Value(value = "${storage.maxWriteChannelsOpened}") private int maxOpenedChannels = 128; /** * Writes the content of the {@link ByteBuffer} to the channel that has the supplied path. * Channel will be open if necessary. * * @param byteBuffer * {@link ByteBuffer} that holds the data to be written. Note that the caller of this * method is responsible for maintaining the buffer's position and limit. * @param channelPath * Path to the channel's file. * @param completionRunnable * Runnable that will be executed after the complete content of the buffer has been * written. If not needed null can be passed. * @return Position where the data will be written in the channel. * @throws IOException * Delegates the {@link IOException} from I/O operations. */ public long write(ByteBuffer byteBuffer, Path channelPath, WriteReadCompletionRunnable completionRunnable) throws IOException { CustomAsyncChannel channel = super.getChannel(channelPath); long writingSize = byteBuffer.limit() - byteBuffer.position(); long writingPosition = channel.reserveWritingPosition(writingSize); WriteReadAttachment attachment = new WriteReadAttachment(); attachment.setByteBuffer(byteBuffer); attachment.setSize(writingSize); attachment.setPosition(writingPosition); completionRunnable.setAttemptedWriteReadSize(writingSize); completionRunnable.setAttemptedWriteReadPosition(writingPosition); attachment.setCompletionRunnableFuture(completionRunnable.new RunnableFuture()); attachment.setFileChannel(channel.getFileChannel()); boolean wrote = false; while (!wrote) { wrote = channel.write(byteBuffer, writingPosition, attachment, new WritingCompletionHandler()); if (!wrote) { if (log.isDebugEnabled()) { log.info("Failed to submit writing IO task, channel is closed. Trying to reopen the channel.."); } this.openAsyncChannel(channel); } } return writingPosition; } /** * Writes the content in the {@link ExtendedByteBufferOutputStream} to the given channel path. * This write will actually be a series of asynchronous writes, each for a single buffer * provided by the {@link ExtendedByteBufferOutputStream}. * * @param extendedByteBufferOutputStream * @param channelPath * Path to the channel's file. * @param completionRunnable * Runnable that will be executed after the complete content of each buffer. Note * that the same completion runnable can be executed more than one time. * @param extendedByteBufferOutputStream * the stream to write to. * @return Position where the data will be written in the channel. * @throws IOException * Delegates the {@link IOException} from I/O operations. */ public long write(ExtendedByteBufferOutputStream extendedByteBufferOutputStream, Path channelPath, WriteReadCompletionRunnable completionRunnable) throws IOException { CustomAsyncChannel channel = super.getChannel(channelPath); List<ByteBuffer> byteBuffers = extendedByteBufferOutputStream.getAllByteBuffers(); long totalWritingSize = extendedByteBufferOutputStream.getTotalWriteSize(); long writingPosition = channel.reserveWritingPosition(totalWritingSize); completionRunnable.setAttemptedWriteReadSize(totalWritingSize); completionRunnable.setAttemptedWriteReadPosition(writingPosition); long returnWritingPosition = writingPosition; for (ByteBuffer byteBuffer : byteBuffers) { long writingSize = byteBuffer.limit() - byteBuffer.position(); WriteReadAttachment attachment = new WriteReadAttachment(); attachment.setByteBuffer(byteBuffer); attachment.setSize(writingSize); attachment.setPosition(writingPosition); attachment.setCompletionRunnableFuture(completionRunnable.new RunnableFuture()); attachment.setFileChannel(channel.getFileChannel()); boolean wrote = false; while (!wrote) { wrote = channel.write(byteBuffer, writingPosition, attachment, new WritingCompletionHandler()); if (!wrote) { if (log.isDebugEnabled()) { log.info("Failed to submit writing IO task, channel is closed. Trying to reopen the channel.."); } this.openAsyncChannel(channel); } } writingPosition += writingSize; } return returnWritingPosition; } /** * {@inheritDoc} */ @Override protected int getMaxOpenedChannels() { return maxOpenedChannels; } }