/** * Copyright 2007-2015, Kaazing Corporation. All rights reserved. * * 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 org.kaazing.k3po.driver.internal.netty.bootstrap.file; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelSink; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.UpstreamMessageEvent; import org.kaazing.k3po.driver.internal.netty.bootstrap.channel.AbstractChannel; import org.kaazing.k3po.driver.internal.netty.channel.ChannelAddress; import org.kaazing.k3po.driver.internal.netty.channel.file.FileChannelAddress; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URI; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel.MapMode; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; import static java.nio.channels.FileChannel.MapMode.READ_WRITE; import static org.jboss.netty.channel.Channels.fireChannelOpen; public final class FileChannel extends AbstractChannel<FileChannelConfig> { // Note that read position and write position are independent in the script. We are keeping // two buffers(view on the same MappedByteBuffer) to achieve this and also don't have to // manage positions explicitly private ChannelBuffer readBuffer; private ChannelBuffer writeBuffer; FileChannel(ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink) { super(null, factory, pipeline, sink, new DefaultFileChannelConfig()); fireChannelOpen(this); } @Override protected void setBound() { super.setBound(); } @Override protected void setConnected() { super.setConnected(); } @Override protected boolean setClosed() { // Do we need to call MappedByteBuffer#force() ? // Also unmap the memory mapper buffer without waiting for GC ?? return super.setClosed(); } @Override protected void setLocalAddress(ChannelAddress localAddress) { super.setLocalAddress(localAddress); } @Override public String toString() { ChannelAddress localAddress = this.getLocalAddress(); return localAddress != null ? localAddress.toString() : super.toString(); } void write(ChannelBuffer channelBuffer) { writeBuffer.writeBytes(channelBuffer); } public void setWriteOffset(int offset) { writeBuffer.writerIndex(offset); } public void setReadOffset(int offset) { readBuffer.readerIndex(offset); } public void fireMessageReceived(ChannelHandlerContext ctx) { Channels.fireMessageReceived(ctx, readBuffer, ctx.getChannel().getRemoteAddress()); } void fireMessageReceived(FileChannel fileChannel, ChannelAddress fileAddress) { MessageEvent msg = new UpstreamMessageEvent(fileChannel, fileChannel.readBuffer, fileAddress); fileChannel.getPipeline().sendUpstream(msg); } void mapFile() throws IOException { FileChannelAddress address = (FileChannelAddress) getLocalAddress(); MappedByteBuffer mappedBuffer = mapFile(address.getLocation(), address.mode(), address.size()); readBuffer = ChannelBuffers.wrappedBuffer(mappedBuffer); setReadOffset(0); writeBuffer = ChannelBuffers.wrappedBuffer(mappedBuffer); setWriteOffset(0); } private static MappedByteBuffer mapFile(URI fileAddress, String mode, long size) throws IOException { if (fileAddress.isOpaque()) { // robot scripts specify relative file uri as opaque ! URI currentDir = new File(".").toURI(); fileAddress = currentDir.resolve(fileAddress.getSchemeSpecificPart()); } File location = new File(fileAddress); if (!location.exists()) { if (mode.equals("r")) { String msg = String.format("File = %s doesn't exist, cannot be opened in read only mode", location); throw new IllegalArgumentException(msg); } if (size == 0) { String msg = String.format("File = %s is newly created, need size to be specified", location); throw new IllegalArgumentException(msg); } } MapMode mapMode; switch (mode) { case "r": mapMode = READ_ONLY; break; case "rw": mapMode = READ_WRITE; // parent directory must exist to create file File parentDir = location.getParentFile(); parentDir.mkdirs(); break; default: throw new IllegalArgumentException(String.format("Unknown mode = %s for file = %s", mode, location)); } try (RandomAccessFile file = new RandomAccessFile(location, mode); java.nio.channels.FileChannel channel = file.getChannel()) { if (size == 0) { size = channel.size(); } return channel.map(mapMode, 0, size); } } }