/* * Copyright 2013 Cloudera Inc. * * 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.kitesdk.morphline.hadoop.rcfile; import java.io.EOFException; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.PositionedReadable; import org.apache.hadoop.fs.Seekable; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.util.Progressable; /** * Very simple Filesystem Implementation which serves an InputStream for a given * path. This is used to serve the underlying input stream from a FileSystem * Interface. Only open() and getFileStatus() is implemented. */ public final class SingleStreamFileSystem extends FileSystem { private final FSDataInputStream inputStream; private final Path path; private final FileStatus fileStatus; public SingleStreamFileSystem(InputStream inputStream, Path path) throws IOException { this.inputStream = new FSDataInputStream(new ForwardOnlySeekable(inputStream)); this.path = path; // Since this is a stream, we dont know the length of the stream. Setting it // to the maximum size this.fileStatus = new FileStatus(Long.MAX_VALUE, false, 0, 0, 0, path); } @Override public URI getUri() { throw new UnsupportedOperationException("not implemented!"); } @Override public FSDataInputStream open(final Path f, final int bufferSize) throws IOException { if (f.equals(path)) { return inputStream; } throw new UnsupportedOperationException("Path " + f.getName() + " is not found"); } @Override public FSDataOutputStream create(final Path f, final FsPermission permission, final boolean overwrite, final int bufferSize, final short replication, final long blockSize, final Progressable progress) throws IOException { throw new UnsupportedOperationException("not implemented!"); } @Override public FSDataOutputStream append(final Path f, final int bufferSize, final Progressable progress) throws IOException { throw new UnsupportedOperationException("not implemented!"); } @Override public boolean rename(final Path src, final Path dst) throws IOException { throw new UnsupportedOperationException("not implemented!"); } @Override public boolean delete(final Path f, final boolean recursive) throws IOException { throw new UnsupportedOperationException("not implemented!"); } @Override public FileStatus[] listStatus(final Path f) throws FileNotFoundException, IOException { throw new UnsupportedOperationException("not implemented!"); } @Override public void setWorkingDirectory(final Path new_dir) { throw new UnsupportedOperationException("not implemented!"); } @Override public Path getWorkingDirectory() { throw new UnsupportedOperationException("not implemented!"); } @Override public boolean mkdirs(final Path f, final FsPermission permission) throws IOException { throw new UnsupportedOperationException("not implemented!"); } @Override public FileStatus getFileStatus(final Path f) throws IOException { if (path.equals(f)) { return fileStatus; } throw new UnsupportedOperationException("Path " + f.getName() + " is not found"); } @Override public boolean delete(Path path) throws IOException { throw new UnsupportedOperationException("not implemented!"); } /////////////////////////////////////////////////////////////////////////////// // Nested classes: /////////////////////////////////////////////////////////////////////////////// /** * Forward-only Seekable InputStream to use for reading SequenceFiles. Will throw an exception if * an attempt is made to seek backwards. */ private static class ForwardOnlySeekable extends InputStream implements Seekable, PositionedReadable { private ForwardOnlySeekableInputStream fosInputStream; public ForwardOnlySeekable(InputStream inputStream) { this.fosInputStream = new ForwardOnlySeekableInputStream(inputStream); } /** * Seek to the given offset from the start of the file. * The next read() will be from that location. Can't * seek past the end of the file. */ public void seek(long pos) throws IOException { fosInputStream.seek(pos); } /** * Return the current offset from the start of the file */ public long getPos() throws IOException { return fosInputStream.tell(); } /** * Seeks a different copy of the data. Returns true if * found a new source, false otherwise. */ public boolean seekToNewSource(long targetPos) throws IOException { throw new UnsupportedOperationException("not implemented!"); } /** * Read upto the specified number of bytes, from a given * position within a file, and return the number of bytes read. This does not * change the current offset of a file, and is thread-safe. */ public int read(long position, byte[] buffer, int offset, int length) throws IOException { throw new UnsupportedOperationException("not implemented!"); } /** * Read the specified number of bytes, from a given * position within a file. This does not * change the current offset of a file, and is thread-safe. */ public void readFully(long position, byte[] buffer, int offset, int length) throws IOException { throw new UnsupportedOperationException("not implemented!"); } /** * Read number of bytes equal to the length of the buffer, from a given * position within a file. This does not * change the current offset of a file, and is thread-safe. */ public void readFully(long position, byte[] buffer) throws IOException { throw new UnsupportedOperationException("not implemented!"); } public int read() throws IOException { byte [] b = new byte[1]; int len = fosInputStream.read(b, 0, 1); int ret = (len == -1)? -1 : b[0] & 0xFF; return ret; } } /////////////////////////////////////////////////////////////////////////////// // Nested classes: /////////////////////////////////////////////////////////////////////////////// /** * A SeekableInput backed by an {@link InputStream} that can only advance * forward, not backwards. */ private static final class ForwardOnlySeekableInputStream { // implements SeekableInput { private final InputStream in; private long pos = 0; public ForwardOnlySeekableInputStream(InputStream in) { this.in = in; } // @Override public long tell() throws IOException { return pos; } // @Override public int read(byte b[], int off, int len) throws IOException { int n = in.read(b, off, len); if (n > 0) { pos += n; } return n; } // @Override public long length() throws IOException { throw new UnsupportedOperationException("Random access is not supported"); } // @Override public void seek(long p) throws IOException { long todo = p - pos; if (todo < 0) { throw new UnsupportedOperationException("Seeking backwards is not supported"); } skip(todo); } private long skip(long len) throws IOException { // borrowed from org.apache.hadoop.io.IOUtils.skipFully() len = Math.max(0, len); long todo = len; while (todo > 0) { long ret = in.skip(todo); if (ret == 0) { // skip may return 0 even if we're not at EOF. Luckily, we can // use the read() method to figure out if we're at the end. int b = in.read(); if (b == -1) { throw new EOFException( "Premature EOF from inputStream after " + "skipping " + (len - todo) + " byte(s)."); } ret = 1; } todo -= ret; pos += ret; } return len; } // @Override public void close() throws IOException { in.close(); } } }