Package tachyon.client

Source Code of tachyon.client.TachyonFile

package tachyon.client;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.io.Closer;

import tachyon.Constants;
import tachyon.TachyonURI;
import tachyon.UnderFileSystem;
import tachyon.conf.UserConf;
import tachyon.thrift.ClientBlockInfo;
import tachyon.thrift.ClientFileInfo;
import tachyon.thrift.NetAddress;
import tachyon.util.CommonUtils;
import tachyon.util.NetworkUtils;
import tachyon.worker.nio.DataServerMessage;

/**
* Tachyon File.
*/
public class TachyonFile implements Comparable<TachyonFile> {
  private static final Logger LOG = LoggerFactory.getLogger(Constants.LOGGER_TYPE);

  final TachyonFS mTachyonFS;
  final int mFileId;
  private final UserConf mUserConf = UserConf.get();

  private Object mUFSConf = null;

  /**
   * A Tachyon File handler, based file id
   *
   * @param tfs the Tachyon file system client handler
   * @param fid the file id
   */
  TachyonFile(TachyonFS tfs, int fid) {
    mTachyonFS = tfs;
    mFileId = fid;
  }

  @Override
  public int compareTo(TachyonFile o) {
    if (mFileId == o.mFileId) {
      return 0;
    }
    return mFileId < o.mFileId ? -1 : 1;
  }

  @Override
  public boolean equals(Object obj) {
    if ((obj != null) && (obj instanceof TachyonFile)) {
      return compareTo((TachyonFile) obj) == 0;
    }
    return false;
  }

  /**
   * Return the block id of a block in the file, specified by blockIndex
   *
   * @param blockIndex the index of the block in this file
   * @return the block id
   * @throws IOException
   */
  public long getBlockId(int blockIndex) throws IOException {
    return mTachyonFS.getBlockId(mFileId, blockIndex);
  }

  /**
   * Get the block id by the file id and offset. it will check whether the file and the block exist.
   *
   * @param offset The offset of the file.
   * @return the block id if exists
   * @throws IOException
   */
  long getBlockIdBasedOnOffset(long offset) throws IOException {
    int index = (int) (offset / mTachyonFS.getFileStatus(mFileId, true).getBlockSizeByte());

    return mTachyonFS.getBlockId(mFileId, index);
  }

  /**
   * Return the block's size of this file
   *
   * @return the block's size in bytes
   * @throws IOException
   */
  public long getBlockSizeByte() throws IOException {
    return mTachyonFS.getFileStatus(mFileId, true).getBlockSizeByte();
  }

  /**
   * Get a ClientBlockInfo by the file id and block index
   *
   * @param blockIndex The index of the block in the file.
   * @return the ClientBlockInfo of the specified block
   * @throws IOException
   */
  public synchronized ClientBlockInfo getClientBlockInfo(int blockIndex) throws IOException {
    return mTachyonFS.getClientBlockInfo(getBlockId(blockIndex));
  }

  /**
   * Return the creation time of this file
   *
   * @return the creation time, in milliseconds
   * @throws IOException
   */
  public long getCreationTimeMs() throws IOException {
    return mTachyonFS.getFileStatus(mFileId, true).getCreationTimeMs();
  }

  public int getDiskReplication() {
    // TODO Implement it.
    return 3;
  }

  /**
   * Return the InStream of this file, use the specified read type. If it has no block, return an
   * EmptyBlockInStream. Else if it has only one block ,return a BlockInStream of the block. Else,
   * return a FileInStream.
   *
   * @param readType the InStream's read type
   * @return the InStream
   * @throws IOException
   */
  public InStream getInStream(ReadType readType) throws IOException {
    if (readType == null) {
      throw new IOException("ReadType can not be null.");
    }

    if (!isComplete()) {
      throw new IOException("The file " + this + " is not complete.");
    }

    List<Long> blocks = mTachyonFS.getFileStatus(mFileId, false).getBlockIds();

    if (blocks.size() == 0) {
      return new EmptyBlockInStream(this, readType);
    } else if (blocks.size() == 1) {
      return BlockInStream.get(this, readType, 0, mUFSConf);
    }

    return new FileInStream(this, readType, mUFSConf);
  }

  /**
   * Returns the local filename for the block if that file exists on the local file system. This is
   * an alpha power-api feature for applications that want short-circuit-read files directly. There
   * is no guarantee that the file still exists after this call returns, as Tachyon may evict blocks
   * from memory at any time.
   *
   * @param blockIndex The index of the block in the file.
   * @return filename on local file system or null if file not present on local file system.
   * @throws IOException
   */
  public String getLocalFilename(int blockIndex) throws IOException {
    ClientBlockInfo blockInfo = getClientBlockInfo(blockIndex);

    String rootFolder = mTachyonFS.getLocalDataFolder();
    if (rootFolder != null) {
      String localFileName = CommonUtils.concat(rootFolder, blockInfo.getBlockId());
      File file = new File(localFileName);
      if (file.exists()) {
        return localFileName;
      }
    }
    return null;
  }

  /**
   * Return the net address of all the location hosts
   *
   * @return the list of those net address, in String
   * @throws IOException
   */
  public List<String> getLocationHosts() throws IOException {
    List<NetAddress> locations = getClientBlockInfo(0).getLocations();
    List<String> ret = null;
    if (locations != null) {
      ret = new ArrayList<String>(locations.size());
      for (int k = 0; k < locations.size(); k ++) {
        ret.add(locations.get(k).mHost);
      }
    }

    return ret;
  }

  /**
   * Return the number of blocks the file has.
   *
   * @return the number of blocks
   * @throws IOException
   */
  public int getNumberOfBlocks() throws IOException {
    return mTachyonFS.getFileStatus(mFileId, false).getBlockIds().size();
  }

  /**
   * Return the OutStream of this file, use the specified write type. Always return a FileOutStream.
   *
   * @param writeType the OutStream's write type
   * @return the OutStream
   * @throws IOException
   */
  public OutStream getOutStream(WriteType writeType) throws IOException {
    if (isComplete()) {
      throw new IOException("Overriding after completion not supported.");
    }

    if (writeType == null) {
      throw new IOException("WriteType can not be null.");
    }

    return new FileOutStream(this, writeType, mUFSConf);
  }

  /**
   * Return the path of this file in the Tachyon file system
   *
   * @return the path
   * @throws IOException
   */
  public String getPath() throws IOException {
    return mTachyonFS.getFileStatus(mFileId, false).getPath();
  }

  /**
   * To get the configuration object for UnderFileSystem.
   *
   * @return configuration object used for concrete ufs instance
   */
  public Object getUFSConf() {
    return mUFSConf;
  }

  /**
   * Return the under filesystem path in the under file system of this file
   *
   * @return the under filesystem path
   * @throws IOException
   */
  String getUfsPath() throws IOException {
    ClientFileInfo info = mTachyonFS.getFileStatus(mFileId, true);

    if (!info.getUfsPath().isEmpty()) {
      return info.getUfsPath();
    }

    return mTachyonFS.getFileStatus(mFileId, false).getUfsPath();
  }

  @Override
  public int hashCode() {
    return mFileId;
  }

  /**
   * Return whether this file is complete or not
   *
   * @return true if this file is complete, false otherwise
   * @throws IOException
   */
  public boolean isComplete() throws IOException {
    ClientFileInfo info = mTachyonFS.getFileStatus(mFileId, true);

    return info.isComplete || mTachyonFS.getFileStatus(mFileId, false).isComplete;
  }

  /**
   * @return true if this is a directory, false otherwise
   * @throws IOException
   */
  public boolean isDirectory() throws IOException {
    return mTachyonFS.getFileStatus(mFileId, true).isFolder;
  }

  /**
   * @return true if this is a file, false otherwise
   * @throws IOException
   */
  public boolean isFile() throws IOException {
    return !isDirectory();
  }

  /**
   * Return whether the file is in memory or not. Note that a file may be partly in memory. This
   * value is true only if the file is fully in memory.
   *
   * @return true if the file is fully in memory, false otherwise
   * @throws IOException
   */
  public boolean isInMemory() throws IOException {
    return mTachyonFS.getFileStatus(mFileId, false).getInMemoryPercentage() == 100;
  }

  /**
   * @return the file size in bytes
   * @throws IOException
   */
  public long length() throws IOException {
    return mTachyonFS.getFileStatus(mFileId, false).getLength();
  }

  /**
   * @return true if this file is pinned, false otherwise
   * @throws IOException
   */
  public boolean needPin() throws IOException {
    return mTachyonFS.getFileStatus(mFileId, false).isPinned;
  }

  /**
   * Advanced API.
   *
   * Return a TachyonByteBuffer of the block specified by the blockIndex
   *
   * @param blockIndex The block index of the current file to read.
   * @return TachyonByteBuffer containing the block.
   * @throws IOException
   */
  public TachyonByteBuffer readByteBuffer(int blockIndex) throws IOException {
    if (!isComplete()) {
      return null;
    }

    TachyonByteBuffer ret = readLocalByteBuffer(blockIndex);
    if (ret == null) {
      // TODO Make it local cache if the OpType is try cache.
      ret = readRemoteByteBuffer(getClientBlockInfo(blockIndex));
    }

    return ret;
  }

  /**
   * Get the the whole block.
   *
   * @param blockIndex The block index of the current file to read.
   * @return TachyonByteBuffer containing the block.
   * @throws IOException
   */
  TachyonByteBuffer readLocalByteBuffer(int blockIndex) throws IOException {
    return readLocalByteBuffer(blockIndex, 0, -1);
  }

  /**
   * Read local block return a TachyonByteBuffer
   *
   * @param blockIndex The id of the block.
   * @param offset The start position to read.
   * @param len The length to read. -1 represents read the whole block.
   * @return <code>TachyonByteBuffer</code> containing the block.
   * @throws IOException
   */
  private TachyonByteBuffer readLocalByteBuffer(int blockIndex, long offset, long len)
      throws IOException {
    if (offset < 0) {
      throw new IOException("Offset can not be negative: " + offset);
    }
    if (len < 0 && len != -1) {
      throw new IOException("Length can not be negative except -1: " + len);
    }

    ClientBlockInfo info = getClientBlockInfo(blockIndex);
    long blockId = info.blockId;

    int blockLockId = mTachyonFS.getBlockLockId();
    if (!mTachyonFS.lockBlock(blockId, blockLockId)) {
      return null;
    }
    String localFileName = getLocalFilename(blockIndex);
    Closer closer = Closer.create();
    if (localFileName != null) {
      try {
        RandomAccessFile localFile = closer.register(new RandomAccessFile(localFileName, "r"));

        long fileLength = localFile.length();
        String error = null;
        if (offset > fileLength) {
          error = String.format("Offset(%d) is larger than file length(%d)", offset, fileLength);
        }
        if (error == null && len != -1 && offset + len > fileLength) {
          error =
              String.format("Offset(%d) plus length(%d) is larger than file length(%d)", offset,
                  len, fileLength);
        }
        if (error != null) {
          throw new IOException(error);
        }

        if (len == -1) {
          len = fileLength - offset;
        }

        FileChannel localFileChannel = closer.register(localFile.getChannel());
        final ByteBuffer buf = localFileChannel.map(FileChannel.MapMode.READ_ONLY, offset, len);
        mTachyonFS.accessLocalBlock(blockId);
        return new TachyonByteBuffer(mTachyonFS, buf, blockId, blockLockId);
      } catch (FileNotFoundException e) {
        LOG.info(localFileName + " is not on local disk.");
      } catch (IOException e) {
        LOG.warn("Failed to read local file " + localFileName + " because:", e);
      } finally {
        closer.close();
      }
    }

    mTachyonFS.unlockBlock(blockId, blockLockId);
    return null;
  }

  /**
   * Get the the whole block from remote workers.
   *
   * @param blockInfo The blockInfo of the block to read.
   * @return TachyonByteBuffer containing the block.
   */
  TachyonByteBuffer readRemoteByteBuffer(ClientBlockInfo blockInfo) {
    ByteBuffer buf = null;

    LOG.info("Try to find and read from remote workers.");
    try {
      List<NetAddress> blockLocations = blockInfo.getLocations();
      LOG.info("readByteBufferFromRemote() " + blockLocations);

      for (int k = 0; k < blockLocations.size(); k ++) {
        String host = blockLocations.get(k).mHost;
        int port = blockLocations.get(k).mSecondaryPort;

        // The data is not in remote machine's memory if port == -1.
        if (port == -1) {
          continue;
        }
        final String hostname = NetworkUtils.getLocalHostName();
        final String hostaddress = NetworkUtils.getLocalIpAddress();
        if (host.equals(hostname) || host.equals(hostaddress)) {
          String localFileName =
              CommonUtils.concat(mTachyonFS.getLocalDataFolder(), blockInfo.blockId);
          LOG.warn("Reading remotely even though request is local; file is " + localFileName);
        }
        LOG.info(host + ":" + port + " current host is " + hostname + " " + hostaddress);

        try {
          buf = retrieveRemoteByteBuffer(new InetSocketAddress(host, port), blockInfo.blockId);
          if (buf != null) {
            break;
          }
        } catch (IOException e) {
          LOG.error("Fail to retrieve byte buffer for block " + blockInfo.blockId + " from remote "
              + host + ":" + port, e);
          buf = null;
        }
      }
    } catch (IOException e) {
      LOG.error("Failed to get read data from remote ", e);
    }

    return buf == null ? null : new TachyonByteBuffer(mTachyonFS, buf, blockInfo.blockId, -1);
  }

  // TODO remove this method. do streaming cache. This is not a right API.
  public boolean recache() throws IOException {
    int numberOfBlocks = getNumberOfBlocks();
    if (numberOfBlocks == 0) {
      return true;
    }

    boolean succeed = true;
    for (int k = 0; k < numberOfBlocks; k ++) {
      succeed &= recache(k);
    }

    return succeed;
  }

  /**
   * Re-cache the block into memory
   *
   * @param blockIndex The block index of the current file.
   * @return true if succeed, false otherwise
   * @throws IOException
   */
  boolean recache(int blockIndex) throws IOException {
    boolean succeed = true;
    String path = getUfsPath();
    UnderFileSystem underFsClient = UnderFileSystem.get(path);

    try {
      InputStream inputStream = underFsClient.open(path);

      long length = getBlockSizeByte();
      long offset = blockIndex * length;
      inputStream.skip(offset);

      byte[] buffer = new byte[mUserConf.FILE_BUFFER_BYTES * 4];

      BlockOutStream bos = new BlockOutStream(this, WriteType.TRY_CACHE, blockIndex);
      try {
        int limit;
        while (length > 0 && ((limit = inputStream.read(buffer)) >= 0)) {
          if (limit != 0) {
            try {
              if (length >= limit) {
                bos.write(buffer, 0, limit);
                length -= limit;
              } else {
                bos.write(buffer, 0, (int) length);
                length = 0;
              }
            } catch (IOException e) {
              LOG.warn(e.getMessage(), e);
              succeed = false;
              break;
            }
          }
        }
      } finally {
        if (succeed) {
          bos.close();
        } else {
          bos.cancel();
        }
      }
    } catch (IOException e) {
      LOG.warn(e.getMessage(), e);
      return false;
    }

    return succeed;
  }

  /**
   * Rename this file
   *
   * @param path the new name
   * @return true if succeed, false otherwise
   * @throws IOException
   */
  public boolean rename(TachyonURI path) throws IOException {
    return mTachyonFS.rename(mFileId, path);
  }

  private ByteBuffer retrieveRemoteByteBuffer(InetSocketAddress address, long blockId)
      throws IOException {
    SocketChannel socketChannel = SocketChannel.open();
    try {
      socketChannel.connect(address);

      LOG.info("Connected to remote machine " + address + " sent");
      DataServerMessage sendMsg = DataServerMessage.createBlockRequestMessage(blockId);
      while (!sendMsg.finishSending()) {
        sendMsg.send(socketChannel);
      }

      LOG.info("Data " + blockId + " to remote machine " + address + " sent");

      DataServerMessage recvMsg = DataServerMessage.createBlockResponseMessage(false, blockId);
      while (!recvMsg.isMessageReady()) {
        int numRead = recvMsg.recv(socketChannel);
        if (numRead == -1) {
          break;
        }
      }
      LOG.info("Data " + blockId + " from remote machine " + address + " received");

      if (!recvMsg.isMessageReady()) {
        LOG.info("Data " + blockId + " from remote machine is not ready.");
        return null;
      }

      if (recvMsg.getBlockId() < 0) {
        LOG.info("Data " + recvMsg.getBlockId() + " is not in remote machine.");
        return null;
      }

      return recvMsg.getReadOnlyData();
    } finally {
      socketChannel.close();
    }
  }

  /**
   * To set the configuration object for UnderFileSystem. The conf object is understood by the
   * concrete underfs' implementation.
   *
   * @param conf The configuration object accepted by ufs.
   */
  public void setUFSConf(Object conf) {
    mUFSConf = conf;
  }

  @Override
  public String toString() {
    try {
      return getPath();
    } catch (IOException e) {
      throw new RuntimeException("File does not exist anymore: " + mFileId);
    }
  }
}
TOP

Related Classes of tachyon.client.TachyonFile

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.