You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by ma...@apache.org on 2004/01/24 01:08:58 UTC

cvs commit: db-ojb/src/java/org/apache/ojb/broker/platforms PlatformOracleImpl.java PlatformDefaultImpl.java Oracle9iLobHandler.java BlobWrapper.java PlatformOracle9iImpl.java Platform.java ClobWrapper.java

mattbaird    2004/01/23 16:08:58

  Modified:    src/java/org/apache/ojb/broker/util JdbcTypesHelper.java
                        ClassHelper.java
               src/java/org/apache/ojb/broker/platforms
                        PlatformOracleImpl.java PlatformDefaultImpl.java
                        Oracle9iLobHandler.java BlobWrapper.java
                        PlatformOracle9iImpl.java Platform.java
                        ClobWrapper.java
  Log:
  Oracle BLOB/CLOB  lovin'
  thin driver in 9i works with > 4k blobs and clobs. thanks to all who helped.
  
  Revision  Changes    Path
  1.5       +32 -4     db-ojb/src/java/org/apache/ojb/broker/util/JdbcTypesHelper.java
  
  Index: JdbcTypesHelper.java
  ===================================================================
  RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/util/JdbcTypesHelper.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- JdbcTypesHelper.java	19 Dec 2003 16:23:52 -0000	1.4
  +++ JdbcTypesHelper.java	24 Jan 2004 00:08:57 -0000	1.5
  @@ -997,6 +997,34 @@
   
       public static final class T_Clob extends BaseType
       {
  +        protected static final int BUFSZ = 32768;
  +        /**
  +         * Convert CLOB to String. Safe for very large objects.
  +         * @param aClob clob with character data
  +         * @return a string containing the clob data
  +         * @throws SQLException if conversion fails or the clob cannot be read
  +         */
  +        protected static String safeClobToString(Clob aClob) throws SQLException {
  +            long length = aClob.length();
  +            if (length == 0) {
  +                return new String();
  +            }
  +            StringBuffer sb = new StringBuffer();
  +            char[] buf = new char[BUFSZ];
  +            java.io.Reader stream = aClob.getCharacterStream();
  +            try {
  +                int numRead;
  +                while ((numRead = stream.read(buf)) != -1)
  +                {
  +                    sb.append(buf, 0, numRead);
  +                }
  +                stream.close();
  +            } catch (java.io.IOException e) {
  +                throw new SQLException(e.getLocalizedMessage());
  +            }
  +            return sb.toString();
  +        }
  +
           public Object sequenceKeyConversion(Long identifier) throws SequenceManagerException
           {
               throw new SequenceManagerException("Not supported sequence key type 'CLOB'");
  @@ -1011,19 +1039,19 @@
           Object readValueFromStatement(CallableStatement stmt, int columnIndex) throws SQLException
           {
               Clob aClob = stmt.getClob(columnIndex);
  -            return (stmt.wasNull() ? null : aClob.getSubString(1L, (int) aClob.length()));
  +            return (stmt.wasNull() ? null : safeClobToString(aClob));
           }
   
           Object readValueFromResultSet(ResultSet rs, String columnName) throws SQLException
           {
               Clob aClob = rs.getClob(columnName);
  -            return (rs.wasNull() ? null : aClob.getSubString(1L, (int) aClob.length()));
  +            return (rs.wasNull() ? null : safeClobToString(aClob));
           }
   
           Object readValueFromResultSet(ResultSet rs, int columnIndex) throws SQLException
           {
               Clob aClob = rs.getClob(columnIndex);
  -            return (rs.wasNull() ? null : aClob.getSubString(1L, (int) aClob.length()));
  +            return (rs.wasNull() ? null : safeClobToString(aClob));
           }
   
           public int getType()
  
  
  
  1.4       +68 -1     db-ojb/src/java/org/apache/ojb/broker/util/ClassHelper.java
  
  Index: ClassHelper.java
  ===================================================================
  RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/util/ClassHelper.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ClassHelper.java	31 Aug 2003 00:46:30 -0000	1.3
  +++ ClassHelper.java	24 Jan 2004 00:08:58 -0000	1.4
  @@ -1,6 +1,8 @@
   package org.apache.ojb.broker.util;
   
   import java.lang.reflect.InvocationTargetException;
  +import java.lang.reflect.Method;
  +import java.lang.reflect.Field;
   
   /**
    *
  @@ -38,6 +40,40 @@
           return target.getConstructor(types).newInstance(args);
       }
   
  +    /**
  +     * Returns a method via reflection look-up of the specific signature.
  +     * @param clazz method's java class
  +     * @param methodName method name
  +     * @param params method signature
  +     * @return method invokable via <code>java.lang.reflect.Method#invoke</code>,
  +     * or <code>null</code> if no matching method can be found
  +     */
  +    public static Method getMethod(Class clazz, String methodName, Class[] params) {
  +        Method method;
  +        try {
  +            method = clazz.getMethod(methodName, params);
  +        } catch (Exception ignore) {
  +            method = null;
  +        }
  +        return method;
  +    }
  +
  +    /**
  +     * Returns a field via reflection look-up.
  +     * @param clazz fields's java class
  +     * @param fieldName field name
  +     * @return field retrievable via <code>java.lang.reflect.Field#getXXX</code>,
  +     * or <code>null</code> if no matching field can be found
  +     */
  +    public static Field getField(Class clazz, String fieldName) {
  +        Field field;
  +        try {
  +            field = clazz.getField(fieldName);
  +        } catch (Exception ignore) {
  +            field = null;
  +        }
  +        return field;
  +    }
   
   
       // *******************************************************************
  @@ -86,4 +122,35 @@
       {
           return newInstance(className, new Class[]{type}, new Object[]{arg});
       }
  +
  +    /**
  +     * Returns a method via reflection look-up of the specific signature.
  +     * @param object runtime object instance
  +     * @param methodName method name
  +     * @param params method signature
  +     * @return method invokable via <code>java.lang.reflect.Method#invoke</code>,
  +     * or <code>null</code> if no matching method can be found
  +     */
  +    public static Method getMethod(Object object, String methodName, Class[] params) {
  +        return getMethod(object.getClass(), methodName, params);
  +    }
  +
  +    /**
  +     * Returns a method via reflection look-up of the specific signature.
  +     * @param className class name
  +     * @param methodName method name
  +     * @param params method signature
  +     * @return method invokable via <code>java.lang.reflect.Method#invoke</code>,
  +     * or <code>null</code> if no matching method can be found
  +     */
  +    public static Method getMethod(String className, String methodName, Class[] params) {
  +        Method method = null;
  +        try {
  +            Class clazz = getClass(className, false);
  +            method = getMethod(clazz, methodName, params);
  +        } catch (Exception ignore) {
  +        }
  +        return method;
  +    }
  +
   }
  
  
  
  1.16      +54 -7     db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracleImpl.java
  
  Index: PlatformOracleImpl.java
  ===================================================================
  RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracleImpl.java,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- PlatformOracleImpl.java	29 Jul 2003 16:54:12 -0000	1.15
  +++ PlatformOracleImpl.java	24 Jan 2004 00:08:58 -0000	1.16
  @@ -64,6 +64,8 @@
   import java.lang.reflect.Field;
   import java.security.AccessController;
   import java.security.PrivilegedAction;
  +import java.sql.Connection;
  +import java.sql.DatabaseMetaData;
   import java.sql.PreparedStatement;
   import java.sql.SQLException;
   import java.sql.Statement;
  @@ -80,6 +82,11 @@
   
   public class PlatformOracleImpl extends PlatformDefaultImpl
   {
  +    protected static final String THIN_URL_PREFIX = "jdbc:oracle:thin";
  +    // Oracle:thin handles direct BLOB insert <= 4000 and update <= 2000
  +    protected static final int THIN_BLOB_MAX_SIZE = 2000;
  +    // Oracle:thin handles direct CLOB insert and update <= 4000
  +    protected static final int THIN_CLOB_MAX_SIZE = 4000;
       private Logger logger = LoggerFactory.getLogger(PlatformOracleImpl.class);
   
       /**
  @@ -114,9 +121,13 @@
                   (sqlType == Types.BLOB)) && (value instanceof byte[]))
           {
               byte buf[] = (byte[]) value;
  +            int length = buf.length;
  +            if (isUsingOracleThinDriver(ps.getConnection()) && length > THIN_BLOB_MAX_SIZE) {
  +                throw new SQLException("Oracle thin driver cannot update BLOB values with length>2000. (Consider using Oracle9i as OJB platform.)");
  +            }
               ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
               changePreparedStatementResultSetType(ps);
  -            ps.setBinaryStream(index, inputStream, buf.length);
  +            ps.setBinaryStream(index, inputStream, length);
           }
           else if (value instanceof Double)
           {
  @@ -132,20 +143,26 @@
   		{
   			 ps.setLong(index, ((Long) value).longValue());
   		}
  -        else if (sqlType == Types.CLOB)
  +        else if (sqlType == Types.CLOB &&
  +                (value instanceof String || value instanceof byte[]))
           {
               Reader reader = null;
               int length = 0;
               if (value instanceof String)
               {
  -                reader = new StringReader((String) value);
  -                length = (((String) value)).length();
  +                String stringValue = (String) value;
  +                length = stringValue.length();
  +                reader = new StringReader(stringValue);
               }
  -            else if (value instanceof byte[])
  +            else
               {
                   byte buf[] = (byte[]) value;
                   ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
                   reader = new InputStreamReader(inputStream);
  +                length = buf.length;
  +            }
  +            if (isUsingOracleThinDriver(ps.getConnection()) && length > THIN_CLOB_MAX_SIZE) {
  +                throw new SQLException("Oracle thin driver cannot insert CLOB values with length>4000. (Consider using Oracle9i as OJB platform.)");
               }
               ps.setCharacterStream(index, reader, length);
           }
  @@ -178,7 +195,7 @@
           }
           catch (Exception e)
           {
  -            this.logger.info("Not using classes12.zip.");
  +            logger.info("Not using classes12.zip.");
           }
       }
   
  @@ -204,4 +221,34 @@
       {
           return "drop sequence " + sequenceName;
       }
  +
  +    /**
  +     * Checks if the supplied connection is using the Oracle thin driver.
  +     * @param conn database connection for which to check JDBC-driver
  +     * @return <code>true</code> if the connection is using Oracle thin driver,
  +     * <code>false</code> otherwise.
  +     */
  +    protected static boolean isUsingOracleThinDriver(Connection conn)
  +    {
  +        if (conn == null)
  +        {
  +            return false;
  +        }
  +        final DatabaseMetaData dbMetaData;
  +        final String dbUrl;
  +        try
  +        {
  +            dbMetaData = conn.getMetaData();
  +            dbUrl = dbMetaData.getURL();
  +            if (dbUrl != null && dbUrl.startsWith(THIN_URL_PREFIX))
  +            {
  +                return true;
  +            }
  +        }
  +        catch (Exception e)
  +        {
  +        }
  +        return false;
  +    }
  +
   }
  
  
  
  1.22      +1 -7      db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformDefaultImpl.java
  
  Index: PlatformDefaultImpl.java
  ===================================================================
  RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformDefaultImpl.java,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- PlatformDefaultImpl.java	16 Nov 2003 18:21:48 -0000	1.21
  +++ PlatformDefaultImpl.java	24 Jan 2004 00:08:58 -0000	1.22
  @@ -333,12 +333,6 @@
           throw new UnsupportedOperationException("This feature is not supported by this implementation");
       }
   
  -	public Object getClob(ResultSet rs, int jdbcType, String columnId) throws SQLException
  -	{
  -		java.sql.Clob aClob = rs.getClob(columnId);
  -		return (rs.wasNull() ? null : aClob.getSubString(1L, (int) aClob.length()));
  -	}
  -    
       /* (non-Javadoc)
        * @see org.apache.ojb.broker.platforms.Platform#addPagingSql(java.lang.StringBuffer)
        */
  
  
  
  1.2       +298 -178  db-ojb/src/java/org/apache/ojb/broker/platforms/Oracle9iLobHandler.java
  
  Index: Oracle9iLobHandler.java
  ===================================================================
  RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/Oracle9iLobHandler.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Oracle9iLobHandler.java	29 Jul 2003 16:54:12 -0000	1.1
  +++ Oracle9iLobHandler.java	24 Jan 2004 00:08:58 -0000	1.2
  @@ -54,75 +54,82 @@
    * <http://www.apache.org/>.
    */
   
  +import org.apache.ojb.broker.util.logging.Logger;
  +import org.apache.ojb.broker.util.logging.LoggerFactory;
  +
   import java.io.*;
   import java.sql.Connection;
  +import java.lang.reflect.InvocationTargetException;
   
   /**
  - * handles the Oracle LOB problems for 9i
  + * Handles the Oracle LOB problems for 9i.
    *
    * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
  + * @author <a href="mailto:erik@cj.com">Erik Forkalsrud</a>
  + * @author <a href="mailto:martin.kalen@curalia.se">Martin Kal&eacute;n</a>
  + * @version CVS $Id$
    */
  -
   public class Oracle9iLobHandler
   {
  -	Connection m_connection = null;
  -	// Temporary LOBs
  -	BlobWrapper m_tempBlob = null;
  +
  +    protected static Logger logger = LoggerFactory.getLogger(Oracle9iLobHandler.class);
   
   	private static ClobWrapper createTempCLOB(Connection connection, ClobWrapper clob)
   	{
  -		ClobWrapper tempClob = null;
  -		if (clob != null)
  -		{
  -			try
  -			{
  -				// Create a temporary CLOB with duration session
  -				tempClob = ClobWrapper.createTemporary(connection, true, ClobWrapper.DURATION_SESSION);
  -
  -				// Open the CLOB in readonly mode
  -				clob.open(ClobWrapper.MODE_READONLY);
  -
  -				// Open the temporary CLOB in readwrite mode to enable writing
  -				tempClob.open(ClobWrapper.MODE_READWRITE);
  +        if (clob == null) {
  +            return null;
  +        }
   
  -				// No of bytes read each trip to database
  -				int bytesread = 0;
  -
  -				// Get the input stream for reading from the CLOB
  -				Reader clobReader = clob.getCharacterStream();
  -
  -				// Get the output stream for writing into the CLOB
  -				Writer tempClobWriter = tempClob.getCharacterOutputStream();
  -
  -				// Create a buffer to read data
  -				// getBufferSize() returns the optimal buffer size
  -				char[] charbuffer = new char[clob.getBufferSize()];
  -
  -				// Read from the CLOB and write into the temporary CLOB
  -				while ((bytesread = clobReader.read(charbuffer)) != -1)
  -					tempClobWriter.write(charbuffer, 0, bytesread);
  -
  -				// Flush and close the streams
  -				tempClobWriter.flush();
  -				tempClobWriter.close();
  -				clobReader.close();
  -
  -				// Close the CLOBs
  -				clob.close();
  -				tempClob.close();
  -			}
  -			catch (Exception ex)
  -			{
  -				// Since an error has been caught, free the temporary LOBs
  -				freeTempLOB(tempClob, null);
  -			}
  -		}
  +		ClobWrapper tempClob = null;
  +        try
  +        {
  +            // Create a temporary CLOB with duration session
  +            tempClob = ClobWrapper.createTemporary(connection, true, ClobWrapper.getDurationSessionValue());
  +
  +            // Open the CLOB in readonly mode
  +            clob.open(ClobWrapper.getModeReadOnlyValue());
  +
  +            // Open the temporary CLOB in readwrite mode to enable writing
  +            tempClob.open(ClobWrapper.getModeReadWriteValue());
  +
  +            // No of bytes read each trip to database
  +            int bytesread;
  +
  +            // Get the input stream for reading from the CLOB
  +            Reader clobReader = clob.getCharacterStream();
  +
  +            // Get the output stream for writing into the CLOB
  +            Writer tempClobWriter = tempClob.getCharacterOutputStream();
  +
  +            // Create a buffer to read data
  +            // getBufferSize() returns the optimal buffer size
  +            char[] charbuffer = new char[clob.getBufferSize()];
  +
  +            // Read from the CLOB and write into the temporary CLOB
  +            while ((bytesread = clobReader.read(charbuffer)) != -1)
  +            {
  +                tempClobWriter.write(charbuffer, 0, bytesread);
  +            }
  +
  +            // Flush and close the streams
  +            tempClobWriter.flush();
  +            tempClobWriter.close();
  +            clobReader.close();
  +
  +            // Close the CLOBs
  +            clob.close();
  +            tempClob.close();
  +        }
  +        catch (Exception e)
  +        {
  +            logger.error("Error during temporary CLOB write", e);
  +            freeTempLOB(tempClob, null);
  +        }
   		return tempClob;
   	}
   
   	public static String convertCLOBtoString(Connection connection, Object nativeclob)
   	{
  -		String retval = null;
   		ClobWrapper temp = new ClobWrapper();
   		temp.setClob(nativeclob);
   		/**
  @@ -130,139 +137,252 @@
   		 */
   		ClobWrapper clob = createTempCLOB(connection, temp);
   
  -		if (clob != null)
  -		{
  -			// Buffer to hold the CLOB data
  -			StringBuffer clobdata = new StringBuffer();
  -			// No of bytes read each trip to database
  -			int bytesread = 0;
  -			try
  -			{
  -				// Open the CLOB in readonly mode
  -				clob.open(ClobWrapper.MODE_READONLY);
  -				// Open the stream to read data
  -				Reader clobReader = clob.getCharacterStream();
  -				//  Buffer size is fixed using the getBufferSize() method which returns
  -				//  the optimal buffer size to read data from the LOB
  -				char[] charbuffer = new char[clob.getBufferSize()];
  -				// Keep reading from the CLOB and append it to the stringbuffer till
  -				// there is no more to read
  -				while ((bytesread = clobReader.read(charbuffer)) != -1)
  -				{
  -					clobdata.append(charbuffer, 0, bytesread);
  -				}
  -				// Close the input stream
  -				clobReader.close();
  -				// Close the CLOB
  -				clob.close();
  -				retval = clobdata.toString();
  -				clobdata = null;
  -			}
  -			catch (Exception ex)
  -			{
  -				// report
  -			}
  -		}
  +		if (clob == null) {
  +            return null;
  +        }
  +
  +        String retval = null;
  +        // Buffer to hold the CLOB data
  +        StringBuffer clobdata = new StringBuffer();
  +        // No of bytes read each trip to database
  +        int bytesread = 0;
  +        try
  +        {
  +            // Open the CLOB in readonly mode
  +            clob.open(ClobWrapper.getModeReadOnlyValue());
  +            // Open the stream to read data
  +            Reader clobReader = clob.getCharacterStream();
  +            //  Buffer size is fixed using the getBufferSize() method which returns
  +            //  the optimal buffer size to read data from the LOB
  +            char[] charbuffer = new char[clob.getBufferSize()];
  +            // Keep reading from the CLOB and append it to the stringbuffer till
  +            // there is no more to read
  +            while ((bytesread = clobReader.read(charbuffer)) != -1)
  +            {
  +                clobdata.append(charbuffer, 0, bytesread);
  +            }
  +            // Close the input stream
  +            clobReader.close();
  +            // Close the CLOB
  +            clob.close();
  +            retval = clobdata.toString();
  +            clobdata = null;
  +        }
  +        catch (Exception e)
  +        {
  +            logger.error("Error during CLOB read", e);
  +            freeTempLOB(clob, null);
  +        }
   		return retval;
   	}
   
   	public static Object createCLOBFromString(Connection conn, String clobData)
   	{
  -		ClobWrapper clob = new ClobWrapper();
  -		if (clobData != null)
  -		{
  -			try
  -			{
  -				clob = ClobWrapper.createTemporary(conn, true, ClobWrapper.DURATION_SESSION);
  -
  -				// Open the temporary CLOB in readwrite mode to enable writing
  -				clob.open(ClobWrapper.MODE_READWRITE);
  -
  -				// Clear the previous contents of the CLOB
  -				clob.trim(0);
  -
  -				// Get the output stream to write
  -				Writer tempClobWriter = clob.getCharacterOutputStream();
  -
  -				// Write the data into the temporary CLOB
  -				tempClobWriter.write(clobData);
  -
  -				// Flush and close the stream
  -				tempClobWriter.flush();
  -				tempClobWriter.close();
  -
  -				// Close the temporary CLOB
  -				clob.close();
  -			}
  -			catch (Exception ex)
  -			{
  -				// Since an error has been caught, free the temporary LOBs
  -				freeTempLOB(clob, null);
  -			}
  -		}
  -		return clob.getClob();
  +        if (clobData == null) {
  +            return null;
  +        }
  +        ClobWrapper clob = null;
  +        try
  +        {
  +            clob = ClobWrapper.createTemporary(conn, true, ClobWrapper.getDurationSessionValue());
  +            if (clob != null) {
  +                // Open the temporary CLOB in readwrite mode to enable writing
  +                clob.open(ClobWrapper.getModeReadWriteValue());
  +
  +                // Clear the previous contents of the CLOB
  +                clob.trim(0);
  +
  +                // Get the output stream to write
  +                Writer tempClobWriter = clob.getCharacterOutputStream();
  +
  +                if (tempClobWriter != null) {
  +                    // Write the data into the temporary CLOB
  +                    tempClobWriter.write(clobData);
  +
  +                    // Flush and close the stream
  +                    tempClobWriter.flush();
  +                    tempClobWriter.close();
  +                }
  +
  +                // Close the temporary CLOB
  +                clob.close();
  +            }
  +        }
  +        catch (InvocationTargetException ite) {
  +            Throwable t = ite.getTargetException();
  +            freeTempLOB(clob, null);
  +            if (t instanceof java.lang.UnsatisfiedLinkError) {
  +                logger.error("Oracle JDBC-driver version does not match installed OCI-driver");
  +            } else {
  +                logger.error("Error during temporary CLOB write", t);
  +            }
  +        }
  +        catch (Exception e)
  +        {
  +            logger.error("Error during temporary CLOB write", e);
  +            freeTempLOB(clob, null);
  +        }
  +		return clob == null ? null : clob.getClob();
   	}
   
  -	/**
  -	 *   Updates the content of the temporary BLOB with the new data in the file.
  -	 **/
  -	void createBLOBFromFile(File file)
  -	{
  -		if (file != null)
  -		{
  -			try
  -			{
  -				// If temporary BLOB has not yet been created, create new
  -				if (m_tempBlob == null)
  -				{
  -					BlobWrapper temp = new BlobWrapper();
  -					m_tempBlob = temp.createTemporary(m_connection, true,
  -													  BlobWrapper.DURATION_SESSION);
  -				}
  -
  -				// Open the temporary BLOB in readwrite mode to enable writing
  -				m_tempBlob.open(BlobWrapper.MODE_READWRITE);
  -
  -				// Clear the contents of the temporary BLOB
  -				m_tempBlob.trim(0);
  -
  -				// Get the input stream to read from the file
  -				FileInputStream fileIStream = new FileInputStream(file);
  -
  -				// Get the output stream to write into the temporary BLOB
  -				OutputStream tempBlobOStream = m_tempBlob.getBinaryOutputStream();
  -
  -				// Get the optimal buffer size to read bytes
  -				byte[] buffer = new byte[m_tempBlob.getBufferSize()];
  -
  -				// No of bytes read in each trip to database
  -				int bytesread = 0;
  -
  -				// Read from the file and write to the temporary BLOB
  -				while ((bytesread = fileIStream.read(buffer)) != -1)
  -					tempBlobOStream.write(buffer, 0, bytesread);
  -
  -				// Flush and close the streams
  -				tempBlobOStream.flush();
  -				fileIStream.close();
  -				tempBlobOStream.close();
  -
  -				// Close the temporary BLOB
  -				m_tempBlob.close();
  -			}
  -			catch (Exception ex)
  -			{
  -				// Since an error has been caught, free the temporary LOBs
  -				freeTempLOB(null, m_tempBlob);
  -			}
  -		}
  -	}
  +    private static BlobWrapper createTempBLOB(Connection connection, BlobWrapper blob)
  +    {
  +        if (blob == null) {
  +            return null;
  +        }
  +
  +        BlobWrapper tempBlob = null;
  +        try
  +        {
  +            // Create a temporary BLOB with duration session
  +            tempBlob = BlobWrapper.createTemporary(connection, true, BlobWrapper.getDurationSessionValue());
  +
  +            // Open the CLOB in readonly mode
  +            blob.open(BlobWrapper.getModeReadOnlyValue());
  +
  +            // Open the temporary CLOB in readwrite mode to enable writing
  +            tempBlob.open(BlobWrapper.getModeReadWriteValue());
  +
  +            // No of bytes read each trip to database
  +            int bytesread;
  +
  +            // Get the input stream for reading from the BLOB
  +            InputStream blobInputStream = blob.getBinaryStream();
  +
  +            // Get the output stream for writing into the BLOB
  +            OutputStream tempBlobOutputStream = tempBlob.getBinaryOutputStream();
  +
  +            // Create a buffer to read data
  +            // getBufferSize() returns the optimal buffer size
  +            byte[] bytebuffer = new byte[blob.getBufferSize()];
  +
  +            // Read from the BLOB and write into the temporary BLOB
  +            while ((bytesread = blobInputStream.read(bytebuffer)) != -1)
  +            {
  +                tempBlobOutputStream.write(bytebuffer, 0, bytesread);
  +            }
  +
  +            // Flush and close the streams
  +            tempBlobOutputStream.flush();
  +            tempBlobOutputStream.close();
  +            blobInputStream.close();
  +
  +            // Close the BLOBs
  +            blob.close();
  +            tempBlob.close();
  +        }
  +        catch (Exception e)
  +        {
  +            logger.error("Error during temporary BLOB write", e);
  +            freeTempLOB(null, tempBlob);
  +        }
  +        return tempBlob;
  +    }
  +
  +    public static byte[] convertBLOBtoByteArray(Connection connection, Object nativeblob)
  +    {
  +        BlobWrapper temp = new BlobWrapper();
  +        temp.setBlob(nativeblob);
  +        /**
  +         * first, convert the blob to another blob. Thanks Oracle, you rule.
  +         */
  +        BlobWrapper blob = createTempBLOB(connection, temp);
  +        if (blob == null) {
  +            return null;
  +        }
  +
  +        byte[] retval = null;
  +        // Buffer to hold the BLOB data
  +        ByteArrayOutputStream blobdata = new ByteArrayOutputStream();
  +        // No of bytes read each trip to database
  +        int bytesread = 0;
  +        try
  +        {
  +            // Open the BLOB in readonly mode
  +            blob.open(BlobWrapper.getModeReadOnlyValue());
  +            // Open the stream to read data
  +            InputStream blobInputStream = blob.getBinaryStream();
  +            //  Buffer size is fixed using the getBufferSize() method which returns
  +            //  the optimal buffer size to read data from the LOB
  +            byte[] bytebuffer = new byte[blob.getBufferSize()];
  +            // Keep reading from the BLOB and append it to the bytebuffer till
  +            // there is no more to read
  +            while ((bytesread = blobInputStream.read(bytebuffer)) != -1)
  +            {
  +                blobdata.write(bytebuffer, 0, bytesread);
  +            }
  +            // Close the input and output streams stream
  +            blobInputStream.close();
  +            blobdata.flush();
  +            blobdata.close();
  +
  +            // Close the BLOB
  +            blob.close();
  +            retval = blobdata.toByteArray();
  +            blobdata = null;
  +        }
  +        catch (Exception e)
  +        {
  +            logger.error("Error during BLOB read", e);
  +            freeTempLOB(null, blob);
  +        }
  +        return retval;
  +    }
  +
  +    public static Object createBLOBFromByteArray(Connection conn, byte[] blobData)
  +    {
  +        if (blobData == null) {
  +            return null;
  +        }
  +
  +        BlobWrapper blob = null;
  +        try
  +        {
  +            blob = BlobWrapper.createTemporary(conn, true, BlobWrapper.getDurationSessionValue());
  +
  +            // Open the temporary BLOB in readwrite mode to enable writing
  +            blob.open(BlobWrapper.getModeReadWriteValue());
  +
  +            // Clear the previous contents of the BLOB
  +            blob.trim(0);
  +
  +            // Get the output stream to write
  +            OutputStream tempBlobOutputStream = blob.getBinaryOutputStream();
  +
  +            // Write the data into the temporary BLOB
  +            tempBlobOutputStream.write(blobData);
  +
  +            // Flush and close the stream
  +            tempBlobOutputStream.flush();
  +            tempBlobOutputStream.close();
  +
  +            // Close the temporary BLOB
  +            blob.close();
  +        }
  +        catch (InvocationTargetException ite) {
  +            Throwable t = ite.getTargetException();
  +            freeTempLOB(null, blob);
  +            if (t instanceof java.lang.UnsatisfiedLinkError) {
  +                logger.error("Oracle JDBC-driver version does not match installed OCI-driver");
  +            } else {
  +                logger.error("Error during temporary BLOB write", t);
  +            }
  +        }
  +        catch (Exception e)
  +        {
  +            logger.error("Error during temporary BLOB write", e);
  +            freeTempLOB(null, blob);
  +        }
  +        return blob == null ? null : blob.getBlob();
  +    }
   
   	/**
  -	 *   Frees the temporary LOBs when an exception is raised in the application
  -	 *   or when the LOBs are no longer needed. If the LOBs are not freed , the
  -	 *   space used by these LOBs are not reclaimed.
  -	 **/
  +	 * Frees the temporary LOBs when an exception is raised in the application
  +	 * or when the LOBs are no longer needed. If the LOBs are not freed, the
  +	 * space used by these LOBs are not reclaimed.
  +     * @param clob CLOB-wrapper to free or null
  +     * @param blob BLOB-wrapper to free or null
  +	 */
   	private static void freeTempLOB(ClobWrapper clob, BlobWrapper blob)
   	{
   		try
  @@ -277,7 +397,6 @@
   
   				// Free the memory used by this CLOB
   				clob.freeTemporary();
  -				clob = null;
   			}
   
   			if (blob != null)
  @@ -290,11 +409,12 @@
   
   				// Free the memory used by this BLOB
   				blob.freeTemporary();
  -				blob = null;
   			}
   		}
  -		catch (Exception ex)
  +		catch (Exception e)
   		{
  +            logger.error("Error during temporary LOB release", e);
   		}
   	}
  +
   }
  
  
  
  1.2       +237 -60   db-ojb/src/java/org/apache/ojb/broker/platforms/BlobWrapper.java
  
  Index: BlobWrapper.java
  ===================================================================
  RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/BlobWrapper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- BlobWrapper.java	29 Jul 2003 16:54:12 -0000	1.1
  +++ BlobWrapper.java	24 Jan 2004 00:08:58 -0000	1.2
  @@ -54,71 +54,248 @@
    * <http://www.apache.org/>.
    */
   
  -import java.sql.Connection;
  -import java.sql.SQLException;
  +import org.apache.commons.lang.BooleanUtils;
  +import org.apache.ojb.broker.util.ClassHelper;
  +
   import java.io.InputStream;
   import java.io.OutputStream;
  +import java.lang.reflect.Method;
  +import java.lang.reflect.Field;
  +import java.sql.Connection;
  +import java.sql.SQLException;
   
   /**
  - * Created by IntelliJ IDEA.
  - * User: matthew.baird
  - * Date: Jun 26, 2003
  - * Time: 3:58:47 PM
  - * To change this template use Options | File Templates.
  + * Wraps the Oracle BLOB type and makes it accessible via reflection
  + * without having to import the Oracle Classes.
  + * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
  + * @author <a href="mailto:erik@cj.com">Erik Forkalsrud</a>
  + * @author <a href="martin.kalen@curalia.se">Martin Kal&eacute;n</a>
  + * @version CVS $Id$
    */
   public class BlobWrapper
   {
  -	public static final int MAX_CHUNK_SIZE = 32768;
  -	public static final int DURATION_SESSION = 10;
  -	public static final int DURATION_CALL = 12;
  -	static final int OLD_WRONG_DURATION_SESSION = 1;
  -	static final int OLD_WRONG_DURATION_CALL = 2;
  -	public static final int MODE_READONLY = 0;
  -	public static final int MODE_READWRITE = 1;
  -
  -	private Object m_blob;
  -
  -	public BlobWrapper createTemporary(Connection conn, boolean b, int i)
  -	{
  -		return new BlobWrapper();
  -	}
  -
  -	public void open(int i) throws SQLException
  -	{
  -
  -	}
  -
  -	public int getBufferSize() throws SQLException
  -	{
  -		return 1;
  -	}
  -
  -	public void close() throws SQLException
  -	{
  -	}
  -
  -	public void trim(long l) throws SQLException
  -	{
  -
  -	}
  -
  -	public void freeTemporary() throws SQLException
  -	{
  -
  -	}
  -
  -	public InputStream getBinaryStream() throws SQLException
  -	{
  -		return null;
  -	}
  -
  -	public OutputStream getBinaryOutputStream() throws SQLException
  -	{
  -		return null;
  -	}
  -
  -	public boolean isOpen() throws SQLException
  -	{
  -		return true;
  -	}
  +	protected Object m_blob;
  +
  +    // Fields - values must be looked up via reflection not be compile-time Oracle-version dependent
  +    protected static Field durationSession;
  +    protected static Field durationCall;
  +    protected static Field modeReadOnly;
  +    protected static Field modeReadWrite;
  +
  +	// Methods
  +	protected static Method createTemporary;
  +    protected static Method freeTemporary;
  +	protected static Method open;
  +	protected static Method isOpen;
  +	protected static Method getBinaryStream;
  +	protected static Method getBinaryOutputStream;
  +	protected static Method getBufferSize;
  +	protected static Method close;
  +	protected static Method trim;
  +
  +    /**
  +     * Initialize all methods and fields via reflection.
  +     */
  +    static
  +    {
  +        try
  +        {
  +            Class blobClass = ClassHelper.getClass("oracle.sql.BLOB", false);
  +            createTemporary = blobClass.getMethod("createTemporary", new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE});
  +            freeTemporary = blobClass.getMethod("freeTemporary", null);
  +            open = blobClass.getMethod("open", new Class[]{Integer.TYPE});
  +            isOpen = blobClass.getMethod("isOpen", null);
  +            getBinaryStream = blobClass.getMethod("getBinaryStream", null);
  +            getBinaryOutputStream = blobClass.getMethod("getBinaryOutputStream", null);
  +            getBufferSize = blobClass.getMethod("getBufferSize", null);
  +            close = blobClass.getMethod("close", null);
  +            trim = blobClass.getMethod("trim", new Class[]{Long.TYPE});
  +
  +            durationSession = ClassHelper.getField(blobClass, "DURATION_SESSION");
  +            durationCall = ClassHelper.getField(blobClass, "DURATION_CALL");
  +            modeReadOnly = ClassHelper.getField(blobClass, "MODE_READONLY");
  +            modeReadWrite = ClassHelper.getField(blobClass, "MODE_READWRITE");
  +        }
  +        catch (Exception ingore)
  +        {
  +        }
  +    }
  +
  +    public Object getBlob()
  +    {
  +        return m_blob;
  +    }
  +
  +    public void setBlob(Object blob)
  +    {
  +        m_blob = blob;
  +    }
  +
  +    protected static int staticIntFieldValue(Field field) {
  +        int value = 0;
  +        try {
  +            value = field.getInt(null);
  +        } catch (Exception ignore) {
  +            value = -1;
  +        }
  +        return value;
  +    }
  +
  +    public static int getDurationSessionValue() {
  +        return staticIntFieldValue(durationSession);
  +    }
  +
  +    public static int getDurationCallValue() {
  +        return staticIntFieldValue(durationCall);
  +    }
  +
  +    public static int getModeReadOnlyValue() {
  +        return staticIntFieldValue(modeReadOnly);
  +    }
  +
  +    public static int getModeReadWriteValue() {
  +        return staticIntFieldValue(modeReadWrite);
  +    }
  +
  +    public static BlobWrapper createTemporary(Connection conn, boolean b, int i) throws Exception
  +    {
  +        BlobWrapper retval = new BlobWrapper();
  +        // Passing null to invoke static method
  +        retval.setBlob(createTemporary.invoke(null, new Object[]{conn, BooleanUtils.toBooleanObject(b), new Integer(i)}));
  +        return retval;
  +    }
  +
  +    public void open(int i) throws SQLException
  +    {
  +        if (m_blob == null) {
  +            return;
  +        }
  +        try
  +        {
  +            open.invoke(m_blob, new Object[]{new Integer(i)});
  +        }
  +        catch (Throwable e)
  +        {
  +            throw new SQLException(e.getMessage());
  +        }
  +    }
  +
  +    public boolean isOpen() throws SQLException
  +    {
  +        if (m_blob == null) {
  +            return false;
  +        }
  +		boolean open = false;
  +		try
  +		{
  +			Boolean retval = (Boolean) isOpen.invoke(m_blob, null);
  +            if (retval != null) {
  +                open = retval.booleanValue();
  +            }
  +		}
  +		catch (Throwable e)
  +		{
  +			throw new SQLException(e.getMessage());
  +		}
  +		return open;
  +    }
  +
  +    public InputStream getBinaryStream() throws SQLException
  +    {
  +        if (m_blob == null) {
  +            return null;
  +        }
  +        InputStream retval = null;
  +        try
  +        {
  +            retval = (InputStream) getBinaryStream.invoke(m_blob, null);
  +        }
  +        catch (Throwable e)
  +        {
  +            throw new SQLException(e.getMessage());
  +        }
  +        return retval;
  +    }
  +
  +    public OutputStream getBinaryOutputStream() throws SQLException
  +    {
  +        if (m_blob == null) {
  +            return null;
  +        }
  +        OutputStream retval = null;
  +        try
  +        {
  +            retval = (OutputStream) getBinaryOutputStream.invoke(m_blob, null);
  +        }
  +        catch (Throwable e)
  +        {
  +            throw new SQLException(e.getMessage());
  +        }
  +        return retval;
  +    }
  +
  +    public int getBufferSize() throws SQLException
  +    {
  +        if (m_blob == null) {
  +            return 0;
  +        }
  +        Integer retval = null;
  +        try
  +        {
  +            retval = (Integer) getBufferSize.invoke(m_blob, null);
  +        }
  +        catch (Throwable e)
  +        {
  +            throw new SQLException(e.getMessage());
  +        }
  +        return retval.intValue();
  +    }
  +
  +    public void close() throws SQLException
  +    {
  +        if (m_blob == null) {
  +            return;
  +        }
  +        try
  +        {
  +            close.invoke(m_blob, null);
  +        }
  +        catch (Throwable e)
  +        {
  +            throw new SQLException(e.getMessage());
  +        }
  +
  +    }
  +
  +    public void trim(long l) throws SQLException
  +    {
  +        if (m_blob == null) {
  +            return;
  +        }
  +        try
  +        {
  +            trim.invoke(m_blob, new Object[]{new Long(l)});
  +        }
  +        catch (Throwable e)
  +        {
  +            throw new SQLException(e.getMessage());
  +        }
  +
  +    }
  +
  +    public void freeTemporary() throws SQLException
  +    {
  +        if (m_blob == null) {
  +            return;
  +        }
  +        try
  +        {
  +            freeTemporary.invoke(m_blob, null);
  +        }
  +        catch (Throwable e)
  +        {
  +            throw new SQLException(e.getMessage());
  +        }
  +    }
  +
   }
  
  
  
  1.9       +283 -399  db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java
  
  Index: PlatformOracle9iImpl.java
  ===================================================================
  RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- PlatformOracle9iImpl.java	8 Dec 2003 10:18:16 -0000	1.8
  +++ PlatformOracle9iImpl.java	24 Jan 2004 00:08:58 -0000	1.9
  @@ -54,22 +54,25 @@
    * <http://www.apache.org/>.
    */
   
  +import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
  +import org.apache.ojb.broker.metadata.JdbcType;
  +import org.apache.ojb.broker.util.ClassHelper;
  +import org.apache.ojb.broker.util.JdbcTypesHelper;
  +
   import java.io.ByteArrayInputStream;
  -import java.lang.reflect.InvocationTargetException;
   import java.lang.reflect.Method;
   import java.sql.Connection;
   import java.sql.PreparedStatement;
  -import java.sql.ResultSet;
   import java.sql.SQLException;
  -import java.sql.Statement;
   import java.sql.Types;
  -
  -import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
  +import java.util.Collections;
  +import java.util.Map;
  +import java.util.WeakHashMap;
   
   /**
  - *  This class is a concrete implementation of <code>Platform</code>. Provides
  - *  an implementation that works around some issues with Oracle in general and
  - *  Oracle 9i's Thin driver in particular.
  + * This class is a concrete implementation of <code>Platform</code>. Provides
  + * an implementation that works around some issues with Oracle in general and
  + * Oracle 9i's Thin driver in particular.
    *
    * Optimization: Oracle Batching (not standard JDBC batching)
    * see http://technet.oracle.com/products/oracle9i/daily/jun07.html
  @@ -81,400 +84,281 @@
    * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html
    *
    * TODO: Optimization: use ROWNUM to minimize the effects of not having server side cursors
  - *
    * see http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
    *
  - * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
  + * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
  + * @author <a href="mailto:erik@cj.com">Erik Forkalsrud</a>
  + * @author <a href="mailto:martin.kalen@curalia.se">Martin Kal&eacute;n</a>
  + * @version CVS $Id$
  + * @see Platform
  + * @see PlatformDefaultImpl
  + * @see PlatformOracleImpl
    */
  -
   public class PlatformOracle9iImpl extends PlatformOracleImpl
   {
  -	private static final Object[] BATCH_SIZE = {new Integer(10)};
  -	private static final Class[] PARAM_TYPE_INTEGER = {Integer.TYPE};
  -	private static final Class[] PARAM_TYPE_BOOLEAN = {Boolean.TYPE};
  -	private static boolean STATEMENT_CACHING_SUPPORTED = true;
  -	private static boolean SET_EXECUTE_BATCH_METHOD_EXISTS = true;
  -	private static boolean SEND_BATCH_METHOD_EXISTS = true;
  -	private static Method SET_EXECUTE_BATCH = null;
  -	private static Method SEND_BATCH = null;
  -	private static Method SET_STATEMENT_CACHING_ENABLE = null;
  -	private static Method SET_IMPLICIT_CACHING_ENABLED = null;
  -	private static Method SET_CLOB = null;
  -	private static Method SET_BLOB = null;
  -	private static boolean SET_CLOB_AND_LOB_SUPPORTED = false;
  -	private static Method GET_CLOB = null;
  -	private static boolean GET_CLOB_AND_LOB_SUPPORTED = false;
  -
  -	private static boolean ROW_PREFETCH_SUPPORTED = true;
  -	private static Method SET_ROW_PREFETCH = null;
  -	private static final int STATEMENT_CACHE_SIZE = 100;
  -	private static final int ROW_PREFETCH_SIZE = 100;
  -
  -	// Session time zone has to be set while accessing Timestamp with
  -	// Time Zone and Timestamp with Local TimeZone datatypes.
  -	// OracleConnection has the APIs to set the session time zone.
  -	// Sets the session time zone to the time zone specified in the java
  -	// virtual machine.
  -	//((OracleConnection) m_connection).setSessionTimeZone(TimeZone.getDefault().getID());
  -
  -
  -	/**
  -	 * enable oracle statement caching
  -	 *
  -	 * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html
  -	 *
  -	 * @param jcd
  -	 * @param conn
  -	 * @throws PlatformException
  -	 */
  -	public void initializeJdbcConnection(JdbcConnectionDescriptor jcd, Connection conn) throws PlatformException
  -	{
  -		/**
  -		 * do all the generic initialization first
  -		 */
  -		super.initializeJdbcConnection(jcd, conn);
  -
  -		if (SET_STATEMENT_CACHING_ENABLE == null && SET_IMPLICIT_CACHING_ENABLED == null && STATEMENT_CACHING_SUPPORTED)
  -		{
  -			try
  -			{
  -				SET_STATEMENT_CACHING_ENABLE = conn.getClass().getMethod("setStatementCacheSize", PARAM_TYPE_INTEGER);
  -				SET_IMPLICIT_CACHING_ENABLED = conn.getClass().getMethod("setImplicitCachingEnabled", PARAM_TYPE_BOOLEAN);
  -			}
  -			catch (NoSuchMethodException e)
  -			{
  -				STATEMENT_CACHING_SUPPORTED = false;
  -			}
  -			catch (SecurityException e)
  -			{
  -				STATEMENT_CACHING_SUPPORTED = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -		}
  -		if (STATEMENT_CACHING_SUPPORTED)
  -		{
  -			try
  -			{
  -				SET_STATEMENT_CACHING_ENABLE.invoke(conn, new Object[]{new Integer(STATEMENT_CACHE_SIZE)}); // use a 100 item cache
  -				SET_IMPLICIT_CACHING_ENABLED.invoke(conn, new Object[]{Boolean.TRUE}); // use implicit caching
  -			}
  -			catch (IllegalAccessException e)
  -			{
  -				STATEMENT_CACHING_SUPPORTED = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (IllegalArgumentException e)
  -			{
  -				STATEMENT_CACHING_SUPPORTED = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (InvocationTargetException e)
  -			{
  -				STATEMENT_CACHING_SUPPORTED = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -		}
  -	}
  -
  -	/**
  -	 *
  -	 * doesn't seem to improve performance.
  -	 *
  -	 * support for oracle prefetching
  -	 *
  -	 * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html
  -	 *
  -	 * @param stmt
  -	 * @throws PlatformException
  -	 */
  -	public void XXXXafterStatementCreate(Statement stmt) throws PlatformException
  -	{
  -		super.afterStatementCreate(stmt);
  -		if (ROW_PREFETCH_SUPPORTED && SET_ROW_PREFETCH == null)
  -		{
  -			try
  -			{
  -				SET_ROW_PREFETCH = stmt.getClass().getMethod("setRowPrefetch", PARAM_TYPE_INTEGER);
  -			}
  -			catch (NoSuchMethodException e)
  -			{
  -				ROW_PREFETCH_SUPPORTED = false;
  -			}
  -			catch (SecurityException e)
  -			{
  -				ROW_PREFETCH_SUPPORTED = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -		}
  -		if (ROW_PREFETCH_SUPPORTED)
  -		{
  -			try
  -			{
  -				SET_ROW_PREFETCH.invoke(stmt, new Object[]{new Integer(ROW_PREFETCH_SIZE)}); // use a 100 item cache
  -			}
  -			catch (IllegalAccessException e)
  -			{
  -				STATEMENT_CACHING_SUPPORTED = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (IllegalArgumentException e)
  -			{
  -				STATEMENT_CACHING_SUPPORTED = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (InvocationTargetException e)
  -			{
  -				STATEMENT_CACHING_SUPPORTED = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -		}
  -	}
  -
  -	/**
  -	 * don't bind to the Oracle drivers and thus have to cast, rather do the
  -	 * reflection to find out if we can use the setExecuteBatch method with this
  -	 * particular driver, and degrade to default JDBC batching if necessary
  -	 * @param stmt
  -	 * @throws PlatformException
  -	 */
  -	public void beforeBatch(PreparedStatement stmt) throws PlatformException
  -	{
  -		// ((OraclePreparedStatement)ps).setExecuteBatch (3);
  -		if (SET_EXECUTE_BATCH_METHOD_EXISTS && SEND_BATCH_METHOD_EXISTS)
  -		{
  -			try
  -			{
  -				if (SET_EXECUTE_BATCH == null)
  -				{
  -					SET_EXECUTE_BATCH = stmt.getClass().getMethod("setExecuteBatch", PARAM_TYPE_INTEGER);
  -				}
  -				SET_EXECUTE_BATCH.invoke(stmt, BATCH_SIZE);
  -			}
  -			catch (IllegalAccessException e)
  -			{
  -				SET_EXECUTE_BATCH_METHOD_EXISTS = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (IllegalArgumentException e)
  -			{
  -				SET_EXECUTE_BATCH_METHOD_EXISTS = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (InvocationTargetException e)
  -			{
  -				SET_EXECUTE_BATCH_METHOD_EXISTS = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (NoSuchMethodException e)
  -			{
  -				SET_EXECUTE_BATCH_METHOD_EXISTS = false;
  -			}
  -		}
  -		if (!SET_EXECUTE_BATCH_METHOD_EXISTS)
  -		{
  -			super.beforeBatch(stmt);
  -		}
  -	}
  -
  -	/**
  -	 * in oracle batching we call executeUpdate instead of addBatch
  -	 * @param stmt
  -	 * @throws PlatformException
  -	 */
  -	public void addBatch(PreparedStatement stmt) throws PlatformException
  -	{
  -		// ps.executeUpdate();
  -		if (SET_EXECUTE_BATCH_METHOD_EXISTS && SEND_BATCH_METHOD_EXISTS)
  -		{
  -			try
  -			{
  -				stmt.executeUpdate();
  -			}
  -			catch (SQLException e)
  -			{
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -		}
  -		else
  -		{
  -			super.addBatch(stmt);
  -		}
  -	}
  -
  -	/**
  -	 * equivelent of calling ((OraclePreparedStatement)ps).sendBatch() but we do it reflectively so
  -	 * we don't have to bind to the Oracle classes
  -	 * JDBC sends the queued request
  -	 * @param stmt the statement you want to execute sendBatch on.
  -	 * @return an int array of the success statuses.
  -	 * @throws PlatformException
  -	 */
  -	public int[] executeBatch(PreparedStatement stmt) throws PlatformException
  -	{
  -		int[] retval = null;
  -		if (SEND_BATCH_METHOD_EXISTS)
  -		{
  -			try
  -			{
  -				if (SEND_BATCH == null)
  -				{
  -					SEND_BATCH = stmt.getClass().getMethod("sendBatch", null);
  -				}
  -				int numberOfItemsSubmitted = ((Integer) SEND_BATCH.invoke(stmt, null)).intValue();
  -				retval = new int[numberOfItemsSubmitted];
  -				for (int i = 0; i < numberOfItemsSubmitted; i++)
  -				{
  -					retval[i] = 1;
  -				}
  -			}
  -			catch (IllegalAccessException e)
  -			{
  -				SEND_BATCH_METHOD_EXISTS = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (IllegalArgumentException e)
  -			{
  -				SEND_BATCH_METHOD_EXISTS = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (InvocationTargetException e)
  -			{
  -				SEND_BATCH_METHOD_EXISTS = false;
  -				throw new PlatformException(e.getMessage(), e);
  -			}
  -			catch (NoSuchMethodException e)
  -			{
  -				SEND_BATCH_METHOD_EXISTS = false;
  -			}
  -		}
  -		if (!SEND_BATCH_METHOD_EXISTS)
  -		{
  -			retval = super.executeBatch(stmt);
  -		}
  -		return retval;
  -	}
  -
  -	public void setObjectForStatement(PreparedStatement ps, int index, Object value, int sqlType) throws SQLException
  -	{
  -		if (SET_CLOB == null && SET_CLOB_AND_LOB_SUPPORTED)
  -		{
  -			try
  -			{
  -				SET_CLOB = ps.getClass().getMethod("setCLOB", new Class[]{Integer.TYPE, Class.forName("oracle.sql.CLOB")});
  -				SET_BLOB = ps.getClass().getMethod("setBLOB", new Class[]{Integer.TYPE, Class.forName("oracle.sql.BLOB")});
  -				SET_CLOB_AND_LOB_SUPPORTED = true;
  -			}
  -			catch (Throwable t)
  -			{
  -				SET_CLOB_AND_LOB_SUPPORTED = false;
  -			}
  -		}
  -		if (((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY)) && (value instanceof byte[]))
  -		{
  -			byte buf[] = (byte[]) value;
  -			ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
  -			super.changePreparedStatementResultSetType(ps);
  -			ps.setBinaryStream(index, inputStream, buf.length);
  -		}
  -		else if (value instanceof Double)
  -		{
  -			// workaround for the bug in Oracle thin driver
  -			ps.setDouble(index, ((Double) value).doubleValue());
  -		}
  -		else if (sqlType == Types.BIGINT && value instanceof Integer)
  -		{
  -			// workaround: Oracle thin driver problem when expecting long
  -			ps.setLong(index, ((Integer) value).intValue());
  -		}
  -		else if (sqlType == Types.INTEGER && value instanceof Long)
  -		{
  -			ps.setLong(index, ((Long) value).longValue());
  -		}
  -		else if (sqlType == Types.CLOB && value instanceof String)
  -		{
  -			if (SET_CLOB_AND_LOB_SUPPORTED)
  -			{
  -				try
  -				{
  -					SET_CLOB.invoke(ps, new Object[]{new Integer(index), Oracle9iLobHandler.createCLOBFromString(ps.getConnection(), (String) value)});
  -				}
  -				catch (IllegalAccessException e)
  -				{
  -					throw new SQLException(e.getMessage());
  -				}
  -				catch (IllegalArgumentException e)
  -				{
  -					throw new SQLException(e.getMessage());
  -				}
  -				catch (InvocationTargetException e)
  -				{
  -					throw new SQLException(e.getMessage());
  -				}
  -			}
  -		}
  -		else if (sqlType == Types.BLOB)
  -		{
  -			if (SET_CLOB_AND_LOB_SUPPORTED)
  -			{
  -				try
  -				{
  -					SET_BLOB.invoke(ps, new Object[]{new Integer(index)});
  -				}
  -				catch (IllegalAccessException e)
  -				{
  -					throw new SQLException(e.getMessage());
  -				}
  -				catch (IllegalArgumentException e)
  -				{
  -					throw new SQLException(e.getMessage());
  -				}
  -				catch (InvocationTargetException e)
  -				{
  -					throw new SQLException(e.getMessage());
  -				}
  -			}
  -		}
  -		else
  -		{
  -			super.setObjectForStatement(ps, index, value, sqlType);
  -		}
  -	}
  -
  -	public Object getClob(ResultSet rs, int jdbcType, String columnId) throws SQLException
  -	{
  -		String retval = "";
  -		if (GET_CLOB == null && GET_CLOB_AND_LOB_SUPPORTED)
  -		{
  -			try
  -			{
  -				GET_CLOB = rs.getClass().getMethod("getCLOB", new Class[]{String.class});
  -				GET_CLOB_AND_LOB_SUPPORTED = true;
  -			}
  -			catch (Throwable t)
  -			{
  -				GET_CLOB_AND_LOB_SUPPORTED = false;
  -			}
  -		}
  -		if (GET_CLOB_AND_LOB_SUPPORTED)
  -		{
  -			try
  -			{
  -				Object obj = GET_CLOB.invoke(rs, new Object[]{columnId});
  -				retval = Oracle9iLobHandler.convertCLOBtoString(rs.getStatement().getConnection(), obj);
  -			}
  -			catch (IllegalAccessException e)
  -			{
  -				throw new SQLException(e.getMessage());
  -			}
  -			catch (IllegalArgumentException e)
  -			{
  -				throw new SQLException(e.getMessage());
  -			}
  -			catch (InvocationTargetException e)
  -			{
  -				throw new SQLException(e.getMessage());
  -			}
  -		}
  -		return retval;
  -	}
  +    protected static final int STATEMENT_CACHE_SIZE = 100;
  +    protected static final int ROW_PREFETCH_SIZE = 100;
  +
  +    // From Oracle9i JDBC Developer's Guide and Reference:
  +    // "Batch values between 5 and 30 tend to be the most effective."
  +    protected static final int STATEMENTS_PER_BATCH = 20;
  +    protected static Map m_batchStatementsInProgress = Collections.synchronizedMap(new WeakHashMap(STATEMENTS_PER_BATCH));
  +
  +    protected static final Class[] PARAM_TYPE_INTEGER = {Integer.TYPE};
  +    protected static final Class[] PARAM_TYPE_BOOLEAN = {Boolean.TYPE};
  +    protected static final Class[] PARAM_TYPE_STRING = {String.class};
  +
  +    protected static final Object[] PARAM_STATEMENT_CACHE_SIZE = new Object[]{new Integer(STATEMENT_CACHE_SIZE)};
  +    protected static final Object[] PARAM_ROW_PREFETCH_SIZE = new Object[]{new Integer(ROW_PREFETCH_SIZE)};
  +    protected static final Object[] PARAM_STATEMENT_BATCH_SIZE = new Object[]{new Integer(STATEMENTS_PER_BATCH)};
  +    protected static final Object[] PARAM_BOOLEAN_TRUE = new Object[]{Boolean.TRUE};
  +
  +    protected static final JdbcType BASE_CLOB = JdbcTypesHelper.getJdbcTypeByName("clob");
  +    protected static final JdbcType BASE_BLOB = JdbcTypesHelper.getJdbcTypeByName("blob");
  +
  +    /**
  +     * Enables Oracle statement caching if supported by the JDBC-driver.
  +     * See
  +     * {@link "http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html"}
  +     * @param jcd the OJB <code>JdbcConnectionDescriptor</code> (metadata) for the connection to be initialized
  +     * @param conn the <code>Connection</code>-object (physical) to be initialized
  +     * @see PlatformDefaultImpl#initializeJdbcConnection
  +     */
  +    public void initializeJdbcConnection(JdbcConnectionDescriptor jcd, Connection conn) throws PlatformException
  +    {
  +        // Do all the generic initialization in PlatformDefaultImpl first
  +        super.initializeJdbcConnection(jcd, conn);
  +
  +        // Check for OracleConnection-specific statement caching methods
  +        final Method methodSetStatementCacheSize;
  +        final Method methodSetImplicitCachingEnabled;
  +        methodSetStatementCacheSize = ClassHelper.getMethod(conn, "setStatementCacheSize", PARAM_TYPE_INTEGER);
  +        methodSetImplicitCachingEnabled = ClassHelper.getMethod(conn, "setImplicitCachingEnabled", PARAM_TYPE_BOOLEAN);
  +
  +        final boolean statementCachingSupported = methodSetStatementCacheSize != null && methodSetImplicitCachingEnabled != null;
  +        if (statementCachingSupported)
  +        {
  +            try
  +            {
  +                // Set number of cached statements and enable implicit caching
  +                methodSetStatementCacheSize.invoke(conn, PARAM_STATEMENT_CACHE_SIZE);
  +                methodSetImplicitCachingEnabled.invoke(conn, PARAM_BOOLEAN_TRUE);
  +            }
  +            catch (Exception e)
  +            {
  +                throw new PlatformException(e.getLocalizedMessage(), e);
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Enables Oracle row prefetching if supported.
  +     * See http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html.
  +     * This is RDBMS server-to-client prefetching and thus one layer below
  +     * the OJB-internal prefetching-to-cache introduced in version 1.0rc5.
  +     * @param stmt the statement just created
  +     * @throws PlatformException upon JDBC failure
  +     */
  +    public void afterStatementCreate(java.sql.Statement stmt) throws PlatformException
  +    {
  +        super.afterStatementCreate(stmt);
  +
  +        // Check for OracleStatement-specific row prefetching support
  +        final Method methodSetRowPrefetch;
  +        methodSetRowPrefetch = ClassHelper.getMethod(stmt, "setRowPrefetch", PARAM_TYPE_INTEGER);
  +
  +        final boolean rowPrefetchingSupported = methodSetRowPrefetch != null;
  +        if (rowPrefetchingSupported)
  +        {
  +            try
  +            {
  +                // Set number of prefetched rows
  +                methodSetRowPrefetch.invoke(stmt, PARAM_ROW_PREFETCH_SIZE);
  +            }
  +            catch (Exception e)
  +            {
  +                throw new PlatformException(e.getLocalizedMessage(), e);
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Try Oracle update batching and call setExecuteBatch or revert to
  +     * JDBC update batching. See 12-2 Update Batching in the Oracle9i
  +     * JDBC Developer's Guide and Reference.
  +     * @param stmt the prepared statement to be used for batching
  +     * @throws PlatformException upon JDBC failure
  +     */
  +    public void beforeBatch(PreparedStatement stmt) throws PlatformException
  +    {
  +        // Check for Oracle batching support
  +        final Method methodSetExecuteBatch;
  +        final Method methodSendBatch;
  +        methodSetExecuteBatch = ClassHelper.getMethod(stmt, "setExecuteBatch", PARAM_TYPE_INTEGER);
  +        methodSendBatch = ClassHelper.getMethod(stmt, "sendBatch", null);
  +
  +        final boolean statementBatchingSupported = methodSetExecuteBatch != null && methodSendBatch != null;
  +        if (statementBatchingSupported)
  +        {
  +            try
  +            {
  +                // Set number of statements per batch
  +                methodSetExecuteBatch.invoke(stmt, PARAM_STATEMENT_BATCH_SIZE);
  +                m_batchStatementsInProgress.put(stmt, methodSendBatch);
  +            }
  +            catch (Exception e)
  +            {
  +                throw new PlatformException(e.getLocalizedMessage(), e);
  +            }
  +        }
  +        else
  +        {
  +            super.beforeBatch(stmt);
  +        }
  +    }
  +
  +    /**
  +     * Try Oracle update batching and call executeUpdate or revert to
  +     * JDBC update batching.
  +     * @param stmt the statement beeing added to the batch
  +     * @throws PlatformException upon JDBC failure
  +     */
  +    public void addBatch(PreparedStatement stmt) throws PlatformException
  +    {
  +        // Check for Oracle batching support
  +        final boolean statementBatchingSupported = m_batchStatementsInProgress.containsKey(stmt);
  +        if (statementBatchingSupported)
  +        {
  +            try
  +            {
  +                stmt.executeUpdate();
  +            }
  +            catch (SQLException e)
  +            {
  +                throw new PlatformException(e.getLocalizedMessage(), e);
  +            }
  +        }
  +        else
  +        {
  +            super.addBatch(stmt);
  +        }
  +    }
  +
  +    /**
  +     * Try Oracle update batching and call sendBatch or revert to
  +     * JDBC update batching.
  +     * @param stmt the batched prepared statement about to be executed
  +     * @return always <code>null</code> if Oracle update batching is used,
  +     * since it is impossible to dissolve total row count into distinct
  +     * statement counts. If JDBC update batching is used, an int array is
  +     * returned containing number of updated rows for each batched statement.
  +     * @throws PlatformException upon JDBC failure
  +     */
  +    public int[] executeBatch(PreparedStatement stmt) throws PlatformException
  +    {
  +        // Check for Oracle batching support
  +        final Method methodSendBatch = (Method) m_batchStatementsInProgress.remove(stmt);
  +        final boolean statementBatchingSupported = methodSendBatch != null;
  +
  +        int[] retval = null;
  +        if (statementBatchingSupported)
  +        {
  +            try
  +            {
  +                // sendBatch() returns total row count as an Integer
  +                methodSendBatch.invoke(stmt, null);
  +            }
  +            catch (Exception e)
  +            {
  +                throw new PlatformException(e.getLocalizedMessage(), e);
  +            }
  +        }
  +        else
  +        {
  +            retval = super.executeBatch(stmt);
  +        }
  +        return retval;
  +    }
  +
  +    /** @see Platform#setObjectForStatement */
  +    public void setObjectForStatement(PreparedStatement ps, int index, Object value, int sqlType) throws SQLException
  +    {
  +        boolean blobHandlingSupported = false;
  +        boolean clobHandlingSupported = false;
  +        Method methodSetBlob = null;
  +        Method methodSetClob = null;
  +
  +        // Check for Oracle JDBC-driver LOB-support
  +        if (sqlType == Types.CLOB) {
  +            try {
  +                Class clobClass = ClassHelper.getClass("oracle.sql.CLOB", false);
  +                methodSetClob = ClassHelper.getMethod(ps, "setCLOB", new Class[]{Integer.TYPE, clobClass});
  +                clobHandlingSupported = methodSetClob != null;
  +            } catch (Exception ignore) {
  +            }
  +        }
  +        else if (sqlType == Types.BLOB) {
  +            try {
  +                Class blobClass = ClassHelper.getClass("oracle.sql.BLOB", false);
  +                methodSetBlob = ClassHelper.getMethod(ps, "setBLOB", new Class[]{Integer.TYPE, blobClass});
  +                blobHandlingSupported = methodSetBlob != null;
  +            } catch (Exception ignore) {
  +            }
  +        }
  +
  +        // Type-specific Oracle conversions
  +        if (((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY)) && (value instanceof byte[]))
  +        {
  +            byte buf[] = (byte[]) value;
  +            ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
  +            super.changePreparedStatementResultSetType(ps);
  +            ps.setBinaryStream(index, inputStream, buf.length);
  +        }
  +        else if (value instanceof Double)
  +        {
  +            // workaround for the bug in Oracle thin driver
  +            ps.setDouble(index, ((Double) value).doubleValue());
  +        }
  +        else if (sqlType == Types.BIGINT && value instanceof Integer)
  +        {
  +            // workaround: Oracle thin driver problem when expecting long
  +            ps.setLong(index, ((Integer) value).intValue());
  +        }
  +        else if (sqlType == Types.INTEGER && value instanceof Long)
  +        {
  +            ps.setLong(index, ((Long) value).longValue());
  +        }
  +        else if (sqlType == Types.CLOB && clobHandlingSupported && value instanceof String)
  +        {
  +            // TODO: If using Oracle update batching with the thin driver, throw exception on 4k limit
  +            try
  +            {
  +                Object clob = Oracle9iLobHandler.createCLOBFromString(ps.getConnection(), (String) value);
  +                methodSetClob.invoke(ps, new Object[]{new Integer(index), clob});
  +            }
  +            catch (Exception e)
  +            {
  +                throw new SQLException(e.getLocalizedMessage());
  +            }
  +        }
  +        else if (sqlType == Types.BLOB && blobHandlingSupported && value instanceof byte[])
  +        {
  +            // TODO: If using Oracle update batching with the thin driver, throw exception on 2k limit
  +            try
  +            {
  +                Object blob = Oracle9iLobHandler.createBLOBFromByteArray(ps.getConnection(), (byte[]) value);
  +                methodSetBlob.invoke(ps, new Object[]{new Integer(index), blob});
  +            }
  +            catch (Exception e)
  +            {
  +                throw new SQLException(e.getLocalizedMessage());
  +            }
  +        }
  +        else
  +        {
  +            // Fall-through to superclass
  +            super.setObjectForStatement(ps, index, value, sqlType);
  +        }
  +    }
  +
   }
  
  
  
  1.20      +1 -7      db-ojb/src/java/org/apache/ojb/broker/platforms/Platform.java
  
  Index: Platform.java
  ===================================================================
  RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/Platform.java,v
  retrieving revision 1.19
  retrieving revision 1.20
  diff -u -r1.19 -r1.20
  --- Platform.java	23 Nov 2003 10:29:23 -0000	1.19
  +++ Platform.java	24 Jan 2004 00:08:58 -0000	1.20
  @@ -214,11 +214,6 @@
        */
       public String getLastInsertIdentityQuery(String tableName);
   
  -	/**
  -	 * Oracle has funky clobs.
  -	 */
  -	public Object getClob(ResultSet rs, int jdbcType, String columnId) throws SQLException;
  -    
       /**
        * Answer true if LIMIT or equivalent is supported
        * <b> SQL-Paging is not yet supported </b>
  @@ -229,7 +224,6 @@
       /**
        * Add the LIMIT or equivalent to the SQL 
        * <b> SQL-Paging is not yet supported </b>
  -     * @return 
        */
       public void addPagingSql(StringBuffer anSqlString);
       
  
  
  
  1.5       +96 -66    db-ojb/src/java/org/apache/ojb/broker/platforms/ClobWrapper.java
  
  Index: ClobWrapper.java
  ===================================================================
  RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/ClobWrapper.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- ClobWrapper.java	8 Dec 2003 10:18:16 -0000	1.4
  +++ ClobWrapper.java	24 Jan 2004 00:08:58 -0000	1.5
  @@ -54,55 +54,54 @@
    * <http://www.apache.org/>.
    */
   
  +import org.apache.commons.lang.BooleanUtils;
  +import org.apache.ojb.broker.util.ClassHelper;
  +
   import java.io.Reader;
   import java.io.Writer;
  -import java.lang.reflect.InvocationTargetException;
   import java.lang.reflect.Method;
  +import java.lang.reflect.Field;
   import java.sql.Connection;
   import java.sql.SQLException;
   
  -import org.apache.commons.lang.BooleanUtils;
  -import org.apache.ojb.broker.util.logging.LoggerFactory;
  -
   /**
  - * Wraps the Oracle CLOB type and makes it accessible via reflection without having to import the Oracle Classes.
  - * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
  + * Wraps the Oracle CLOB type and makes it accessible via reflection
  + * without having to import the Oracle Classes.
  + * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
  + * @author <a href="martin.kalen@curalia.se">Martin Kal&eacute;n</a>
  + * @version CVS $Id$
    */
  -
   public class ClobWrapper
   {
  -	public static final int MAX_CHUNK_SIZE = 32768;
  -	public static final int DURATION_SESSION = 10;
  -	public static final int DURATION_CALL = 12;
  -	static final int OLD_WRONG_DURATION_SESSION = 1;
  -	static final int OLD_WRONG_DURATION_CALL = 2;
  -	public static final int MODE_READONLY = 0;
  -	public static final int MODE_READWRITE = 1;
  -	private Object m_clob = null;
  -
  -	// methods
  -	private static Method createTemporary = null;
  -	private static Method open = null;
  -	private static Method isOpen = null;
  -	private static Method getCharacterStream = null;
  -	private static Method getCharacterOutputStream = null;
  -	private static Method getBufferSize = null;
  -	private static Method close = null;
  -	private static Method trim = null;
  -	private static Method freeTemporary = null;
  +	protected Object m_clob;
  +
  +    // Fields - values must be looked up via reflection not be compile-time Oracle-version dependent
  +    protected static Field durationSession;
  +    protected static Field durationCall;
  +    protected static Field modeReadOnly;
  +    protected static Field modeReadWrite;
  +
  +	// Methods
  +	protected static Method createTemporary;
  +    protected static Method freeTemporary;
  +	protected static Method open;
  +	protected static Method isOpen;
  +	protected static Method getCharacterStream;
  +	protected static Method getCharacterOutputStream;
  +	protected static Method getBufferSize;
  +	protected static Method close;
  +	protected static Method trim;
   
   	/**
  -	 * initialize all the methods
  +	 * Initialize all methods and fields via reflection.
   	 */
   	static
   	{
   		try
   		{
  -			/**
  -			 * try to find it
  -			 */
  -			Class clobClass = Class.forName("oracle.sql.CLOB");
  -			createTemporary = clobClass.getDeclaredMethod("createTemporary", new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE});
  +			Class clobClass = ClassHelper.getClass("oracle.sql.CLOB", false);
  +			createTemporary = clobClass.getMethod("createTemporary", new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE});
  +            freeTemporary = clobClass.getMethod("freeTemporary", null);
   			open = clobClass.getMethod("open", new Class[]{Integer.TYPE});
   			isOpen = clobClass.getMethod("isOpen", null);
   			getCharacterStream = clobClass.getMethod("getCharacterStream", null);
  @@ -110,19 +109,14 @@
   			getBufferSize = clobClass.getMethod("getBufferSize", null);
   			close = clobClass.getMethod("close", null);
   			trim = clobClass.getMethod("trim", new Class[]{Long.TYPE});
  -			freeTemporary = clobClass.getMethod("freeTemporary", null);
  -		}
  -		catch (NoSuchMethodException e)
  -		{
  -			System.out.println(e.getMessage());
  -		}
  -		catch (SecurityException e)
  -		{
  -			System.out.println(e.getMessage());
  +
  +            durationSession = ClassHelper.getField(clobClass, "DURATION_SESSION");
  +            durationCall = ClassHelper.getField(clobClass, "DURATION_CALL");
  +            modeReadOnly = ClassHelper.getField(clobClass, "MODE_READONLY");
  +            modeReadWrite = ClassHelper.getField(clobClass, "MODE_READWRITE");
   		}
  -		catch (ClassNotFoundException e)
  +		catch (Exception ingore)
   		{
  -			System.out.println(e.getMessage());
   		}
   	}
   
  @@ -136,33 +130,45 @@
   		m_clob = clob;
   	}
   
  -	public static ClobWrapper createTemporary(Connection conn, boolean b, int i)
  +    protected static int staticIntFieldValue(Field field) {
  +        int value = 0;
  +        try {
  +            value = field.getInt(null);
  +        } catch (Exception ignore) {
  +            value = -1;
  +        }
  +        return value;
  +    }
  +
  +    public static int getDurationSessionValue() {
  +        return staticIntFieldValue(durationSession);
  +    }
  +
  +    public static int getDurationCallValue() {
  +        return staticIntFieldValue(durationCall);
  +    }
  +
  +    public static int getModeReadOnlyValue() {
  +        return staticIntFieldValue(modeReadOnly);
  +    }
  +
  +    public static int getModeReadWriteValue() {
  +        return staticIntFieldValue(modeReadWrite);
  +    }
  +
  +	public static ClobWrapper createTemporary(Connection conn, boolean b, int i) throws Exception
   	{
   		ClobWrapper retval = new ClobWrapper();
  -		try
  -		{
  -			/**
  -			 * passing null to the invoke means this is a static method.
  -			 */
  -			retval.m_clob = createTemporary.invoke(null, new Object[]{conn, BooleanUtils.toBooleanObject(b), new Integer(i)});
  -		}
  -		catch (IllegalAccessException e)
  -		{
  -			LoggerFactory.getDefaultLogger().error(e);
  -		}
  -		catch (IllegalArgumentException e)
  -		{
  -			LoggerFactory.getDefaultLogger().error(e);
  -		}
  -		catch (InvocationTargetException e)
  -		{
  -			LoggerFactory.getDefaultLogger().error(e);
  -		}
  +        // Passing null to invoke static method
  +        retval.setClob(createTemporary.invoke(null, new Object[]{conn, BooleanUtils.toBooleanObject(b), new Integer(i)}));
   		return retval;
   	}
   
   	public void open(int i) throws SQLException
   	{
  +        if (m_clob == null) {
  +            return;
  +        }
   		try
   		{
   			open.invoke(m_clob, new Object[]{new Integer(i)});
  @@ -175,20 +181,29 @@
   
   	public boolean isOpen() throws SQLException
   	{
  -		Boolean retval = null;
  +        if (m_clob == null) {
  +            return false;
  +        }
  +		boolean open = false;
   		try
   		{
  -			retval = (Boolean) isOpen.invoke(m_clob, null);
  +			Boolean retval = (Boolean) isOpen.invoke(m_clob, null);
  +            if (retval != null) {
  +                open = retval.booleanValue();
  +            }
   		}
   		catch (Throwable e)
   		{
   			throw new SQLException(e.getMessage());
   		}
  -		return retval.booleanValue();
  +		return open;
   	}
   
   	public Reader getCharacterStream() throws SQLException
   	{
  +        if (m_clob == null) {
  +            return null;
  +        }
   		Reader retval = null;
   		try
   		{
  @@ -203,6 +218,9 @@
   
   	public Writer getCharacterOutputStream() throws SQLException
   	{
  +        if (m_clob == null) {
  +            return null;
  +        }
   		Writer retval = null;
   		try
   		{
  @@ -217,6 +235,9 @@
   
   	public int getBufferSize() throws SQLException
   	{
  +        if (m_clob == null) {
  +            return 0;
  +        }
   		Integer retval = null;
   		try
   		{
  @@ -231,6 +252,9 @@
   
   	public void close() throws SQLException
   	{
  +        if (m_clob == null) {
  +            return;
  +        }
   		try
   		{
   			close.invoke(m_clob, null);
  @@ -244,6 +268,9 @@
   
   	public void trim(long l) throws SQLException
   	{
  +        if (m_clob == null) {
  +            return;
  +        }
   		try
   		{
   			trim.invoke(m_clob, new Object[]{new Long(l)});
  @@ -257,6 +284,9 @@
   
   	public void freeTemporary() throws SQLException
   	{
  +        if (m_clob == null) {
  +            return;
  +        }
   		try
   		{
   			freeTemporary.invoke(m_clob, null);
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org