Skip to content

Commit

Permalink
issue #495: require toString() for types used as aggregate identifier
Browse files Browse the repository at this point in the history
Aggregate field annotated with @EntityID or @AggregateIdentifier should override Object.toString()\

- StubAggregate classes now use UUID or String as identifier type
- java.lang.Object is no longer supported as identifier type
  • Loading branch information
bliessens committed Feb 8, 2018
1 parent 4d98198 commit ff2e341
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 32 deletions.
Expand Up @@ -20,6 +20,8 @@
import org.axonframework.commandhandling.model.AggregateRoot; import org.axonframework.commandhandling.model.AggregateRoot;
import org.axonframework.commandhandling.model.AggregateVersion; import org.axonframework.commandhandling.model.AggregateVersion;
import org.axonframework.commandhandling.model.EntityId; import org.axonframework.commandhandling.model.EntityId;
import org.axonframework.common.AxonConfigurationException;
import org.axonframework.common.IdentifierValidator;
import org.axonframework.common.ReflectionUtils; import org.axonframework.common.ReflectionUtils;
import org.axonframework.common.annotation.AnnotationUtils; import org.axonframework.common.annotation.AnnotationUtils;
import org.axonframework.eventhandling.EventMessage; import org.axonframework.eventhandling.EventMessage;
Expand Down Expand Up @@ -119,7 +121,7 @@ private class AnnotatedAggregateModel<T> implements AggregateModel<T> {
private Field versionField; private Field versionField;
private String routingKey; private String routingKey;


public AnnotatedAggregateModel(Class<? extends T> aggregateType, AnnotatedHandlerInspector<T> handlerInspector) { AnnotatedAggregateModel(Class<? extends T> aggregateType, AnnotatedHandlerInspector<T> handlerInspector) {
this.inspectedType = aggregateType; this.inspectedType = aggregateType;
this.commandHandlers = new HashMap<>(); this.commandHandlers = new HashMap<>();
this.eventHandlers = new ArrayList<>(); this.eventHandlers = new ArrayList<>();
Expand Down Expand Up @@ -147,7 +149,7 @@ private void prepareHandlers() {


private void inspectAggregateType() { private void inspectAggregateType() {
aggregateType = AnnotationUtils.findAnnotationAttributes(inspectedType, AggregateRoot.class) aggregateType = AnnotationUtils.findAnnotationAttributes(inspectedType, AggregateRoot.class)
.map(map -> (String) map.get("type")).filter(i -> i.length() > 0).orElse(inspectedType.getSimpleName()); .map(map -> (String) map.get("type")).filter(i -> i.length() > 0).orElse(inspectedType.getSimpleName());
} }


private void inspectFields() { private void inspectFields() {
Expand All @@ -173,8 +175,14 @@ private void inspectFields() {
routingKey = field.getName(); routingKey = field.getName();
}); });
} }
if (identifierField != null) {
final Class<?> idClazz = identifierField.getType();
if (!IdentifierValidator.getInstance().isValidIdentifier(idClazz)) {
throw new AxonConfigurationException(format("Aggregate identifier type [%s] should override Object.toString()", idClazz.getName()));
}
}
AnnotationUtils.findAnnotationAttributes(field, AggregateVersion.class) AnnotationUtils.findAnnotationAttributes(field, AggregateVersion.class)
.ifPresent(attributes -> versionField = field); .ifPresent(attributes -> versionField = field);
} }
} }


Expand Down Expand Up @@ -247,7 +255,7 @@ public Long getVersion(T target) {
* @return the handler of the message if present on the model * @return the handler of the message if present on the model
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Optional<MessageHandlingMember<? super T>> getHandler(Message<?> message) { private Optional<MessageHandlingMember<? super T>> getHandler(Message<?> message) {
for (MessageHandlingMember<? super T> handler : eventHandlers) { for (MessageHandlingMember<? super T> handler : eventHandlers) {
if (handler.canHandle(message)) { if (handler.canHandle(message)) {
return Optional.of(handler); return Optional.of(handler);
Expand Down
Expand Up @@ -33,16 +33,16 @@
public class StubAggregate { public class StubAggregate {


@AggregateIdentifier @AggregateIdentifier
private Object identifier; private String identifier;


private int invocationCount; private int invocationCount;


public StubAggregate() { public StubAggregate() {
identifier = UUID.randomUUID(); identifier = UUID.randomUUID().toString();
} }


public StubAggregate(Object identifier) { public StubAggregate(Object identifier) {
this.identifier = identifier; this.identifier = identifier.toString();
} }


public void doSomething() { public void doSomething() {
Expand Down
Expand Up @@ -144,7 +144,7 @@ public void testLinkedListIsModifiedDuringIterationInRecursiveHierarchy() {
public void testHashSetIsModifiedDuringIterationInRecursiveHierarchy() { public void testHashSetIsModifiedDuringIterationInRecursiveHierarchy() {
testCollectionIsModifiedDuringIterationInRecursiveHierarchy(HashSet::new); testCollectionIsModifiedDuringIterationInRecursiveHierarchy(HashSet::new);
} }

@Test @Test
public void testCopyOnWriteArrayListIsModifiedDuringIterationInRecursiveHierarchy() { public void testCopyOnWriteArrayListIsModifiedDuringIterationInRecursiveHierarchy() {
testCollectionIsModifiedDuringIterationInRecursiveHierarchy(CopyOnWriteArrayList::new); testCollectionIsModifiedDuringIterationInRecursiveHierarchy(CopyOnWriteArrayList::new);
Expand All @@ -154,7 +154,7 @@ public void testCopyOnWriteArrayListIsModifiedDuringIterationInRecursiveHierarch
public void testConcurrentLinkedQueueIsModifiedDuringIterationInRecursiveHierarchy() { public void testConcurrentLinkedQueueIsModifiedDuringIterationInRecursiveHierarchy() {
testCollectionIsModifiedDuringIterationInRecursiveHierarchy(ConcurrentLinkedQueue::new); testCollectionIsModifiedDuringIterationInRecursiveHierarchy(ConcurrentLinkedQueue::new);
} }

private void testCollectionIsModifiedDuringIterationInRecursiveHierarchy(Supplier<Collection<SomeRecursiveEntity>> supplier) { private void testCollectionIsModifiedDuringIterationInRecursiveHierarchy(Supplier<Collection<SomeRecursiveEntity>> supplier) {
// Note that if the inspector does not support recursive entities this will throw an StackOverflowError. // Note that if the inspector does not support recursive entities this will throw an StackOverflowError.
AggregateModel<SomeRecursiveEntity> inspector = AnnotatedAggregateMetaModelFactory.inspectAggregate(SomeRecursiveEntity.class); AggregateModel<SomeRecursiveEntity> inspector = AnnotatedAggregateMetaModelFactory.inspectAggregate(SomeRecursiveEntity.class);
Expand All @@ -178,14 +178,14 @@ private void testCollectionIsModifiedDuringIterationInRecursiveHierarchy(Supplie


// Assert child1: it should have 1 child // Assert child1: it should have 1 child
assertEquals(1, child1.children.size()); assertEquals(1, child1.children.size());

// Now move child3 up one level so it is a child of child1. // Now move child3 up one level so it is a child of child1.
// The resulting hierarchy will look as follows: // The resulting hierarchy will look as follows:
// root // root
// child1 // child1
// child2 // child2
// child3 // child3

// Note that if the inspector does not use copy-iterators this will throw an ConcurrentModificationException. // Note that if the inspector does not use copy-iterators this will throw an ConcurrentModificationException.
inspector.publish(asEventMessage(new MoveChildUp(childId2, childId3)), root); inspector.publish(asEventMessage(new MoveChildUp(childId2, childId3)), root);
assertEquals(2, child1.children.size()); assertEquals(2, child1.children.size());
Expand Down Expand Up @@ -241,6 +241,13 @@ public void testIllegalFactoryMethodThrowsExceptionClass() throws Exception {
AnnotatedAggregateMetaModelFactory.inspectAggregate(SomeIllegalAnnotatedFactoryMethodClass.class); AnnotatedAggregateMetaModelFactory.inspectAggregate(SomeIllegalAnnotatedFactoryMethodClass.class);
} }


@Test(expected = AxonConfigurationException.class)
public void typedAggregateIdentifier() {
AggregateModel<TypedIdentifierAggregate> inspector = AnnotatedAggregateMetaModelFactory.inspectAggregate(TypedIdentifierAggregate.class);

assertNotNull(inspector.getIdentifier(new TypedIdentifierAggregate()));
}

private static class JavaxPersistenceAnnotatedHandlers { private static class JavaxPersistenceAnnotatedHandlers {


@Id @Id
Expand Down Expand Up @@ -291,6 +298,27 @@ public void handle(AtomicLong value) {


} }


private static class CustomIdentifier {
}

@AggregateRoot
private static class TypedIdentifierAggregate {

@AggregateIdentifier
private CustomIdentifier aggregateIdentifier = new CustomIdentifier();

@CommandHandler
public boolean handleInSubclass(String test) {
return test.contains("sub");
}

@EventHandler
public void handle(AtomicLong value) {
value.incrementAndGet();
}

}

private static class SomeOtherEntity { private static class SomeOtherEntity {


@CommandHandler @CommandHandler
Expand All @@ -303,7 +331,7 @@ public void handle(AtomicLong value) {
value.incrementAndGet(); value.incrementAndGet();
} }
} }

private static class CreateChild { private static class CreateChild {
private final String parentId; private final String parentId;
private final String childId; private final String childId;
Expand Down Expand Up @@ -339,10 +367,10 @@ public SomeRecursiveEntity(Supplier<Collection<SomeRecursiveEntity>> supplier, S
this.entityId = entityId; this.entityId = entityId;
this.children = new SomeIterable<>(supplier.get()); this.children = new SomeIterable<>(supplier.get());
} }

public SomeRecursiveEntity getChild(String childId) { public SomeRecursiveEntity getChild(String childId) {
for(SomeRecursiveEntity c : children) { for (SomeRecursiveEntity c : children) {
if(Objects.equals(c.entityId, childId)) { if (Objects.equals(c.entityId, childId)) {
return c; return c;
} }
} }
Expand Down
@@ -0,0 +1,44 @@
package org.axonframework.common;

import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class IdentifierValidatorTest {

private final IdentifierValidator validator = IdentifierValidator.getInstance();

@Test
public void boxedPrimitivesAreValidIdentifiers() {
assertTrue(validator.isValidIdentifier(Long.class));
assertTrue(validator.isValidIdentifier(Integer.class));
assertTrue(validator.isValidIdentifier(Double.class));
assertTrue(validator.isValidIdentifier(Short.class));
}

@Test
public void stringIsValidIdentifier() {
assertTrue(validator.isValidIdentifier(CharSequence.class));
}

@Test
public void typeWithoutToStringIsNotAccepted() {
assertFalse(validator.isValidIdentifier(CustomType.class));
}

@Test
public void typeWithOverriddenToString() {
assertTrue(validator.isValidIdentifier(CustomType2.class));
}

private static class CustomType {
}

private static class CustomType2 {
@Override
public String toString() {
return "ok";
}
}
}
Expand Up @@ -96,7 +96,7 @@ public void testCreateSnapshot_AggregateMarkedDeletedWillNotGenerateSnapshot() {
DomainEventMessage firstEvent = new GenericDomainEventMessage<>("type", aggregateIdentifier, (long) 0, DomainEventMessage firstEvent = new GenericDomainEventMessage<>("type", aggregateIdentifier, (long) 0,
"Mock contents", MetaData.emptyInstance()); "Mock contents", MetaData.emptyInstance());
DomainEventMessage secondEvent = new GenericDomainEventMessage<>("type", aggregateIdentifier, (long) 0, DomainEventMessage secondEvent = new GenericDomainEventMessage<>("type", aggregateIdentifier, (long) 0,
"deleted", MetaData.emptyInstance()); "deleted", MetaData.emptyInstance());
DomainEventStream eventStream = DomainEventStream.of(firstEvent, secondEvent); DomainEventStream eventStream = DomainEventStream.of(firstEvent, secondEvent);
StubAggregate aggregate = new StubAggregate(aggregateIdentifier); StubAggregate aggregate = new StubAggregate(aggregateIdentifier);
when(mockAggregateFactory.createAggregateRoot(aggregateIdentifier, firstEvent)).thenReturn(aggregate); when(mockAggregateFactory.createAggregateRoot(aggregateIdentifier, firstEvent)).thenReturn(aggregate);
Expand All @@ -111,14 +111,14 @@ public void testCreateSnapshot_AggregateMarkedDeletedWillNotGenerateSnapshot() {
public static class StubAggregate { public static class StubAggregate {


@AggregateIdentifier @AggregateIdentifier
private Object identifier; private String identifier;


public StubAggregate() { public StubAggregate() {
identifier = UUID.randomUUID(); identifier = UUID.randomUUID().toString();
} }


public StubAggregate(Object identifier) { public StubAggregate(Object identifier) {
this.identifier = identifier; this.identifier = identifier.toString();
} }


public void doSomething() { public void doSomething() {
Expand All @@ -134,7 +134,7 @@ protected void handle(EventMessage event) {
identifier = ((DomainEventMessage) event).getAggregateIdentifier(); identifier = ((DomainEventMessage) event).getAggregateIdentifier();
// See Issue # // See Issue #
if ("Mock contents".equals(event.getPayload().toString())) { if ("Mock contents".equals(event.getPayload().toString())) {
apply("Another"); apply("Another");
} }
if ("deleted".equals(event.getPayload().toString())) { if ("deleted".equals(event.getPayload().toString())) {
markDeleted(); markDeleted();
Expand Down
Expand Up @@ -29,7 +29,7 @@ public class StubAggregate {
private int changeCounter; private int changeCounter;


@AggregateIdentifier @AggregateIdentifier
private Object identifier; private String identifier;


public StubAggregate(Object aggregateId) { public StubAggregate(Object aggregateId) {
apply(new StubAggregateCreatedEvent(aggregateId)); apply(new StubAggregateCreatedEvent(aggregateId));
Expand All @@ -48,7 +48,7 @@ public void causeTrouble() {


@EventSourcingHandler @EventSourcingHandler
private void onCreated(StubAggregateCreatedEvent event) { private void onCreated(StubAggregateCreatedEvent event) {
this.identifier = event.getAggregateIdentifier(); this.identifier = event.getAggregateIdentifier().toString();
changeCounter = 0; changeCounter = 0;
} }


Expand Down
Expand Up @@ -36,14 +36,14 @@ public class StubAggregate {
private int invocationCount; private int invocationCount;


@AggregateIdentifier @AggregateIdentifier
private Object identifier; private String identifier;


public StubAggregate() { public StubAggregate() {
identifier = UUID.randomUUID(); identifier = UUID.randomUUID().toString();
} }


public StubAggregate(Object identifier) { public StubAggregate(Object identifier) {
this.identifier = identifier; this.identifier = identifier.toString();
} }


public void doSomething() { public void doSomething() {
Expand Down
Expand Up @@ -37,11 +37,11 @@ class AnnotatedAggregate implements AnnotatedAggregateInterface {
private transient int counter; private transient int counter;
private Integer lastNumber; private Integer lastNumber;
@AggregateIdentifier @AggregateIdentifier
private Object identifier; private String identifier;
private MyEntity entity; private MyEntity entity;


public AnnotatedAggregate(Object identifier) { public AnnotatedAggregate(Object identifier) {
this.identifier = identifier; this.identifier = identifier.toString();
} }


public AnnotatedAggregate() { public AnnotatedAggregate() {
Expand Down Expand Up @@ -71,7 +71,7 @@ public void doSomethingIllegal(IllegalStateChangeCommand command) {


@EventSourcingHandler @EventSourcingHandler
public void handleMyEvent(MyEvent event) { public void handleMyEvent(MyEvent event) {
identifier = event.getAggregateIdentifier(); identifier = event.getAggregateIdentifier() == null ? null : event.getAggregateIdentifier().toString();
lastNumber = event.getSomeValue(); lastNumber = event.getSomeValue();
if (entity == null) { if (entity == null) {
entity = new MyEntity(); entity = new MyEntity();
Expand Down
Expand Up @@ -203,13 +203,14 @@ public static class InterceptorAggregate {
private transient int counter; private transient int counter;
private Integer lastNumber; private Integer lastNumber;
@AggregateIdentifier @AggregateIdentifier
private Object identifier; private String identifier;
private MyEntity entity; private MyEntity entity;


public InterceptorAggregate() { public InterceptorAggregate() {
} }


public InterceptorAggregate(Object aggregateIdentifier) { public InterceptorAggregate(Object aggregateIdentifier) {
identifier = aggregateIdentifier.toString();
} }


@CommandHandler @CommandHandler
Expand All @@ -225,7 +226,7 @@ public void handle(TestCommand command, MetaData metaData) {


@EventHandler @EventHandler
public void handle(StandardAggregateCreatedEvent event) { public void handle(StandardAggregateCreatedEvent event) {
this.identifier = event.getAggregateIdentifier(); this.identifier = event.getAggregateIdentifier().toString();
} }


} }
Expand Down
Expand Up @@ -35,10 +35,11 @@ class StandardAggregate {
private transient int counter; private transient int counter;
private Integer lastNumber; private Integer lastNumber;
@AggregateIdentifier @AggregateIdentifier
private Object identifier; private String identifier;
private MyEntity entity; private MyEntity entity;


public StandardAggregate(Object aggregateIdentifier) { public StandardAggregate(Object aggregateIdentifier) {
identifier = aggregateIdentifier.toString();
} }


public StandardAggregate(int initialValue, Object aggregateIdentifier) { public StandardAggregate(int initialValue, Object aggregateIdentifier) {
Expand All @@ -59,7 +60,7 @@ public void doSomethingIllegal(Integer newIllegalValue) {


@EventSourcingHandler @EventSourcingHandler
public void handleMyEvent(MyEvent event) { public void handleMyEvent(MyEvent event) {
identifier = event.getAggregateIdentifier(); identifier = event.getAggregateIdentifier().toString();
lastNumber = event.getSomeValue(); lastNumber = event.getSomeValue();
if (entity == null) { if (entity == null) {
entity = new MyEntity(); entity = new MyEntity();
Expand Down

0 comments on commit ff2e341

Please sign in to comment.