From bf1d5f217fa1ac2002a0d828b866f22d4a3e2f8f Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Fri, 29 Sep 2017 09:47:15 -0400 Subject: [PATCH 1/4] First fetch from the backingMap. --- pom.xml | 2 +- .../net/helenus/core/AbstractEntityDraft.java | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index e3ad62a..0362552 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 net.helenus helenus-net-core - 2.0.47-SNAPSHOT + 2.0.48-SNAPSHOT jar helenus diff --git a/src/main/java/net/helenus/core/AbstractEntityDraft.java b/src/main/java/net/helenus/core/AbstractEntityDraft.java index adf5cf7..7054739 100644 --- a/src/main/java/net/helenus/core/AbstractEntityDraft.java +++ b/src/main/java/net/helenus/core/AbstractEntityDraft.java @@ -28,18 +28,21 @@ public abstract class AbstractEntityDraft implements Drafted { public E build() { return Helenus.map(getEntityClass(), toMap()); } protected T get(String key, Class returnType) { - T value = (T) entityMap.get(key); + T value = (T) backingMap.get(key); if (value == null) { + value = (T) entityMap.get(key); + if (value == null) { - if (Primitives.allPrimitiveTypes().contains(returnType)) { + if (Primitives.allPrimitiveTypes().contains(returnType)) { - DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType); - if (type == null) { - throw new RuntimeException("unknown primitive type " + returnType); + DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType); + if (type == null) { + throw new RuntimeException("unknown primitive type " + returnType); + } + + return (T) type.getDefaultValue(); } - - return (T) type.getDefaultValue(); } } From 0c6cda1db6393ef8e21f9c00a7f074fc3b686b24 Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Fri, 29 Sep 2017 10:21:48 -0400 Subject: [PATCH 2/4] Switching version so that the build number is the third part of the version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0362552..71feca8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 net.helenus helenus-net-core - 2.0.48-SNAPSHOT + 2.1 jar helenus From be8d1bf029dac0e6d9dec0de3a77d5b33f420d2e Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Fri, 29 Sep 2017 10:48:37 -0400 Subject: [PATCH 3/4] Use backingMap for the mutated key set (and save some space/overhead). --- src/main/java/net/helenus/core/AbstractEntityDraft.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/helenus/core/AbstractEntityDraft.java b/src/main/java/net/helenus/core/AbstractEntityDraft.java index 7054739..661e9b7 100644 --- a/src/main/java/net/helenus/core/AbstractEntityDraft.java +++ b/src/main/java/net/helenus/core/AbstractEntityDraft.java @@ -13,7 +13,6 @@ import net.helenus.mapping.MappingUtil; public abstract class AbstractEntityDraft implements Drafted { private final Map backingMap = new HashMap(); - private final Set mutatedSet = new HashSet(); private final MapExportable entity; private final Map entityMap; @@ -56,7 +55,6 @@ public abstract class AbstractEntityDraft implements Drafted { } backingMap.put(key, value); - mutatedSet.add(key); return value; } @@ -72,14 +70,12 @@ public abstract class AbstractEntityDraft implements Drafted { if (map.containsKey(key) && !value.equals(map.get(key))) { backingMap.put(key, value); - mutatedSet.add(key); return value; } return map.get(key); } else { backingMap.put(key, value); - mutatedSet.add(key); return null; } @@ -99,7 +95,6 @@ public abstract class AbstractEntityDraft implements Drafted { if (key != null) { Object value = backingMap.get(key); backingMap.put(key, null); - mutatedSet.add(key); return value; } return null; @@ -136,7 +131,7 @@ public abstract class AbstractEntityDraft implements Drafted { } else { combined = new HashMap(backingMap.size()); } - for (String key : mutatedSet) { + for (String key : mutated()) { combined.put(key, backingMap.get(key)); } return combined; @@ -144,7 +139,7 @@ public abstract class AbstractEntityDraft implements Drafted { @Override public Set mutated() { - return mutatedSet; + return backingMap.keySet(); } @Override From 25c5c6b9692796e3ec2ab38ad92e248c859d2f83 Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Wed, 4 Oct 2017 15:46:01 -0400 Subject: [PATCH 4/4] 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. --- .../net/helenus/core/AbstractEntityDraft.java | 24 +++- src/main/java/net/helenus/core/Helenus.java | 9 +- .../net/helenus/core/SessionInitializer.java | 25 ++++- .../helenus/core/reflect/DslExportable.java | 4 + .../core/reflect/DslInvocationHandler.java | 104 ++++++++++++------ .../core/reflect/MapperInvocationHandler.java | 6 + .../integration/core/draft/Inventory.java | 19 ++-- .../test/integration/core/draft/Supply.java | 32 +++--- 8 files changed, 155 insertions(+), 68 deletions(-) diff --git a/src/main/java/net/helenus/core/AbstractEntityDraft.java b/src/main/java/net/helenus/core/AbstractEntityDraft.java index 661e9b7..58a575d 100644 --- a/src/main/java/net/helenus/core/AbstractEntityDraft.java +++ b/src/main/java/net/helenus/core/AbstractEntityDraft.java @@ -26,6 +26,12 @@ public abstract class AbstractEntityDraft implements Drafted { public E build() { return Helenus.map(getEntityClass(), toMap()); } + @SuppressWarnings("unchecked") + protected T get(Getter getter, Class returnType) { + return (T) get(this.methodNameFor(getter), returnType); + } + + @SuppressWarnings("unchecked") protected T get(String key, Class returnType) { T value = (T) backingMap.get(key); @@ -48,8 +54,11 @@ public abstract class AbstractEntityDraft implements Drafted { return value; } - protected Object set(String key, Object value) { + protected Object set(Getter getter, Object value) { + return set(this.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 implements Drafted { return value; } + @SuppressWarnings("unchecked") + protected T mutate(Getter getter, T value) { + return (T) mutate(this.methodNameFor(getter), value); + } + protected Object mutate(String key, Object value) { Objects.requireNonNull(key); @@ -81,13 +95,13 @@ public abstract class AbstractEntityDraft implements Drafted { } } - private String methodNameFor(Getter getter) { + private String methodNameFor(Getter getter) { return MappingUtil.resolveMappingProperty(getter) .getProperty() .getPropertyName(); } - public Object unset(Getter getter) { + public Object unset(Getter getter) { return unset(methodNameFor(getter)); } @@ -100,8 +114,8 @@ public abstract class AbstractEntityDraft implements Drafted { return null; } - public boolean reset(Getter getter, T desiredValue) { - return this.reset(methodNameFor(getter), desiredValue); + public boolean reset(Getter getter, T desiredValue) { + return this.reset(this.methodNameFor(getter), desiredValue); } public boolean reset(String key, T desiredValue) { diff --git a/src/main/java/net/helenus/core/Helenus.java b/src/main/java/net/helenus/core/Helenus.java index 26a453b..48c1aba 100644 --- a/src/main/java/net/helenus/core/Helenus.java +++ b/src/main/java/net/helenus/core/Helenus.java @@ -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) { diff --git a/src/main/java/net/helenus/core/SessionInitializer.java b/src/main/java/net/helenus/core/SessionInitializer.java index bdfd23e..d7f0d7e 100644 --- a/src/main/java/net/helenus/core/SessionInitializer.java +++ b/src/main/java/net/helenus/core/SessionInitializer.java @@ -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 initList = new ArrayList(); + private final List>> initList = new ArrayList>>(); 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); diff --git a/src/main/java/net/helenus/core/reflect/DslExportable.java b/src/main/java/net/helenus/core/reflect/DslExportable.java index 88cc117..e3d2e4a 100644 --- a/src/main/java/net/helenus/core/reflect/DslExportable.java +++ b/src/main/java/net/helenus/core/reflect/DslExportable.java @@ -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); } diff --git a/src/main/java/net/helenus/core/reflect/DslInvocationHandler.java b/src/main/java/net/helenus/core/reflect/DslInvocationHandler.java index 8f2fb83..608a5b8 100644 --- a/src/main/java/net/helenus/core/reflect/DslInvocationHandler.java +++ b/src/main/java/net/helenus/core/reflect/DslInvocationHandler.java @@ -34,7 +34,12 @@ import net.helenus.support.HelenusException; public class DslInvocationHandler implements InvocationHandler { - private final HelenusEntity entity; + private HelenusEntity entity = null; + private Metadata metadata = null; + + private final Class iface; + private final ClassLoader classLoader; + private final Optional parent; private final Map map = new HashMap(); @@ -48,52 +53,66 @@ public class DslInvocationHandler implements InvocationHandler { Optional 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 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 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 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); diff --git a/src/main/java/net/helenus/core/reflect/MapperInvocationHandler.java b/src/main/java/net/helenus/core/reflect/MapperInvocationHandler.java index 7df8a51..27d48cf 100644 --- a/src/main/java/net/helenus/core/reflect/MapperInvocationHandler.java +++ b/src/main/java/net/helenus/core/reflect/MapperInvocationHandler.java @@ -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 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); } diff --git a/src/test/java/net/helenus/test/integration/core/draft/Inventory.java b/src/test/java/net/helenus/test/integration/core/draft/Inventory.java index a4f7a12..52448d2 100644 --- a/src/test/java/net/helenus/test/integration/core/draft/Inventory.java +++ b/src/test/java/net/helenus/test/integration/core/draft/Inventory.java @@ -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.get("id", UUID.class); + return this.get(inventory::id, UUID.class); } public long EMEA() { - return this.get("EMEA", long.class); + return this.get(inventory::EMEA, long.class); } public Draft EMEA(long count) { - mutate("EMEA", count); + mutate(inventory::EMEA, count); return this; } public long APAC() { - return this.get("APAC", long.class); + return this.get(inventory::APAC, long.class); } public Draft APAC(long count) { - mutate("APAC", count); + mutate(inventory::APAC, count); return this; } public long NORAM() { - return this.get("NORAM", long.class); + return this.get(inventory::NORAM, long.class); } public Draft NORAM(long count) { - mutate("NORAM", count); + mutate(inventory::NORAM, count); return this; } diff --git a/src/test/java/net/helenus/test/integration/core/draft/Supply.java b/src/test/java/net/helenus/test/integration/core/draft/Supply.java index 1b09724..91f1689 100644 --- a/src/test/java/net/helenus/test/integration/core/draft/Supply.java +++ b/src/test/java/net/helenus/test/integration/core/draft/Supply.java @@ -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.get("id", UUID.class); + return this.get(supply::id, UUID.class); } public String region() { - return this.get("region", String.class); + return this.get(supply::region, String.class); } // Mutable properties: public String code() { - return this.get("code", String.class); + return this.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.get("description", String.class); + return this.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 demand() { - return this.>get("demand", Map.class); + return this.>get(supply::demand, Map.class); } public Draft demand(Map demand) { - mutate("demand", demand); + mutate(supply::demand, demand); return this; } @@ -96,11 +100,11 @@ public interface Supply { } public List suppliers() { - return this.>get("suppliers", List.class); + return this.>get(supply::suppliers, List.class); } public Draft suppliers(List suppliers) { - mutate("suppliers", suppliers); + mutate(supply::suppliers, suppliers); return this; } @@ -109,11 +113,11 @@ public interface Supply { } public Set shipments() { - return this.>get("shipments", Set.class); + return this.>get(supply::shipments, Set.class); } public Draft shipments(Set shipments) { - mutate("shipments", shipments); + mutate(supply::shipments, shipments); return this; }