/*********************************************************************
* IOUtil.java
* created on 15.08.2004 by netseeker
* $Source$
* $Date$
* $Revision$
*
* ====================================================================
*
* Copyright 2005-2006 netseeker aka Michael Manske
*
* 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.
* ====================================================================
*
* This file is part of the ejoe framework.
* For more information on the author, please see
* <http://www.manskes.de/>.
*
*********************************************************************/
package de.netseeker.ejoe.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import de.netseeker.ejoe.EJConstants;
import de.netseeker.ejoe.adapter.SerializeAdapter;
/**
* Some useful methods for closing io streams, io readers, channels and Selector quitely, as well as methods for
* non-blocking, semi-blocking and full blocking io read/write operations.
*
* @author netseeker aka Michael Manske
* @since 0.3.1
*/
public final class IOUtil
{
private static final Logger logger = Logger.getLogger( IOUtil.class.getName() );
private static Charset csDefault = Charset.forName( EJConstants.EJOE_DEFAULT_CHARSET );
/**
* Tries to close an OutputStream and handles null values and IOExceptions quietly
*
* @param out The output stream to close quietly
*/
public static void closeQuiet( OutputStream out )
{
if ( out != null )
{
try
{
out.close();
}
catch ( IOException e )
{
// do nothing
}
}
}
/**
* Tries to close a Writer and handles null values and IOExceptions quietly
*
* @param out The writer to close quietly
*/
public static void closeQuiet( Writer out )
{
if ( out != null )
{
try
{
out.close();
}
catch ( IOException e )
{
// do nothing
}
}
}
/**
* Tries to close an InputStream and handles null values and IOExceptions quietly
*
* @param in the input stream to close quietly
*/
public static void closeQuiet( InputStream in )
{
if ( in != null )
{
try
{
in.close();
}
catch ( IOException e )
{
// do nothing
}
}
}
/**
* Tries to close a Reader and handles null values and IOExceptions quietly
*
* @param reader the reader to close quietly
*/
public static void closeQuiet( Reader reader )
{
if ( reader != null )
{
try
{
reader.close();
}
catch ( IOException e )
{
// do nothing
}
}
}
/**
* Tries to close an NIO Channel and handles null values and IOExceptions quietly
*
* @param channel the channel to close quietly
*/
public static void closeQuiet( Channel channel )
{
if ( channel != null )
{
try
{
channel.close();
}
catch ( IOException e )
{
// do nothing
}
}
}
/**
* Tries to close a NIO Selector and handles null values and IOExceptions quietly
*
* @param selector the selector to close quietly
*/
public static void closeQuiet( Selector selector )
{
if ( selector != null && selector.isOpen() )
{
try
{
selector.close();
}
catch ( IOException e )
{
// do nothing
}
}
}
/**
* Invokes a SerializeAdapter for the given Object and handles compression and buffering
*
* @param adapter
* @param out
* @param obj
* @param buffered
* @param compressed
* @throws Exception
*/
public static void adapterSerialize( final SerializeAdapter adapter, OutputStream out, Object obj,
boolean compressed, final int compressionLevel ) throws Exception
{
logger.log( Level.FINEST, "Using compression: " + compressed );
boolean flushed = false;
if ( compressed )
{
GZIPOutputStream sOut = new GZIPOutputStream( out, EJConstants.BUFFERED_STREAM_SIZE )
{
{
def.setLevel( compressionLevel );
}
};
adapter.write( obj, new UncloseableOutputStream( sOut, adapter.requiresCustomEOFHandling() ) );
sOut.finish();
flushed = true;
}
else if ( !adapter.isSelfBuffered() )
{
OutputStream sOut = new UncloseableOutputStream(
new FastBufferedOutputStream(
out,
EJConstants.BUFFERED_STREAM_SIZE ) );
adapter.write( obj, sOut );
sOut.flush();
flushed = true;
}
else
{
adapter.write( obj, new UncloseableOutputStream( out, adapter.requiresCustomEOFHandling() ) );
}
if ( !flushed ) out.flush();
}
/**
* Invokes a (De)SerializeAdapter for the given Object and handles decompression and buffering
*
* @param adapter
* @param in
* @param buffered
* @param compressed
* @return
* @throws Exception
*/
public static Object adapterDeserialize( final SerializeAdapter adapter, InputStream in, boolean compressed )
throws Exception
{
logger.log( Level.FINEST, "Using compression: " + compressed );
if ( compressed )
{
if ( !logger.isLoggable( Level.FINEST ) )
{
return adapter
.read( new UncloseableInputStream( new GZIPInputStream( in, EJConstants.BUFFERED_STREAM_SIZE ),
adapter.requiresCustomEOFHandling() ) );
}
else
{
return adapter
.read( new LoggingInputStream(
new UncloseableInputStream(
new GZIPInputStream(
in,
EJConstants.BUFFERED_STREAM_SIZE ),
adapter.requiresCustomEOFHandling() ) ) );
}
}
else if ( !adapter.isSelfBuffered() )
{
if ( !logger.isLoggable( Level.FINEST ) )
{
return adapter
.read( new UncloseableInputStream(
new FastBufferedInputStream(
in,
EJConstants.BUFFERED_STREAM_SIZE ),
adapter.requiresCustomEOFHandling() ) );
}
else
{
return adapter
.read( new LoggingInputStream(
new UncloseableInputStream(
new FastBufferedInputStream(
in,
EJConstants.BUFFERED_STREAM_SIZE ),
adapter.requiresCustomEOFHandling() ) ) );
}
}
else
{
if ( !logger.isLoggable( Level.FINEST ) )
return adapter.read( new UncloseableInputStream( in, adapter.requiresCustomEOFHandling() ) );
else
return adapter.read( new LoggingInputStream( new UncloseableInputStream( in, adapter
.requiresCustomEOFHandling() ) ) );
}
}
/**
* Reads and returns a ByteBuffer from an InputStream
*
* @param in InputStream from which read a ByteBuffer
* @return a ByteBuffer read from the InputStream
* @throws IOException
*/
public static ByteBuffer readDirect( InputStream in ) throws IOException
{
InputStream bIn = new UncloseableInputStream( new FastBufferedInputStream( in, EJConstants.BUFFERED_STREAM_SIZE ));
byte[] buffer = new byte[EJConstants.BUFFERED_STREAM_SIZE];
ByteBufferOutputStream out = new ByteBufferOutputStream();
ByteBuffer result = null;
try
{
int read = -1;
int count = 0;
// read the length of this request
int length = bIn.read();
while ( count < length && (read = bIn.read( buffer )) != -1 )
{
out.write( buffer, 0, read );
count += read;
}
result = out.getBackingBuffer();
result.flip();
}
finally
{
IOUtil.closeQuiet( bIn );
IOUtil.closeQuiet( out );
}
return result;
}
/**
* Writes a ByteBuffer directly into an OutputStream
*
* @param out
* @param buf
* @throws IOException
*/
public static void writeDirect( OutputStream out, ByteBuffer buf ) throws IOException
{
OutputStream bOut = new UncloseableOutputStream( new FastBufferedOutputStream( out, EJConstants.BUFFERED_STREAM_SIZE ) );
// tell the receiver the length of this request
bOut.write( buf.remaining() );
bOut.flush();
if ( buf.hasArray() )
{
bOut.write( buf.array() );
bOut.flush();
}
else
{
byte[] tmp = new byte[EJConstants.BUFFERED_STREAM_SIZE];
while ( buf.hasRemaining() )
{
int length = buf.remaining();
if ( length >= tmp.length )
{
buf.get( tmp );
bOut.write( tmp );
bOut.flush();
}
else
{
buf.get( tmp, 0, length );
bOut.write( tmp, 0, length );
bOut.flush();
}
}
}
}
/**
* Set the SO_SNDBUF hint on a connected socket to the size of the data which are expected to be written next time
*
* @param socket the connected socket
* @param size size to set for SO_SNDBUF
* @throws SocketException
*/
public static void setSendBufferSize( Socket socket, int size ) throws SocketException
{
if ( size > 0 )
{
if ( size <= EJConstants.EJOE_MAX_SOCKET_BUF_SIZE )
{
socket.setSendBufferSize( size );
}
else
{
socket.setSendBufferSize( EJConstants.EJOE_MAX_SOCKET_BUF_SIZE );
}
}
}
/**
* Set the SO_RCVBUF hint on a connected socket to the size of the data which are expected to be read next time
*
* @param socket the connected socket
* @param size size to set for SO_RCVBUF
* @throws SocketException
*/
public static void setReceiveBufferSize( Socket socket, int size ) throws SocketException
{
if ( size > 0 )
{
if ( size <= EJConstants.EJOE_MAX_SOCKET_BUF_SIZE )
{
socket.setReceiveBufferSize( size );
}
else
{
socket.setReceiveBufferSize( EJConstants.EJOE_MAX_SOCKET_BUF_SIZE );
}
}
}
/**
* Converts the given string into a UTF8-encoded array of bytes. If UTF8 is not supported the default charset is
* used.
*
* @param str
* @return
*/
public static byte[] encodeToBytes( String str )
{
return encodeToBytes( str, EJConstants.EJOE_DEFAULT_CHARSET );
}
/**
* Converts the given string into a encoded array of bytes. For encoding the given charSet is used. If the given
* charSet not supported the default charset is used.
*
* @param str string to be encoded
* @param cs Charset to use for encoding
* @return
*/
public static byte[] encodeToBytes( String str, String cs )
{
byte[] result;
try
{
result = str.getBytes( cs );
}
catch ( UnsupportedEncodingException e )
{
result = str.getBytes();
}
return result;
}
/**
* Converts the given string into a UTF8-encoded ByteBuffer. If UTF8 is not supported the default charset is used.
*
* @param str
* @return
*/
public static ByteBuffer encodeToByteBuffer( String str )
{
return csDefault.encode( str );
}
/**
* @param dataBuf
* @return
*/
public static String decodeToString( ByteBuffer dataBuf )
{
ByteBuffer tmpBuf = dataBuf.duplicate();
if ( tmpBuf.position() > 0 )
{
tmpBuf.flip();
}
return csDefault.decode( tmpBuf ).toString();
}
/**
* @param dataBuf
* @param charset
* @return
*/
public static String decodeToString( ByteBuffer dataBuf, String charset )
{
ByteBuffer tmpBuf = dataBuf.duplicate();
if ( tmpBuf.position() > 0 )
{
tmpBuf.flip();
}
return Charset.forName( charset ).decode( tmpBuf ).toString();
}
/**
* Converts a byte to an array of boolean
*
* @param b a byte
* @return representation of the given byte as array of booleans
*/
public static boolean[] byteToBBits( byte b )
{
boolean[] bits = new boolean[8];
for ( int i = 0; i < bits.length; i++ )
{
bits[i] = ((b & (1 << i)) != 0);
}
return bits;
}
/**
* Converts a array of boolean to a byte
*
* @param bits array of booleans representing the bits of a byte
* @return byte representation of the given array
*/
public static byte bBitsToByte( boolean[] bits )
{
int value = 0;
for ( int i = 0; i < 8; i++ )
{
if ( bits[i] == true )
{
value = value | (1 << i);
}
}
return (byte) value;
}
/**
* Converts a bit-like String representation into an array of boolean
*
* @param sBits bit-like String representation of bits
* @return array of boolean, each item represents one bit
*/
public static boolean[] sBitsToBBits( String sBits )
{
boolean[] bBits = new boolean[sBits.length()];
for ( int i = 0; i < bBits.length; i++ )
{
bBits[i] = sBits.charAt( i ) == '1';
}
return bBits;
}
/**
* Converts a array of boolean into a bit-like String representation
*
* @param bBits array of boolean to convert
* @return bit-like String representation of the given array of boolean
*/
public static String bBitsToSBits( boolean[] bBits )
{
StringBuffer sb = new StringBuffer();
for ( int i = 0; i < bBits.length; i++ )
{
sb.append( bBits[i] ? 1 : 0 );
}
return sb.toString();
}
}