View Javadoc

1   /**********************************************************************
2    * IOUtil.java
3    * created on 15.08.2004 by netseeker
4    * $Source$
5    * $Date$
6    * $Revision$
7    *
8    * ====================================================================
9    *
10   *  Copyright 2005-2006 netseeker aka Michael Manske
11   *
12   *  Licensed under the Apache License, Version 2.0 (the "License");
13   *  you may not use this file except in compliance with the License.
14   *  You may obtain a copy of the License at
15   *
16   *      http://www.apache.org/licenses/LICENSE-2.0
17   *
18   *  Unless required by applicable law or agreed to in writing, software
19   *  distributed under the License is distributed on an "AS IS" BASIS,
20   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21   *  See the License for the specific language governing permissions and
22   *  limitations under the License.
23   * ====================================================================
24   *
25   * This file is part of the ejoe framework.
26   * For more information on the author, please see
27   * <http://www.manskes.de/>.
28   *
29   *********************************************************************/
30  
31  package de.netseeker.ejoe.io;
32  
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.OutputStream;
36  import java.io.Reader;
37  import java.io.UnsupportedEncodingException;
38  import java.io.Writer;
39  import java.net.Socket;
40  import java.net.SocketException;
41  import java.nio.ByteBuffer;
42  import java.nio.channels.Channel;
43  import java.nio.channels.Selector;
44  import java.nio.charset.Charset;
45  import java.util.logging.Level;
46  import java.util.logging.Logger;
47  import java.util.zip.GZIPInputStream;
48  import java.util.zip.GZIPOutputStream;
49  
50  import de.netseeker.ejoe.EJConstants;
51  import de.netseeker.ejoe.adapter.SerializeAdapter;
52  
53  /***
54   * Some useful methods for closing io streams, io readers, channels and Selector quitely, as well as methods for
55   * non-blocking, semi-blocking and full blocking io read/write operations.
56   * 
57   * @author netseeker aka Michael Manske
58   * @since 0.3.1
59   */
60  public final class IOUtil
61  {
62      private static final Logger logger    = Logger.getLogger( IOUtil.class.getName() );
63  
64      private static Charset      csDefault = Charset.forName( EJConstants.EJOE_DEFAULT_CHARSET );
65  
66      /***
67       * Tries to close an OutputStream and handles null values and IOExceptions quietly
68       * 
69       * @param out The output stream to close quietly
70       */
71      public static void closeQuiet( OutputStream out )
72      {
73          if ( out != null )
74          {
75              try
76              {
77                  out.close();
78              }
79              catch ( IOException e )
80              {
81                  // do nothing
82              }
83          }
84      }
85  
86      /***
87       * Tries to close a Writer and handles null values and IOExceptions quietly
88       * 
89       * @param out The writer to close quietly
90       */
91      public static void closeQuiet( Writer out )
92      {
93          if ( out != null )
94          {
95              try
96              {
97                  out.close();
98              }
99              catch ( IOException e )
100             {
101                 // do nothing
102             }
103         }
104     }
105 
106     /***
107      * Tries to close an InputStream and handles null values and IOExceptions quietly
108      * 
109      * @param in the input stream to close quietly
110      */
111     public static void closeQuiet( InputStream in )
112     {
113         if ( in != null )
114         {
115             try
116             {
117                 in.close();
118             }
119             catch ( IOException e )
120             {
121                 // do nothing
122             }
123         }
124     }
125 
126     /***
127      * Tries to close a Reader and handles null values and IOExceptions quietly
128      * 
129      * @param reader the reader to close quietly
130      */
131     public static void closeQuiet( Reader reader )
132     {
133         if ( reader != null )
134         {
135             try
136             {
137                 reader.close();
138             }
139             catch ( IOException e )
140             {
141                 // do nothing
142             }
143         }
144     }
145 
146     /***
147      * Tries to close an NIO Channel and handles null values and IOExceptions quietly
148      * 
149      * @param channel the channel to close quietly
150      */
151     public static void closeQuiet( Channel channel )
152     {
153         if ( channel != null )
154         {
155             try
156             {
157                 channel.close();
158             }
159             catch ( IOException e )
160             {
161                 // do nothing
162             }
163         }
164     }
165 
166     /***
167      * Tries to close a NIO Selector and handles null values and IOExceptions quietly
168      * 
169      * @param selector the selector to close quietly
170      */
171     public static void closeQuiet( Selector selector )
172     {
173         if ( selector != null && selector.isOpen() )
174         {
175             try
176             {
177                 selector.close();
178             }
179             catch ( IOException e )
180             {
181                 // do nothing
182             }
183         }
184     }
185 
186     /***
187      * Invokes a SerializeAdapter for the given Object and handles compression and buffering
188      * 
189      * @param adapter
190      * @param out
191      * @param obj
192      * @param buffered
193      * @param compressed
194      * @throws Exception
195      */
196     public static void adapterSerialize( final SerializeAdapter adapter, OutputStream out, Object obj,
197                                          boolean compressed, final int compressionLevel ) throws Exception
198     {
199         logger.log( Level.FINEST, "Using compression: " + compressed );
200         boolean flushed = false;
201 
202         if ( compressed )
203         {
204             GZIPOutputStream sOut = new GZIPOutputStream( out, EJConstants.BUFFERED_STREAM_SIZE )
205                 {
206                     {
207                         def.setLevel( compressionLevel );
208                     }
209                 };
210 
211             adapter.write( obj, new UncloseableOutputStream( sOut, adapter.requiresCustomEOFHandling() ) );
212 
213             sOut.finish();
214             flushed = true;
215         }
216         else if ( !adapter.isSelfBuffered() )
217         {
218             OutputStream sOut = new UncloseableOutputStream(
219                                                              new FastBufferedOutputStream(
220                                                                                            out,
221                                                                                            EJConstants.BUFFERED_STREAM_SIZE ) );
222             adapter.write( obj, sOut );
223             sOut.flush();
224             flushed = true;
225         }
226         else
227         {
228             adapter.write( obj, new UncloseableOutputStream( out, adapter.requiresCustomEOFHandling() ) );
229         }
230 
231         if ( !flushed ) out.flush();
232     }
233 
234     /***
235      * Invokes a (De)SerializeAdapter for the given Object and handles decompression and buffering
236      * 
237      * @param adapter
238      * @param in
239      * @param buffered
240      * @param compressed
241      * @return
242      * @throws Exception
243      */
244     public static Object adapterDeserialize( final SerializeAdapter adapter, InputStream in, boolean compressed )
245             throws Exception
246     {
247         logger.log( Level.FINEST, "Using compression: " + compressed );
248 
249         if ( compressed )
250         {
251             if ( !logger.isLoggable( Level.FINEST ) )
252             {
253                 return adapter
254                         .read( new UncloseableInputStream( new GZIPInputStream( in, EJConstants.BUFFERED_STREAM_SIZE ),
255                                                            adapter.requiresCustomEOFHandling() ) );
256             }
257             else
258             {
259                 return adapter
260                         .read( new LoggingInputStream(
261                                                        new UncloseableInputStream(
262                                                                                    new GZIPInputStream(
263                                                                                                         in,
264                                                                                                         EJConstants.BUFFERED_STREAM_SIZE ),
265                                                                                    adapter.requiresCustomEOFHandling() ) ) );
266             }
267         }
268         else if ( !adapter.isSelfBuffered() )
269         {
270             if ( !logger.isLoggable( Level.FINEST ) )
271             {
272                 return adapter
273                         .read( new UncloseableInputStream(
274                                                            new FastBufferedInputStream(
275                                                                                         in,
276                                                                                         EJConstants.BUFFERED_STREAM_SIZE ),
277                                                            adapter.requiresCustomEOFHandling() ) );
278             }
279             else
280             {
281                 return adapter
282                         .read( new LoggingInputStream(
283                                                        new UncloseableInputStream(
284                                                                                    new FastBufferedInputStream(
285                                                                                                                 in,
286                                                                                                                 EJConstants.BUFFERED_STREAM_SIZE ),
287                                                                                    adapter.requiresCustomEOFHandling() ) ) );
288             }
289         }
290         else
291         {
292             if ( !logger.isLoggable( Level.FINEST ) )
293                 return adapter.read( new UncloseableInputStream( in, adapter.requiresCustomEOFHandling() ) );
294             else
295                 return adapter.read( new LoggingInputStream( new UncloseableInputStream( in, adapter
296                         .requiresCustomEOFHandling() ) ) );
297         }
298     }
299 
300     /***
301      * Reads and returns a ByteBuffer from an InputStream
302      * 
303      * @param in InputStream from which read a ByteBuffer
304      * @return a ByteBuffer read from the InputStream
305      * @throws IOException
306      */
307     public static ByteBuffer readDirect( InputStream in ) throws IOException
308     {
309         InputStream bIn = new UncloseableInputStream( new FastBufferedInputStream( in, EJConstants.BUFFERED_STREAM_SIZE ));
310         byte[] buffer = new byte[EJConstants.BUFFERED_STREAM_SIZE];
311         ByteBufferOutputStream out = new ByteBufferOutputStream();
312         ByteBuffer result = null;
313         
314         try
315         {
316             int read = -1;
317             int count = 0;
318             // read the length of this request
319             int length = bIn.read();
320 
321             while ( count < length && (read = bIn.read( buffer )) != -1 )
322             {
323                 out.write( buffer, 0, read );
324                 count += read;
325             }
326 
327             result = out.getBackingBuffer();
328             result.flip();
329         }
330         finally
331         {
332             IOUtil.closeQuiet( bIn );
333             IOUtil.closeQuiet( out );
334         }
335 
336         return result;
337     }
338 
339     /***
340      * Writes a ByteBuffer directly into an OutputStream
341      * 
342      * @param out
343      * @param buf
344      * @throws IOException
345      */
346     public static void writeDirect( OutputStream out, ByteBuffer buf ) throws IOException
347     {
348         OutputStream bOut = new UncloseableOutputStream( new FastBufferedOutputStream( out, EJConstants.BUFFERED_STREAM_SIZE ) );
349         // tell the receiver the length of this request
350         bOut.write( buf.remaining() );
351         bOut.flush();
352 
353         if ( buf.hasArray() )
354         {
355             bOut.write( buf.array() );
356             bOut.flush();
357         }
358         else
359         {
360             byte[] tmp = new byte[EJConstants.BUFFERED_STREAM_SIZE];
361             while ( buf.hasRemaining() )
362             {
363                 int length = buf.remaining();
364                 if ( length >= tmp.length )
365                 {
366                     buf.get( tmp );
367                     bOut.write( tmp );
368                     bOut.flush();
369                 }
370                 else
371                 {
372                     buf.get( tmp, 0, length );
373                     bOut.write( tmp, 0, length );
374                     bOut.flush();
375                 }
376             }
377         }
378     }
379 
380     /***
381      * Set the SO_SNDBUF hint on a connected socket to the size of the data which are expected to be written next time
382      * 
383      * @param socket the connected socket
384      * @param size size to set for SO_SNDBUF
385      * @throws SocketException
386      */
387     public static void setSendBufferSize( Socket socket, int size ) throws SocketException
388     {
389         if ( size > 0 )
390         {
391             if ( size <= EJConstants.EJOE_MAX_SOCKET_BUF_SIZE )
392             {
393                 socket.setSendBufferSize( size );
394             }
395             else
396             {
397                 socket.setSendBufferSize( EJConstants.EJOE_MAX_SOCKET_BUF_SIZE );
398             }
399         }
400     }
401 
402     /***
403      * Set the SO_RCVBUF hint on a connected socket to the size of the data which are expected to be read next time
404      * 
405      * @param socket the connected socket
406      * @param size size to set for SO_RCVBUF
407      * @throws SocketException
408      */
409     public static void setReceiveBufferSize( Socket socket, int size ) throws SocketException
410     {
411         if ( size > 0 )
412         {
413             if ( size <= EJConstants.EJOE_MAX_SOCKET_BUF_SIZE )
414             {
415                 socket.setReceiveBufferSize( size );
416             }
417             else
418             {
419                 socket.setReceiveBufferSize( EJConstants.EJOE_MAX_SOCKET_BUF_SIZE );
420             }
421         }
422     }
423 
424     /***
425      * Converts the given string into a UTF8-encoded array of bytes. If UTF8 is not supported the default charset is
426      * used.
427      * 
428      * @param str
429      * @return
430      */
431     public static byte[] encodeToBytes( String str )
432     {
433         return encodeToBytes( str, EJConstants.EJOE_DEFAULT_CHARSET );
434     }
435 
436     /***
437      * Converts the given string into a encoded array of bytes. For encoding the given charSet is used. If the given
438      * charSet not supported the default charset is used.
439      * 
440      * @param str string to be encoded
441      * @param cs Charset to use for encoding
442      * @return
443      */
444     public static byte[] encodeToBytes( String str, String cs )
445     {
446         byte[] result;
447 
448         try
449         {
450             result = str.getBytes( cs );
451         }
452         catch ( UnsupportedEncodingException e )
453         {
454             result = str.getBytes();
455         }
456 
457         return result;
458     }
459 
460     /***
461      * Converts the given string into a UTF8-encoded ByteBuffer. If UTF8 is not supported the default charset is used.
462      * 
463      * @param str
464      * @return
465      */
466     public static ByteBuffer encodeToByteBuffer( String str )
467     {
468         return csDefault.encode( str );
469     }
470 
471     /***
472      * @param dataBuf
473      * @return
474      */
475     public static String decodeToString( ByteBuffer dataBuf )
476     {
477         ByteBuffer tmpBuf = dataBuf.duplicate();
478         if ( tmpBuf.position() > 0 )
479         {
480             tmpBuf.flip();
481         }
482         return csDefault.decode( tmpBuf ).toString();
483     }
484 
485     /***
486      * @param dataBuf
487      * @param charset
488      * @return
489      */
490     public static String decodeToString( ByteBuffer dataBuf, String charset )
491     {
492         ByteBuffer tmpBuf = dataBuf.duplicate();
493         if ( tmpBuf.position() > 0 )
494         {
495             tmpBuf.flip();
496         }
497         return Charset.forName( charset ).decode( tmpBuf ).toString();
498     }
499 
500     /***
501      * Converts a byte to an array of boolean
502      * 
503      * @param b a byte
504      * @return representation of the given byte as array of booleans
505      */
506     public static boolean[] byteToBBits( byte b )
507     {
508         boolean[] bits = new boolean[8];
509         for ( int i = 0; i < bits.length; i++ )
510         {
511             bits[i] = ((b & (1 << i)) != 0);
512         }
513         return bits;
514     }
515 
516     /***
517      * Converts a array of boolean to a byte
518      * 
519      * @param bits array of booleans representing the bits of a byte
520      * @return byte representation of the given array
521      */
522     public static byte bBitsToByte( boolean[] bits )
523     {
524         int value = 0;
525         for ( int i = 0; i < 8; i++ )
526         {
527             if ( bits[i] == true )
528             {
529                 value = value | (1 << i);
530             }
531         }
532         return (byte) value;
533     }
534 
535     /***
536      * Converts a bit-like String representation into an array of boolean
537      * 
538      * @param sBits bit-like String representation of bits
539      * @return array of boolean, each item represents one bit
540      */
541     public static boolean[] sBitsToBBits( String sBits )
542     {
543         boolean[] bBits = new boolean[sBits.length()];
544         for ( int i = 0; i < bBits.length; i++ )
545         {
546             bBits[i] = sBits.charAt( i ) == '1';
547         }
548 
549         return bBits;
550     }
551 
552     /***
553      * Converts a array of boolean into a bit-like String representation
554      * 
555      * @param bBits array of boolean to convert
556      * @return bit-like String representation of the given array of boolean
557      */
558     public static String bBitsToSBits( boolean[] bBits )
559     {
560         StringBuffer sb = new StringBuffer();
561         for ( int i = 0; i < bBits.length; i++ )
562         {
563             sb.append( bBits[i] ? 1 : 0 );
564         }
565 
566         return sb.toString();
567     }
568 }