package ru.semiot.services.tsdbservice;
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
import static java.time.temporal.ChronoField.NANO_OF_SECOND;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
import static ru.semiot.services.tsdbservice.ServiceConfig.CONFIG;
import com.datastax.driver.core.BatchStatement;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.semiot.services.tsdbservice.model.Observation;
import rx.Observable;
import rx.Subscriber;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.List;
public class TSDBClient {
private static final Logger logger = LoggerFactory.getLogger(TSDBClient.class);
private static final String CREATE_KEYSPACE =
"CREATE KEYSPACE IF NOT EXISTS semiot WITH replication = { "
+ " 'class': 'SimpleStrategy', "
+ " 'replication_factor': 1 } ;";
private static final String CREATE_OBSERVATION_TABLE =
"CREATE TABLE IF NOT EXISTS semiot.observation ("
+ " sensor_id text,"
+ " system_id text,"
+ " event_time timestamp,"
+ " property text,"
+ " feature_of_interest text,"
+ " value text,"
+ " PRIMARY KEY ((system_id,sensor_id),event_time)"
+ ")" +
"WITH CLUSTERING ORDER BY (event_time DESC);";
private static final String CREATE_COMMAND_PARAMETER_TYPE =
"CREATE TYPE IF NOT EXISTS semiot.command_parameter (" +
"for_parameter text, value text, datatype text" +
");";
private static final String CREATE_COMMAND_PROPERTY_TYPE =
"CREATE TYPE IF NOT EXISTS semiot.command_property (" +
"property text," +
"value text," +
"datatype text" +
");";
private static final String CREATE_COMMANDRESULT_TABLE =
"CREATE TABLE IF NOT EXISTS semiot.commandresult (" +
" system_id text," +
" process_id text," +
" event_time timestamp," +
" command_type text," +
" command_properties list<frozen <command_property>>," +
" command_parameters list<frozen <command_parameter>>," +
" PRIMARY KEY((system_id, process_id), event_time)" +
")" +
"WITH CLUSTERING ORDER BY (event_time DESC);";
private static final DateTimeFormatter CQL_TIMESTAMP_FORMAT = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendValue(HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 2)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2)
.optionalStart()
.appendFraction(NANO_OF_SECOND, 2, 9, true)
.appendOffsetId()
.parseStrict()
.toFormatter();
private static volatile TSDBClient instance = null;
Cluster cluster;
Session session;
private TSDBClient() {}
public static TSDBClient getInstance() {
if (instance == null) {
synchronized (TSDBClient.class) {
if (instance == null) {
instance = new TSDBClient();
}
}
}
return instance;
}
public static Subscriber onError() {
return new Subscriber() {
@Override
public void onCompleted() {}
@Override
public void onError(Throwable e) {
logger.error(e.getMessage(), e);
}
@Override
public void onNext(Object o) {}
};
}
public void init() {
logger.info("Connecting to {}", CONFIG.tsdbUrl());
cluster = Cluster.builder().addContactPoint(CONFIG.tsdbUrl()).build();
session = cluster.connect();
createTables();
logger.info("Connected to {}", CONFIG.tsdbUrl());
}
private void createTables() {
session.execute(CREATE_KEYSPACE);
logger.info("Created 'semiot' keyspace");
session.execute(CREATE_OBSERVATION_TABLE);
session.execute(CREATE_COMMAND_PARAMETER_TYPE);
session.execute(CREATE_COMMAND_PROPERTY_TYPE);
session.execute(CREATE_COMMANDRESULT_TABLE);
logger.info("Created types and tables!");
}
public void stop() {
cluster.close();
}
public void write(Observation obs) {
session.execute(obs.insert());
}
public Observable<ResultSet> executeAsync(Statement statement) {
ResultSetFuture rsf = session.executeAsync(statement);
return Observable.from(rsf);
}
public Observable<ResultSet> executeAsync(String query) {
ResultSetFuture rsf = session.executeAsync(query);
return Observable.from(rsf);
}
public Observable<ResultSet> executeAsync(List<String> queries) {
BatchStatement batch = new BatchStatement();
for (String query : queries) {
batch.add(new SimpleStatement(query));
}
ResultSetFuture rsf = session.executeAsync(batch);
return Observable.from(rsf);
}
public static String formatToCQLTimestamp(ZonedDateTime dateTime) {
return dateTime.format(CQL_TIMESTAMP_FORMAT);
}
}