/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.eas.client.dataflow; import com.eas.client.metadata.Fields; import com.eas.client.metadata.Parameter; import com.eas.client.metadata.Parameters; import com.eas.concurrent.CallableConsumer; import com.eas.script.Scripts; import com.eas.util.BinaryUtils; import com.eas.util.RowsetJsonConstants; import com.eas.util.StringUtils; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.*; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import java.util.concurrent.Callable; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import javax.sql.DataSource; /** * This flow dataSource intended to support the flow process from and to jdbc * data sources. * * @author mg * @param <JKT> Jdbc source key type. It may be long number or string * identifier. * @see FlowProvider */ public abstract class JdbcFlowProvider<JKT> extends DatabaseFlowProvider<JKT> { protected static final Logger queriesLogger = Logger.getLogger(JdbcFlowProvider.class.getName()); protected DataSource dataSource; protected Fields expectedFields; protected Consumer<Runnable> asyncDataPuller; protected boolean procedure; protected ResultSet lowLevelResults; protected Connection lowLevelConnection; protected PreparedStatement lowLevelStatement; /** * A flow dataSource, intended to support jdbc data sources. * * @param aJdbcSourceTag Jdbc source key value. It may be long number or * string identifier. * @param aDataSource A DataSource instance, that would supply resources for * use them by flow dataSource in single operations, like retriving data of * applying data changes. * @param aAsyncDataPuller * @param aClause A sql clause, dataSource should use to achieve * PreparedStatement instance to use it in the result set querying process. * @param aExpectedFields * @see DataSource */ public JdbcFlowProvider(JKT aJdbcSourceTag, DataSource aDataSource, Consumer<Runnable> aAsyncDataPuller, String aClause, Fields aExpectedFields) { super(aJdbcSourceTag, aClause); dataSource = aDataSource; asyncDataPuller = aAsyncDataPuller; expectedFields = aExpectedFields; assert dataSource != null : "Flow provider can't exist without a data source"; assert clause != null : "Flow provider can't exist without a selecting sql clause"; } @Override public boolean isProcedure() { return procedure; } @Override public void setProcedure(boolean aValue) { procedure = aValue; } /** * {@inheritDoc} */ @Override public Collection<Map<String, Object>> nextPage(Consumer<Collection<Map<String, Object>>> onSuccess, Consumer<Exception> onFailure) throws Exception { if (!isPaged() || lowLevelResults == null) { throw new FlowProviderNotPagedException(BAD_NEXTPAGE_REFRESH_CHAIN_MSG); } else { JdbcReader reader = obtainJdbcReader(); Callable<Collection<Map<String, Object>>> doWork = () -> { try { return reader.readRowset(lowLevelResults, pageSize); } catch (SQLException ex) { throw new FlowProviderFailedException(ex); } finally { if (lowLevelResults.isClosed() || lowLevelResults.isAfterLast()) { endPaging(); } } }; if (onSuccess != null) { asyncDataPuller.accept(() -> { try { Collection<Map<String, Object>> rs = doWork.call(); try { onSuccess.accept(rs); } catch (Exception ex) { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.SEVERE, null, ex); } } catch (Exception ex) { if (onFailure != null) { onFailure.accept(ex); } } }); return null; } else { return doWork.call(); } } } protected abstract JdbcReader obtainJdbcReader(); private void endPaging() throws Exception { assert isPaged(); close(); } @Override public void close() throws Exception { if (lowLevelResults != null) { lowLevelResults.close(); // See refresh method, hacky statement closing. if (lowLevelStatement != null) { lowLevelStatement.close(); } // See refresh method, hacky connection closing. if (lowLevelConnection != null) { lowLevelConnection.close(); } lowLevelResults = null; lowLevelStatement = null; lowLevelConnection = null; } } public static int assumeJdbcType(Object aValue) { int jdbcType; if (aValue instanceof CharSequence) { jdbcType = Types.VARCHAR; } else if (aValue instanceof Number) { jdbcType = Types.DOUBLE; } else if (aValue instanceof java.util.Date) { jdbcType = Types.TIMESTAMP; } else if (aValue instanceof Boolean) { jdbcType = Types.BOOLEAN; } else { jdbcType = Types.VARCHAR; } return jdbcType; } // Ms sql non jdbc string types public static final int NON_JDBC_LONG_STRING = 258; public static final int NON_JDBC_MEDIUM_STRING = 259; public static final int NON_JDBC_MEMO_STRING = 260; public static final int NON_JDBC_SHORT_STRING = 261; private static BigDecimal number2BigDecimal(Number aNumber) { if (aNumber instanceof Float || aNumber instanceof Double) { return new BigDecimal(aNumber.doubleValue()); } else if (aNumber instanceof BigInteger) { return new BigDecimal((BigInteger) aNumber); } else if (aNumber instanceof BigDecimal) { return (BigDecimal) aNumber; } else { return new BigDecimal(aNumber.longValue()); } } public static Object get(Wrapper aRs, int aColumnIndex) throws SQLException { try { int sqlType = aRs instanceof ResultSet ? ((ResultSet) aRs).getMetaData().getColumnType(aColumnIndex) : ((CallableStatement) aRs).getParameterMetaData().getParameterType(aColumnIndex); Object value = null; switch (sqlType) { case Types.JAVA_OBJECT: case Types.DATALINK: case Types.DISTINCT: case Types.NULL: case Types.ROWID: case Types.REF: case Types.SQLXML: case Types.ARRAY: case Types.STRUCT: case Types.OTHER: value = aRs instanceof ResultSet ? ((ResultSet) aRs).getString(aColumnIndex) : ((CallableStatement) aRs).getString(aColumnIndex); break; case Types.BINARY: case Types.VARBINARY: case Types.LONGVARBINARY: value = aRs instanceof ResultSet ? ((ResultSet) aRs).getBytes(aColumnIndex) : ((CallableStatement) aRs).getBytes(aColumnIndex); break; case Types.BLOB: value = aRs instanceof ResultSet ? ((ResultSet) aRs).getBlob(aColumnIndex) : ((CallableStatement) aRs).getBlob(aColumnIndex); if (value != null) { try (InputStream is = ((Blob) value).getBinaryStream()) { value = BinaryUtils.readStream(is, -1); } } break; // clobs case Types.CLOB: value = aRs instanceof ResultSet ? ((ResultSet) aRs).getClob(aColumnIndex) : ((CallableStatement) aRs).getClob(aColumnIndex); if (value != null) { try (Reader reader = ((Clob) value).getCharacterStream()) { value = StringUtils.readReader(reader, -1); } } break; case Types.NCLOB: value = aRs instanceof ResultSet ? ((ResultSet) aRs).getNClob(aColumnIndex) : ((CallableStatement) aRs).getNClob(aColumnIndex); if (value != null) { try (Reader reader = ((NClob) value).getCharacterStream()) { value = StringUtils.readReader(reader, -1); } } break; // numbers case Types.DECIMAL: case Types.NUMERIC: // target type - BigDecimal value = aRs instanceof ResultSet ? ((ResultSet) aRs).getBigDecimal(aColumnIndex) : ((CallableStatement) aRs).getBigDecimal(aColumnIndex); break; case Types.BIGINT: // target type - BigInteger value = aRs instanceof ResultSet ? ((ResultSet) aRs).getBigDecimal(aColumnIndex) : ((CallableStatement) aRs).getBigDecimal(aColumnIndex); if (value != null) { value = ((BigDecimal) value).toBigInteger(); } break; case Types.SMALLINT: // target type - Short value = aRs instanceof ResultSet ? ((ResultSet) aRs).getShort(aColumnIndex) : ((CallableStatement) aRs).getShort(aColumnIndex); break; case Types.TINYINT: case Types.INTEGER: // target type - Int value = aRs instanceof ResultSet ? ((ResultSet) aRs).getInt(aColumnIndex) : ((CallableStatement) aRs).getInt(aColumnIndex); break; case Types.REAL: case Types.FLOAT: // target type - Float value = aRs instanceof ResultSet ? ((ResultSet) aRs).getFloat(aColumnIndex) : ((CallableStatement) aRs).getFloat(aColumnIndex); break; case Types.DOUBLE: // target type - Double value = aRs instanceof ResultSet ? ((ResultSet) aRs).getDouble(aColumnIndex) : ((CallableStatement) aRs).getDouble(aColumnIndex); break; // strings case Types.CHAR: case Types.NCHAR: case Types.VARCHAR: case Types.NVARCHAR: case Types.LONGVARCHAR: case Types.LONGNVARCHAR: case NON_JDBC_LONG_STRING: case NON_JDBC_MEDIUM_STRING: case NON_JDBC_MEMO_STRING: case NON_JDBC_SHORT_STRING: // target type - string value = aRs instanceof ResultSet ? ((ResultSet) aRs).getString(aColumnIndex) : ((CallableStatement) aRs).getString(aColumnIndex); break; // booleans case Types.BOOLEAN: case Types.BIT: // target type - Boolean value = aRs instanceof ResultSet ? ((ResultSet) aRs).getBoolean(aColumnIndex) : ((CallableStatement) aRs).getBoolean(aColumnIndex); break; // dates, times case Types.DATE: value = aRs instanceof ResultSet ? ((ResultSet) aRs).getDate(aColumnIndex) : ((CallableStatement) aRs).getDate(aColumnIndex); break; case Types.TIMESTAMP: value = aRs instanceof ResultSet ? ((ResultSet) aRs).getTimestamp(aColumnIndex) : ((CallableStatement) aRs).getTimestamp(aColumnIndex); break; case Types.TIME: value = aRs instanceof ResultSet ? ((ResultSet) aRs).getTime(aColumnIndex) : ((CallableStatement) aRs).getTime(aColumnIndex); break; } if (aRs instanceof ResultSet ? ((ResultSet) aRs).wasNull() : ((CallableStatement) aRs).wasNull()) { value = null; } return value; } catch (SQLException ex) { throw ex; } catch (IOException ex) { throw new SQLException(ex); } } public static int assign(Object aValue, int aParameterIndex, PreparedStatement aStmt, int aParameterJdbcType, String aParameterSqlTypeName) throws SQLException { if (aValue != null) { /* if (aValue instanceof JSObject) { aValue = aSpace.toJava(aValue); } */ switch (aParameterJdbcType) { // Some strange types. No one knows how to work with them. case Types.JAVA_OBJECT: case Types.DATALINK: case Types.DISTINCT: case Types.NULL: case Types.ROWID: case Types.REF: case Types.SQLXML: case Types.ARRAY: case Types.OTHER: try { aStmt.setObject(aParameterIndex, aValue, aParameterJdbcType); } catch (Exception ex) { aStmt.setNull(aParameterIndex, aParameterJdbcType, aParameterSqlTypeName); Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.STRUCT: try { aStmt.setObject(aParameterIndex, aValue, Types.STRUCT); } catch (Exception ex) { aStmt.setNull(aParameterIndex, aParameterJdbcType, aParameterSqlTypeName); Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.BINARY: case Types.VARBINARY: case Types.LONGVARBINARY: // target type - byte[] if (aValue instanceof byte[]) { aStmt.setBytes(aParameterIndex, (byte[]) aValue); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.BLOB: // target type - java.sql.Blob if (aValue instanceof Blob) { aStmt.setBlob(aParameterIndex, (Blob) aValue); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.CLOB: // target type - java.sql.Clob if (aValue instanceof Clob) { aStmt.setClob(aParameterIndex, (Clob) aValue); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.NCLOB: // target type - java.sql.NClob if (aValue instanceof NClob) { aStmt.setNClob(aParameterIndex, (NClob) aValue); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.DECIMAL: case Types.NUMERIC: // target type - BigDecimal // target type - BigDecimal BigDecimal castedDecimal = null; if (aValue instanceof Number) { castedDecimal = number2BigDecimal((Number) aValue); } else if (aValue instanceof String) { castedDecimal = new BigDecimal((String) aValue); } else if (aValue instanceof Boolean) { castedDecimal = new BigDecimal(((Boolean) aValue) ? 1 : 0); } else if (aValue instanceof java.util.Date) { castedDecimal = new BigDecimal(((java.util.Date) aValue).getTime()); } if (castedDecimal != null) { aStmt.setBigDecimal(aParameterIndex, castedDecimal); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.BIGINT: // target type - BigInteger BigInteger castedInteger = null; if (aValue instanceof Number) { castedInteger = BigInteger.valueOf(((Number) aValue).longValue()); } else if (aValue instanceof String) { castedInteger = new BigInteger((String) aValue); } else if (aValue instanceof Boolean) { castedInteger = BigInteger.valueOf(((Boolean) aValue) ? 1 : 0); } else if (aValue instanceof java.util.Date) { castedInteger = BigInteger.valueOf(((java.util.Date) aValue).getTime()); } if (castedInteger != null) { aStmt.setBigDecimal(aParameterIndex, new BigDecimal(castedInteger)); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.SMALLINT: // target type - Short // target type - Short Short castedShort = null; if (aValue instanceof Number) { castedShort = ((Number) aValue).shortValue(); } else if (aValue instanceof String) { castedShort = Double.valueOf((String) aValue).shortValue(); } else if (aValue instanceof Boolean) { castedShort = Integer.valueOf(((Boolean) aValue) ? 1 : 0).shortValue(); } else if (aValue instanceof java.util.Date) { castedShort = Integer.valueOf((int) ((java.util.Date) aValue).getTime()).shortValue(); } if (castedShort != null) { aStmt.setShort(aParameterIndex, castedShort); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.TINYINT: case Types.INTEGER: // target type - Integer Integer castedInt = null; if (aValue instanceof Number) { castedInt = ((Number) aValue).intValue(); } else if (aValue instanceof String) { castedInt = Double.valueOf((String) aValue).intValue(); } else if (aValue instanceof Boolean) { castedInt = (Boolean) aValue ? 1 : 0; } else if (aValue instanceof java.util.Date) { castedInt = (int) ((java.util.Date) aValue).getTime(); } if (castedInt != null) { aStmt.setInt(aParameterIndex, castedInt); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.REAL: case Types.FLOAT: // target type - Float Float castedFloat = null; if (aValue instanceof Number) { castedFloat = ((Number) aValue).floatValue(); } else if (aValue instanceof String) { castedFloat = Float.valueOf((String) aValue); } else if (aValue instanceof Boolean) { castedFloat = Float.valueOf(((Boolean) aValue) ? 1 : 0); } else if (aValue instanceof java.util.Date) { castedFloat = (float) ((java.util.Date) aValue).getTime(); } if (castedFloat != null) { aStmt.setFloat(aParameterIndex, castedFloat); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.DOUBLE: // target type - Double Double castedDouble = null; if (aValue instanceof Number) { castedDouble = ((Number) aValue).doubleValue(); } else if (aValue instanceof String) { castedDouble = Double.valueOf((String) aValue); } else if (aValue instanceof Boolean) { castedDouble = Double.valueOf(((Boolean) aValue) ? 1 : 0); } else if (aValue instanceof java.util.Date) { castedDouble = (double) ((java.util.Date) aValue).getTime(); } if (castedDouble != null) { aStmt.setDouble(aParameterIndex, castedDouble); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.NCHAR: case Types.NVARCHAR: case Types.LONGNVARCHAR: // target type - string // target type - string String castedString = null; if (aValue instanceof Number) { castedString = ((Number) aValue).toString(); } else if (aValue instanceof String) { castedString = (String) aValue; } else if (aValue instanceof Boolean) { castedString = ((Boolean) aValue) ? ((Boolean) aValue).toString() : ""; } else if (aValue instanceof java.util.Date) { castedString = String.valueOf(((java.util.Date) aValue).getTime()); } else if (aValue instanceof Clob) { castedString = ((Clob) aValue).getSubString(1, (int) ((Clob) aValue).length()); } if (castedString != null) { if (aParameterJdbcType == Types.NCHAR || aParameterJdbcType == Types.NVARCHAR || aParameterJdbcType == Types.LONGNVARCHAR) { aStmt.setNString(aParameterIndex, castedString); } else { aStmt.setString(aParameterIndex, castedString); } } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.BIT: case Types.BOOLEAN: // target type - Boolean Boolean castedBoolean = null; if (aValue instanceof Number) { castedBoolean = !(((Number) aValue).intValue() == 0); } else if (aValue instanceof String || aValue instanceof Clob) { String s; if (aValue instanceof String) { s = (String) aValue; } else { s = ((Clob) aValue).getSubString(1, (int) ((Clob) aValue).length()); } castedBoolean = !s.isEmpty(); } else if (aValue instanceof Boolean) { castedBoolean = (Boolean) aValue; } else if (aValue instanceof java.util.Date) { castedBoolean = !((java.util.Date) aValue).equals(new java.util.Date(0)); } if (castedBoolean != null) { aStmt.setBoolean(aParameterIndex, castedBoolean); } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; case Types.DATE: case Types.TIMESTAMP: case Types.TIME: // target type - date java.util.Date castedDate = null; if (aValue instanceof Number) { castedDate = new java.util.Date(((Number) aValue).longValue()); } else if (aValue instanceof String) { castedDate = new java.util.Date(Long.valueOf((String) aValue)); } else if (aValue instanceof Boolean) { castedDate = new java.util.Date(((Boolean) aValue) ? 1 : 0); } else if (aValue instanceof java.util.Date) { castedDate = (java.util.Date) aValue; } if (castedDate != null) { if (aParameterJdbcType == Types.DATE) { aStmt.setDate(aParameterIndex, new java.sql.Date(castedDate.getTime()));//, Calendar.getInstance(TimeZone.getTimeZone("UTC"))); } else if (aParameterJdbcType == Types.TIMESTAMP) { aStmt.setTimestamp(aParameterIndex, new java.sql.Timestamp(castedDate.getTime()));//, Calendar.getInstance(TimeZone.getTimeZone("UTC"))); } else if (aParameterJdbcType == Types.TIME) { aStmt.setTime(aParameterIndex, new java.sql.Time(castedDate.getTime()));//, Calendar.getInstance(TimeZone.getTimeZone("UTC"))); } else { assert false; } } else { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.WARNING, FALLED_TO_NULL_MSG, aValue.getClass().getName()); } break; } } else { try { if (aParameterJdbcType == Types.TIME || aParameterJdbcType == Types.TIME_WITH_TIMEZONE || aParameterJdbcType == Types.TIMESTAMP || aParameterJdbcType == Types.TIMESTAMP_WITH_TIMEZONE) {// Crazy jdbc drivers of some databases (PostgreSQL for example) ignore such types, while setting nulls aParameterJdbcType = Types.DATE; aParameterSqlTypeName = null; } if (aParameterSqlTypeName != null && !aParameterSqlTypeName.isEmpty()) { aStmt.setNull(aParameterIndex, aParameterJdbcType, aParameterSqlTypeName); } else { aStmt.setNull(aParameterIndex, aParameterJdbcType); } } catch (SQLException ex) { aStmt.setNull(aParameterIndex, aParameterJdbcType, aParameterSqlTypeName); } } return aParameterJdbcType; } protected static final String FALLED_TO_NULL_MSG = "Some value falled to null while tranferring to a database. May be it''s class is unsupported: {0}"; /** * {@inheritDoc} */ @Override public Collection<Map<String, Object>> refresh(Parameters aParams, Consumer<Collection<Map<String, Object>>> onSuccess, Consumer<Exception> onFailure) throws Exception { return select(aParams, (ResultSet rs) -> { if (rs != null) { JdbcReader reader = obtainJdbcReader(); return reader.readRowset(rs, pageSize); } else { return new ArrayList<>(); } }, onSuccess, onFailure); } public <T> T select(Parameters aParams, CallableConsumer<T, ResultSet> aResultSetProcessor, Consumer<T> onSuccess, Consumer<Exception> onFailure) throws Exception { if (lowLevelResults != null) { assert isPaged(); // Let's abort paging process endPaging(); } Callable<T> doWork = () -> { String sqlClause = clause; Connection connection = dataSource.getConnection(); if (connection != null) { try { PreparedStatement stmt = getFlowStatement(connection, sqlClause); if (stmt != null) { try { prepareConnection(connection); try { Map<Integer, Integer> assignedJdbcTypes = new HashMap<>(); for (int i = 1; i <= aParams.getParametersCount(); i++) { Parameter param = aParams.get(i); int assignedJdbcType = assignParameter(param, stmt, i, connection); assignedJdbcTypes.put(i, assignedJdbcType); } logQuery(sqlClause, aParams, assignedJdbcTypes); ResultSet rs = null; if (procedure) { assert stmt instanceof CallableStatement; CallableStatement cStmt = (CallableStatement) stmt; cStmt.execute(); // let's return parameters for (int i = 1; i <= aParams.getParametersCount(); i++) { Parameter param = aParams.get(i); acceptOutParameter(param, cStmt, i, connection); } // let's return a ResultSet rs = cStmt.getResultSet(); } else { rs = stmt.executeQuery(); } if (rs != null) { try { return aResultSetProcessor.call(rs); } finally { if (isPaged()) { lowLevelResults = rs; } else { rs.close(); } } } else { return aResultSetProcessor.call(null); } } catch (SQLException ex) { throw new FlowProviderFailedException(ex); } finally { assert dataSource != null; // since we've got a statement, dataSource must present. unprepareConnection(connection); } } finally { if (isPaged()) { // Paged statements can't be closed, because of ResultSet existance. lowLevelStatement = stmt; } else { stmt.close(); } } } else { return null; } } finally { if (isPaged()) { // Paged connections can't be closed, because of ResultSet existance. lowLevelConnection = connection; } else { connection.close(); } } } else { return null; } }; if (onSuccess != null) { asyncDataPuller.accept(() -> { try { T processed = doWork.call(); try { onSuccess.accept(processed); } catch (Exception ex) { Logger.getLogger(JdbcFlowProvider.class.getName()).log(Level.SEVERE, null, ex); } } catch (Exception ex) { if (onFailure != null) { onFailure.accept(ex); } } }); return null; } else { return doWork.call(); } } protected static void logQuery(String sqlClause, Parameters aParams, Map<Integer, Integer> aAssignedJdbcTypes) { if (queriesLogger.isLoggable(Level.FINE)) { boolean finerLogs = queriesLogger.isLoggable(Level.FINER); queriesLogger.log(Level.FINE, "Executing sql:\n{0}\nwith {1} parameters{2}", new Object[]{sqlClause, aParams.getParametersCount(), finerLogs && aParams.getParametersCount() > 0 ? ":" : ""}); if (finerLogs) { for (int i = 1; i <= aParams.getParametersCount(); i++) { Parameter param = aParams.get(i); Object paramValue = param.getValue(); if (paramValue != null && Scripts.DATE_TYPE_NAME.equals(param.getType())) { java.util.Date dateValue = (java.util.Date) paramValue; SimpleDateFormat sdf = new SimpleDateFormat(RowsetJsonConstants.DATE_FORMAT); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); String jsonLikeText = sdf.format(dateValue); queriesLogger.log(Level.FINER, "order: {0}; name: {1}; jdbc type: {2}; json like timestamp: {3}; raw timestamp: {4};", new Object[]{i, param.getName(), aAssignedJdbcTypes.get(i), jsonLikeText, dateValue.getTime()}); } else {// nulls, String, Number, Boolean queriesLogger.log(Level.FINER, "order: {0}; name: {1}; jdbc type: {2}; value: {3};", new Object[]{i, param.getName(), aAssignedJdbcTypes.get(i), param.getValue()}); } } } } } protected void acceptOutParameter(Parameter aParameter, CallableStatement aStatement, int aParameterIndex, Connection aConnection) throws SQLException { if (aParameter.getMode() == ParameterMetaData.parameterModeOut || aParameter.getMode() == ParameterMetaData.parameterModeInOut) { try { Object outedParamValue = get(aStatement, aParameterIndex); aParameter.setValue(outedParamValue); } catch (SQLException ex) { String pType = aParameter.getType(); if (pType != null) { switch (pType) { case Scripts.STRING_TYPE_NAME: aParameter.setValue(aStatement.getString(aParameterIndex)); break; case Scripts.NUMBER_TYPE_NAME: aParameter.setValue(aStatement.getDouble(aParameterIndex)); break; case Scripts.DATE_TYPE_NAME: aParameter.setValue(aStatement.getDate(aParameterIndex)); break; case Scripts.BOOLEAN_TYPE_NAME: aParameter.setValue(aStatement.getBoolean(aParameterIndex)); break; default: aParameter.setValue(aStatement.getObject(aParameterIndex)); } } else { aParameter.setValue(aStatement.getObject(aParameterIndex)); } } } } protected int assignParameter(Parameter aParameter, PreparedStatement aStatement, int aParameterIndex, Connection aConnection) throws SQLException { Object paramValue = aParameter.getValue(); int jdbcType; /* String sqlTypeName; // Crazy DBMS-es in most cases can't answer the question about parameter's type properly! // PostgreSQL, for example starts answer the question after some time (about 4-8 hours). // But before it raises SQLException. And after that, starts to report TIMESTAMP parameters // as DATE parameters. // This leads to parameters values shifting while statement.setDate() and errorneous select results! try { jdbcType = aStatement.getParameterMetaData().getParameterType(aParameterIndex); sqlTypeName = aStatement.getParameterMetaData().getParameterTypeName(aParameterIndex); } catch (SQLException ex) { */ if (paramValue != null || aParameter.getType() == null) { jdbcType = assumeJdbcType(paramValue); //sqlTypeName = null; } else { //sqlTypeName = null; jdbcType = calcJdbcType(aParameter.getType(), paramValue); } /* } */ int assignedJdbcType = assign(paramValue, aParameterIndex, aStatement, jdbcType, null);//sqlTypeName); checkOutParameter(aParameter, aStatement, aParameterIndex, jdbcType); return assignedJdbcType; } public static int calcJdbcType(String aType, Object aParamValue) { int jdbcType; switch (aType) { case Scripts.STRING_TYPE_NAME: jdbcType = java.sql.Types.VARCHAR; break; case Scripts.NUMBER_TYPE_NAME: jdbcType = java.sql.Types.DOUBLE; break; case Scripts.DATE_TYPE_NAME: jdbcType = java.sql.Types.TIMESTAMP; break; case Scripts.BOOLEAN_TYPE_NAME: jdbcType = java.sql.Types.BOOLEAN; break; default: jdbcType = assumeJdbcType(aParamValue); } return jdbcType; } protected void checkOutParameter(Parameter param, PreparedStatement stmt, int aParameterIndex, int jdbcType) throws SQLException { if (procedure && (param.getMode() == ParameterMetaData.parameterModeOut || param.getMode() == ParameterMetaData.parameterModeInOut)) { assert stmt instanceof CallableStatement; CallableStatement cStmt = (CallableStatement) stmt; cStmt.registerOutParameter(aParameterIndex, jdbcType); } } protected abstract void prepareConnection(Connection aConnection) throws Exception; protected abstract void unprepareConnection(Connection aConnection) throws Exception; /** * Returns PreparedStatement instance. Let's consider some caching system. * It will provide some prepared statement instance, according to passed sql * clause. * * @param aConnection java.sql.Connection instance to be used. * @param aClause Sql clause to process. * @return StatementResourceDescriptor instance, provided according to sql * clause. * @throws com.eas.client.dataflow.FlowProviderFailedException */ protected PreparedStatement getFlowStatement(Connection aConnection, String aClause) throws FlowProviderFailedException { try { assert aConnection != null; if (procedure) { return aConnection.prepareCall(aClause); } else { return aConnection.prepareStatement(aClause); } } catch (Exception ex) { throw new FlowProviderFailedException(ex); } } }