/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ejb.plugins.cmp.jdbc; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.rmi.MarshalledObject; import java.rmi.RemoteException; import java.sql.Blob; import java.sql.Clob; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import javax.ejb.Handle; import org.jboss.logging.Logger; /** * Implementations of this interface are used to read java.sql.ResultSet. * * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a> * @version <tt>$Revision: 81030 $</tt> */ public interface JDBCResultSetReader { /** * Reads one column from the java.sql.ResultSet. * @param rs the java.sql.ResultSet to read from * @param index the index of the column * @param destination the expected Java class of result * @param log the logger * @return column value * @throws SQLException */ Object get(ResultSet rs, int index, Class destination, Logger log) throws SQLException; public static final JDBCResultSetReader CLOB_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { Clob clob = rs.getClob(index); String content; if(clob == null) { content = null; } else { final Reader reader = clob.getCharacterStream(); if(reader != null) { int intLength = (int)clob.length(); char[] chars; try { if(intLength <= 8192) { chars = new char[intLength]; reader.read(chars); content = String.valueOf(chars); } else { StringBuffer buf = new StringBuffer(intLength); chars = new char[8192]; int i = reader.read(chars); while(i > 0) { buf.append(chars, 0, i); i = reader.read(chars); } content = buf.toString(); } } catch(IOException e) { throw new SQLException("Failed to read CLOB character stream: " + e.getMessage()); } finally { JDBCUtil.safeClose(reader); } } else { content = null; } } return content; } }; public static final JDBCResultSetReader LONGVARCHAR_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return JDBCUtil.getLongString(rs, index); } }; public static final JDBCResultSetReader BINARY_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { Object value = null; byte[] bytes = rs.getBytes(index); if(!rs.wasNull()) { if(destination == byte[].class) value = bytes; else value = JDBCUtil.convertToObject(bytes); } return value; } }; public static final JDBCResultSetReader VARBINARY_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { Object value = null; byte[] bytes = rs.getBytes(index); if(!rs.wasNull()) { if(destination == byte[].class) value = bytes; else value = JDBCUtil.convertToObject(bytes); } return value; } }; public static final JDBCResultSetReader BLOB_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { Blob blob = rs.getBlob(index); Object value; if(blob == null) { value = null; } else { InputStream binaryData = blob.getBinaryStream(); if(binaryData != null) { if(destination == byte[].class) value = JDBCUtil.getByteArray(binaryData); else value = JDBCUtil.convertToObject(binaryData); } else { value = null; } } return value; } }; public static final JDBCResultSetReader LONGVARBINARY_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { Object value = null; InputStream binaryData = rs.getBinaryStream(index); if(binaryData != null && !rs.wasNull()) { if(destination == byte[].class) value = JDBCUtil.getByteArray(binaryData); else value = JDBCUtil.convertToObject(binaryData); } return value; } }; public static final JDBCResultSetReader JAVA_OBJECT_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getObject(index); } }; public static final JDBCResultSetReader STRUCT_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getObject(index); } }; public static final JDBCResultSetReader ARRAY_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getObject(index); } }; public static final JDBCResultSetReader OTHER_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getObject(index); } }; public static final JDBCResultSetReader JAVA_UTIL_DATE_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getTimestamp(index); } protected Object coerceToJavaType(Object value, Class destination) { // make new copy as sub types have problems in comparions java.util.Date result; // handle timestamp special becauses it hoses the milisecond values if(value instanceof java.sql.Timestamp) { java.sql.Timestamp ts = (java.sql.Timestamp)value; // Timestamp returns whole seconds from getTime and partial // seconds are retrieved from getNanos() // Adrian Brock: Not in 1.4 it doesn't long temp = ts.getTime(); if(temp % 1000 == 0) temp += ts.getNanos() / 1000000; result = new java.util.Date(temp); } else { result = new java.util.Date(((java.util.Date)value).getTime()); } return result; } }; public static final JDBCResultSetReader JAVA_SQL_DATE_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getDate(index); } protected Object coerceToJavaType(Object value, Class destination) { // make a new copy object; you never know what a driver will return return new java.sql.Date(((java.sql.Date)value).getTime()); } }; public static final JDBCResultSetReader JAVA_SQL_TIME_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getTime(index); } protected Object coerceToJavaType(Object value, Class destination) { // make a new copy object; you never know what a driver will return return new java.sql.Time(((java.sql.Time)value).getTime()); } }; public static final JDBCResultSetReader JAVA_SQL_TIMESTAMP_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getTimestamp(index); } protected Object coerceToJavaType(Object value, Class destination) { // make a new copy object; you never know what a driver will return java.sql.Timestamp orignal = (java.sql.Timestamp)value; java.sql.Timestamp copy = new java.sql.Timestamp(orignal.getTime()); copy.setNanos(orignal.getNanos()); return copy; } }; public static final JDBCResultSetReader BIGDECIMAL_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getBigDecimal(index); } }; public static final JDBCResultSetReader REF_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getRef(index); } }; public static final JDBCResultSetReader BYTE_ARRAY_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getBytes(index); } }; public static final JDBCResultSetReader OBJECT_READER = new AbstractResultSetReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getObject(index); } }; public static final JDBCResultSetReader STRING_READER = new JDBCResultSetReader() { public Object get(ResultSet rs, int index, Class destination, Logger log) throws SQLException { final String result = rs.getString(index); if(log.isTraceEnabled()) { log.trace("result: i=" + index + ", type=" + destination.getName() + ", value=" + result); } return result; } }; abstract class AbstractPrimitiveReader extends AbstractResultSetReader { // ResultSetReader implementation public Object get(ResultSet rs, int index, Class destination, Logger log) throws SQLException { Object result = readResult(rs, index, destination); if(rs.wasNull()) result = null; else result = coerceToJavaType(result, destination); if(log.isTraceEnabled()) { log.trace("result: i=" + index + ", type=" + destination.getName() + ", value=" + result); } return result; } // Protected protected Object coerceToJavaType(Object value, Class destination) throws SQLException { return value; } } public static final JDBCResultSetReader BOOLEAN_READER = new AbstractPrimitiveReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return (rs.getBoolean(index) ? Boolean.TRUE : Boolean.FALSE); } }; public static final JDBCResultSetReader BYTE_READER = new AbstractPrimitiveReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return new Byte(rs.getByte(index)); } }; public static final JDBCResultSetReader CHARACTER_READER = new AbstractPrimitiveReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return rs.getString(index); } protected Object coerceToJavaType(Object value, Class destination) { // // java.lang.String --> java.lang.Character or char // // just grab first character if(value instanceof String && (destination == Character.class || destination == Character.TYPE)) { return new Character(((String)value).charAt(0)); } else { return value; } } }; public static final JDBCResultSetReader SHORT_READER = new AbstractPrimitiveReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return new Short(rs.getShort(index)); } }; public static final JDBCResultSetReader INT_READER = new AbstractPrimitiveReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return new Integer(rs.getInt(index)); } }; public static final JDBCResultSetReader LONG_READER = new AbstractPrimitiveReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return new Long(rs.getLong(index)); } }; public static final JDBCResultSetReader FLOAT_READER = new AbstractPrimitiveReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return new Float(rs.getFloat(index)); } }; public static final JDBCResultSetReader DOUBLE_READER = new AbstractPrimitiveReader() { protected Object readResult(ResultSet rs, int index, Class destination) throws SQLException { return new Double(rs.getDouble(index)); } }; abstract class AbstractResultSetReader implements JDBCResultSetReader { public Object get(ResultSet rs, int index, Class destination, Logger log) throws SQLException { Object result = readResult(rs, index, destination); if(result != null) result = coerceToJavaType(result, destination); if(log.isTraceEnabled()) { log.trace("result: i=" + index + ", type=" + destination.getName() + ", value=" + result); } return result; } protected abstract Object readResult(ResultSet rs, int index, Class destination) throws SQLException; protected Object coerceToJavaType(Object value, Class destination) throws SQLException { try { // // java.rmi.MarshalledObject // // get unmarshalled value if(value instanceof MarshalledObject && !destination.equals(MarshalledObject.class)) { value = ((MarshalledObject)value).get(); } // // javax.ejb.Handle // // get the object back from the handle if(value instanceof Handle) { value = ((Handle)value).getEJBObject(); } // Did we get the desired result? if(destination.isAssignableFrom(value.getClass())) { return value; } if(destination == java.math.BigInteger.class && value.getClass() == java.math.BigDecimal.class) { return ((java.math.BigDecimal)value).toBigInteger(); } // oops got the wrong type - nothing we can do String className = null; Object interfaces = null; ClassLoader cl = null; if (value != null) { Class valueClass = value.getClass(); className = valueClass.getName(); interfaces = Arrays.asList(valueClass.getInterfaces()); cl = valueClass.getClassLoader(); } throw new SQLException("Got a " + className + "[cl=" + cl + " + interfaces=" + interfaces + ", value=" + value + "] while looking for a " + destination.getName() + "[cl=" + destination.getClassLoader() + "]"); } catch(RemoteException e) { throw new SQLException("Unable to load EJBObject back from Handle: " + e); } catch(IOException e) { throw new SQLException("Unable to load to deserialize result: " + e); } catch(ClassNotFoundException e) { throw new SQLException("Unable to load to deserialize result: " + e); } } } }