Enabled Helenus.dsl() to return a valid object even when the cluster metadata isn't known in advance and later learn more when it is. Changed the AbstractEntityDraft to have both by-string and by-getter mutation/accessor methods. These two combine to make Draft objects more type safe.

This commit is contained in:
Greg Burd 2017-10-04 15:46:01 -04:00
parent 0e9d1086ed
commit 01102e2299
8 changed files with 155 additions and 68 deletions

View file

@ -26,6 +26,12 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
public E build() { return Helenus.map(getEntityClass(), toMap()); }
@SuppressWarnings("unchecked")
protected <T> T get(Getter<T> getter, Class<?> returnType) {
return (T) get(this.<T>methodNameFor(getter), returnType);
}
@SuppressWarnings("unchecked")
protected <T> T get(String key, Class<?> returnType) {
T value = (T) backingMap.get(key);
@ -48,8 +54,11 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
return value;
}
protected Object set(String key, Object value) {
protected <T> Object set(Getter<T> getter, Object value) {
return set(this.<T>methodNameFor(getter), value);
}
protected Object set(String key, Object value) {
if (key == null || value == null) {
return null;
}
@ -58,6 +67,11 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
return value;
}
@SuppressWarnings("unchecked")
protected <T> T mutate(Getter<T> getter, T value) {
return (T) mutate(this.<T>methodNameFor(getter), value);
}
protected Object mutate(String key, Object value) {
Objects.requireNonNull(key);
@ -81,13 +95,13 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
}
}
private String methodNameFor(Getter<?> getter) {
private <T> String methodNameFor(Getter<T> getter) {
return MappingUtil.resolveMappingProperty(getter)
.getProperty()
.getPropertyName();
}
public Object unset(Getter<?> getter) {
public <T> Object unset(Getter<T> getter) {
return unset(methodNameFor(getter));
}
@ -100,8 +114,8 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
return null;
}
public <T> boolean reset(Getter<?> getter, T desiredValue) {
return this.<T>reset(methodNameFor(getter), desiredValue);
public <T> boolean reset(Getter<T> getter, T desiredValue) {
return this.<T>reset(this.<T>methodNameFor(getter), desiredValue);
}
public <T> boolean reset(String key, T desiredValue) {

View file

@ -140,7 +140,14 @@ public final class Helenus {
}
public static HelenusEntity entity(Class<?> iface) {
return entity(iface, metadataForEntity.get(iface));
Metadata metadata = metadataForEntity.get(iface);
if (metadata == null) {
HelenusSession session = session();
if (session != null) {
metadata = session.getMetadata();
}
}
return entity(iface, metadata);
}
public static HelenusEntity entity(Class<?> iface, Metadata metadata) {

View file

@ -25,10 +25,14 @@ import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import net.helenus.core.reflect.DslExportable;
import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusEntityType;
import net.helenus.mapping.MappingUtil;
import net.helenus.mapping.value.ColumnValuePreparer;
import net.helenus.mapping.value.ColumnValueProvider;
import net.helenus.support.Either;
import net.helenus.support.HelenusException;
import net.helenus.support.PackageUtil;
@ -53,7 +57,7 @@ public final class SessionInitializer extends AbstractSessionOperations {
private KeyspaceMetadata keyspaceMetadata;
private final List<Object> initList = new ArrayList<Object>();
private final List<Either<Object, Class<?>>> initList = new ArrayList<Either<Object, Class<?>>>();
private AutoDdl autoDdl = AutoDdl.UPDATE;
SessionInitializer(Session session) {
@ -181,7 +185,9 @@ public final class SessionInitializer extends AbstractSessionOperations {
PackageUtil.getClasses(packageName)
.stream()
.filter(c -> c.isInterface() && !c.isAnnotation())
.forEach(initList::add);
.forEach(clazz -> {
initList.add(Either.right(clazz));
});
} catch (IOException | ClassNotFoundException e) {
throw new HelenusException("fail to add package " + packageName, e);
}
@ -193,7 +199,7 @@ public final class SessionInitializer extends AbstractSessionOperations {
int len = dsls.length;
for (int i = 0; i != len; ++i) {
Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty");
initList.add(obj);
initList.add(Either.left(obj));
}
return this;
}
@ -261,7 +267,18 @@ public final class SessionInitializer extends AbstractSessionOperations {
Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator");
initList.forEach(dsl -> sessionRepository.add(dsl));
initList.forEach((either) -> {
Class<?> iface = null;
if (either.isLeft()) {
iface = MappingUtil.getMappingInterface(either.getLeft());
} else {
iface = either.getRight();
}
DslExportable dsl = (DslExportable) Helenus.dsl(iface);
dsl.setMetadata(session.getCluster().getMetadata());
sessionRepository.add(dsl);
});
TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes);
UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns);

View file

@ -15,14 +15,18 @@
*/
package net.helenus.core.reflect;
import com.datastax.driver.core.Metadata;
import net.helenus.mapping.HelenusEntity;
public interface DslExportable {
public static final String GET_ENTITY_METHOD = "getHelenusMappingEntity";
public static final String GET_PARENT_METHOD = "getParentDslHelenusPropertyNode";
public static final String SET_METADATA_METHOD = "setMetadata";
HelenusEntity getHelenusMappingEntity();
HelenusPropertyNode getParentDslHelenusPropertyNode();
void setMetadata(Metadata metadata);
}

View file

@ -34,7 +34,12 @@ import net.helenus.support.HelenusException;
public class DslInvocationHandler<E> implements InvocationHandler {
private final HelenusEntity entity;
private HelenusEntity entity = null;
private Metadata metadata = null;
private final Class<E> iface;
private final ClassLoader classLoader;
private final Optional<HelenusPropertyNode> parent;
private final Map<Method, HelenusProperty> map = new HashMap<Method, HelenusProperty>();
@ -48,52 +53,66 @@ public class DslInvocationHandler<E> implements InvocationHandler {
Optional<HelenusPropertyNode> parent,
Metadata metadata) {
this.entity = new HelenusMappingEntity(iface, metadata);
this.metadata = metadata;
this.parent = parent;
this.iface = iface;
this.classLoader = classLoader;
}
public void setMetadata(Metadata metadata) {
if (metadata != null) {
this.metadata = metadata;
entity = init(metadata);
}
}
private HelenusEntity init(Metadata metadata) {
HelenusEntity entity = new HelenusMappingEntity(iface, metadata);
if (this.entity != null) {
for (HelenusProperty prop : entity.getOrderedProperties()) {
map.put(prop.getGetterMethod(), prop);
map.put(prop.getGetterMethod(), prop);
AbstractDataType type = prop.getDataType();
Class<?> javaType = prop.getJavaType();
AbstractDataType type = prop.getDataType();
Class<?> javaType = prop.getJavaType();
if (type instanceof UDTDataType && !UDTValue.class.isAssignableFrom(javaType)) {
if (type instanceof UDTDataType && !UDTValue.class.isAssignableFrom(javaType)) {
Object childDsl =
Helenus.dsl(
javaType,
classLoader,
Optional.of(new HelenusPropertyNode(prop, parent)),
metadata);
Object childDsl =
Helenus.dsl(
javaType,
classLoader,
Optional.of(new HelenusPropertyNode(prop, parent)),
metadata);
udtMap.put(prop.getGetterMethod(), childDsl);
}
if (type instanceof DTDataType) {
DTDataType dataType = (DTDataType) type;
if (dataType.getDataType() instanceof TupleType
&& !TupleValue.class.isAssignableFrom(javaType)) {
Object childDsl =
Helenus.dsl(
javaType,
classLoader,
Optional.of(new HelenusPropertyNode(prop, parent)),
metadata);
tupleMap.put(prop.getGetterMethod(), childDsl);
udtMap.put(prop.getGetterMethod(), childDsl);
}
if (type instanceof DTDataType) {
DTDataType dataType = (DTDataType) type;
if (dataType.getDataType() instanceof TupleType
&& !TupleValue.class.isAssignableFrom(javaType)) {
Object childDsl =
Helenus.dsl(
javaType,
classLoader,
Optional.of(new HelenusPropertyNode(prop, parent)),
metadata);
tupleMap.put(prop.getGetterMethod(), childDsl);
}
}
}
}
}
return entity;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HelenusEntity entity = this.entity;
String methodName = method.getName();
if ("equals".equals(methodName) && method.getParameterCount() == 1) {
@ -107,6 +126,15 @@ public class DslInvocationHandler<E> implements InvocationHandler {
return false;
}
if (DslExportable.SET_METADATA_METHOD.equals(methodName)
&& args.length == 1
&& args[0] instanceof Metadata) {
if (metadata == null) {
this.setMetadata((Metadata) args[0]);
}
return null;
}
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
throw new HelenusException("invalid getter method " + method);
}
@ -115,6 +143,14 @@ public class DslInvocationHandler<E> implements InvocationHandler {
return hashCode();
}
if (DslExportable.GET_PARENT_METHOD.equals(methodName)) {
return parent.get();
}
if (entity == null) {
entity = init(metadata);
}
if ("toString".equals(methodName)) {
return entity.toString();
}
@ -123,10 +159,6 @@ public class DslInvocationHandler<E> implements InvocationHandler {
return entity;
}
if (DslExportable.GET_PARENT_METHOD.equals(methodName)) {
return parent.get();
}
HelenusProperty prop = map.get(method);
if (prop == null) {
prop = entity.getProperty(methodName);

View file

@ -23,6 +23,8 @@ import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.Map;
import net.helenus.core.Helenus;
import net.helenus.mapping.annotation.Transient;
import net.helenus.support.HelenusException;
@ -91,6 +93,10 @@ public class MapperInvocationHandler<E> implements InvocationHandler, Serializab
return iface.getSimpleName() + ": " + src.toString();
}
if ("dsl".equals(methodName)) {
return Helenus.dsl(iface);
}
if (MapExportable.TO_MAP_METHOD.equals(methodName)) {
return Collections.unmodifiableMap(src);
}

View file

@ -3,6 +3,7 @@ package net.helenus.test.integration.core.draft;
import java.util.UUID;
import net.helenus.core.AbstractAuditedEntityDraft;
import net.helenus.core.Helenus;
import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.annotation.*;
@ -10,6 +11,8 @@ import net.helenus.mapping.annotation.*;
@Table
public interface Inventory {
static Inventory inventory = Helenus.dsl(Inventory.class);
@PartitionKey UUID id();
@Column("emea") @Types.Counter long EMEA();
@Column("noram") @Types.Counter long NORAM();
@ -27,7 +30,7 @@ public interface Inventory {
super(null);
// Primary Key:
set("id", id);
set(inventory::id, id);
}
Draft(Inventory inventory) {
@ -40,33 +43,33 @@ public interface Inventory {
// Immutable properties:
public UUID id() {
return this.<UUID>get("id", UUID.class);
return this.<UUID>get(inventory::id, UUID.class);
}
public long EMEA() {
return this.<Long>get("EMEA", long.class);
return this.<Long>get(inventory::EMEA, long.class);
}
public Draft EMEA(long count) {
mutate("EMEA", count);
mutate(inventory::EMEA, count);
return this;
}
public long APAC() {
return this.<Long>get("APAC", long.class);
return this.<Long>get(inventory::APAC, long.class);
}
public Draft APAC(long count) {
mutate("APAC", count);
mutate(inventory::APAC, count);
return this;
}
public long NORAM() {
return this.<Long>get("NORAM", long.class);
return this.<Long>get(inventory::NORAM, long.class);
}
public Draft NORAM(long count) {
mutate("NORAM", count);
mutate(inventory::NORAM, count);
return this;
}

View file

@ -1,5 +1,6 @@
package net.helenus.test.integration.core.draft;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -8,6 +9,7 @@ import java.util.UUID;
import com.datastax.driver.core.utils.UUIDs;
import net.helenus.core.AbstractEntityDraft;
import net.helenus.core.Helenus;
import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.annotation.*;
@ -15,6 +17,8 @@ import net.helenus.mapping.annotation.*;
@Table
public interface Supply {
static Supply supply = Helenus.dsl(Supply.class);
@PartitionKey UUID id();
@ClusteringColumn(ordinal=0) default String region() { return "NORAM"; }
@ -36,8 +40,8 @@ public interface Supply {
super(null);
// Primary Key:
set("id", UUIDs.timeBased());
set("region", region);
set(supply::id, UUIDs.timeBased());
set(supply::region, region);
}
Draft(Supply supply) {
@ -48,20 +52,20 @@ public interface Supply {
// Immutable properties:
public UUID id() {
return this.<UUID>get("id", UUID.class);
return this.<UUID>get(supply::id, UUID.class);
}
public String region() {
return this.<String>get("region", String.class);
return this.<String>get(supply::region, String.class);
}
// Mutable properties:
public String code() {
return this.<String>get("code", String.class);
return this.<String>get(supply::code, String.class);
}
public Draft code(String code) {
mutate("code", code);
mutate(supply::code, code);
return this;
}
@ -70,11 +74,11 @@ public interface Supply {
}
public String description() {
return this.<String>get("description", String.class);
return this.<String>get(supply::description, String.class);
}
public Draft description(String description) {
mutate("description", description);
mutate(supply::description, description);
return this;
}
@ -83,11 +87,11 @@ public interface Supply {
}
public Map<String, Long> demand() {
return this.<Map<String, Long>>get("demand", Map.class);
return this.<Map<String, Long>>get(supply::demand, Map.class);
}
public Draft demand(Map<String, Long> demand) {
mutate("demand", demand);
mutate(supply::demand, demand);
return this;
}
@ -96,11 +100,11 @@ public interface Supply {
}
public List<String> suppliers() {
return this.<List<String>>get("suppliers", List.class);
return this.<List<String>>get(supply::suppliers, List.class);
}
public Draft suppliers(List<String> suppliers) {
mutate("suppliers", suppliers);
mutate(supply::suppliers, suppliers);
return this;
}
@ -109,11 +113,11 @@ public interface Supply {
}
public Set<String> shipments() {
return this.<Set<String>>get("shipments", Set.class);
return this.<Set<String>>get(supply::shipments, Set.class);
}
public Draft shipments(Set<String> shipments) {
mutate("shipments", shipments);
mutate(supply::shipments, shipments);
return this;
}