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 be8d1bf029
commit 25c5c6b969
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()); } 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) { protected <T> T get(String key, Class<?> returnType) {
T value = (T) backingMap.get(key); T value = (T) backingMap.get(key);
@ -48,8 +54,11 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
return value; 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) { if (key == null || value == null) {
return null; return null;
} }
@ -58,6 +67,11 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
return value; 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) { protected Object mutate(String key, Object value) {
Objects.requireNonNull(key); 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) return MappingUtil.resolveMappingProperty(getter)
.getProperty() .getProperty()
.getPropertyName(); .getPropertyName();
} }
public Object unset(Getter<?> getter) { public <T> Object unset(Getter<T> getter) {
return unset(methodNameFor(getter)); return unset(methodNameFor(getter));
} }
@ -100,8 +114,8 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
return null; return null;
} }
public <T> boolean reset(Getter<?> getter, T desiredValue) { public <T> boolean reset(Getter<T> getter, T desiredValue) {
return this.<T>reset(methodNameFor(getter), desiredValue); return this.<T>reset(this.<T>methodNameFor(getter), desiredValue);
} }
public <T> boolean reset(String key, T 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) { 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) { 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.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Consumer; import java.util.function.Consumer;
import net.helenus.core.reflect.DslExportable;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusEntityType; import net.helenus.mapping.HelenusEntityType;
import net.helenus.mapping.MappingUtil;
import net.helenus.mapping.value.ColumnValuePreparer; import net.helenus.mapping.value.ColumnValuePreparer;
import net.helenus.mapping.value.ColumnValueProvider; import net.helenus.mapping.value.ColumnValueProvider;
import net.helenus.support.Either;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
import net.helenus.support.PackageUtil; import net.helenus.support.PackageUtil;
@ -53,7 +57,7 @@ public final class SessionInitializer extends AbstractSessionOperations {
private KeyspaceMetadata keyspaceMetadata; 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; private AutoDdl autoDdl = AutoDdl.UPDATE;
SessionInitializer(Session session) { SessionInitializer(Session session) {
@ -181,7 +185,9 @@ public final class SessionInitializer extends AbstractSessionOperations {
PackageUtil.getClasses(packageName) PackageUtil.getClasses(packageName)
.stream() .stream()
.filter(c -> c.isInterface() && !c.isAnnotation()) .filter(c -> c.isInterface() && !c.isAnnotation())
.forEach(initList::add); .forEach(clazz -> {
initList.add(Either.right(clazz));
});
} catch (IOException | ClassNotFoundException e) { } catch (IOException | ClassNotFoundException e) {
throw new HelenusException("fail to add package " + packageName, e); throw new HelenusException("fail to add package " + packageName, e);
} }
@ -193,7 +199,7 @@ public final class SessionInitializer extends AbstractSessionOperations {
int len = dsls.length; int len = dsls.length;
for (int i = 0; i != len; ++i) { for (int i = 0; i != len; ++i) {
Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty"); Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty");
initList.add(obj); initList.add(Either.left(obj));
} }
return this; return this;
} }
@ -261,7 +267,18 @@ public final class SessionInitializer extends AbstractSessionOperations {
Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator"); 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); TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes);
UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns); UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns);

View file

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

View file

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

View file

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

View file

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

View file

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