Skip to content

Commit

Permalink
feat(assemble): support method based operation configuration (GitHub #…
Browse files Browse the repository at this point in the history
  • Loading branch information
Createsequence committed Feb 21, 2024
1 parent 91d7003 commit 4d97c97
Show file tree
Hide file tree
Showing 22 changed files with 258 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public static MapCacheManager newWeakConcurrentMapCacheManager() {
* @param timeUnit time unit
* @return cache instance
*/
@Override
@NonNull
protected <K> MapCacheObject<K> doCreateCache(String name, Long expireTime, TimeUnit timeUnit) {
return new MapCacheObject<>(name, createMap());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
import cn.crane4j.core.support.AnnotationFinder;
import cn.crane4j.core.support.reflect.PropertyOperator;
import cn.crane4j.core.util.Asserts;
import cn.crane4j.core.util.StringUtils;
import cn.crane4j.core.util.ConfigurationUtil;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;

/**
* A condition parser implementation to process annotation based condition with property.
Expand Down Expand Up @@ -40,9 +39,8 @@ protected AbstractPropertyConditionParser(
@Override
protected AbstractCondition createCondition(AnnotatedElement element, A annotation) {
String property = getPropertyName(element, annotation);
property = StringUtils.isEmpty(property) && element instanceof Field ?
((Field)element).getName() : property;
Asserts.isNotEmpty(property, "@{} must specify a property to apply!", annotationType.getSimpleName());
property = ConfigurationUtil.getElementIdentifier(element, property);
Asserts.isNotEmpty(property, "The property to be checked is not specified in the @{} on {}", annotationType.getSimpleName());
return new PropertyValueCondition(property);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public ConditionOnExpressionParser(
@Override
protected AbstractCondition createCondition(AnnotatedElement element, ConditionOnExpression annotation) {
String expression = annotation.value();
Asserts.isNotEmpty(expression, "not specified expression to apply with annotation @{} on {}", annotationType.getSimpleName(), element);
Asserts.isNotEmpty(expression, "The property to be checked is not specified in the @{} on {}", annotationType.getSimpleName(), element);
return new ExpressionCondition(expression);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package cn.crane4j.core.condition;

import cn.crane4j.annotation.condition.ConditionOnExpression;
import cn.crane4j.annotation.condition.ConditionOnProperty;
import cn.crane4j.core.parser.operation.KeyTriggerOperation;
import cn.crane4j.core.support.AnnotationFinder;
import cn.crane4j.core.support.converter.ConverterManager;
import cn.crane4j.core.support.reflect.PropertyOperator;
import cn.crane4j.core.util.Asserts;
import cn.crane4j.core.util.ClassUtils;
import cn.crane4j.core.util.StringUtils;
import cn.crane4j.core.util.ConfigurationUtil;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.util.Objects;

/**
Expand Down Expand Up @@ -74,9 +72,8 @@ protected ConditionDescriptor getConditionDescriptor(ConditionOnProperty annotat
private String getPropertyName(
AnnotatedElement element, ConditionOnProperty annotation) {
String property = annotation.property();
property = StringUtils.isEmpty(property) && element instanceof Field ?
((Field)element).getName() : property;
Asserts.isNotEmpty(property, "@{} must specify a property to apply: {}", ConditionOnExpression.class.getSimpleName(), element);
property = ConfigurationUtil.getElementIdentifier(element, property);
Asserts.isNotEmpty(property, "The property to be checked is not specified in the @{} on {}", annotationType.getSimpleName(), element);
return property;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected ConditionDescriptor getConditionDescriptor(ConditionOnTargetType annot
@Nullable
@Override
protected AbstractCondition createCondition(AnnotatedElement element, ConditionOnTargetType annotation) {
Asserts.isNotEmpty(annotation.value(), "@{} must specify expected types to apply: {}", annotationType.getSimpleName(), element);
Asserts.isNotEmpty(annotation.value(), "The expected value is not specified in the @{} on {}", annotationType.getSimpleName(), element);
return annotation.strict() ?
new StrictlyTargetTypeCondition(annotation.value()) : new TargetTypeCondition(annotation.value());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import cn.crane4j.core.container.Container;
import cn.crane4j.core.container.ContainerManager;
import cn.crane4j.core.exception.OperationExecuteException;
import cn.crane4j.core.executor.handler.AssembleOperationHandler;
import cn.crane4j.core.executor.handler.DisassembleOperationHandler;
import cn.crane4j.core.parser.BeanOperations;
import cn.crane4j.core.parser.operation.AssembleOperation;
Expand Down Expand Up @@ -202,7 +203,7 @@ protected AssembleExecution createAssembleExecution(
targets = filterTargetsForSupportedOperation(targets, operation);
String namespace = operation.getContainer();
Container<?> container = options.getContainer(containerManager, namespace);
Asserts.isNotNull(container, "container of [{}] not found", namespace);
Asserts.isNotNull(container, "container [{}] not found", namespace);
return AssembleExecution.create(beanOperations, operation, container, targets);
}

Expand Down Expand Up @@ -314,14 +315,16 @@ private boolean waitForOperationActiveUntilTimeout(BeanOperations operations) {
* <p>Try to execute the operation.<br />
* If necessary, output the log when throwing an exception.
*
* @param execute execute
* @param handler handler
* @param executions executions
* @param container container
*/
protected static void tryExecute(Runnable execute) {
protected static void doExecute(
AssembleOperationHandler handler, Container<?> container, Collection<AssembleExecution> executions) {
try {
execute.run();
} catch(Exception e) {
log.warn("execute operation fail: {}", e.getMessage());
e.printStackTrace();
handler.process(container, executions);
} catch(Exception ex) {
log.warn("execute operation fail: {}", ex.getMessage(), ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ protected void executeOperations(List<AssembleExecution> executions, Options opt

private void doExecuteOperations(AssembleExecution execution) {
Container<?> container = execution.getContainer();
tryExecute(() -> execution.getHandler().process(container, Collections.singleton(execution)));
doExecute(execution.getHandler(), container, Collections.singletonList(execution));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,8 @@ protected void executeOperations(List<AssembleExecution> executions, Options opt
* @param executionGroups grouped assembly operations
*/
protected void doExecuteOperations(Map<Container<?>, Map<AssembleOperationHandler, List<AssembleExecution>>> executionGroups) {
executionGroups.forEach((c, he) ->
he.forEach((h, es) ->
tryExecute(() -> h.process(c, es))
));
executionGroups.forEach((container, he) ->
he.forEach((handler, executions) -> doExecute(handler, container, executions))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,8 @@ public OrderedBeanOperationExecutor(ContainerManager containerManager, Comparato
*/
@Override
protected void executeOperations(List<AssembleExecution> executions, Options options) throws OperationExecuteException {
try {
executions.stream()
.sorted(Comparator.comparing(AssembleExecution::getOperation, comparator))
.forEach(e -> tryExecute(() -> e.getHandler().process(e.getContainer(), Collections.singletonList(e))));
} catch (Exception e) {
throw new OperationExecuteException(e);
}
executions.stream()
.sorted(Comparator.comparing(AssembleExecution::getOperation, comparator))
.forEach(e -> doExecute(e.getHandler(), e.getContainer(), Collections.singletonList(e)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import cn.crane4j.core.parser.operation.SimpleKeyTriggerOperation;
import cn.crane4j.core.support.AnnotationFinder;
import cn.crane4j.core.support.Crane4jGlobalSorter;
import cn.crane4j.core.util.ConfigurationUtil;
import cn.crane4j.core.util.MultiMap;
import cn.crane4j.core.util.ReflectUtils;
import cn.crane4j.core.util.StringUtils;
Expand All @@ -18,7 +19,7 @@

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -84,6 +85,7 @@ private List<KeyTriggerOperation> parseOperations(
Class<?> beanType = (Class<?>)source;
annotations.putAll(beanType, parseAnnotationForClass(beanType));
annotations.putAll(parseAnnotationForFields(beanType));
annotations.putAll(parseAnnotationForMethods(beanType));
} else {
annotations.putAll(source, parseAnnotationForElement(source));
}
Expand Down Expand Up @@ -119,6 +121,24 @@ protected Set<A> parseAnnotationForClass(Class<?> beanType) {
return parseAnnotationForElement(beanType);
}

/**
* Parse annotation for methods
*
* @param beanType bean type
* @return element and annotation map
* @since 2.6.0
*/
protected MultiMap<AnnotatedElement, A> parseAnnotationForMethods(Class<?> beanType) {
MultiMap<AnnotatedElement, A> result = MultiMap.arrayListMultimap();
Method[] methods = Stream.of(ReflectUtils.getDeclaredMethods(beanType))
.filter(method -> method.getParameterCount() == 0)
.filter(method -> !Objects.equals(method.getReturnType(), Void.TYPE))
.toArray(Method[]::new);
ReflectUtils.scanAllAnnotationFromElements(
annotationFinder, annotationType, methods, result::put);
return result;
}

/**
* Parse annotation for fields
*
Expand Down Expand Up @@ -165,11 +185,8 @@ protected KeyTriggerOperation createOperation(
protected String parseId(
AnnotatedElement element, StandardAnnotation annotation) {
String id = annotation.getId();
if (StringUtils.isNotEmpty(id)) {
return id;
}
return element instanceof Field ?
((Field)element).getName() : annotation.getKey();
return StringUtils.isNotEmpty(id) ?
id : ConfigurationUtil.getElementIdentifier(element, annotation.getKey());
}

/**
Expand All @@ -180,10 +197,7 @@ protected String parseId(
* @return groups
*/
protected String parseKey(AnnotatedElement element, StandardAnnotation standardAnnotation) {
// we should allow the key to be empty,
// where the key value is the targets themselves.
return (element instanceof Field) ?
((Field) element).getName() : standardAnnotation.getKey();
return ConfigurationUtil.getElementIdentifier(element, standardAnnotation.getKey());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,10 @@ protected ContainerMethodResolver(Collection<MethodContainerFactory> methodConta
@Nullable
public Container<Object> resolve(AssembleMethod annotation) {
Class<?> targetType = resolveTargetType(annotation);
Asserts.isNotNull(targetType, "cannot resolve target type for annotation {}", annotation);
Asserts.isNotNull(targetType, "cannot resolve target type from annotation {}", annotation);
Method resolvedMethod = getContainerMethod(annotation, targetType);
Asserts.isNotNull(
resolvedMethod, "cannot resolve method of class [{}] for annotation [{}]", targetType, annotation
resolvedMethod, "cannot resolve method of class [{}] from annotation [{}]", targetType, annotation
);
Object target = Modifier.isStatic(resolvedMethod.getModifiers()) ?
null : getTargetInstance(targetType, annotation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ public DisassembleOperationHandler getDisassembleOperationHandler(@Nullable Stri
@Override
public CacheManager getCacheManager(String name) {
CacheManager factory = cacheManagerMap.get(name);
Asserts.isNotNull(factory, "cannot find cache factory [{}]", name);
Asserts.isNotNull(factory, "cannot find cache manager [{}]", name);
return factory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
Expand All @@ -45,6 +49,29 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ConfigurationUtil {

/**
* Get element identifier.
*
* @param source source
* @param defaultValue default id
* @return name if source is field, method or parameter, otherwise return defaultValue
*/
public static String getElementIdentifier(AnnotatedElement source, String defaultValue) {
if (StringUtils.isNotEmpty(defaultValue)) {
return defaultValue;
}
if (source instanceof Field) {
return ((Field)source).getName();
}
if (source instanceof Method) {
return ((Method)source).getName();
}
if (source instanceof Parameter) {
return ((Parameter)source).getName();
}
return defaultValue;
}

/**
* Create {@link OperatorProxyFactory} instance.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -66,6 +68,16 @@ public void executeWithCondition() {
Assert.assertNull(bean2.getName());
}

@Test
public void executeMethodBasedOperation() {
MethodBasedOperationBean bean1 = new MethodBasedOperationBean(1, 1);

BeanOperations beanOperations = parseOperations(MethodBasedOperationBean.class);
executor.execute(Collections.singleton(bean1), beanOperations);
Assert.assertEquals("one", bean1.getName1());
Assert.assertEquals("one", bean1.getName2());
}

@Getter
@RequiredArgsConstructor
private static class Source {
Expand Down Expand Up @@ -99,4 +111,25 @@ private static class ConditionalBean {
private Integer id;
private String name;
}


@Assemble(key = "getField1", container = "test", props = @Mapping(ref = "name1", src = "value"))
@RequiredArgsConstructor
private static class MethodBasedOperationBean {
@Getter
private final Integer field1;
@Getter
@Setter
private String name1;

private final Integer field2;
@Getter
@Setter
private String name2;

@Assemble(container = "test", props = @Mapping(ref = "name2", src = "value"))
public Integer getId() {
return field1;
}
}
}
Loading

0 comments on commit 4d97c97

Please sign in to comment.