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
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
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
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
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
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
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
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
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 }