001package com.randomnoun.common.spring;
002
003/* (c) 2013 randomnoun. All Rights Reserved. This work is licensed under a
004 * BSD Simplified License. (http://www.randomnoun.com/bsd-simplified.html)
005 */
006
007import java.io.*;
008import java.sql.*;
009import java.util.*;
010
011import javax.sql.*;
012
013import org.apache.log4j.Logger;
014import org.springframework.jdbc.core.*;
015import org.springframework.jdbc.support.DatabaseMetaDataCallback;
016import org.springframework.jdbc.support.JdbcUtils;
017import org.springframework.jdbc.support.MetaDataAccessException;
018import org.springframework.jdbc.support.lob.DefaultLobHandler;
019import org.springframework.jdbc.support.lob.LobHandler;
020
021import com.randomnoun.common.StreamUtil;
022import com.randomnoun.common.spring.ClobRowMapper;
023
024/**
025 * This class is intended to replace the standard RowMapper provided by Spring
026 * to allow CLOBs to be treated as String objects transparently (i.e. the Map
027 * which represents a row returned by this class will not contain any
028 * oracle.sql.CLOB objects).
029 *
030 * <p>LobHandlers aren't required in recent Oracle drivers, so this entire class is 
031 * probably obsolete these days.
032 *
033 * 
034 * @author knoxg
035 */
036public class ClobRowMapper implements RowMapper<Map<String, Object>>
037{
038    
039    /** Logger for this class */
040    public final Logger logger = Logger.getLogger(ClobRowMapper.class);
041
042    /** Oracle Large OBject handler. */
043    final LobHandler lobHandler; 
044
045    public String detectDatabase(DataSource ds) {
046        logger.debug("Looking up default SQLErrorCodes for DataSource");
047        String dbName = "unknown";
048        
049        try {
050            @SuppressWarnings("unchecked")
051                        Map<String, Object> dbmdInfo = (Map<String, Object>) JdbcUtils.extractDatabaseMetaData(ds, new DatabaseMetaDataCallback() {
052                public Object processMetaData(DatabaseMetaData dbmd) throws SQLException {
053                    Map<String, Object> info = new HashMap<String, Object>(2);
054                    if (dbmd != null) {
055                        info.put("DatabaseProductName", dbmd.getDatabaseProductName());
056                        info.put("DriverVersion", dbmd.getDriverVersion());
057                    }
058                    return info;
059                }
060            });
061            if (dbmdInfo != null) {
062                
063                // should always be the case outside of test environments
064                dbName = (String) dbmdInfo.get("DatabaseProductName");
065                String driverVersion = (String) dbmdInfo.get("DriverVersion");
066                logger.debug("Found dbName='" + dbName + "', driverVerson='" + driverVersion + "'");
067
068                if (dbName != null && dbName.startsWith("DB2")) {
069                    dbName = "DB2";
070                }
071                /* 
072                if (dbName != null) {
073                    this.dataSourceProductName.put(ds, dbName);
074                    logger.info("Database Product Name is " + dbName);
075                    logger.info("Driver Version is " + driverVersion);
076                    SQLErrorCodes sec = (SQLErrorCodes) this.rdbmsErrorCodes.get(dbName);
077                    if (sec != null) {
078                        return sec;
079                    }
080                    logger.info("Error Codes for " + dbName + " not found");
081                }
082                */
083            }
084        }
085        catch (MetaDataAccessException ex) {
086            logger.warn("Error while getting database metadata", ex);
087        }
088
089        // fallback is to return an empty ErrorCodes instance
090        logger.debug("Returning dbName='" + dbName + "'");
091        return dbName;
092    }
093            
094            
095    /**
096     * Creates a new ClobRowMapper object.
097     */
098    public ClobRowMapper(JdbcTemplate jt)
099    {
100        lobHandler = new DefaultLobHandler();
101        /*
102        DataSource ds = jt.getDataSource();
103        try {
104            if (detectDatabase(ds).equals("DB2")) {
105                lobHandler = new DefaultLobHandler();
106            } else {
107                // lobHandler = new OracleLobHandler();
108            }
109        } catch (Exception e) {
110            throw new RuntimeException("Could not instantiate lobHandler");
111        }
112        */
113    }
114
115    /** Map rows to a disconnected HashMap representation */
116    public Map<String, Object> mapRow(ResultSet rs, int rowNum)
117        throws SQLException
118    {
119        Map<String, Object> row = new HashMap<String, Object>();
120        String key;
121        Object value;
122
123        ResultSetMetaData metaData = rs.getMetaData();
124        int columnCount = metaData.getColumnCount();
125
126        for (int i = 1; i <= columnCount; i++) {
127                        // key.toUpperCase required for SqlServer; DB2 & Oracle will automatically
128                        // do this anyway
129            key = metaData.getColumnLabel(i).toUpperCase();  
130            value = rs.getObject(i);
131            if (value != null && value.getClass().getName().equals("oracle.sql.CLOB")) {
132                value = lobHandler.getClobAsString(rs, i);
133            } else if (value instanceof java.sql.Clob) {
134                Clob clob = (Clob) value;
135                ByteArrayOutputStream baos = new ByteArrayOutputStream();
136                try {
137                    StreamUtil.copyStream(clob.getAsciiStream(), baos, 1024);
138                } catch (IOException ioe) {
139                    throw (SQLException) new SQLException("IO error transferring CLOB").initCause(ioe);
140                }
141                
142                value = baos.toString();
143            }
144            row.put(key, value);
145        }
146
147        return row;
148    }
149}