Fix misuse of Drafted interface in tests. WIP fixing use of immutable collections in UPDATE/draft logic.

This commit is contained in:
Greg Burd 2017-11-13 15:55:24 -05:00
parent 33d2459538
commit 7a56059036
14 changed files with 96 additions and 101 deletions

View file

@ -51,12 +51,7 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
} else { } else {
// Collections fetched from the entityMap // Collections fetched from the entityMap
if (value instanceof Collection) { if (value instanceof Collection) {
try { value = (T) SerializationUtils.<Serializable>clone((Serializable) value);
value = MappingUtil.<T>clone(value);
} catch (CloneNotSupportedException e) {
// TODO(gburd): deep?shallow? copy of List, Map, Set to a mutable collection.
value = (T) SerializationUtils.<Serializable>clone((Serializable) value);
}
} }
} }
} }

View file

@ -22,6 +22,7 @@ import com.google.common.base.Stopwatch;
import com.google.common.collect.HashBasedTable; import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table; import com.google.common.collect.Table;
import com.google.common.collect.TreeTraverser; import com.google.common.collect.TreeTraverser;
import java.io.Serializable;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -30,9 +31,9 @@ import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.core.operation.AbstractOperation; import net.helenus.core.operation.AbstractOperation;
import net.helenus.core.operation.BatchOperation; import net.helenus.core.operation.BatchOperation;
import net.helenus.core.reflect.Drafted;
import net.helenus.mapping.MappingUtil; import net.helenus.mapping.MappingUtil;
import net.helenus.support.Either; import net.helenus.support.Either;
import org.apache.commons.lang3.SerializationUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -218,15 +219,11 @@ public abstract class AbstractUnitOfWork<E extends Exception>
result = checkParentCache(facets); result = checkParentCache(facets);
if (result.isPresent()) { if (result.isPresent()) {
Object r = result.get(); Object r = result.get();
try { Class<?> iface = MappingUtil.getMappingInterface(r);
Class<?> iface = MappingUtil.getMappingInterface(r); if (Helenus.entity(iface).isDraftable()) {
if (Drafted.class.isAssignableFrom(iface)) { cacheUpdate(r, facets);
cacheUpdate(r, facets); } else {
} else { cacheUpdate(SerializationUtils.<Serializable>clone((Serializable) r), facets);
cacheUpdate(MappingUtil.clone(r), facets);
}
} catch (CloneNotSupportedException e) {
result = Optional.empty();
} }
} }
return result; return result;

View file

@ -24,18 +24,20 @@ import com.google.common.base.Function;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Helenus;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
import net.helenus.core.cache.CacheUtil; import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.core.reflect.Drafted;
import net.helenus.mapping.MappingUtil; import net.helenus.mapping.MappingUtil;
import net.helenus.support.Fun; import net.helenus.support.Fun;
import org.apache.commons.lang3.SerializationUtils;
public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOperation<E, O>> public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOperation<E, O>>
extends AbstractStatementOperation<E, O> { extends AbstractStatementOperation<E, O> {
@ -153,26 +155,22 @@ public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOpe
cachedResult = (E) sessionOps.checkCache(tableName, facets); cachedResult = (E) sessionOps.checkCache(tableName, facets);
if (cachedResult != null) { if (cachedResult != null) {
Class<?> iface = MappingUtil.getMappingInterface(cachedResult); Class<?> iface = MappingUtil.getMappingInterface(cachedResult);
try { if (Helenus.entity(iface).isDraftable()) {
if (Drafted.class.isAssignableFrom(iface)) { result = Optional.of(cachedResult);
result = Optional.of(cachedResult); } else {
} else { result =
result = Optional.of(MappingUtil.clone(cachedResult)); Optional.of(
} (E)
sessionCacheHits.mark(); SerializationUtils.<Serializable>clone(
cacheHits.mark(); (Serializable) cachedResult));
uow.recordCacheAndDatabaseOperationCount(1, 0); }
} catch (CloneNotSupportedException e) { sessionCacheHits.mark();
result = Optional.empty(); cacheHits.mark();
sessionCacheMiss.mark(); uow.recordCacheAndDatabaseOperationCount(1, 0);
cacheMiss.mark(); if (result.isPresent()) {
uow.recordCacheAndDatabaseOperationCount(-1, 0); updateCache = true;
} finally { } else {
if (result.isPresent()) { updateCache = false;
updateCache = true;
} else {
updateCache = false;
}
} }
} else { } else {
updateCache = false; updateCache = false;

View file

@ -24,6 +24,7 @@ import com.google.common.base.Function;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -31,12 +32,13 @@ import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Stream; import java.util.stream.Stream;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Helenus;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
import net.helenus.core.cache.CacheUtil; import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.core.reflect.Drafted;
import net.helenus.mapping.MappingUtil; import net.helenus.mapping.MappingUtil;
import net.helenus.support.Fun; import net.helenus.support.Fun;
import org.apache.commons.lang3.SerializationUtils;
public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperation<E, O>> public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperation<E, O>>
extends AbstractStatementOperation<E, O> { extends AbstractStatementOperation<E, O> {
@ -160,26 +162,20 @@ public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperati
if (cachedResult != null) { if (cachedResult != null) {
Class<?> iface = MappingUtil.getMappingInterface(cachedResult); Class<?> iface = MappingUtil.getMappingInterface(cachedResult);
E result = null; E result = null;
try { if (Helenus.entity(iface).isDraftable()) {
if (Drafted.class.isAssignableFrom(iface)) { result = cachedResult;
result = cachedResult; } else {
} else { result =
result = MappingUtil.clone(cachedResult); (E) SerializationUtils.<Serializable>clone((Serializable) cachedResult);
} }
resultStream = Stream.of(result); resultStream = Stream.of(result);
sessionCacheHits.mark(); sessionCacheHits.mark();
cacheHits.mark(); cacheHits.mark();
uow.recordCacheAndDatabaseOperationCount(1, 0); uow.recordCacheAndDatabaseOperationCount(1, 0);
} catch (CloneNotSupportedException e) { if (result != null) {
resultStream = null; updateCache = true;
sessionCacheMiss.mark(); } else {
uow.recordCacheAndDatabaseOperationCount(-1, 0); updateCache = false;
} finally {
if (result != null) {
updateCache = true;
} else {
updateCache = false;
}
} }
} else { } else {
updateCache = false; updateCache = false;

View file

@ -31,7 +31,6 @@ import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.core.cache.UnboundFacet; import net.helenus.core.cache.UnboundFacet;
import net.helenus.core.reflect.DefaultPrimitiveTypes; import net.helenus.core.reflect.DefaultPrimitiveTypes;
import net.helenus.core.reflect.Drafted;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.core.reflect.MapExportable; import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
@ -211,7 +210,7 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
private T newInstance(Class<?> iface) { private T newInstance(Class<?> iface) {
if (values.size() > 0) { if (values.size() > 0) {
boolean immutable = iface.isAssignableFrom(Drafted.class); boolean immutable = entity.isDraftable();
Collection<HelenusProperty> properties = entity.getOrderedProperties(); Collection<HelenusProperty> properties = entity.getOrderedProperties();
Map<String, Object> backingMap = new HashMap<String, Object>(properties.size()); Map<String, Object> backingMap = new HashMap<String, Object>(properties.size());

View file

@ -203,7 +203,9 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else if (draft != null) { } else if (draft != null) {
String key = prop.getPropertyName(); String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key); list =
(List<V>) new ArrayList<V>((List<V>) draftMap.get(key)); // copy immutable -> mutable list
draft.put(key, list);
list.add(0, value); list.add(0, value);
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else { } else {
@ -235,7 +237,9 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else if (draft != null && value.size() > 0) { } else if (draft != null && value.size() > 0) {
String key = p.getProperty().getPropertyName(); String key = p.getProperty().getPropertyName();
list = (List<V>) draftMap.get(key); list =
(List<V>) new ArrayList<V>((List<V>) draftMap.get(key)); // copy immutable -> mutable list
draft.put(key, list);
list.addAll(0, value); list.addAll(0, value);
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else { } else {
@ -266,7 +270,10 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
list = (List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false); list = (List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
} else { } else {
String key = prop.getPropertyName(); String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key); list =
(List<V>)
new ArrayList<V>((List<V>) draftMap.get(key)); // copy immutable -> mutable list
draft.put(key, list);
} }
if (idx < 0) { if (idx < 0) {
list.add(0, value); list.add(0, value);
@ -305,7 +312,9 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else if (draft != null) { } else if (draft != null) {
String key = prop.getPropertyName(); String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key); list =
(List<V>) new ArrayList<V>((List<V>) draftMap.get(key)); // copy immutable -> mutable list
draft.put(key, list);
list.add(value); list.add(value);
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else { } else {
@ -336,7 +345,9 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else if (draft != null && value.size() > 0) { } else if (draft != null && value.size() > 0) {
String key = prop.getPropertyName(); String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key); list =
(List<V>) new ArrayList<V>((List<V>) draftMap.get(key)); // copy immutable -> mutable list
draft.put(key, list);
list.addAll(value); list.addAll(value);
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else { } else {
@ -367,7 +378,9 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else if (draft != null) { } else if (draft != null) {
String key = prop.getPropertyName(); String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key); list =
(List<V>) new ArrayList<V>((List<V>) draftMap.get(key)); // copy immutable -> mutable list
draft.put(key, list);
list.remove(value); list.remove(value);
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else { } else {
@ -398,7 +411,9 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else if (draft != null) { } else if (draft != null) {
String key = prop.getPropertyName(); String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key); list =
(List<V>) new ArrayList<V>((List<V>) draftMap.get(key)); // copy immutable -> mutable list
draft.put(key, list);
list.removeAll(value); list.removeAll(value);
facet = new BoundFacet(prop, list); facet = new BoundFacet(prop, list);
} else { } else {
@ -467,7 +482,8 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
facet = new BoundFacet(prop, set); facet = new BoundFacet(prop, set);
} else if (draft != null) { } else if (draft != null) {
String key = prop.getPropertyName(); String key = prop.getPropertyName();
set = (Set<V>) draftMap.get(key); set = (Set<V>) new HashSet<V>((Set<V>) draftMap.get(key));
draft.put(key, set);
set.add(value); set.add(value);
facet = new BoundFacet(prop, set); facet = new BoundFacet(prop, set);
} else { } else {

View file

@ -34,4 +34,6 @@ public interface HelenusEntity {
HelenusProperty getProperty(String name); HelenusProperty getProperty(String name);
List<Facet> getFacets(); List<Facet> getFacets();
boolean isDraftable();
} }

View file

@ -39,6 +39,7 @@ public final class HelenusMappingEntity implements HelenusEntity {
private final HelenusEntityType type; private final HelenusEntityType type;
private final IdentityName name; private final IdentityName name;
private final boolean cacheable; private final boolean cacheable;
private final boolean draftable;
private final ImmutableMap<String, Method> methods; private final ImmutableMap<String, Method> methods;
private final ImmutableMap<String, HelenusProperty> props; private final ImmutableMap<String, HelenusProperty> props;
private final ImmutableList<HelenusProperty> orderedProps; private final ImmutableList<HelenusProperty> orderedProps;
@ -112,6 +113,16 @@ public final class HelenusMappingEntity implements HelenusEntity {
// Caching // Caching
cacheable = (null != iface.getDeclaredAnnotation(Cacheable.class)); cacheable = (null != iface.getDeclaredAnnotation(Cacheable.class));
// Draft
Class<?> draft;
try {
draft = Class.forName(iface.getName() + "$Draft");
} catch (Exception ignored) {
draft = null;
}
draftable = (draft != null);
// Materialized view
List<HelenusProperty> primaryKeyProperties = new ArrayList<>(); List<HelenusProperty> primaryKeyProperties = new ArrayList<>();
ImmutableList.Builder<Facet> facetsBuilder = ImmutableList.builder(); ImmutableList.Builder<Facet> facetsBuilder = ImmutableList.builder();
if (iface.getDeclaredAnnotation(MaterializedView.class) == null) { if (iface.getDeclaredAnnotation(MaterializedView.class) == null) {
@ -212,6 +223,11 @@ public final class HelenusMappingEntity implements HelenusEntity {
return cacheable; return cacheable;
} }
@Override
public boolean isDraftable() {
return draftable;
}
@Override @Override
public Class<?> getMappingInterface() { public Class<?> getMappingInterface() {
return iface; return iface;

View file

@ -16,7 +16,6 @@
package net.helenus.mapping; package net.helenus.mapping;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -305,28 +304,6 @@ public final class MappingUtil {
} }
} }
// https://stackoverflow.com/a/4882306/366692
public static <T> T clone(T object) throws CloneNotSupportedException {
Object clone = null;
// Use reflection, because there is no other way
try {
Method method = object.getClass().getMethod("clone");
clone = method.invoke(object);
} catch (InvocationTargetException e) {
rethrow(e.getCause());
} catch (Exception cause) {
rethrow(cause);
}
if (object.getClass().isInstance(clone)) {
@SuppressWarnings("unchecked") // clone class <= object class <= T
T t = (T) clone;
return t;
} else {
throw new ClassCastException(clone.getClass().getName());
}
}
private static void rethrow(Throwable cause) throws CloneNotSupportedException { private static void rethrow(Throwable cause) throws CloneNotSupportedException {
if (cause instanceof RuntimeException) { if (cause instanceof RuntimeException) {
throw (RuntimeException) cause; throw (RuntimeException) cause;

View file

@ -19,7 +19,6 @@ import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.helenus.core.reflect.Drafted;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
@ -35,7 +34,7 @@ public final class ValueProviderMap implements Map<String, Object> {
this.source = source; this.source = source;
this.valueProvider = valueProvider; this.valueProvider = valueProvider;
this.entity = entity; this.entity = entity;
this.immutable = entity.getMappingInterface().isAssignableFrom(Drafted.class); this.immutable = entity.isDraftable();
} }
private static void throwShouldNeverCall(String methodName) { private static void throwShouldNeverCall(String methodName) {

View file

@ -9,7 +9,7 @@ import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.annotation.*; import net.helenus.mapping.annotation.*;
@Table @Table
public interface Inventory extends Entity, Drafted<Inventory> { public interface Inventory extends Entity {
static Inventory inventory = Helenus.dsl(Inventory.class); static Inventory inventory = Helenus.dsl(Inventory.class);
@ -38,7 +38,7 @@ public interface Inventory extends Entity, Drafted<Inventory> {
return new Draft(this); return new Draft(this);
} }
class Draft extends AbstractAuditedEntityDraft<Inventory> { class Draft extends AbstractAuditedEntityDraft<Inventory> implements Drafted<Inventory> {
// Entity/Draft pattern-enabling methods: // Entity/Draft pattern-enabling methods:
Draft(UUID id) { Draft(UUID id) {

View file

@ -15,7 +15,7 @@ import net.helenus.mapping.annotation.*;
@Table @Table
@Cacheable @Cacheable
public interface Supply extends Entity, Drafted<Supply> { public interface Supply extends Entity {
static Supply supply = Helenus.dsl(Supply.class); static Supply supply = Helenus.dsl(Supply.class);
@ -52,8 +52,7 @@ public interface Supply extends Entity, Drafted<Supply> {
return new Draft(this); return new Draft(this);
} }
class Draft extends AbstractEntityDraft<Supply> { class Draft extends AbstractEntityDraft<Supply> implements Drafted<Supply> {
// Entity/Draft pattern-enabling methods: // Entity/Draft pattern-enabling methods:
Draft(String region) { Draft(String region) {
super(null); super(null);

View file

@ -19,6 +19,7 @@ import static net.helenus.core.Query.eq;
import com.datastax.driver.core.ConsistencyLevel; import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.utils.UUIDs; import com.datastax.driver.core.utils.UUIDs;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
import java.util.UUID; import java.util.UUID;
import net.bytebuddy.utility.RandomString; import net.bytebuddy.utility.RandomString;
@ -38,7 +39,7 @@ import org.junit.Test;
@Table @Table
@Cacheable @Cacheable
interface Widget extends Entity { interface Widget extends Entity, Serializable {
@PartitionKey @PartitionKey
UUID id(); UUID id();

View file

@ -39,7 +39,7 @@ public interface Account {
return new Draft(); return new Draft();
} }
class Draft implements Drafted { // TODO class Draft implements Drafted<Account> {
@Override @Override
public Set<String> mutated() { public Set<String> mutated() {
@ -47,7 +47,7 @@ public interface Account {
} }
@Override @Override
public Object build() { public Account build() {
return null; return null;
} }