/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.facebook.infrastructure.net.io;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.io.*;
import org.apache.log4j.Logger;
import org.apache.commons.lang.ArrayUtils;
import com.facebook.infrastructure.db.Table;
/**
* Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com )
*/
class ContentStreamState extends StartState
{
private static Logger logger_ = Logger.getLogger(ContentStreamState.class);
private static long count_ = 64*1024*1024;
/* Return this byte array to exit event loop */
private static byte[] bytes_ = new byte[1];
private long bytesRead_ = 0L;
private FileChannel fc_;
private StreamContextManager.StreamContext streamContext_;
private StreamContextManager.StreamStatus streamStatus_;
ContentStreamState(TcpReader stream)
{
super(stream);
SocketChannel socketChannel = stream.getStream();
InetSocketAddress remoteAddress = (InetSocketAddress)socketChannel.socket().getRemoteSocketAddress();
String remoteHost = remoteAddress.getHostName();
streamContext_ = StreamContextManager.getStreamContext(remoteHost);
streamStatus_ = StreamContextManager.getStreamStatus(remoteHost);
}
private void createFileChannel() throws IOException
{
if ( fc_ == null )
{
logger_.debug("Creating file for " + streamContext_.getTargetFile());
FileOutputStream fos = new FileOutputStream( streamContext_.getTargetFile(), true );
fc_ = fos.getChannel();
}
}
public byte[] read() throws IOException, ReadNotCompleteException
{
SocketChannel socketChannel = stream_.getStream();
InetSocketAddress remoteAddress = (InetSocketAddress)socketChannel.socket().getRemoteSocketAddress();
String remoteHost = remoteAddress.getHostName();
createFileChannel();
if ( streamContext_ != null )
{
try
{
bytesRead_ += fc_.transferFrom(socketChannel, bytesRead_, ContentStreamState.count_);
if ( bytesRead_ != streamContext_.getExpectedBytes() )
throw new ReadNotCompleteException("Specified number of bytes have not been read from the Socket Channel");
}
catch ( IOException ex )
{
/* Ask the source node to re-stream this file. */
streamStatus_.setAction(StreamContextManager.StreamCompletionAction.STREAM);
handleStreamCompletion(remoteHost);
/* Delete the orphaned file. */
File file = new File(streamContext_.getTargetFile());
file.delete();
throw ex;
}
if ( bytesRead_ == streamContext_.getExpectedBytes() )
{
logger_.debug("Removing stream context " + streamContext_);
handleStreamCompletion(remoteHost);
bytesRead_ = 0L;
fc_.close();
morphState();
}
}
return ArrayUtils.EMPTY_BYTE_ARRAY;
}
private void handleStreamCompletion(String remoteHost) throws IOException
{
/*
* Streaming is complete. If all the data that has to be received inform the sender via
* the stream completion callback so that the source may perform the requisite cleanup.
*/
IStreamComplete streamComplete = StreamContextManager.getStreamCompletionHandler(remoteHost);
if ( streamComplete != null )
{
streamComplete.onStreamCompletion(remoteHost, streamContext_, streamStatus_);
}
}
public void morphState() throws IOException
{
/* We instantiate an array of size 1 so that we can exit the event loop of the read. */
StartState nextState = stream_.getSocketState(TcpReader.TcpReaderState.DONE);
if ( nextState == null )
{
nextState = new DoneState(stream_, ContentStreamState.bytes_);
stream_.putSocketState( TcpReader.TcpReaderState.DONE, nextState );
}
else
{
nextState.setContextData(ContentStreamState.bytes_);
}
stream_.morphState( nextState );
}
public void setContextData(Object data)
{
throw new UnsupportedOperationException("This method is not supported in the ContentStreamState");
}
}