/** * Copyright (C) 2014 Stratio (http://stratio.com) * * Licensed 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 com.stratio.ingestion.sink.cassandra; import static com.stratio.ingestion.sink.cassandra.CassandraUtils.parseValue; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.flume.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.datastax.driver.core.BatchStatement; import com.datastax.driver.core.ColumnMetadata; import com.datastax.driver.core.ConsistencyLevel; import com.datastax.driver.core.Session; import com.datastax.driver.core.TableMetadata; import com.datastax.driver.core.querybuilder.Insert; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.google.common.base.Charsets; class CassandraTable { private static final Logger log = LoggerFactory.getLogger(CassandraTable.class); private final Session session; private final TableMetadata table; private final ConsistencyLevel consistencyLevel; private final String bodyColumn; private final List<ColumnMetadata> columns; private final int totalColumns; private final List<String> primaryKeys; private final boolean ignoreCase; public CassandraTable( final Session session, final TableMetadata table, final ConsistencyLevel consistencyLevel, final String bodyColumn) { this(session, table, consistencyLevel, bodyColumn, false); } public CassandraTable( final Session session, final TableMetadata table, final ConsistencyLevel consistencyLevel, final String bodyColumn, final boolean ignoreCase) { this.session = session; this.table = table; this.consistencyLevel = consistencyLevel; this.bodyColumn = bodyColumn; this.columns = table.getColumns(); this.totalColumns = this.columns.size(); this.primaryKeys = new ArrayList<String>(); for (final ColumnMetadata column : table.getPrimaryKey()) { primaryKeys.add(column.getName()); } this.ignoreCase = ignoreCase; } public void save(final List<Event> events) { final BatchStatement batch = new BatchStatement(); for (final Event event : events) { final Map<String, Object> parsedEvent = parse(event); if (parsedEvent.isEmpty()) { log.warn("Event {} could not be mapped. Suggestion: Cassandra is case sensitive, so maybe you can check field names.", event); continue; } if (!hasPrimaryKey(parsedEvent)) { break; } final Insert insert = QueryBuilder.insertInto(table); for (final Map.Entry<String, Object> entry : parsedEvent.entrySet()) { insert.value(entry.getKey(), entry.getValue()); } if (log.isTraceEnabled()) { log.trace("Preparing insert for table {}: {}", table.getName(), insert.getQueryString()); } batch.add(insert); } if (batch.getStatements().isEmpty()) { log.warn("No event produced insert query for table {}", table.getName()); return; } batch.setConsistencyLevel(consistencyLevel); session.execute(batch); } private boolean hasPrimaryKey(final Map<String, Object> parsedEvent) { for (final String primaryKey : primaryKeys) { if (!parsedEvent.containsKey(primaryKey)) { log.info("Event {} misses primary key ({}), skipping", parsedEvent, primaryKey); return false; } } return true; } public Map<String, Object> parse(final Event event) { // translate to lowercase for ignorecase option final Map<String, String> headers = ignoreCase ? processHeadersIgnoreCase(event.getHeaders()) : event.getHeaders(); final int maxValues = Math.min(headers.size(), totalColumns); final Map<String, Object> result = new HashMap<String, Object>(maxValues); for (final ColumnMetadata column : columns) { final String columnName = ignoreCase ? column.getName().toLowerCase() : column.getName(); if (headers.containsKey(columnName) && !columnName.equals(bodyColumn)) { result.put(columnName, parseValue(column.getType(), headers.get(columnName))); } else if (columnName.equals(bodyColumn)) { result.put(columnName, parseValue(column.getType(), new String(event.getBody(), Charsets.UTF_8))); } } return result; } private Map<String, String> processHeadersIgnoreCase(Map<String, String> headers) { Map<String, String> headersLowerCase = new HashMap<>(headers.size()); Iterator<Entry<String, String>> iter = headers.entrySet().iterator(); Entry<String, String> entry; String keyInLowerCase; while (iter.hasNext()) { entry = iter.next(); keyInLowerCase = entry.getKey().toLowerCase(); headersLowerCase.put(keyInLowerCase, entry.getValue()); } return headersLowerCase; } }