All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.net.SocketOutputStream Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
/**
 * 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 org.apache.hadoop.net;

import java.io.EOFException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.WritableByteChannel;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.io.LongWritable;

/**
 * This implements an output stream that can have a timeout while writing.
 * This sets non-blocking flag on the socket channel.
 * So after creating this object , read() on 
 * {@link Socket#getInputStream()} and write() on 
 * {@link Socket#getOutputStream()} on the associated socket will throw 
 * llegalBlockingModeException.
 * Please use {@link SocketInputStream} for reading.
 */
@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
@InterfaceStability.Unstable
public class SocketOutputStream extends OutputStream 
                                implements WritableByteChannel {                                
  
  private Writer writer;
  
  private static class Writer extends SocketIOWithTimeout {
    WritableByteChannel channel;
    
    Writer(WritableByteChannel channel, long timeout) throws IOException {
      super((SelectableChannel)channel, timeout);
      this.channel = channel;
    }
    
    @Override
    int performIO(ByteBuffer buf) throws IOException {
      return channel.write(buf);
    }
  }
  
  /**
   * Create a new ouput stream with the given timeout. If the timeout
   * is zero, it will be treated as infinite timeout. The socket's
   * channel will be configured to be non-blocking.
   * 
   * @param channel 
   *        Channel for writing, should also be a {@link SelectableChannel}.  
   *        The channel will be configured to be non-blocking.
   * @param timeout timeout in milliseconds. must not be negative.
   * @throws IOException
   */
  public SocketOutputStream(WritableByteChannel channel, long timeout) 
                                                         throws IOException {
    SocketIOWithTimeout.checkChannelValidity(channel);
    writer = new Writer(channel, timeout);
  }
  
  /**
   * Same as SocketOutputStream(socket.getChannel(), timeout):

* * Create a new ouput stream with the given timeout. If the timeout * is zero, it will be treated as infinite timeout. The socket's * channel will be configured to be non-blocking. * * @see SocketOutputStream#SocketOutputStream(WritableByteChannel, long) * * @param socket should have a channel associated with it. * @param timeout timeout timeout in milliseconds. must not be negative. * @throws IOException */ public SocketOutputStream(Socket socket, long timeout) throws IOException { this(socket.getChannel(), timeout); } @Override public void write(int b) throws IOException { /* If we need to, we can optimize this allocation. * probably no need to optimize or encourage single byte writes. */ byte[] buf = new byte[1]; buf[0] = (byte)b; write(buf, 0, 1); } @Override public void write(byte[] b, int off, int len) throws IOException { ByteBuffer buf = ByteBuffer.wrap(b, off, len); while (buf.hasRemaining()) { try { if (write(buf) < 0) { throw new IOException("The stream is closed"); } } catch (IOException e) { /* Unlike read, write can not inform user of partial writes. * So will close this if there was a partial write. */ if (buf.capacity() > buf.remaining()) { writer.close(); } throw e; } } } @Override public synchronized void close() throws IOException { /* close the channel since Socket.getOuputStream().close() * closes the socket. */ writer.channel.close(); writer.close(); } /** * Returns underlying channel used by this stream. * This is useful in certain cases like channel for * {@link FileChannel#transferTo(long, long, WritableByteChannel)} */ public WritableByteChannel getChannel() { return writer.channel; } //WritableByteChannle interface @Override public boolean isOpen() { return writer.isOpen(); } @Override public int write(ByteBuffer src) throws IOException { return writer.doIO(src, SelectionKey.OP_WRITE); } /** * waits for the underlying channel to be ready for writing. * The timeout specified for this stream applies to this wait. * * @throws SocketTimeoutException * if select on the channel times out. * @throws IOException * if any other I/O error occurs. */ public void waitForWritable() throws IOException { writer.waitForIO(SelectionKey.OP_WRITE); } /** * Transfers data from FileChannel using * {@link FileChannel#transferTo(long, long, WritableByteChannel)}. * Updates waitForWritableTime and transferToTime * with the time spent blocked on the network and the time spent transferring * data from disk to network respectively. * * Similar to readFully(), this waits till requested amount of * data is transfered. * * @param fileCh FileChannel to transfer data from. * @param position position within the channel where the transfer begins * @param count number of bytes to transfer. * @param waitForWritableTime nanoseconds spent waiting for the socket * to become writable * @param transferToTime nanoseconds spent transferring data * * @throws EOFException * If end of input file is reached before requested number of * bytes are transfered. * * @throws SocketTimeoutException * If this channel blocks transfer longer than timeout for * this stream. * * @throws IOException Includes any exception thrown by * {@link FileChannel#transferTo(long, long, WritableByteChannel)}. */ public void transferToFully(FileChannel fileCh, long position, int count, LongWritable waitForWritableTime, LongWritable transferToTime) throws IOException { long waitTime = 0; long transferTime = 0; while (count > 0) { /* * Ideally we should wait after transferTo returns 0. But because of * a bug in JRE on Linux (http://bugs.sun.com/view_bug.do?bug_id=5103988), * which throws an exception instead of returning 0, we wait for the * channel to be writable before writing to it. If you ever see * IOException with message "Resource temporarily unavailable" * thrown here, please let us know. * * Once we move to JAVA SE 7, wait should be moved to correct place. */ long start = System.nanoTime(); waitForWritable(); long wait = System.nanoTime(); int nTransfered = (int) fileCh.transferTo(position, count, getChannel()); if (nTransfered == 0) { //check if end of file is reached. if (position >= fileCh.size()) { throw new EOFException("EOF Reached. file size is " + fileCh.size() + " and " + count + " more bytes left to be " + "transfered."); } //otherwise assume the socket is full. //waitForWritable(); // see comment above. } else if (nTransfered < 0) { throw new IOException("Unexpected return of " + nTransfered + " from transferTo()"); } else { position += nTransfered; count -= nTransfered; } long transfer = System.nanoTime(); waitTime += wait - start; transferTime += transfer - wait; } if (waitForWritableTime != null) { waitForWritableTime.set(waitTime); } if (transferToTime != null) { transferToTime.set(transferTime); } } /** * Call * {@link #transferToFully(FileChannel, long, int, LongWritable, LongWritable) * } * with null waitForWritableTime and transferToTime */ public void transferToFully(FileChannel fileCh, long position, int count) throws IOException { transferToFully(fileCh, position, count, null, null); } public void setTimeout(int timeoutMs) { writer.setTimeout(timeoutMs); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy