/* * Copyright (c) 2013-2014 the original author or authors * * 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 io.werval.server.netty; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; import java.nio.file.Files; import io.werval.api.exceptions.WervalException; import io.netty.buffer.AbstractByteBuf; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static io.netty.buffer.Unpooled.wrappedBuffer; /** * Read-only ByteBuff wrapping a File. * <p>Used by {@link HttpRequestAggregator} to aggregate requests bodies.</p> * <p>Used by {@link WervalHttpHandler} to parse requests bodies.</p> */ // CHECKSTYLE:OFF public final class FileByteBuff extends AbstractByteBuf { private static final Logger LOG = LoggerFactory.getLogger( FileByteBuff.class ); private static final String NOT_SUPPORTED = "Not supported"; private static final String READ_ONLY = "Read Only."; private final File file; private final long length; private int refCnt = 1; public FileByteBuff( File file ) throws FileNotFoundException { super( Integer.MAX_VALUE ); this.file = file; this.length = file.length(); } @Override public int readableBytes() { if( length < Integer.MIN_VALUE || length > Integer.MAX_VALUE ) { throw new IllegalArgumentException( length + " cannot be cast to int without changing its value." ); } return (int) length; } public InputStream getInputStream() { try { return new FileInputStream( file ); } catch( FileNotFoundException ex ) { throw new WervalException( "File " + file + " backing FileByteBuff disapeared! " + ex.getMessage(), ex ); } } public byte[] readAllBytes() { try { return Files.readAllBytes( file.toPath() ); } catch( IOException ex ) { throw new WervalException( ex.getMessage(), ex ); } } @Override protected byte _getByte( int index ) { try( RandomAccessFile raf = new RandomAccessFile( file, "r" ) ) { raf.seek( index ); return raf.readByte(); } catch( IOException ex ) { throw new WervalException( ex.getMessage(), ex ); } } @Override protected short _getShort( int index ) { try( RandomAccessFile raf = new RandomAccessFile( file, "r" ) ) { raf.seek( index ); return raf.readShort(); } catch( IOException ex ) { throw new WervalException( ex.getMessage(), ex ); } } @Override protected int _getUnsignedMedium( int index ) { throw new UnsupportedOperationException( NOT_SUPPORTED ); } @Override protected int _getInt( int index ) { try( RandomAccessFile raf = new RandomAccessFile( file, "r" ) ) { raf.seek( index ); return raf.readInt(); } catch( IOException ex ) { throw new WervalException( ex.getMessage(), ex ); } } @Override protected long _getLong( int index ) { try( RandomAccessFile raf = new RandomAccessFile( file, "r" ) ) { raf.seek( index ); return raf.readLong(); } catch( IOException ex ) { throw new WervalException( ex.getMessage(), ex ); } } @Override protected void _setByte( int index, int value ) { throw new UnsupportedOperationException( READ_ONLY ); } @Override protected void _setShort( int index, int value ) { throw new UnsupportedOperationException( READ_ONLY ); } @Override protected void _setMedium( int index, int value ) { throw new UnsupportedOperationException( READ_ONLY ); } @Override protected void _setInt( int index, int value ) { throw new UnsupportedOperationException( READ_ONLY ); } @Override protected void _setLong( int index, long value ) { throw new UnsupportedOperationException( READ_ONLY ); } @Override public int capacity() { if( length < Integer.MIN_VALUE || length > Integer.MAX_VALUE ) { throw new IllegalArgumentException( length + " cannot be cast to int without changing its value." ); } return (int) length; } @Override public ByteBuf capacity( int newCapacity ) { throw new UnsupportedOperationException( READ_ONLY ); } @Override public ByteBufAllocator alloc() { return null; } @Override public ByteOrder order() { return ByteOrder.nativeOrder(); } @Override public ByteBuf unwrap() { return null; } @Override public boolean isDirect() { return true; } @Override public ByteBuf getBytes( int index, ByteBuf dst, int dstIndex, int length ) { throw new UnsupportedOperationException( NOT_SUPPORTED ); } @Override public ByteBuf getBytes( int index, byte[] dst, int dstIndex, int length ) { checkIndex( index, length ); checkDstIndex( index, length, dstIndex, dst.length ); try( RandomAccessFile raf = new RandomAccessFile( file, "r" ) ) { raf.seek( index ); raf.read( dst, dstIndex, length ); return this; } catch( IOException ex ) { throw new WervalException( ex.getMessage(), ex ); } } @Override public ByteBuf getBytes( int index, ByteBuffer dst ) { checkIndex( index ); try( RandomAccessFile raf = new RandomAccessFile( file, "r" ) ) { raf.seek( index ); byte[] buffer = new byte[ 8 ]; while( dst.position() < dst.limit() ) { int read = raf.read( buffer ); if( read == -1 ) { break; } dst.put( buffer, 0, read ); } return this; } catch( IOException ex ) { throw new WervalException( ex.getMessage(), ex ); } } @Override public ByteBuf getBytes( int index, OutputStream out, int length ) throws IOException { throw new UnsupportedOperationException( NOT_SUPPORTED ); } @Override public int getBytes( int index, GatheringByteChannel out, int length ) throws IOException { throw new UnsupportedOperationException( NOT_SUPPORTED ); } @Override public ByteBuf setBytes( int index, ByteBuf src, int srcIndex, int length ) { throw new UnsupportedOperationException( READ_ONLY ); } @Override public ByteBuf setBytes( int index, byte[] src, int srcIndex, int length ) { throw new UnsupportedOperationException( READ_ONLY ); } @Override public ByteBuf setBytes( int index, ByteBuffer src ) { throw new UnsupportedOperationException( READ_ONLY ); } @Override public int setBytes( int index, InputStream in, int length ) throws IOException { throw new UnsupportedOperationException( READ_ONLY ); } @Override public int setBytes( int index, ScatteringByteChannel in, int length ) throws IOException { throw new UnsupportedOperationException( READ_ONLY ); } @Override public ByteBuf copy( int index, int length ) { byte[] buf = new byte[ length ]; try( RandomAccessFile raf = new RandomAccessFile( file, "r" ) ) { raf.read( buf, index, length ); return wrappedBuffer( buf ); } catch( IOException ex ) { throw new WervalException( ex.getMessage(), ex ); } } @Override public int nioBufferCount() { return -1; } @Override public ByteBuffer nioBuffer( int index, int length ) { throw new UnsupportedOperationException( NOT_SUPPORTED ); } @Override public ByteBuffer[] nioBuffers( int index, int length ) { throw new UnsupportedOperationException( NOT_SUPPORTED ); } @Override public boolean hasArray() { return false; } @Override public byte[] array() { throw new UnsupportedOperationException( NOT_SUPPORTED ); } @Override public int arrayOffset() { throw new UnsupportedOperationException( NOT_SUPPORTED ); } @Override public boolean hasMemoryAddress() { return false; } @Override public long memoryAddress() { throw new UnsupportedOperationException( NOT_SUPPORTED ); } @Override public ByteBuf retain( int increment ) { refCnt += increment; return this; } @Override public ByteBuf retain() { return retain( 1 ); } @Override public int refCnt() { return refCnt; } @Override public boolean release() { return release( 1 ); } @Override public boolean release( int decrement ) { refCnt -= decrement; if( refCnt <= 0 ) { try { Files.deleteIfExists( file.toPath() ); } catch( IOException ex ) { LOG.warn( "Unable to delete File in FileByteBuff on release: {}", ex.getMessage(), ex ); } return true; } return false; } @Override public ByteBuffer internalNioBuffer( int index, int length ) { throw new UnsupportedOperationException( NOT_SUPPORTED ); } } // CHECKSTYLE:ON