/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.type.descriptor.java;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.sql.Clob;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;

import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.BinaryStream;
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
import org.hibernate.internal.CoreMessageLogger;

import org.jboss.logging.Logger;

A help for dealing with BLOB and CLOB data
Author:Steve Ebersole
/** * A help for dealing with BLOB and CLOB data * * @author Steve Ebersole */
public final class DataHelper { private DataHelper() { }
The size of the buffer we will use to deserialize larger streams
/** The size of the buffer we will use to deserialize larger streams */
private static final int BUFFER_SIZE = 1024 * 4; private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, DataHelper.class.getName() ); public static boolean isNClob(final Class type) { return java.sql.NClob.class.isAssignableFrom( type ); }
Extract the contents of the given reader/stream as a string. The reader will be closed.
Params:
  • reader – The reader for the content
Returns:The content as string
/** * Extract the contents of the given reader/stream as a string. * The reader will be closed. * * @param reader The reader for the content * * @return The content as string */
public static String extractString(Reader reader) { return extractString( reader, BUFFER_SIZE ); }
Extract the contents of the given reader/stream as a string. The reader will be closed.
Params:
  • reader – The reader for the content
  • lengthHint – if the length is known in advance the implementation can be slightly more efficient
Returns:The content as string
/** * Extract the contents of the given reader/stream as a string. * The reader will be closed. * * @param reader The reader for the content * @param lengthHint if the length is known in advance the implementation can be slightly more efficient * * @return The content as string */
public static String extractString(Reader reader, int lengthHint) { // read the Reader contents into a buffer and return the complete string final int bufferSize = getSuggestedBufferSize( lengthHint ); final StringBuilder stringBuilder = new StringBuilder( bufferSize ); try { char[] buffer = new char[bufferSize]; while (true) { int amountRead = reader.read( buffer, 0, bufferSize ); if ( amountRead == -1 ) { break; } stringBuilder.append( buffer, 0, amountRead ); } } catch ( IOException ioe ) { throw new HibernateException( "IOException occurred reading text", ioe ); } finally { try { reader.close(); } catch (IOException e) { LOG.unableToCloseStream( e ); } } return stringBuilder.toString(); }
Extracts a portion of the contents of the given reader/stream as a string.
Params:
  • characterStream – The reader for the content
  • start – The start position/offset (0-based, per general stream/reader contracts).
  • length – The amount to extract
Returns:The content as string
/** * Extracts a portion of the contents of the given reader/stream as a string. * * @param characterStream The reader for the content * @param start The start position/offset (0-based, per general stream/reader contracts). * @param length The amount to extract * * @return The content as string */
private static String extractString(Reader characterStream, long start, int length) { if ( length == 0 ) { return ""; } StringBuilder stringBuilder = new StringBuilder( length ); try { long skipped = characterStream.skip( start ); if ( skipped != start ) { throw new HibernateException( "Unable to skip needed bytes" ); } final int bufferSize = getSuggestedBufferSize( length ); char[] buffer = new char[bufferSize]; int charsRead = 0; while ( true ) { int amountRead = characterStream.read( buffer, 0, bufferSize ); if ( amountRead == -1 ) { break; } stringBuilder.append( buffer, 0, amountRead ); if ( amountRead < bufferSize ) { // we have read up to the end of stream break; } charsRead += amountRead; if ( charsRead >= length ) { break; } } } catch ( IOException ioe ) { throw new HibernateException( "IOException occurred reading a binary value", ioe ); } return stringBuilder.toString(); }
Extract a portion of a reader, wrapping the portion in a new reader.
Params:
  • characterStream – The reader for the content
  • start – The start position/offset (0-based, per general stream/reader contracts).
  • length – The amount to extract
Returns:The content portion as a reader
/** * Extract a portion of a reader, wrapping the portion in a new reader. * * @param characterStream The reader for the content * @param start The start position/offset (0-based, per general stream/reader contracts). * @param length The amount to extract * * @return The content portion as a reader */
public static Object subStream(Reader characterStream, long start, int length) { return new StringReader( extractString( characterStream, start, length ) ); }
Extract by bytes from the given stream.
Params:
  • inputStream – The stream of bytes.
Returns:The contents as a byte[]
/** * Extract by bytes from the given stream. * * @param inputStream The stream of bytes. * * @return The contents as a {@code byte[]} */
public static byte[] extractBytes(InputStream inputStream) { if ( BinaryStream.class.isInstance( inputStream ) ) { return ( (BinaryStream ) inputStream ).getBytes(); } // read the stream contents into a buffer and return the complete byte[] ByteArrayOutputStream outputStream = new ByteArrayOutputStream(BUFFER_SIZE); try { byte[] buffer = new byte[BUFFER_SIZE]; while (true) { int amountRead = inputStream.read( buffer ); if ( amountRead == -1 ) { break; } outputStream.write( buffer, 0, amountRead ); } } catch ( IOException ioe ) { throw new HibernateException( "IOException occurred reading a binary value", ioe ); } finally { try { inputStream.close(); } catch ( IOException e ) { LOG.unableToCloseInputStream( e ); } try { outputStream.close(); } catch ( IOException e ) { LOG.unableToCloseOutputStream( e ); } } return outputStream.toByteArray(); }
Extract a portion of the bytes from the given stream.
Params:
  • inputStream – The stream of bytes.
  • start – The start position/offset (0-based, per general stream/reader contracts).
  • length – The amount to extract
Returns:The extracted bytes
/** * Extract a portion of the bytes from the given stream. * * @param inputStream The stream of bytes. * @param start The start position/offset (0-based, per general stream/reader contracts). * @param length The amount to extract * * @return The extracted bytes */
public static byte[] extractBytes(InputStream inputStream, long start, int length) { if ( BinaryStream.class.isInstance( inputStream ) && Integer.MAX_VALUE > start ) { byte[] data = ( (BinaryStream ) inputStream ).getBytes(); int size = Math.min( length, data.length ); byte[] result = new byte[size]; System.arraycopy( data, (int) start, result, 0, size ); return result; } ByteArrayOutputStream outputStream = new ByteArrayOutputStream( length ); try { long skipped = inputStream.skip( start ); if ( skipped != start ) { throw new HibernateException( "Unable to skip needed bytes" ); } byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = 0; while ( true ) { int amountRead = inputStream.read( buffer ); if ( amountRead == -1 ) { break; } outputStream.write( buffer, 0, amountRead ); if ( amountRead < buffer.length ) { // we have read up to the end of stream break; } bytesRead += amountRead; if ( bytesRead >= length ) { break; } } } catch ( IOException ioe ) { throw new HibernateException( "IOException occurred reading a binary value", ioe ); } return outputStream.toByteArray(); }
Extract a portion of the bytes from the given stream., wrapping them in a new stream.
Params:
  • inputStream – The stream of bytes.
  • start – The start position/offset (0-based, per general stream/reader contracts).
  • length – The amount to extract
Returns:The extracted bytes as a stream
/** * Extract a portion of the bytes from the given stream., wrapping them in a new stream. * * @param inputStream The stream of bytes. * @param start The start position/offset (0-based, per general stream/reader contracts). * @param length The amount to extract * * @return The extracted bytes as a stream */
public static InputStream subStream(InputStream inputStream, long start, int length) { return new BinaryStreamImpl( extractBytes( inputStream, start, length ) ); }
Extract the contents of the given Clob as a string.
Params:
  • value – The clob to to be extracted from
Returns:The content as string
/** * Extract the contents of the given Clob as a string. * * @param value The clob to to be extracted from * * @return The content as string */
public static String extractString(final Clob value) { try { final Reader characterStream = value.getCharacterStream(); final long length = determineLengthForBufferSizing( value ); return length > Integer.MAX_VALUE ? extractString( characterStream, Integer.MAX_VALUE ) : extractString( characterStream, (int) length ); } catch ( SQLException e ) { throw new HibernateException( "Unable to access lob stream", e ); } }
Determine a buffer size for reading the underlying character stream.
Params:
  • value – The Clob value
Throws:
Returns:The appropriate buffer size (Clob.length() by default.
/** * Determine a buffer size for reading the underlying character stream. * * @param value The Clob value * * @return The appropriate buffer size ({@link java.sql.Clob#length()} by default. * * @throws SQLException */
private static long determineLengthForBufferSizing(Clob value) throws SQLException { try { return value.length(); } catch ( SQLFeatureNotSupportedException e ) { return BUFFER_SIZE; } }
Make sure we allocate a buffer sized not bigger than 2048, not higher than what is actually needed, and at least one.
Params:
  • lengthHint – the expected size of the full value
Returns:the buffer size
/** * Make sure we allocate a buffer sized not bigger than 2048, * not higher than what is actually needed, and at least one. * * @param lengthHint the expected size of the full value * @return the buffer size */
private static int getSuggestedBufferSize(final int lengthHint) { return Math.max( 1, Math.min( lengthHint , BUFFER_SIZE ) ); } }