/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.event.output.adapter.rdbms;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.databridge.commons.Attribute;
import org.wso2.carbon.databridge.commons.AttributeType;
import org.wso2.carbon.event.output.adapter.core.OutputEventAdapter;
import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterConfiguration;
import org.wso2.carbon.event.output.adapter.core.exception.ConnectionUnavailableException;
import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterException;
import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterRuntimeException;
import org.wso2.carbon.event.output.adapter.core.exception.TestConnectionNotSupportedException;
import org.wso2.carbon.event.output.adapter.rdbms.internal.ExecutionInfo;
import org.wso2.carbon.event.output.adapter.rdbms.internal.ds.RDBMSEventAdapterServiceValueHolder;
import org.wso2.carbon.event.output.adapter.rdbms.internal.util.RDBMSEventAdapterConstants;
import org.wso2.carbon.ndatasource.common.DataSourceException;
import org.wso2.carbon.ndatasource.core.CarbonDataSource;
import javax.sql.DataSource;
import java.sql.*;
import java.util.*;
/**
* Class will Insert or Update/Insert values to selected RDBMS
*/
public class RDBMSEventAdapter implements OutputEventAdapter {
private static final Log log = LogFactory.getLog(RDBMSEventAdapter.class);
private OutputEventAdapterConfiguration eventAdapterConfiguration;
private Map<String, String> globalProperties;
private ResourceBundle resourceBundle;
private Map<String, String> dbTypeMappings;
private ExecutionInfo executionInfo = null;
private DataSource dataSource;
private boolean isUpdate;
public RDBMSEventAdapter(OutputEventAdapterConfiguration eventAdapterConfiguration,
Map<String, String> globalProperties) {
this.eventAdapterConfiguration = eventAdapterConfiguration;
this.globalProperties = globalProperties;
}
@Override
public void init() throws OutputEventAdapterException {
resourceBundle = ResourceBundle
.getBundle("org.wso2.carbon.event.output.adapter.rdbms.i18n.Resources", Locale.getDefault());
populateDbMappings();
}
@Override
public void testConnect() throws TestConnectionNotSupportedException {
DataSource dataSource;
Connection con = null;
try {
CarbonDataSource carbonDataSource = RDBMSEventAdapterServiceValueHolder.getDataSourceService().
getDataSource(eventAdapterConfiguration.getStaticProperties().
get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_DATASOURCE_NAME));
if (carbonDataSource != null) {
dataSource = (DataSource) carbonDataSource.getDSObject();
con = dataSource.getConnection();
} else {
throw new OutputEventAdapterRuntimeException("There is no datasource found by the name "
+ RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_DATASOURCE_NAME + " to connect.");
}
} catch (Exception e) {
throw new OutputEventAdapterRuntimeException(e);
} finally {
cleanupConnections(null, con);
}
}
@Override
public void connect() {
Connection con = null;
try {
CarbonDataSource carbonDataSource = RDBMSEventAdapterServiceValueHolder.getDataSourceService()
.getDataSource(
eventAdapterConfiguration.getStaticProperties().get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_DATASOURCE_NAME));
dataSource = (DataSource) carbonDataSource.getDSObject();
con = ((DataSource) carbonDataSource.getDSObject()).getConnection();
} catch (DataSourceException e) {
log.error("No data-source found by the name: " + eventAdapterConfiguration.getStaticProperties()
.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_DATASOURCE_NAME), e);
throw new ConnectionUnavailableException(e.getMessage(), e);
} catch (SQLException e) {
throw new ConnectionUnavailableException(e);
} finally {
cleanupConnections(null, con);
}
}
@Override
public void publish(Object message, Map<String, String> dynamicProperties) {
String tableName;
try {
if (message instanceof Map) {
tableName = eventAdapterConfiguration.getStaticProperties().get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_TABLE_NAME);
String executionMode = eventAdapterConfiguration.getStaticProperties().get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_EXECUTION_MODE);
String updateColumnKeys = eventAdapterConfiguration.getStaticProperties().get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_UPDATE_KEYS);
if (executionInfo == null) {
executionInfo = new ExecutionInfo();
initializeDatabaseExecutionInfo(tableName, executionMode, updateColumnKeys, message);
}
executeProcessActions(message, tableName);
} else {
throw new OutputEventAdapterRuntimeException(
message.getClass().toString() + "is not a compatible type. Hence Event is dropped.");
}
} catch (OutputEventAdapterException e) {
log.error(e.getMessage() + " Hence Event is dropped.", e);
}
}
/**
* Construct all the queries and assign to executionInfo instance
*/
private void initializeDatabaseExecutionInfo(String tableName, String executionMode, String updateColumnKeys,
Object message) {
if (resourceBundle.getString(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_EXECUTION_MODE_UPDATE)
.equalsIgnoreCase(executionMode)) {
isUpdate = true;
executionInfo.setUpdateMode(true);
}
//Constructing (eg: ID varchar2(255),INFORMATION varchar2(255)) type values : columnTypes
StringBuilder columnTypes = new StringBuilder("");
//Constructing (eg: id,information) type values : columns
StringBuilder columns = new StringBuilder("");
//Constructing (eg: ?,?,?) type values : valuePositionsBuilder
StringBuilder valuePositionsBuilder = new StringBuilder("");
List<Attribute> tableInsertColumnList = new ArrayList<Attribute>();
boolean appendComma = false;
for (Map.Entry<String, Object> entry : (((Map<String, Object>) message).entrySet())) {
AttributeType type = null;
String columnName = entry.getKey().toUpperCase();
if (appendComma) {
columnTypes.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_COMMA));
}
columnTypes.append(columnName).append(" ");
if (entry.getValue() instanceof Integer) {
type = AttributeType.INT;
columnTypes.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_INTEGER));
} else if (entry.getValue() instanceof Long) {
type = AttributeType.LONG;
columnTypes.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_LONG));
} else if (entry.getValue() instanceof Float) {
type = AttributeType.FLOAT;
columnTypes.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_FLOAT));
} else if (entry.getValue() instanceof Double) {
type = AttributeType.DOUBLE;
columnTypes.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_DOUBLE));
} else if (entry.getValue() instanceof String) {
type = AttributeType.STRING;
columnTypes.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_STRING));
} else if (entry.getValue() instanceof Boolean) {
type = AttributeType.BOOL;
columnTypes.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_BOOLEAN));
}
Attribute attribute = new Attribute(entry.getKey(), type);
if (appendComma) {
columns.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_COMMA));
valuePositionsBuilder
.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_COMMA));
} else {
appendComma = true;
}
tableInsertColumnList.add(attribute);
columns.append(attribute.getName());
valuePositionsBuilder
.append(dbTypeMappings.get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_QUESTION_MARK));
}
//Constructing query to create a new table
String createTableQuery = constructQuery(tableName, dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_CREATE_TABLE), columnTypes, null, null, null, null);
//constructing query to insert date into the table row
String insertTableRowQuery = constructQuery(tableName, dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_INSERT_DATA), null, columns, valuePositionsBuilder, null, null);
//Constructing query to check for the table existence
String isTableExistQuery = constructQuery(tableName, dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_TABLE_EXIST), null, null, null, null, null);
executionInfo.setPreparedInsertStatement(insertTableRowQuery);
executionInfo.setPreparedCreateTableStatement(createTableQuery);
executionInfo.setInsertQueryColumnOrder(tableInsertColumnList);
executionInfo.setPreparedTableExistenceCheckStatement(isTableExistQuery);
if (executionMode.equalsIgnoreCase(
resourceBundle.getString(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_EXECUTION_MODE_UPDATE))) {
String[] queryAttributes = updateColumnKeys.trim().split(",");
List<Attribute> queryAttributeList = new ArrayList<Attribute>(queryAttributes.length);
for (String queryAttribute : queryAttributes) {
for (Attribute attribute : executionInfo.getInsertQueryColumnOrder()) {
if (queryAttribute.trim().equalsIgnoreCase(attribute.getName())) {
queryAttributeList.add(attribute);
break;
}
}
}
executionInfo.setExistenceCheckQueryColumnOrder(queryAttributeList);
//Constructing (eg: information = ? , latitude = ?) type values : columnValues
StringBuilder columnValues = new StringBuilder("");
List<Attribute> updateAttributes = new ArrayList<Attribute>();
appendComma = false;
for (Attribute at : executionInfo.getInsertQueryColumnOrder()) {
if (!executionInfo.getExistenceCheckQueryColumnOrder().contains(at)) {
if (appendComma) {
columnValues.append(" ").append(dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_COMMA)).append(" ");
}
columnValues.append(at.getName());
columnValues.append(" ").append(dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_EQUAL)).append(" ")
.append(dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_QUESTION_MARK)).append(" ");
updateAttributes.add(at);
appendComma = true;
}
}
//Constructing (eg: id = ?) type values for WHERE condition : condition
StringBuilder condition = new StringBuilder("");
boolean appendAnd = false;
for (Attribute at : executionInfo.getExistenceCheckQueryColumnOrder()) {
if (appendAnd) {
condition.append(" ").append(dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_AND)).append(" ");
}
condition.append(at.getName());
condition.append(" ").append(dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_EQUAL)).append(" ")
.append(dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_QUESTION_MARK)).append(" ");
updateAttributes.add(at);
appendAnd = true;
}
executionInfo.setUpdateQueryColumnOrder(updateAttributes);
//constructing query to update data into the table
String tableUpdateRowQuery = constructQuery(tableName, dbTypeMappings.get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_UPDATE_TABLE), null, null, null, columnValues, condition);
executionInfo.setPreparedUpdateStatement(tableUpdateRowQuery);
}
}
public void executeProcessActions(Object message, String tableName)
throws OutputEventAdapterException {
createTableIfNotExist(tableName);
if (isUpdate) {
synchronized (this) {
executeDbActions(message);
}
} else {
executeDbActions(message);
}
}
public void executeDbActions(Object message)
throws OutputEventAdapterException {
PreparedStatement stmt = null;
Connection con = null;
Map<String, Object> map = (Map<String, Object>) message;
boolean executeInsert = true;
try {
try {
con = dataSource.getConnection();
con.setAutoCommit(false);
} catch (SQLException e) {
throw new ConnectionUnavailableException(e);
}
if (executionInfo.isUpdateMode()) {
stmt = con.prepareStatement(executionInfo.getPreparedUpdateStatement());
populateStatement(map, stmt, executionInfo.getUpdateQueryColumnOrder());
int updatedRows = stmt.executeUpdate();
con.commit();
stmt.close();
if (updatedRows > 0) {
executeInsert = false;
}
}
if (executeInsert) {
stmt = con.prepareStatement(executionInfo.getPreparedInsertStatement());
populateStatement(map, stmt, executionInfo.getInsertQueryColumnOrder());
stmt.executeUpdate();
con.commit();
}
} catch (SQLException e) {
throw new OutputEventAdapterException(
"Cannot Execute Insert/Update Query for event " + message.toString() + " " + e.getMessage(), e);
} finally {
cleanupConnections(stmt, con);
}
}
/**
* Populating column values to table Insert query
*/
private void populateStatement(Map<String, Object> map, PreparedStatement stmt, List<Attribute> colOrder)
throws OutputEventAdapterException {
Attribute attribute = null;
try {
for (int i = 0; i < colOrder.size(); i++) {
attribute = colOrder.get(i);
Object value = map.get(attribute.getName());
if (value != null || attribute.getType() == AttributeType.STRING) {
switch (attribute.getType()) {
case INT:
stmt.setInt(i + 1, (Integer) value);
break;
case LONG:
stmt.setLong(i + 1, (Long) value);
break;
case FLOAT:
stmt.setFloat(i + 1, (Float) value);
break;
case DOUBLE:
stmt.setDouble(i + 1, (Double) value);
break;
case STRING:
stmt.setString(i + 1, (String) value);
break;
case BOOL:
stmt.setBoolean(i + 1, (Boolean) value);
break;
}
} else {
throw new OutputEventAdapterException("Cannot Execute Insert/Update. Null value detected for " +
"attribute" + attribute.getName());
}
}
} catch (SQLException e) {
cleanupConnections(stmt, null);
throw new OutputEventAdapterException("Cannot set value to attribute name " + attribute.getName() + ". " +
"Hence dropping the event." + e.getMessage(), e);
}
}
public void createTableIfNotExist(String tableName)
throws OutputEventAdapterException {
if (!executionInfo.isTableExist()) {
Statement stmt = null;
Boolean tableExists = true;
Connection con = null;
try {
try {
con = dataSource.getConnection();
con.setAutoCommit(false);
} catch (SQLException e) {
throw new ConnectionUnavailableException(e);
}
stmt = con.createStatement();
try {
stmt.executeQuery(executionInfo.getPreparedTableExistenceCheckStatement());
executionInfo.setTableExist(true);
} catch (SQLException e) {
tableExists = false;
if (log.isDebugEnabled()) {
log.debug("Table " + tableName + " does not Exist. Table Will be created. ");
}
}
try {
if (!tableExists) {
stmt.executeUpdate(executionInfo.getPreparedCreateTableStatement());
con.commit();
executionInfo.setTableExist(true);
}
} catch (SQLException e) {
throw new OutputEventAdapterException("Cannot Execute Create Table Query. " + e.getMessage(), e);
}
} catch (SQLException e) {
throw new ConnectionUnavailableException(e);
} finally {
cleanupConnections(stmt, con);
}
}
}
private void cleanupConnections(Statement stmt, Connection connection) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
log.error("unable to close statement." + e.getMessage(), e);
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
log.error("unable to close connection." + e.getMessage(), e);
}
}
}
/**
* Replace attribute values with target build queries
*/
public String constructQuery(String tableName, String query, StringBuilder columnTypes, StringBuilder columns,
StringBuilder values, StringBuilder columnValues, StringBuilder condition) {
if (query.contains(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_TABLE_NAME)) {
query = query.replace(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_TABLE_NAME, tableName);
}
if (query.contains(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_COLUMN_TYPES)) {
query = query.replace(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_COLUMN_TYPES,
columnTypes.toString());
}
if (query.contains(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_COLUMNS)) {
query = query.replace(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_COLUMNS,
columns.toString());
}
if (query.contains(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_VALUES)) {
query = query.replace(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_VALUES, values.toString());
}
if (query.contains(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_COLUMN_VALUES)) {
query = query.replace(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_COLUMN_VALUES,
columnValues.toString());
}
if (query.contains(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_CONDITION)) {
query = query.replace(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_ATTRIBUTE_CONDITION,
condition.toString());
}
return query;
}
/**
* Populate specific db Mappings
*/
private void populateDbMappings() throws OutputEventAdapterException {
String dbName = null;
dbTypeMappings = new HashMap<String, String>();
Connection con = null;
try {
CarbonDataSource carbonDataSource = RDBMSEventAdapterServiceValueHolder.getDataSourceService()
.getDataSource(
eventAdapterConfiguration.getStaticProperties().get(RDBMSEventAdapterConstants
.ADAPTER_GENERIC_RDBMS_DATASOURCE_NAME));
if (carbonDataSource != null) {
con = ((DataSource) carbonDataSource.getDSObject()).getConnection();
DatabaseMetaData databaseMetaData = con.getMetaData();
dbName = databaseMetaData.getDatabaseProductName();
dbName = dbName.toLowerCase();
} else {
throw new OutputEventAdapterException("There is no data-source called " + eventAdapterConfiguration
.getStaticProperties().get(RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_DATASOURCE_NAME));
}
} catch (DataSourceException e) {
log.error(
"There is no data-source found by the name: " + eventAdapterConfiguration.getStaticProperties().get(
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_DATASOURCE_NAME), e);
throw new ConnectionUnavailableException(e.getMessage(), e);
} catch (SQLException e) {
throw new ConnectionUnavailableException(e);
} finally {
cleanupConnections(null, con);
}
// Map<String, String> defaultMappings = new HashMap<String, String>();
String[] staticAttributes = {RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_STRING,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_DOUBLE,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_INTEGER,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_LONG,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_FLOAT,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_BOOLEAN,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_CREATE_TABLE,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_INSERT_DATA,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_TABLE_EXIST,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_UPDATE_TABLE,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_PROPERTY_DATA_TYPE_IN_TABLE,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_PROPERTY_SELECT_FROM_TABLE,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_COMMA,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_QUESTION_MARK,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_EQUAL,
RDBMSEventAdapterConstants.ADAPTER_GENERIC_RDBMS_AND};
Boolean staticAttributeExist;
String attribute = null;
Map<String, String> defaultMappings = new HashMap<String, String>();
for (String staticAttribute : staticAttributes) {
staticAttributeExist = false;
for (Map.Entry<String, String> entry : globalProperties.entrySet()) {
attribute = staticAttribute;
if (staticAttribute.equals(entry.getKey())) {
staticAttributeExist = true;
defaultMappings.put(entry.getKey(), entry.getValue());
break;
}
}
if (!staticAttributeExist) {
throw new OutputEventAdapterRuntimeException("A mandatory attribute " + attribute + " does not exist");
}
}
Boolean valueExist;
for (Map.Entry<String, String> defaultMap : defaultMappings.entrySet()) {
valueExist = false;
for (Map.Entry<String, String> entry : globalProperties.entrySet()) {
if (entry.getKey().contains(dbName)) {
if (entry.getKey().contains(defaultMap.getKey())) {
dbTypeMappings.put(defaultMap.getKey(), entry.getValue());
valueExist = true;
break;
}
}
}
if (!valueExist) {
dbTypeMappings.put(defaultMap.getKey(), defaultMap.getValue());
}
}
}
@Override
public void disconnect() {
if (dataSource != null) {
dataSource = null;
}
if (executionInfo != null) {
executionInfo.setTableExist(false);
}
}
@Override
public void destroy() {
if (executionInfo != null) {
executionInfo = null;
}
if (dataSource != null) {
dataSource = null;
}
}
@Override
public boolean isPolled() {
return false;
}
}