package uk.bl.monitrix.database.cassandra.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import play.Logger;
import uk.bl.monitrix.database.cassandra.CassandraProperties;
import uk.bl.monitrix.model.Alert;
import uk.bl.monitrix.model.Alert.AlertType;
import uk.bl.monitrix.model.AlertLog;
import uk.bl.monitrix.model.CrawlLog;
/**
* A CassandraDB-backed implementation of {@link AlertLog}.
* @author Rainer Simon <rainer.simon@ait.ac.at>
*/
public class CassandraAlertLog implements AlertLog {
private static long HOUR_IN_MILLIS = 60 * 60 * 1000;
private final String TABLE_ALERTS = CassandraProperties.KEYSPACE + "." + CassandraProperties.COLLECTION_ALERT_LOG;
protected Session session;
private CrawlLog crawlLog;
public CassandraAlertLog(Session session, CrawlLog crawlLog) {
this.session = session;
this.crawlLog = crawlLog;
}
@Override
public long countAll() {
long count = 0L;
try {
ResultSet results = session.execute("SELECT COUNT(*) FROM " + TABLE_ALERTS + ";");
count = results.one().getLong("count");
} catch(NoHostAvailableException ex) {
Logger.warn("No hosts available ...");
}
return count;
}
@Override
public Iterator<Alert> listAll() {
return map(session.execute("SELECT * FROM " + TABLE_ALERTS + ";").iterator());
}
private List<Alert> appendAlertsUntil(List<Alert> alerts, int limit, long hour) {
List<Alert> alertsForThisHour = new ArrayList<Alert>();
try {
Iterator<Row> rows = session.execute("SELECT * FROM " + TABLE_ALERTS + " WHERE " + CassandraProperties.FIELD_ALERT_LOG_TIMESTAMP_HR + "=" +
hour + " ALLOW FILTERING ;").iterator();
while (rows.hasNext())
alertsForThisHour.add(new CassandraAlert(rows.next()));
Collections.sort(alertsForThisHour, new Comparator<Alert>() {
@Override
public int compare(Alert a, Alert b) {
return (int) (a.getTimestamp() - b.getTimestamp());
}
});
int counter = 0;
while (alerts.size() < limit && counter < alertsForThisHour.size()) {
alerts.add(alertsForThisHour.get(counter));
counter++;
}
} catch (NoHostAvailableException ex) {
Logger.warn("No hosts available ...");
}
if (alerts.size() != 0 && alerts.size() < limit) {
long crawlStart = (crawlLog.getCrawlStartTime() / HOUR_IN_MILLIS) * HOUR_IN_MILLIS;
long previousHour = hour - HOUR_IN_MILLIS;
if (previousHour >= crawlStart)
return appendAlertsUntil(alerts, limit, previousHour);
else
return alerts;
} else {
return alerts;
}
}
@Override
public List<Alert> getMostRecent(int n) {
long hourOflastActivity = (crawlLog.getTimeOfLastCrawlActivity() / HOUR_IN_MILLIS) * HOUR_IN_MILLIS;
return appendAlertsUntil(new ArrayList<Alert>(), n, hourOflastActivity);
}
@Override
public List<String> getOffendingHosts() {
List<String> hostList = new ArrayList<String>();
try {
Iterator<Row> rows = session.execute("SELECT " + CassandraProperties.FIELD_ALERT_LOG_OFFENDING_HOST + " FROM " +
TABLE_ALERTS + ";").iterator();
Set<String> hosts = new HashSet<String>();
while (rows.hasNext()) {
hosts.add(rows.next().getString(CassandraProperties.FIELD_ALERT_LOG_OFFENDING_HOST));
}
hostList.addAll(hosts);
} catch (NoHostAvailableException ex) {
Logger.warn("No hosts available ...");
}
return hostList;
}
@Override
public long countAlertsForHost(String hostname) {
long count = 0L;
try {
ResultSet results = session.execute("SELECT COUNT(*) FROM " + TABLE_ALERTS + " WHERE " +
CassandraProperties.FIELD_ALERT_LOG_OFFENDING_HOST + "='" + hostname + "';");
count = results.one().getLong("count");
} catch (NoHostAvailableException ex) {
Logger.warn("No hosts available ...");
}
return count;
}
@Override
public long countAlertsForHost(String hostname, AlertType type) {
long count = 0L;
try {
ResultSet results = session.execute("SELECT COUNT(*) FROM " + TABLE_ALERTS + " WHERE " +
CassandraProperties.FIELD_ALERT_LOG_OFFENDING_HOST + "='" + hostname + "' AND " +
CassandraProperties.FIELD_ALERT_LOG_ALERT_TYPE + "='" + type.name() + "' ALLOW FILTERING;");
count = results.one().getLong("count");
} catch (NoHostAvailableException ex) {
Logger.warn("No hosts available ...");
}
return count;
}
@Override
public List<AlertType> getAlertTypesForHost(String hostname) {
List<AlertType> list = new ArrayList<AlertType>();
try {
Iterator<Row> rows = session.execute("SELECT " + CassandraProperties.FIELD_ALERT_LOG_ALERT_TYPE + " FROM " +
TABLE_ALERTS + " WHERE " + CassandraProperties.FIELD_ALERT_LOG_OFFENDING_HOST + "='" + hostname + "';").iterator();
Set<AlertType> set = new HashSet<AlertType>();
while (rows.hasNext()) {
set.add(AlertType.valueOf(rows.next().getString(CassandraProperties.FIELD_ALERT_LOG_ALERT_TYPE)));
}
list.addAll(set);
} catch (NoHostAvailableException ex) {
Logger.warn("No hosts available ...");
}
return list;
}
@Override
public Iterator<Alert> listAlertsForHost(String hostname) {
return map(session.execute("SELECT * FROM " + TABLE_ALERTS + " WHERE " + CassandraProperties.FIELD_ALERT_LOG_OFFENDING_HOST + "='" +
hostname + "';").iterator());
}
/**
* Utility method that maps a database cursor to an Iterator of Alert domain objects.
* @param cursor the DB cursor
* @return the domain objects
*/
private static Iterator<Alert> map(final Iterator<Row> cursor) {
return new Iterator<Alert>() {
@Override
public boolean hasNext() {
return cursor.hasNext();
}
@Override
public Alert next() {
return new CassandraAlert(cursor.next());
}
@Override
public void remove() {
cursor.remove();
}
};
}
}