/** * Creates a new file output stream. * * @param path the file path * @param options the client options * @throws IOException if an I/O error occurs */ public FileOutStream(TachyonURI path, OutStreamOptions options) throws IOException { mUri = Preconditions.checkNotNull(path); mNonce = ClientUtils.getRandomNonNegativeLong(); mBlockSize = options.getBlockSizeBytes(); mTachyonStorageType = options.getTachyonStorageType(); mUnderStorageType = options.getUnderStorageType(); mContext = FileSystemContext.INSTANCE; mPreviousBlockOutStreams = new LinkedList<BufferedBlockOutStream>(); if (mUnderStorageType.isSyncPersist()) { updateUfsPath(); String tmpPath = PathUtils.temporaryFileName(mNonce, mUfsPath); UnderFileSystem ufs = UnderFileSystem.get(tmpPath, ClientContext.getConf()); // TODO(jiri): Implement collection of temporary files left behind by dead clients. mUnderStorageOutputStream = ufs.create(tmpPath, (int) mBlockSize); } else { mUfsPath = null; mUnderStorageOutputStream = null; } mClosed = false; mCanceled = false; mShouldCacheCurrentBlock = mTachyonStorageType.isStore(); mBytesWritten = 0; mLocationPolicy = Preconditions.checkNotNull( options.getLocationPolicy(), PreconditionMessage.FILE_WRITE_LOCATION_POLICY_UNSPECIFIED); }
@Override public void close() throws IOException { if (mClosed) { return; } if (mCurrentBlockOutStream != null) { mPreviousBlockOutStreams.add(mCurrentBlockOutStream); } Boolean canComplete = false; CompleteFileOptions options = CompleteFileOptions.defaults(); if (mUnderStorageType.isSyncPersist()) { String tmpPath = PathUtils.temporaryFileName(mNonce, mUfsPath); UnderFileSystem ufs = UnderFileSystem.get(tmpPath, ClientContext.getConf()); if (mCanceled) { // TODO(yupeng): Handle this special case in under storage integrations. mUnderStorageOutputStream.close(); if (!ufs.exists(tmpPath)) { // Location of the temporary file has changed, recompute it. updateUfsPath(); tmpPath = PathUtils.temporaryFileName(mNonce, mUfsPath); } ufs.delete(tmpPath, false); } else { mUnderStorageOutputStream.flush(); mUnderStorageOutputStream.close(); if (!ufs.exists(tmpPath)) { // Location of the temporary file has changed, recompute it. updateUfsPath(); tmpPath = PathUtils.temporaryFileName(mNonce, mUfsPath); } if (!ufs.rename(tmpPath, mUfsPath)) { throw new IOException("Failed to rename " + tmpPath + " to " + mUfsPath); } options.setUfsLength(ufs.getFileSize(mUfsPath)); canComplete = true; } } if (mTachyonStorageType.isStore()) { try { if (mCanceled) { for (BufferedBlockOutStream bos : mPreviousBlockOutStreams) { bos.cancel(); } } else { for (BufferedBlockOutStream bos : mPreviousBlockOutStreams) { bos.close(); } canComplete = true; } } catch (IOException ioe) { handleCacheWriteException(ioe); } } if (canComplete) { FileSystemMasterClient masterClient = mContext.acquireMasterClient(); try { masterClient.completeFile(mUri, options); } catch (TachyonException e) { throw new IOException(e); } finally { mContext.releaseMasterClient(masterClient); } } if (mUnderStorageType.isAsyncPersist()) { scheduleAsyncPersist(); } mClosed = true; }