/************************************************************************************** * Copyright (C) 2008 EsperTech, Inc. All rights reserved. * * http://esper.codehaus.org * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package com.espertech.esper.epl.db; import com.espertech.esper.client.EPException; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.client.hook.*; import com.espertech.esper.collection.Pair; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.event.bean.BeanEventType; import com.espertech.esper.util.AuditPath; import com.espertech.esper.util.DatabaseTypeBinding; import com.espertech.esper.util.ExecutionPathDebugLog; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.sql.*; import java.util.*; import java.util.Date; /** * Viewable providing historical data from a database. */ public class PollExecStrategyDBQuery implements PollExecStrategy { private static final Log jdbcPerfLog = LogFactory.getLog(AuditPath.JDBC_LOG); private static final Log log = LogFactory.getLog(PollExecStrategyDBQuery.class); private final EventAdapterService eventAdapterService; private final String preparedStatementText; private final Map<String, DBOutputTypeDesc> outputTypes; private final ConnectionCache connectionCache; private final EventType eventType; private final SQLColumnTypeConversion columnTypeConversionHook; private final SQLOutputRowConversion outputRowConversionHook; private final boolean enableJDBCLogging; private Pair<Connection, PreparedStatement> resources; /** * Ctor. * @param eventAdapterService for generating event beans * @param eventType is the event type that this poll generates * @param connectionCache caches Connection and PreparedStatement * @param preparedStatementText is the SQL to use for polling * @param outputTypes describe columns selected by the SQL * @param outputRowConversionHook hook to convert rows, if any hook is registered * @param columnTypeConversionHook hook to convert columns, if any hook is registered */ public PollExecStrategyDBQuery(EventAdapterService eventAdapterService, EventType eventType, ConnectionCache connectionCache, String preparedStatementText, Map<String, DBOutputTypeDesc> outputTypes, SQLColumnTypeConversion columnTypeConversionHook, SQLOutputRowConversion outputRowConversionHook, boolean enableJDBCLogging) { this.eventAdapterService = eventAdapterService; this.eventType = eventType; this.connectionCache = connectionCache; this.preparedStatementText = preparedStatementText; this.outputTypes = outputTypes; this.columnTypeConversionHook = columnTypeConversionHook; this.outputRowConversionHook = outputRowConversionHook; this.enableJDBCLogging = enableJDBCLogging; } public void start() { resources = connectionCache.getConnection(); } public void done() { connectionCache.doneWith(resources); } public void destroy() { connectionCache.destroy(); } public List<EventBean> poll(Object[] lookupValues) { List<EventBean> result; try { result = execute(resources.getSecond(), lookupValues); } catch (EPException ex) { connectionCache.doneWith(resources); throw ex; } return result; } private List<EventBean> execute(PreparedStatement preparedStatement, Object[] lookupValuePerStream) { if (ExecutionPathDebugLog.isDebugEnabled && log.isInfoEnabled()) { log.info(".execute Executing prepared statement '" + preparedStatementText + "'"); } // set parameters SQLInputParameterContext inputParameterContext = null; if (columnTypeConversionHook != null) { inputParameterContext = new SQLInputParameterContext(); } int count = 1; for (int i = 0; i < lookupValuePerStream.length; i++) { try { Object parameter = lookupValuePerStream[i]; if (ExecutionPathDebugLog.isDebugEnabled && log.isInfoEnabled()) { log.info(".execute Setting parameter " + count + " to " + parameter + " typed " + ((parameter == null)? "null" : parameter.getClass())); } if (columnTypeConversionHook != null) { inputParameterContext.setParameterNumber(i + 1); inputParameterContext.setParameterValue(parameter); parameter = columnTypeConversionHook.getParameterValue(inputParameterContext); } setObject(preparedStatement, count, parameter); } catch (SQLException ex) { throw new EPException("Error setting parameter " + count, ex); } count++; } // execute ResultSet resultSet; if (enableJDBCLogging && jdbcPerfLog.isInfoEnabled()) { long startTimeNS = System.nanoTime(); long startTimeMS = System.currentTimeMillis(); try { resultSet = preparedStatement.executeQuery(); } catch (SQLException ex) { throw new EPException("Error executing statement '" + preparedStatementText + '\'', ex); } long endTimeNS = System.nanoTime(); long endTimeMS = System.currentTimeMillis(); jdbcPerfLog.info("Statement '" + preparedStatementText + "' delta nanosec " + (endTimeNS - startTimeNS) + " delta msec " + (endTimeMS - startTimeMS)); } else { try { resultSet = preparedStatement.executeQuery(); } catch (SQLException ex) { throw new EPException("Error executing statement '" + preparedStatementText + '\'', ex); } } // generate events for result set List<EventBean> rows = new LinkedList<EventBean>(); try { SQLColumnValueContext valueContext = null; if (columnTypeConversionHook != null) { valueContext = new SQLColumnValueContext(); } SQLOutputRowValueContext rowContext = null; if (outputRowConversionHook != null) { rowContext = new SQLOutputRowValueContext(); } int rowNum = 0; while (resultSet.next()) { int colNum = 1; Map<String, Object> row = new HashMap<String, Object>(); for (Map.Entry<String, DBOutputTypeDesc> entry : outputTypes.entrySet()) { String columnName = entry.getKey(); Object value; DatabaseTypeBinding binding = entry.getValue().getOptionalBinding(); if (binding != null) { value = binding.getValue(resultSet, columnName); } else { value = resultSet.getObject(columnName); } if (columnTypeConversionHook != null) { valueContext.setColumnName(columnName); valueContext.setColumnNumber(colNum); valueContext.setColumnValue(value); valueContext.setResultSet(resultSet); value = columnTypeConversionHook.getColumnValue(valueContext); } row.put(columnName, value); colNum++; } EventBean eventBeanRow = null; if (this.outputRowConversionHook == null) { eventBeanRow = eventAdapterService.adapterForTypedMap(row, eventType); } else { rowContext.setValues(row); rowContext.setRowNum(rowNum); rowContext.setResultSet(resultSet); Object rowData = outputRowConversionHook.getOutputRow(rowContext); if (rowData != null) { eventBeanRow = eventAdapterService.adapterForTypedBean(rowData, (BeanEventType) eventType); } } if (eventBeanRow != null) { rows.add(eventBeanRow); rowNum++; } } } catch (SQLException ex) { throw new EPException("Error reading results for statement '" + preparedStatementText + '\'', ex); } if (enableJDBCLogging && jdbcPerfLog.isInfoEnabled()) { jdbcPerfLog.info("Statement '" + preparedStatementText + "' " + rows.size() + " rows"); } try { resultSet.close(); } catch (SQLException ex) { throw new EPException("Error closing statement '" + preparedStatementText + '\'', ex); } return rows; } private void setObject(PreparedStatement preparedStatement, int column, Object value) throws SQLException { // Allow java.util.Date conversion for JDBC drivers that don't provide this feature if (value instanceof Date) { value = new Timestamp(((Date)value).getTime()); } else if (value instanceof Calendar) { value = new Timestamp(((Calendar)value).getTimeInMillis()); } preparedStatement.setObject(column, value); } }