/*
* Copyright (C) 2015 Noorq, Inc.
*
* 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.noorq.casser.core;
import java.io.Closeable;
import java.io.PrintStream;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Function;
import com.datastax.driver.core.CloseFuture;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.noorq.casser.core.operation.CountOperation;
import com.noorq.casser.core.operation.DeleteOperation;
import com.noorq.casser.core.operation.InsertOperation;
import com.noorq.casser.core.operation.SelectOperation;
import com.noorq.casser.core.operation.UpdateOperation;
import com.noorq.casser.core.reflect.CasserPropertyNode;
import com.noorq.casser.mapping.CasserEntity;
import com.noorq.casser.mapping.MappingUtil;
import com.noorq.casser.mapping.value.ColumnValuePreparer;
import com.noorq.casser.mapping.value.ColumnValueProvider;
import com.noorq.casser.mapping.value.RowColumnValueProvider;
import com.noorq.casser.mapping.value.StatementColumnValuePreparer;
import com.noorq.casser.mapping.value.ValueProviderMap;
import com.noorq.casser.support.Fun;
import com.noorq.casser.support.Fun.Tuple1;
import com.noorq.casser.support.Fun.Tuple2;
import com.noorq.casser.support.Fun.Tuple6;
public final class CasserSession extends AbstractSessionOperations implements Closeable {
private final Session session;
private volatile String usingKeyspace;
private volatile boolean showCql;
private final PrintStream printStream;
private final SessionRepository sessionRepository;
private final Executor executor;
private final boolean dropSchemaOnClose;
private final RowColumnValueProvider valueProvider;
private final StatementColumnValuePreparer valuePreparer;
CasserSession(Session session,
String usingKeyspace,
boolean showCql,
PrintStream printStream,
SessionRepositoryBuilder sessionRepositoryBuilder,
Executor executor,
boolean dropSchemaOnClose) {
this.session = session;
this.usingKeyspace = Objects.requireNonNull(usingKeyspace, "keyspace needs to be selected before creating session");
this.showCql = showCql;
this.printStream = printStream;
this.sessionRepository = sessionRepositoryBuilder.build();
this.executor = executor;
this.dropSchemaOnClose = dropSchemaOnClose;
this.valueProvider = new RowColumnValueProvider(this.sessionRepository);
this.valuePreparer = new StatementColumnValuePreparer(this.sessionRepository);
}
@Override
public Session currentSession() {
return session;
}
@Override
public String usingKeyspace() {
return usingKeyspace;
}
public CasserSession useKeyspace(String keyspace) {
session.execute(SchemaUtil.use(keyspace, false));
this.usingKeyspace = keyspace;
return this;
}
@Override
public boolean isShowCql() {
return showCql;
}
@Override
public PrintStream getPrintStream() {
return printStream;
}
public CasserSession showCql() {
this.showCql = true;
return this;
}
public CasserSession showCql(boolean showCql) {
this.showCql = showCql;
return this;
}
@Override
public Executor getExecutor() {
return executor;
}
@Override
public SessionRepository getSessionRepository() {
return sessionRepository;
}
@Override
public ColumnValueProvider getValueProvider() {
return valueProvider;
}
@Override
public ColumnValuePreparer getValuePreparer() {
return valuePreparer;
}
public <E> SelectOperation<E> select(Class<E> entityClass) {
Objects.requireNonNull(entityClass, "entityClass is empty");
ColumnValueProvider valueProvider = getValueProvider();
CasserEntity entity = Casser.entity(entityClass);
return new SelectOperation<E>(this, entity, (r) -> {
Map<String, Object> map = new ValueProviderMap(r, valueProvider, entity);
return (E) Casser.map(entityClass, map);
});
}
public SelectOperation<Fun.ArrayTuple> select() {
return new SelectOperation<Fun.ArrayTuple>(this);
}
public SelectOperation<Row> selectAll(Class<?> entityClass) {
Objects.requireNonNull(entityClass, "entityClass is empty");
return new SelectOperation<Row>(this, Casser.entity(entityClass));
}
public <E> SelectOperation<E> selectAll(Class<E> entityClass, Function<Row, E> rowMapper) {
Objects.requireNonNull(entityClass, "entityClass is empty");
Objects.requireNonNull(rowMapper, "rowMapper is empty");
return new SelectOperation<E>(this, Casser.entity(entityClass), rowMapper);
}
public <V1> SelectOperation<Fun.Tuple1<V1>> select(Getter<V1> getter1) {
Objects.requireNonNull(getter1, "field 1 is empty");
CasserPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
return new SelectOperation<Tuple1<V1>>(this, new Mappers.Mapper1<V1>(getValueProvider(), p1), p1);
}
public <V1, V2> SelectOperation<Tuple2<V1, V2>> select(Getter<V1> getter1, Getter<V2> getter2) {
Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty");
CasserPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
CasserPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2);
return new SelectOperation<Fun.Tuple2<V1, V2>>(this, new Mappers.Mapper2<V1, V2>(getValueProvider(), p1, p2), p1, p2);
}
public <V1, V2, V3> SelectOperation<Fun.Tuple3<V1, V2, V3>> select(Getter<V1> getter1, Getter<V2> getter2, Getter<V3> getter3) {
Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty");
CasserPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
CasserPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2);
CasserPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3);
return new SelectOperation<Fun.Tuple3<V1, V2, V3>>(this, new Mappers.Mapper3<V1, V2, V3>(getValueProvider(), p1, p2, p3), p1, p2, p3);
}
public <V1, V2, V3, V4> SelectOperation<Fun.Tuple4<V1, V2, V3, V4>> select(
Getter<V1> getter1, Getter<V2> getter2, Getter<V3> getter3, Getter<V4> getter4) {
Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty");
Objects.requireNonNull(getter4, "field 4 is empty");
CasserPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
CasserPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2);
CasserPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3);
CasserPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4);
return new SelectOperation<Fun.Tuple4<V1, V2, V3, V4>>(this, new Mappers.Mapper4<V1, V2, V3, V4>(getValueProvider(), p1, p2, p3, p4), p1, p2, p3, p4);
}
public <V1, V2, V3, V4, V5> SelectOperation<Fun.Tuple5<V1, V2, V3, V4, V5>> select(
Getter<V1> getter1, Getter<V2> getter2, Getter<V3> getter3, Getter<V4> getter4, Getter<V5> getter5) {
Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty");
Objects.requireNonNull(getter4, "field 4 is empty");
Objects.requireNonNull(getter5, "field 5 is empty");
CasserPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
CasserPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2);
CasserPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3);
CasserPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4);
CasserPropertyNode p5 = MappingUtil.resolveMappingProperty(getter5);
return new SelectOperation<Fun.Tuple5<V1, V2, V3, V4, V5>>(this,
new Mappers.Mapper5<V1, V2, V3, V4, V5>(getValueProvider(), p1, p2, p3, p4, p5),
p1, p2, p3, p4, p5);
}
public <V1, V2, V3, V4, V5, V6> SelectOperation<Fun.Tuple6<V1, V2, V3, V4, V5, V6>> select(
Getter<V1> getter1, Getter<V2> getter2, Getter<V3> getter3,
Getter<V4> getter4, Getter<V5> getter5, Getter<V6> getter6) {
Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty");
Objects.requireNonNull(getter4, "field 4 is empty");
Objects.requireNonNull(getter5, "field 5 is empty");
Objects.requireNonNull(getter6, "field 6 is empty");
CasserPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
CasserPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2);
CasserPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3);
CasserPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4);
CasserPropertyNode p5 = MappingUtil.resolveMappingProperty(getter5);
CasserPropertyNode p6 = MappingUtil.resolveMappingProperty(getter6);
return new SelectOperation<Tuple6<V1, V2, V3, V4, V5, V6>>(this,
new Mappers.Mapper6<V1, V2, V3, V4, V5, V6>(getValueProvider(), p1, p2, p3, p4, p5, p6),
p1, p2, p3, p4, p5, p6);
}
public <V1, V2, V3, V4, V5, V6, V7> SelectOperation<Fun.Tuple7<V1, V2, V3, V4, V5, V6, V7>> select(
Getter<V1> getter1, Getter<V2> getter2, Getter<V3> getter3,
Getter<V4> getter4, Getter<V5> getter5, Getter<V6> getter6,
Getter<V7> getter7) {
Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty");
Objects.requireNonNull(getter4, "field 4 is empty");
Objects.requireNonNull(getter5, "field 5 is empty");
Objects.requireNonNull(getter6, "field 6 is empty");
Objects.requireNonNull(getter7, "field 7 is empty");
CasserPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
CasserPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2);
CasserPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3);
CasserPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4);
CasserPropertyNode p5 = MappingUtil.resolveMappingProperty(getter5);
CasserPropertyNode p6 = MappingUtil.resolveMappingProperty(getter6);
CasserPropertyNode p7 = MappingUtil.resolveMappingProperty(getter7);
return new SelectOperation<Fun.Tuple7<V1, V2, V3, V4, V5, V6, V7>>(this,
new Mappers.Mapper7<V1, V2, V3, V4, V5, V6, V7>(
getValueProvider(),
p1, p2, p3, p4, p5, p6, p7),
p1, p2, p3, p4, p5, p6, p7);
}
public CountOperation count() {
return new CountOperation(this);
}
public CountOperation count(Object dsl) {
Objects.requireNonNull(dsl, "dsl is empty");
return new CountOperation(this, Casser.resolve(dsl));
}
public <V> UpdateOperation update() {
return new UpdateOperation(this);
}
public <V> UpdateOperation update(Getter<V> getter, V v) {
Objects.requireNonNull(getter, "field is empty");
Objects.requireNonNull(v, "value is empty");
CasserPropertyNode p = MappingUtil.resolveMappingProperty(getter);
return new UpdateOperation(this, p, v);
}
public InsertOperation insert() {
return new InsertOperation(this, true);
}
public InsertOperation insert(Object pojo) {
Objects.requireNonNull(pojo, "pojo is empty");
Class<?> iface = MappingUtil.getMappingInterface(pojo);
CasserEntity entity = Casser.entity(iface);
return new InsertOperation(this, entity, pojo, true);
}
public InsertOperation upsert() {
return new InsertOperation(this, false);
}
public InsertOperation upsert(Object pojo) {
Objects.requireNonNull(pojo, "pojo is empty");
Class<?> iface = MappingUtil.getMappingInterface(pojo);
CasserEntity entity = Casser.entity(iface);
return new InsertOperation(this, entity, pojo, false);
}
public DeleteOperation delete() {
return new DeleteOperation(this);
}
public DeleteOperation delete(Object dsl) {
Objects.requireNonNull(dsl, "dsl is empty");
return new DeleteOperation(this, Casser.resolve(dsl));
}
public Session getSession() {
return session;
}
public void close() {
if (session.isClosed()) {
return;
}
if (dropSchemaOnClose) {
dropSchema();
}
session.close();
}
public CloseFuture closeAsync() {
if (!session.isClosed() && dropSchemaOnClose) {
dropSchema();
}
return session.closeAsync();
}
private void dropSchema() {
sessionRepository.entities().forEach(e -> dropEntity(e));
}
private void dropEntity(CasserEntity entity) {
switch(entity.getType()) {
case TABLE:
execute(SchemaUtil.dropTable(entity), true);
break;
case UDT:
execute(SchemaUtil.dropUserType(entity), true);
break;
}
}
}