Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ protected final void executeAssembleOperations(
* </li>
* </ul>
*/
protected abstract void doExecuteAssembleOperations(List<AssembleExecution> executions, BeanOperationExecutor.Options options) throws OperationExecuteException;
protected abstract void doExecuteAssembleOperations(
List<AssembleExecution> executions, BeanOperationExecutor.Options options) throws OperationExecuteException;

@NonNull
private List<AssembleExecution> combineExecutions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,16 @@
* it is recommended to minimize the number of accesses to the {@link Container}.
*
* @author huangchengxing
* @see AbstractOperationAwareBeanOperationExecutor
* @see AsyncBeanOperationExecutor
* @see DisorderedBeanOperationExecutor
* @see OrderedBeanOperationExecutor
* @since 2.9.0
*/
@Slf4j
public abstract class AbstractFlatDisassembleBeanOperationExecutor
extends AbstractOperationAwareBeanOperationExecutor implements BeanOperationExecutor {
public abstract class AbstractBeanOperationFlattingExecutor
extends AbstractOperationAwareBeanOperationExecutor {

protected AbstractFlatDisassembleBeanOperationExecutor(ContainerManager containerManager) {
protected AbstractBeanOperationFlattingExecutor(ContainerManager containerManager) {
super(containerManager);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package cn.crane4j.core.executor;

import cn.crane4j.core.container.Container;
import cn.crane4j.core.container.ContainerManager;
import cn.crane4j.core.executor.handler.DisassembleOperationHandler;
import cn.crane4j.core.parser.BeanOperations;
import cn.crane4j.core.parser.operation.AssembleOperation;
import cn.crane4j.core.parser.operation.DisassembleOperation;
import cn.crane4j.core.parser.operation.KeyTriggerOperation;
import cn.crane4j.core.util.CollectionUtils;
import cn.crane4j.core.util.MultiMap;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;

import java.util.Collection;
import java.util.function.Predicate;

/**
*
* <p>This class serves as a template class and provides a basic skeleton implementation
* for most of the {@link BeanOperationExecutor},
* particularly shielding the complexity of parsing {@link DisassembleOperation} operations.
* Once the {@link #doExecuteAssembleOperations} method is implemented in a subclass,
* it can be used as a {@link BeanOperationExecutor}.
*
* <p>According to the instructions specified in {@link BeanOperations},
* the following steps are performed when executing operations through the {@link BeanOperationExecutor#execute} method:
* <ul>
* <li>
* If there are any {@link DisassembleOperation} operations to be executed,
* recursively extract and flatten the objects that need to be processed from
* the {@code target} object (if it is a collection or an array, iterate over each element);
* </li>
* <li>
* Group all the objects to be processed based on their corresponding {@link BeanOperations},
* and wrap each group into an {@link AssembleExecution} object.
* </li>
* <li>
* Invoke the {@link #doExecuteAssembleOperations} method implemented in the subclass to
* actually perform the operations within each {@link AssembleExecution}.
* </li>
* </ul>
*
* <p>This class only guarantees the sequential execution of {@link DisassembleOperation} operations,
* while the sequential execution of {@link AssembleOperation} operations depends on
* the implementation logic of {@link #doExecuteAssembleOperations}.<br />
* For performance reasons, when implementing the {@link #doExecuteAssembleOperations} method,
* it is recommended to minimize the number of accesses to the {@link Container}.
*
* @author huangchengxing
* @see AsyncBeanOperationRecursiveExecutor
* @see DisorderedBeanOperationRecursiveExecutor
* @see OrderedBeanOperationRecursiveExecutor
* @since 2.9.0
*/
@Slf4j
public abstract class AbstractBeanOperationRecursiveExecutor
extends AbstractOperationAwareBeanOperationExecutor {

protected AbstractBeanOperationRecursiveExecutor(ContainerManager containerManager) {
super(containerManager);
}

/**
* Complete operations on all objects in {@code targets} according to the specified {@link BeanOperations} and {@link Options}.
*
* @param targets targets
* @param operations operations to be performed
* @param options options for execution
* @see #beforeDisassembleOperation
* @see #beforeAssembleOperation
* @see #afterOperationsCompletion
*/
@Override
public void doExecute(
@NonNull Collection<?> targets, @NonNull BeanOperations operations, Options options) {
MultiMap<BeanOperations, Object> currentTargets = MultiMap.linkedListMultimap();
currentTargets.putAll(operations, targets);
while (!currentTargets.isEmpty()) {
executeAssembleOperations(options, currentTargets);
MultiMap<BeanOperations, Object> nextTargets = executeDisassembleOperations(
operations, options, currentTargets
);
afterOperationsCompletion(currentTargets);
currentTargets = nextTargets;
}
}

@Override
protected MultiMap<BeanOperations, Object> doExecuteDisassembleOperations(
Predicate<? super KeyTriggerOperation> filter, MultiMap<BeanOperations, Object> targetWithOps) {
MultiMap<BeanOperations, Object> results = MultiMap.arrayListMultimap();
targetWithOps.asMap().forEach((ops, targets) ->
ops.getDisassembleOperations().stream()
.filter(filter)
.forEach(op -> disassembleAndCollect(targets, op, results))
);
return results;
}

private void disassembleAndCollect(
Collection<Object> targets, DisassembleOperation op, MultiMap<BeanOperations, Object> collectedBeans) {
Collection<Object> actualTarget = filterTargetsForSupportedOperation(targets, op);
if (actualTarget.isEmpty()) {
return;
}
DisassembleOperationHandler handler = op.getDisassembleOperationHandler();
Collection<?> nestedBeans = handler.process(op, targets);
if (CollectionUtils.isNotEmpty(nestedBeans)) {
BeanOperations beanOperations = op.getInternalBeanOperations(actualTarget);
collectedBeans.putAll(beanOperations, nestedBeans);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@
import java.util.stream.Collectors;

/**
* An abstract implementation of {@link AbstractFlatDisassembleBeanOperationExecutor}
* An abstract implementation of {@link AbstractBeanOperationFlattingExecutor}
* that supports the operation aware.
*
* @author huangchengxing
* @see OperationAwareBean
* @see SmartOperationAwareBean
* @since 2.5.0
*/
public abstract class AbstractOperationAwareBeanOperationExecutor
extends AbstractBeanOperationExecutor {
public abstract class AbstractOperationAwareBeanOperationExecutor extends AbstractBeanOperationExecutor {

protected AbstractOperationAwareBeanOperationExecutor(ContainerManager containerManager) {
super(containerManager);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

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 lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -41,44 +39,6 @@ public AsyncBeanOperationExecutor(
this.executor = executor;
}

/**
* <p>Complete the assembly operation.<br />
* All operations of input parameters ensure their orderliness in the same class.
* For example, if there are ordered operations <i>a<i> and <i>b<i> in {@code A.class},
* the order of <i>a<i> and <i>b<i> is still guaranteed when
* the corresponding {@link AssembleExecution} is obtained.
*
* @param executions assembly operations to be completed
* @param options options for execution
* @throws OperationExecuteException thrown when operation execution exception
* @implNote
* <ul>
* <li>If necessary, you need to ensure the execution order of {@link AssembleExecution};</li>
* <li>
* If the network request and other time-consuming operations are required to obtain the data source,
* the number of requests for the data source should be reduced as much as possible;
* </li>
* </ul>
*/
@SuppressWarnings("unchecked")
@Override
protected void doExecuteAssembleOperations(List<AssembleExecution> executions, Options options) throws OperationExecuteException {
CompletableFuture<Void>[] tasks = executions.stream()
.map(execution -> (Runnable)() -> doExecuteOperations(execution))
.map(task -> CompletableFuture.runAsync(task, executor))
.toArray(CompletableFuture[]::new);
try {
CompletableFuture.allOf(tasks).join();
} catch (Exception e) {
throw new OperationExecuteException(e);
}
}

private void doExecuteOperations(AssembleExecution execution) {
Container<?> container = execution.getContainer();
tryExecuteAssembleExecution(execution.getHandler(), container, Collections.singletonList(execution));
}

/**
* <p>Execute the assembly operation.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package cn.crane4j.core.executor;

import cn.crane4j.core.container.Container;
import cn.crane4j.core.container.ContainerManager;
import cn.crane4j.core.exception.OperationExecuteException;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

/**
* <p>The asynchronous implementation of {@link AbstractBeanOperationRecursiveExecutor}.<br />
* It will group the operations to be executed according to the data source container,
* then submit them to the executor in turn, and finally complete them asynchronously.
*
* @author huangchengxing
* @since 2.9.0
*/
public class AsyncBeanOperationRecursiveExecutor extends AbstractBeanOperationRecursiveExecutor {
/**
* thread pool used to perform operations.
*/
private final Executor executor;

/**
* Create an instance of {@link AsyncBeanOperationExecutor}.
*
* @param containerManager container manager
* @param executor thread pool used to perform operations
*/
public AsyncBeanOperationRecursiveExecutor(
ContainerManager containerManager, Executor executor) {
super(containerManager);
this.executor = executor;
}

/**
* <p>Complete the assembly operation.<br />
* All operations of input parameters ensure their orderliness in the same class.
* For example, if there are ordered operations <i>a<i> and <i>b<i> in {@code A.class},
* the order of <i>a<i> and <i>b<i> is still guaranteed when
* the corresponding {@link AssembleExecution} is obtained.
*
* @param executions assembly operations to be completed
* @param options options for execution
* @throws OperationExecuteException thrown when operation execution exception
* @implNote
* <ul>
* <li>If necessary, you need to ensure the execution order of {@link AssembleExecution};</li>
* <li>
* If the network request and other time-consuming operations are required to obtain the data source,
* the number of requests for the data source should be reduced as much as possible;
* </li>
* </ul>
*/
@SuppressWarnings("unchecked")
@Override
protected void doExecuteAssembleOperations(List<AssembleExecution> executions, Options options) throws OperationExecuteException {
CompletableFuture<Void>[] tasks = executions.stream()
.map(execution -> (Runnable)() -> doExecuteOperations(execution))
.map(task -> CompletableFuture.runAsync(task, executor))
.toArray(CompletableFuture[]::new);
try {
CompletableFuture.allOf(tasks).join();
} catch (Exception e) {
throw new OperationExecuteException(e);
}
}

private void doExecuteOperations(AssembleExecution execution) {
Container<?> container = execution.getContainer();
tryExecuteAssembleExecution(execution.getHandler(), container, Collections.singletonList(execution));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* and then distribute them to {@link AssembleOperationHandler} for execution.
*
* @author huangchengxing
* @see AbstractFlatDisassembleBeanOperationExecutor
* @see AbstractBeanOperationFlattingExecutor
* @see AsyncBeanOperationExecutor
* @see DisorderedBeanOperationExecutor
* @see OrderedBeanOperationExecutor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* @author huangchengxing
*/
@Slf4j
public class DisorderedBeanOperationExecutor extends AbstractFlatDisassembleBeanOperationExecutor {
public class DisorderedBeanOperationExecutor extends AbstractBeanOperationFlattingExecutor {

/**
* Create an instance of {@link DisorderedBeanOperationExecutor}.
Expand Down Expand Up @@ -51,7 +51,8 @@ public DisorderedBeanOperationExecutor(ContainerManager containerManager) {
* </ul>
*/
@Override
protected void doExecuteAssembleOperations(List<AssembleExecution> executions, Options options) throws OperationExecuteException {
protected void doExecuteAssembleOperations(
List<AssembleExecution> executions, Options options) throws OperationExecuteException {
Map<Container<?>, Map<AssembleOperationHandler, List<AssembleExecution>>> operations = new LinkedHashMap<>();
executions.forEach(e -> {
Container<?> container = e.getContainer();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cn.crane4j.core.executor;

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 org.checkerframework.checker.nullness.qual.NonNull;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* <p>Synchronization implementation of {@link AbstractBeanOperationRecursiveExecutor}.<br />
* During execution, the number of calls to {@link Container} will be reduced as much as possible,
* but the order of operation execution cannot be guaranteed.
*
* @author huangchengxing
* @since 2.9.0
*/
public class DisorderedBeanOperationRecursiveExecutor extends AbstractBeanOperationRecursiveExecutor {

public DisorderedBeanOperationRecursiveExecutor(ContainerManager containerManager) {
super(containerManager);
}

/**
* <p>Complete the assembly operation.<br />
* All operations of input parameters ensure their orderliness in the same class.
* For example, if there are ordered operations <i>a<i> and <i>b<i> in {@code A.class},
* the order of <i>a<i> and <i>b<i> is still guaranteed when
* the corresponding {@link AssembleExecution} is obtained.
*
* @param executions assembly operations to be completed
* @param options options for execution
* @throws OperationExecuteException thrown when operation execution exception
* @implNote <ul>
* <li>If necessary, you need to ensure the execution order of {@link AssembleExecution};</li>
* <li>
* If the network request and other time-consuming operations are required to obtain the data source,
* the number of requests for the data source should be reduced as much as possible;
* </li>
* </ul>
*/
@Override
protected void doExecuteAssembleOperations(
List<AssembleExecution> executions, Options options) throws OperationExecuteException {
Map<Container<?>, Map<AssembleOperationHandler, List<AssembleExecution>>> operations = new LinkedHashMap<>();
executions.forEach(e -> {
List<AssembleExecution> es = getAssembleExecutions(e, operations);
es.add(e);
});
try {operations.forEach((container, he) ->
he.forEach((handler, execs) -> tryExecuteAssembleExecution(handler, container, execs))
);
} catch (Exception e) {
throw new OperationExecuteException(e);
}
}

@NonNull
private static List<AssembleExecution> getAssembleExecutions(AssembleExecution e, Map<Container<?>, Map<AssembleOperationHandler, List<AssembleExecution>>> operations) {
Container<?> container = e.getContainer();
Map<AssembleOperationHandler, List<AssembleExecution>> he = operations.computeIfAbsent(container, c -> new HashMap<>(8));
return he.computeIfAbsent(e.getHandler(), h -> new ArrayList<>());
}
}
Loading