/*
* JacORB - a free Java ORB
*
* Copyright (C) 2000-2014 Gerald Brose / The JacORB Team.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.jacorb.orb.etf;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.Socket;
import org.jacorb.util.ObjectUtil;
/**
* This an abstract base implementation of the ETF::Connection interface.
*
* @author Nicolas Noffke
* @author Andre Spiegel
*/
public abstract class StreamConnectionBase
extends ConnectionBase
{
/**
* Reads performed on this stream.
*/
protected InputStream in_stream = null;
/**
* Writes performed on this stream.
*/
protected OutputStream out_stream = null;
protected StreamConnectionBase()
{
super();
}
/**
* Initialise this instance as a copy of another. Intended for use within subclass
* constructors.
*/
protected StreamConnectionBase(StreamConnectionBase other)
{
super(other);
this.in_stream = other.in_stream;
this.out_stream = other.out_stream;
}
/**
* Reads bytes from the connection.
*
* @param data holds a byte array to which the bytes will be written. The
* field <code>data.value</code> must be initialized with a valid byte
* array already, it cannot be null.
* @param offset the index in <code>data.value</code> at which the first
* byte will be written.
* @param min_length the minimum number of bytes that shall be read from
* the Connection. The method will block until at least this many bytes
* have been read. If <code>min_length</code> is 0, the method will always
* return immediately without reading any data.
* @param max_length the maximum number of bytes that shall be read from
* the Connection. If <code>max_length</code> is greater than
* <code>min_length</code>, then the transport is free to read
* (<code>max_length</code> - <code>min_length</code>) additional bytes
* beyond <code>min_length</code>.
* @param time_out timeout for this particular read operation. Currently
* ignored in JacORB; we use socket-level timeouts.
* @return the number of bytes actually read. The last byte written to
* <code>data.value</code> is at the index <code>offset</code> + this return
* value. This return type is a change to the ETF draft spec in JacORB.
* It is needed because the mechanism suggested in the draft does not work
* in Java.
* @throws org.omg.CORBA.TIMEOUT if the socket-level timeout expires
* before the read operation completes.
* @throws org.omg.CORBA.TRANSIENT if the I/O is interrupted.
* @throws org.omg.CORBA.COMM_FAILURE if the read operation fails,
* for example because the connection has been closed.
*/
public int read(org.omg.ETF.BufferHolder data,
int offset,
int min_length,
int max_length,
long time_out)
{
int read = 0;
while (read < min_length)
{
int n = 0;
try
{
n = in_stream.read(data.value,
offset + read,
max_length - read);
}
catch (InterruptedIOException e)
{
int soTimeout = getTimeout();
if (soTimeout != 0)
{
if (logger.isDebugEnabled())
{
logger.debug("Socket timeout (timeout period: " +
soTimeout + ")");
}
throw new org.omg.CORBA.TIMEOUT();
}
throw new org.omg.CORBA.TRANSIENT("Interrupted I/O: " + e);
}
catch (IOException se)
{
if (logger.isDebugEnabled())
{
logger.debug("Transport to " + connection_info +
": stream closed " + se.getMessage());
}
throw handleCommFailure(se);
}
if (n < 0)
{
if (logger.isDebugEnabled())
{
logger.debug("Transport to " + connection_info +
": stream closed on read < 0");
}
throw new org.omg.CORBA.COMM_FAILURE("read() did not return any data");
}
read += n;
}
if (logger.isDebugEnabled())
{
// guard this with isDebugEnabled() because auto-boxing of
// parameter "read" would cost too much time
logger.debug("read {} bytes from {}", read, connection_info);
}
return read;
}
/**
* Writes bytes to this Connection.
*
* @param is_first Currently not used in JacORB.
* @param is_last Currently not used in JacORB.
* @param data the buffer that holds the data that is to be written.
* @param offset index of the first byte in <code>data</code> that shall
* be written to the Connection.
* @param length the number of bytes in data that shall be written. The
* last byte in <code>data</code> that is written is at the index
* <code>offset + length</code>.
* @param time_out timeout for this particular write operation. Currently
* ignored in JacORB.
* @throws org.omg.CORBA.COMM_FAILURE if anything goes wrong during
* the write operation.
*/
public void write(boolean is_first,
boolean is_last,
byte[] data,
int offset,
int length,
long time_out)
{
try
{
out_stream.write(data, offset, length);
if (b_out != null)
{
b_out.write(data, offset, length);
}
if (logger.isDebugEnabled())
{
logger.debug("wrote {} bytes to {}", length, connection_info);
}
}
catch (IOException ex)
{
throw handleCommFailure(ex);
}
}
/**
* Causes any buffered data to be actually written to the Connection.
*/
public void flush()
{
try
{
if (b_out != null)
{
if (logger.isInfoEnabled())
{
byte[] buffer = b_out.toByteArray();
logger.info("sendMessages(): " + ObjectUtil.bufToString(buffer, 0, buffer.length));
}
b_out.reset();
}
out_stream.flush();
}
catch (IOException ex)
{
throw handleCommFailure(ex);
}
}
/**
* Simply return true if calling a read on this instance would
* find data in the connection. Otherwise, the function shall
* return false.
*/
public boolean is_data_available()
{
try
{
return in_stream.available() > 0;
}
catch (IOException ex)
{
throw handleCommFailure(ex);
}
}
/**
* Closes the parameter without throwing an exception
* TODO: Use {@link java.io.Closeable}.
*/
protected void silentClose(Object c)
{
try
{
if (c != null)
{
if (c instanceof Socket)
{
((Socket) c).close();
}
else if (c instanceof Closeable)
{
((Closeable) c).close();
}
else
{
logger.error("Unknown parameter " + c.getClass().getName());
}
}
}
catch (IOException e)
{
logger.warn("Exception when closing " + this, e);
}
}
public abstract boolean isSSL();
}