/* Copyright (c) 2015 LinkedIn Corp. 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 com.linkedin.multipart; import com.linkedin.data.ByteString; import com.linkedin.r2.message.stream.entitystream.WriteHandle; import java.io.IOException; /** * Callback registered by the {@link com.linkedin.multipart.MultiPartMIMEWriter} to chain * a {@link com.linkedin.multipart.MultiPartMIMEDataSourceIterator} as a data source. * * @author Karim Vidhani */ final class MultiPartMIMEChainReaderCallback implements MultiPartMIMEDataSourceIteratorCallback { private final WriteHandle _writeHandle; private MultiPartMIMEDataSourceWriter _currentDataSource; private final byte[] _normalEncapsulationBoundary; @Override public void onNewDataSource(final MultiPartMIMEDataSourceWriter multiPartMIMEDataSourceWriter) { multiPartMIMEDataSourceWriter.onInit(new WriteHandle() { @Override public void write(ByteString data) { _writeHandle.write(data); } @Override public void done() { //We intentionally ignore this, since we will be responsible for calling writeHandle.done() when all the data //sources represented by the MultiPartMIMEDataSourceIterator have finished. } @Override public void error(Throwable throwable) { _writeHandle.error(throwable); } @Override public int remaining() { return _writeHandle.remaining(); } }); _currentDataSource = multiPartMIMEDataSourceWriter; ByteString serializedBoundaryAndHeaders = null; try { serializedBoundaryAndHeaders = MultiPartMIMEUtils.serializeBoundaryAndHeaders(_normalEncapsulationBoundary, multiPartMIMEDataSourceWriter); } catch (IOException ioException) { onStreamError(ioException); //Should never happen } _writeHandle.write(serializedBoundaryAndHeaders); if (_writeHandle.remaining() > 0) { multiPartMIMEDataSourceWriter.onWritePossible(); } } @Override public void onFinished() { //When each single data source finishes we notify the write handle that we are done. _writeHandle.done(); } @Override public void onAbandonComplete() { //This can happen if the MultiPartMIMEDataSourceIterator this callback was registered with was used as a data source and it was //told to abandon and the abandon finished. //We don't need to take any action here. } @Override public void onStreamError(final Throwable throwable) { //If there was an error obtaining data sources then we notify the writeHandle. //Note that there may or may not be a current MultiPartMIMEDataSource. If there was then it may already have //invoked _writeHandle.error(). //Regardless its safe to do it again in case this did not happen. _writeHandle.error(throwable); } MultiPartMIMEChainReaderCallback(final WriteHandle writeHandle, final byte[] normalEncapsulationBoundary) { _writeHandle = writeHandle; _normalEncapsulationBoundary = normalEncapsulationBoundary; } MultiPartMIMEDataSourceWriter getCurrentDataSource() { return _currentDataSource; } }