Skip to content

Commit

Permalink
Spaces reset to original
Browse files Browse the repository at this point in the history
  • Loading branch information
JohT committed Jan 8, 2019
1 parent 91a4e4b commit 9008e77
Show file tree
Hide file tree
Showing 2 changed files with 264 additions and 65 deletions.
Expand Up @@ -16,16 +16,21 @@

package org.axonframework.modelling.command;

import org.axonframework.commandhandling.CommandMessage;
import org.axonframework.common.annotation.AnnotationUtils;
import static java.lang.String.format;
import static org.axonframework.common.ReflectionUtils.ensureAccessible;
import static org.axonframework.common.ReflectionUtils.fieldsOf;
import static org.axonframework.common.ReflectionUtils.getFieldValue;
import static org.axonframework.common.ReflectionUtils.methodsOf;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;

import static java.lang.String.format;
import static org.axonframework.common.ReflectionUtils.*;
import org.axonframework.commandhandling.CommandMessage;
import org.axonframework.common.annotation.AnnotationUtils;
import org.axonframework.messaging.Message;

/**
* CommandTargetResolver that uses annotations on the command to identify the methods that provide the
Expand All @@ -44,6 +49,20 @@
*/
public class AnnotationCommandTargetResolver implements CommandTargetResolver {

private Class<? extends Annotation> identifierAnnotation = TargetAggregateIdentifier.class;
private Class<? extends Annotation> versionAnnotation = TargetAggregateVersion.class;

public static final Builder builder() {
return new Builder();
}

/**
* Default settings. Use {@link #builder()} for custom setup.
*/
public AnnotationCommandTargetResolver() {
super();
}

@Override
public VersionedAggregateIdentifier resolveTarget(CommandMessage<?> command) {
String aggregateIdentifier;
Expand All @@ -70,44 +89,101 @@ public VersionedAggregateIdentifier resolveTarget(CommandMessage<?> command) {
return new VersionedAggregateIdentifier(aggregateIdentifier, aggregateVersion);
}

private String findIdentifier(CommandMessage<?> command)
throws InvocationTargetException, IllegalAccessException {
for (Method m : methodsOf(command.getPayloadType())) {
if (m.isAnnotationPresent(TargetAggregateIdentifier.class)) {
ensureAccessible(m);
return Optional.ofNullable(m.invoke(command.getPayload())).map(Object::toString).orElse(null);
}
}
for (Field f : fieldsOf(command.getPayloadType())) {
if (f.isAnnotationPresent(TargetAggregateIdentifier.class)) {
return Optional.ofNullable(getFieldValue(f, command.getPayload())).map(Object::toString).orElse(null);
}
}
return null;
}
private String findIdentifier(Message<?> command) throws InvocationTargetException, IllegalAccessException {
return Optional.ofNullable(invokeAnnotated(command, identifierAnnotation)).map(Object::toString).orElse(null);
}

private Long findVersion(CommandMessage<?> command) throws InvocationTargetException, IllegalAccessException {
for (Method m : methodsOf(command.getPayloadType())) {
if (AnnotationUtils.isAnnotationPresent(m, TargetAggregateVersion.class)) {
ensureAccessible(m);
return asLong(m.invoke(command.getPayload()));
}
}
for (Field f : fieldsOf(command.getPayloadType())) {
if (AnnotationUtils.isAnnotationPresent(f, TargetAggregateVersion.class)) {
return asLong(getFieldValue(f, command.getPayload()));
}
}
return null;
}
private Long findVersion(Message<?> command) throws InvocationTargetException, IllegalAccessException {
return asLong(invokeAnnotated(command, versionAnnotation));
}

private Long asLong(Object fieldValue) {
if (fieldValue == null) {
return null;
} else if (Number.class.isInstance(fieldValue)) {
return ((Number) fieldValue).longValue();
} else {
return Long.parseLong(fieldValue.toString());
}
}
}
private static Object invokeAnnotated(Message<?> command, Class<? extends Annotation> annotation)
throws InvocationTargetException, IllegalAccessException {
for (Method m : methodsOf(command.getPayloadType())) {
if (AnnotationUtils.isAnnotationPresent(m, annotation)) {
ensureAccessible(m);
return m.invoke(command.getPayload());
}
}
for (Field f : fieldsOf(command.getPayloadType())) {
if (AnnotationUtils.isAnnotationPresent(f, annotation)) {
return getFieldValue(f, command.getPayload());
}
}
return null;
}

private Long asLong(Object fieldValue) {
if (fieldValue == null) {
return null;
} else if (Number.class.isInstance(fieldValue)) {
return ((Number) fieldValue).longValue();
} else {
return Long.parseLong(fieldValue.toString());
}
}

@Override
public String toString() {
return "AnnotationCommandTargetResolver [identifierAnnotation=" + identifierAnnotation + ", versionAnnotation="
+ versionAnnotation + "]";
}

public static final class Builder {
private AnnotationCommandTargetResolver resolver;

public Builder() {
this.resolver = new AnnotationCommandTargetResolver();
}

/**
* Sets the annotation, that marks the target aggregate identifier.
* <p>
* Defaults to {@link TargetAggregateIdentifier}.<br>
* <p>
* If you do not want your messages-module (as inner bounded context "API") to
* be dependent of axon annotations (e.g. to aim empty pom dependencies), then
* you can write your own annotation based on the
* {@link TargetAggregateIdentifier} without referencing the original one (as
* meta-annotation).
*
* @param annotation - {@link Class} of type {@link Annotation}.
* @return {@link Builder}
*/
public Builder setTargetAggregateIdentifierAnnotation(Class<? extends Annotation> annotation) {
this.resolver.identifierAnnotation = annotation;
return this;
}

/**
* Sets the annotation, that marks the target aggregate version.
* <p>
* Defaults to {@link TargetAggregateVersion}.
* <p>
* If you do not want your messages-module (as inner bounded context "API") to
* be dependent of axon annotations (e.g. to aim empty pom dependencies), then
* you can write your own annotation based on the {@link TargetAggregateVersion}
* without referencing the original one (as meta-annotation).
*
* @param annotation - {@link Class} of type {@link Annotation}.
* @return {@link Builder}
*/
public Builder setTargetAggregateVersionAnnotation(Class<? extends Annotation> annotation) {
this.resolver.versionAnnotation = annotation;
return this;
}

public AnnotationCommandTargetResolver build() {
try {
return resolver;
} finally {
resolver = null; // builder can only be used once.
}
}

@Override
public String toString() {
return "Builder [resolver=" + resolver + "]";
}
}
}
Expand Up @@ -16,15 +16,20 @@

package org.axonframework.modelling.command;

import org.junit.Before;
import org.junit.Test;

import java.util.UUID;

import static org.axonframework.commandhandling.GenericCommandMessage.asCommandMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.UUID;

import org.axonframework.commandhandling.RoutingKey;
import org.junit.Before;
import org.junit.Test;

/**
* @author Allard Buijze
*/
Expand Down Expand Up @@ -150,25 +155,143 @@ public void testResolveTarget_WithAnnotatedFields_NonNumericVersion() {
testSubject.resolveTarget(asCommandMessage(new FieldAnnotatedCommand(aggregateIdentifier, version)));
}

private static class FieldAnnotatedCommand {
@Test
public void testMetaAnnotations_OnMethods() {
final UUID aggregateIdentifier = UUID.randomUUID();
final Long version = Long.valueOf(98765432109L);

@TargetAggregateIdentifier
private final Object aggregateIdentifier;
VersionedAggregateIdentifier actual = testSubject.resolveTarget(asCommandMessage(new Object() {
@MetaTargetAggregateIdentifier
private UUID getIdentifier() {
return aggregateIdentifier;
}

@TargetAggregateVersion
private final Object version;
@MetaTargetAggregateVersion
private Long version() {
return version;
}
}));
assertEquals(aggregateIdentifier.toString(), actual.getIdentifier());
assertEquals(version, actual.getVersion());
}

public FieldAnnotatedCommand(Object aggregateIdentifier, Object version) {
this.aggregateIdentifier = aggregateIdentifier;
this.version = version;
}
@Test
public void testMetaAnnotations_OnFields() {
final UUID aggregateIdentifier = UUID.randomUUID();
final Long version = Long.valueOf(98765432109L);

public Object getAggregateIdentifier() {
return aggregateIdentifier;
}
VersionedAggregateIdentifier actual = testSubject.resolveTarget(
asCommandMessage(new FieldMetaAnnotatedCommand(aggregateIdentifier, version)));

public Object getVersion() {
return version;
}
}
}
assertEquals(aggregateIdentifier.toString(), actual.getIdentifier());
assertEquals(version, actual.getVersion());
}

@Test
public void testCustomAnnotations_OnMethods() {
testSubject = AnnotationCommandTargetResolver.builder()
.setTargetAggregateIdentifierAnnotation(CustomTargetAggregateIdentifier.class)
.setTargetAggregateVersionAnnotation(CustomTargetAggregateVersion.class)
.build();

final UUID aggregateIdentifier = UUID.randomUUID();
final Long version = Long.valueOf(98765432109L);

VersionedAggregateIdentifier actual = testSubject.resolveTarget(asCommandMessage(new Object() {
@CustomTargetAggregateIdentifier
private UUID getIdentifier() {
return aggregateIdentifier;
}

@CustomTargetAggregateVersion
private Long version() {
return version;
}
}));
assertEquals(aggregateIdentifier.toString(), actual.getIdentifier());
assertEquals(version, actual.getVersion());
}

@Test
public void testCustomAnnotations_OnFields() {
testSubject = AnnotationCommandTargetResolver.builder()
.setTargetAggregateIdentifierAnnotation(CustomTargetAggregateIdentifier.class)
.setTargetAggregateVersionAnnotation(CustomTargetAggregateVersion.class)
.build();

final UUID aggregateIdentifier = UUID.randomUUID();
final Long version = Long.valueOf(98765432109L);

VersionedAggregateIdentifier actual = testSubject.resolveTarget(
asCommandMessage(new FieldCustomAnnotatedCommand(aggregateIdentifier, version)));

assertEquals(aggregateIdentifier.toString(), actual.getIdentifier());
assertEquals(version, actual.getVersion());
}

private static class FieldAnnotatedCommand {

@TargetAggregateIdentifier
private final Object aggregateIdentifier;

@TargetAggregateVersion
private final Object version;

public FieldAnnotatedCommand(Object aggregateIdentifier, Object version) {
this.aggregateIdentifier = aggregateIdentifier;
this.version = version;
}
}

private static class FieldMetaAnnotatedCommand {

@MetaTargetAggregateIdentifier
private final Object aggregateIdentifier;

@MetaTargetAggregateVersion
private final Object version;

public FieldMetaAnnotatedCommand(Object aggregateIdentifier, Object version) {
this.aggregateIdentifier = aggregateIdentifier;
this.version = version;
}
}

private static class FieldCustomAnnotatedCommand {

@CustomTargetAggregateIdentifier
private final Object aggregateIdentifier;

@CustomTargetAggregateVersion
private final Object version;

public FieldCustomAnnotatedCommand(Object aggregateIdentifier, Object version) {
this.aggregateIdentifier = aggregateIdentifier;
this.version = version;
}
}

@RoutingKey
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@TargetAggregateIdentifier
public static @interface MetaTargetAggregateIdentifier {
}

@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@TargetAggregateVersion
public static @interface MetaTargetAggregateVersion {
}

@RoutingKey
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public static @interface CustomTargetAggregateIdentifier {
}

@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public static @interface CustomTargetAggregateVersion {
}
}

0 comments on commit 9008e77

Please sign in to comment.