com.ibm.stocator.fs.swift.SwiftInputStream.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.stocator.fs.swift.SwiftInputStream.java

Source

/**
 * (C) Copyright IBM Corp. 2015, 2016
 * <p/>
 * 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
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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 com.ibm.stocator.fs.swift;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import org.javaswift.joss.headers.object.range.AbstractRange;
import org.javaswift.joss.instructions.DownloadInstructions;
import org.javaswift.joss.model.StoredObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.fs.FSInputStream;
import org.apache.hadoop.fs.Path;

public class SwiftInputStream extends FSInputStream {

    private static final Logger LOG = LoggerFactory.getLogger(SwiftInputStream.class);

    /*
     * File nativeStore instance
     */
    protected final SwiftAPIClient nativeStore;

    /*
     * Data input stream
     */
    private InputStream httpStream;

    /*
     * Current position
     */
    private long pos = 0;

    /*
     * Reference to the stored object
     */
    protected StoredObject storedObject;

    /**
     * Move to a new position within the file relative to where the pointer is now.
     * Always call from a synchronized clause
     *
     * @param offset offset
     */
    private synchronized void incPos(long offset) {
        pos += offset;
    }

    public SwiftInputStream(SwiftAPIClient storeNative) {
        nativeStore = storeNative;
    }

    public SwiftInputStream(SwiftAPIClient storeNative, String hostName, Path path) throws IOException {
        this(storeNative);
        LOG.debug("init: {}", path.toString());
        String objectName = path.toString().substring(hostName.length());
        storedObject = nativeStore.getAccount().getContainer(nativeStore.getDataRoot()).getObject(objectName);
        if (!storedObject.exists()) {
            throw new FileNotFoundException(objectName + " is not exists");
        }
    }

    @Override
    public synchronized int read() throws IOException {
        if (httpStream == null) {
            // not sure we need it. need to re-check.
            seek(0);
        }
        int result = -1;
        result = httpStream.read();
        if (result != -1) {
            incPos(1);
        }
        return result;
    }

    @Override
    public synchronized int read(byte[] b, int off, int len) throws IOException {
        LOG.trace("Reading portion of http stream for: {}. Offset: {} Len: {}", storedObject.getName(), off, len);
        if (httpStream == null) {
            // not sure we need it. need to re-check.
            seek(0);
        }
        int result = -1;
        result = httpStream.read(b, off, len);
        if (result != -1) {
            incPos(result);
        }
        return result;
    }

    @Override
    public synchronized void close() throws IOException {
        LOG.trace("Closing http stream: {}", storedObject.getName());
        try {
            if (httpStream != null) {
                httpStream.close();
            }
        } finally {
            httpStream = null;
        }
    }

    @Override
    public synchronized void seek(long targetPos) throws IOException {
        seekPart1(targetPos);
        DownloadInstructions instructions = seekPart2(targetPos);
        seekPart3(targetPos, instructions);
    }

    protected void seekPart1(long targetPos) throws IOException {
        LOG.debug("seek method to: {}, for {}", targetPos, storedObject.getName());
        if (targetPos < 0) {
            throw new IOException("Negative Seek offset not supported");
        }

        if (httpStream != null) {
            long offset = targetPos - pos;
            if (offset == 0) {
                LOG.trace(
                        "seek called on same position as the previous one. New HTTP Stream is not " + "required.");
                return;
            }
            long blockSize = nativeStore.getBlockSize();
            if (offset < 0) {
                LOG.trace("seek position is outside the current stream; offset: {}. New HTTP Stream is "
                        + "required.", offset);
            } else if ((offset < blockSize)) {
                //if the seek is in  range of that requested, scan forwards
                //instead of closing and re-opening a new HTTP connection
                LOG.trace("seek is within current stream; offset: {} blockSize: {}.", offset, blockSize);
                int result = -1;
                long byteRead;
                for (byteRead = 0; byteRead < offset; byteRead++) {
                    result = httpStream.read();
                    if (result < 0) {
                        break;
                    }
                }
                incPos(byteRead);
                if (targetPos == pos) {
                    LOG.trace("seek reached targetPos: {}. New HTTP Stream is not required.", targetPos);
                    return;
                }
                LOG.trace("seek failed to reach targetPos: {}. New HTTP Stream is required.", targetPos);
            }
            httpStream.close();
        }
        LOG.trace("seek method is opening a new HTTP Stream to: {}, for {}", targetPos, storedObject.getName());
    }

    protected DownloadInstructions seekPart2(long targetPos) throws IOException {
        DownloadInstructions instructions = new DownloadInstructions();
        AbstractRange range = new AbstractRange(targetPos, targetPos + nativeStore.getBlockSize()) {

            @Override
            public long getTo(int arg0) {
                return offset;
            }

            @Override
            public long getFrom(int arg0) {
                return length;
            }
        };
        instructions.setRange(range);
        return instructions;
    }

    protected void seekPart3(long targetPos, DownloadInstructions instructions) throws IOException {
        httpStream = storedObject.downloadObjectAsInputStream(instructions);
        LOG.debug("Seek completed. Got HTTP Stream for: {}", storedObject.getName());
        pos = targetPos;
    }

    @Override
    public synchronized long getPos() throws IOException {
        return pos;
    }

    /**
     * multiple data sources not yet supported
     *
     * @param targetPos new target position
     * @return true if new data source set successfull
     * @throws IOException
     */
    @Override
    public boolean seekToNewSource(long targetPos) throws IOException {
        return false;
    }
}