diff --git a/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundInterceptorBuilder.php b/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundInterceptorBuilder.php index 66a1fca9d..c4bc6b8c5 100644 --- a/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundInterceptorBuilder.php +++ b/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundInterceptorBuilder.php @@ -4,6 +4,7 @@ namespace Ecotone\Messaging\Handler\Processor\MethodInvoker; +use Ecotone\Messaging\Support\InvalidArgumentException; use function array_merge; use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\ParameterConverterAnnotationFactory; @@ -31,7 +32,6 @@ use Ecotone\Messaging\Message; use Ecotone\Messaging\MessagingException; use Ecotone\Messaging\Precedence; -use InvalidArgumentException; /** * licence Apache-2.0 @@ -202,12 +202,16 @@ public function compile(MessagingContainerBuilder $builder, array $endpointAnnot $converterDefinitions[] = ReferenceBuilder::create($parameter->getName(), $parameter->getTypeHint())->compile($interceptingInterface); continue; } - throw new InvalidArgumentException("Can't build around interceptor for {$this->interfaceToCall} because can't find converter for parameter {$parameter}"); + throw InvalidArgumentException::create("Can't build around interceptor for {$this->interfaceToCall} because can't find converter for parameter {$parameter}"); + } + + if ($this->interfaceToCall->canReturnValue() && ! $hasMethodInvocation) { + throw InvalidArgumentException::create("Trying to register {$this->interfaceToCall} as Around Advice which can return value, but doesn't control invocation using " . MethodInvocation::class . '. Have you wanted to register Before/After Advice or forgot to type hint MethodInvocation?'); } return new Definition(AroundMethodInterceptor::class, [ $this->directObject ?: new Reference($this->referenceName), - InterfaceToCallReference::fromInstance($this->interfaceToCall), + $this->interfaceToCall->getMethodName(), $converterDefinitions, $hasMethodInvocation, ]); diff --git a/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundMessageProcessor.php b/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundMessageProcessor.php deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundMethodInterceptor.php b/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundMethodInterceptor.php index d206d2e82..8fe2a4d9e 100644 --- a/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundMethodInterceptor.php +++ b/packages/Ecotone/src/Messaging/Handler/Processor/MethodInvoker/AroundMethodInterceptor.php @@ -2,12 +2,10 @@ namespace Ecotone\Messaging\Handler\Processor\MethodInvoker; -use Ecotone\Messaging\Handler\InterfaceToCall; use Ecotone\Messaging\Handler\InterfaceToCallRegistry; use Ecotone\Messaging\Handler\ParameterConverter; use Ecotone\Messaging\Handler\ReferenceSearchService; use Ecotone\Messaging\Message; -use Ecotone\Messaging\Support\InvalidArgumentException; /** * @author Dariusz Gafka @@ -20,11 +18,8 @@ class AroundMethodInterceptor /** * @param ParameterConverter[] $parameterConverters */ - public function __construct(private object $referenceToCall, private InterfaceToCall $interceptorInterfaceToCall, private array $parameterConverters, private bool $hasMethodInvocation) + public function __construct(private object $referenceToCall, private string $methodName, private array $parameterConverters, private bool $hasMethodInvocation) { - if ($interceptorInterfaceToCall->canReturnValue() && ! $this->hasMethodInvocation) { - throw InvalidArgumentException::create("Trying to register {$interceptorInterfaceToCall} as Around Advice which can return value, but doesn't control invocation using " . MethodInvocation::class . '. Have you wanted to register Before/After Advice or forgot to type hint MethodInvocation?'); - } } /** @@ -62,7 +57,7 @@ public function getReferenceToCall(): object public function getMethodName(): string { - return $this->interceptorInterfaceToCall->getMethodName(); + return $this->methodName; } public function hasMethodInvocation(): bool diff --git a/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/AggregateMethodInvoker.php b/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/AggregateMethodInvoker.php new file mode 100644 index 000000000..60bd3bbfc --- /dev/null +++ b/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/AggregateMethodInvoker.php @@ -0,0 +1,90 @@ + $methodParameterConverters + * @param array $methodParameterNames + * @param array $aroundMethodInterceptors + */ + public function __construct( + private string $aggregateClass, + private string $objectMethodName, + private ?Type $returnType, + private array $methodParameterNames, + private bool $isCommandHandler, + private array $methodParameterConverters, + private array $aroundMethodInterceptors, + ) { + Assert::allInstanceOfType($methodParameterConverters, ParameterConverter::class); + Assert::allInstanceOfType($aroundMethodInterceptors, AroundMethodInterceptor::class); + } + + public function execute(Message $message): ?MessageBuilder + { + $calledAggregate = $message->getHeaders()->containsKey(AggregateMessage::CALLED_AGGREGATE_OBJECT) ? $message->getHeaders()->get(AggregateMessage::CALLED_AGGREGATE_OBJECT) : null; + + $methodInvoker = new MethodInvoker( + objectToInvokeOn: $calledAggregate ?: $this->aggregateClass, + objectMethodName: $this->objectMethodName, + methodParameterConverters: $this->methodParameterConverters, + methodParameterNames: $this->methodParameterNames, + canInterceptorReplaceArguments: false + ); + + if ($this->aroundMethodInterceptors) { + $methodInvokerChainProcessor = new AroundMethodInvocation($message, $this->aroundMethodInterceptors, $methodInvoker); + $result = $methodInvokerChainProcessor->proceed(); + } else { + $result = $methodInvoker->executeEndpoint($message); + } + + return $this->buildMessageFromResult($message, $result); + } + + private function buildMessageFromResult(Message $message, mixed $result): ?MessageBuilder + { + $resultMessage = MessageBuilder::fromMessage($message); + + $resultType = TypeDescriptor::createFromVariable($result); + if ($resultType->isIterable() && $this->returnType?->isCollection()) { + $resultType = $this->returnType; + } + + if (! is_null($result)) { + if ($this->isCommandHandler) { + $calledAggregate = $message->getHeaders()->containsKey(AggregateMessage::CALLED_AGGREGATE_OBJECT) ? $message->getHeaders()->get(AggregateMessage::CALLED_AGGREGATE_OBJECT) : null; + $resultMessage = $resultMessage->setHeader(AggregateMessage::CALLED_AGGREGATE_OBJECT, $calledAggregate); + } + + $resultMessage = $resultMessage + ->setContentType(MediaType::createApplicationXPHPWithTypeParameter($resultType->toString())) + ->setPayload($result) + ; + } + + if ($this->isCommandHandler && is_null($result)) { + $resultMessage = $resultMessage->setHeader(AggregateMessage::NULL_EXECUTION_RESULT, true); + } + + if ($this->isCommandHandler || ! is_null($result)) { + return $resultMessage; + } + return null; + } +} \ No newline at end of file diff --git a/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallAggregateServiceBuilder.php b/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallAggregateServiceBuilder.php index 7edbdad17..1b015958c 100644 --- a/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallAggregateServiceBuilder.php +++ b/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallAggregateServiceBuilder.php @@ -3,7 +3,6 @@ namespace Ecotone\Modelling\AggregateFlow\CallAggregate; use Ecotone\Messaging\Config\Container\Definition; -use Ecotone\Messaging\Config\Container\InterfaceToCallReference; use Ecotone\Messaging\Config\Container\MessagingContainerBuilder; use Ecotone\Messaging\Config\Container\Reference; use Ecotone\Messaging\Handler\ClassDefinition; @@ -18,11 +17,9 @@ use Ecotone\Messaging\Handler\ServiceActivator\ServiceActivatorBuilder; use Ecotone\Messaging\Handler\TypeDescriptor; use Ecotone\Messaging\Support\Assert; -use Ecotone\Modelling\Attribute\AggregateEvents; use Ecotone\Modelling\Attribute\AggregateVersion; use Ecotone\Modelling\Attribute\EventSourcingAggregate; use Ecotone\Modelling\Attribute\EventSourcingSaga; -use Ecotone\Modelling\EventSourcingExecutor\EventSourcingHandlerExecutorBuilder; use Ecotone\Modelling\WithAggregateVersioning; /** @@ -39,11 +36,8 @@ class CallAggregateServiceBuilder extends InputOutputMessageHandlerBuilder imple * @var bool */ private bool $isCommandHandler; - private Definition $eventSourcingHandlerExecutor; - private ?string $aggregateMethodWithEvents; private ?string $aggregateVersionProperty; private bool $isEventSourced = false; - private bool $isReturningAggregate = false; private function __construct(ClassDefinition $aggregateClassDefinition, string $methodName, bool $isCommandHandler, InterfaceToCallRegistry $interfaceToCallRegistry) { @@ -56,20 +50,6 @@ private function initialize(ClassDefinition $aggregateClassDefinition, string $m { $interfaceToCall = $interfaceToCallRegistry->getFor($aggregateClassDefinition->getClassType()->toString(), $methodName); - $this->isReturningAggregate = $interfaceToCall->isReturningAggregate($interfaceToCallRegistry); - - $aggregateMethodWithEvents = null; - $aggregateEventsAnnotation = TypeDescriptor::create(AggregateEvents::class); - foreach ($aggregateClassDefinition->getPublicMethodNames() as $method) { - $methodToCheck = $interfaceToCallRegistry->getFor($aggregateClassDefinition->getClassType()->toString(), $method); - - if ($methodToCheck->hasMethodAnnotation($aggregateEventsAnnotation)) { - $aggregateMethodWithEvents = $method; - break; - } - } - $this->aggregateMethodWithEvents = $aggregateMethodWithEvents; - $eventSourcedAggregateAnnotation = TypeDescriptor::create(EventSourcingAggregate::class); $eventSourcedSagaAnnotation = TypeDescriptor::create(EventSourcingSaga::class); if ($interfaceToCall->hasClassAnnotation($eventSourcedAggregateAnnotation) || $interfaceToCall->hasClassAnnotation($eventSourcedSagaAnnotation)) { @@ -91,7 +71,6 @@ private function initialize(ClassDefinition $aggregateClassDefinition, string $m } $this->interfaceToCall = $interfaceToCall; - $this->eventSourcingHandlerExecutor = EventSourcingHandlerExecutorBuilder::createFor($aggregateClassDefinition, $this->isEventSourced, $interfaceToCallRegistry); $isFactoryMethod = $this->interfaceToCall->isFactoryMethod(); if (! $this->isEventSourced && $isFactoryMethod) { Assert::isTrue($this->interfaceToCall->getReturnType()->isClassNotInterface(), "Factory method {$this->interfaceToCall} for standard aggregate should return object. Did you wanted to register Event Sourced Aggregate?"); @@ -165,11 +144,9 @@ public function __toString() private function callEventSourcedAggregateServiceDefinition(array $compiledMethodParameterConverters, array $interceptors): Definition { return new Definition(CallEventSourcingAggregateService::class, [ - InterfaceToCallReference::fromInstance($this->interfaceToCall), - $compiledMethodParameterConverters, - $interceptors, + $this->compileAggregateMethodInvoker($compiledMethodParameterConverters, $interceptors), new Reference(PropertyReaderAccessor::class), - $this->isCommandHandler, + $this->interfaceToCall->isFactoryMethod() ?? false, $this->aggregateVersionProperty, ]); } @@ -177,10 +154,20 @@ private function callEventSourcedAggregateServiceDefinition(array $compiledMetho private function callStateBasedAggregateServiceDefinition(array $compiledMethodParameterConverters, array $interceptors): Definition { return new Definition(CallStateBasedAggregateService::class, [ - InterfaceToCallReference::fromInstance($this->interfaceToCall), + $this->compileAggregateMethodInvoker($compiledMethodParameterConverters, $interceptors), + ]); + } + + private function compileAggregateMethodInvoker(array $compiledMethodParameterConverters, array $interceptors): Definition + { + return new Definition(AggregateMethodInvoker::class, [ + $this->interfaceToCall->getInterfaceName(), + $this->interfaceToCall->getMethodName(), + $this->interfaceToCall->getReturnType(), + $this->interfaceToCall->getInterfaceParametersNames(), + $this->isCommandHandler, $compiledMethodParameterConverters, $interceptors, - $this->isCommandHandler, ]); } } diff --git a/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallEventSourcingAggregateService.php b/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallEventSourcingAggregateService.php index cf4c7d49a..9e63cd29b 100644 --- a/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallEventSourcingAggregateService.php +++ b/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallEventSourcingAggregateService.php @@ -4,18 +4,9 @@ namespace Ecotone\Modelling\AggregateFlow\CallAggregate; -use Ecotone\Messaging\Conversion\MediaType; use Ecotone\Messaging\Handler\Enricher\PropertyPath; use Ecotone\Messaging\Handler\Enricher\PropertyReaderAccessor; -use Ecotone\Messaging\Handler\InterfaceToCall; -use Ecotone\Messaging\Handler\ParameterConverter; -use Ecotone\Messaging\Handler\Processor\MethodInvoker\AroundMethodInterceptor; -use Ecotone\Messaging\Handler\Processor\MethodInvoker\AroundMethodInvocation; -use Ecotone\Messaging\Handler\Processor\MethodInvoker\MethodInvoker; -use Ecotone\Messaging\Handler\TypeDescriptor; use Ecotone\Messaging\Message; -use Ecotone\Messaging\Support\Assert; -use Ecotone\Messaging\Support\MessageBuilder; use Ecotone\Modelling\AggregateMessage; use Ecotone\Modelling\CallAggregateService; @@ -24,29 +15,17 @@ */ final class CallEventSourcingAggregateService implements CallAggregateService { - private bool $isFactoryMethod; - - /** - * @param array $parameterConverters - * @param array $aroundMethodInterceptors - */ public function __construct( - private InterfaceToCall $interfaceToCall, - private array $parameterConverters, - private array $aroundMethodInterceptors, + private AggregateMethodInvoker $aggregateMethodInvoker, private PropertyReaderAccessor $propertyReaderAccessor, - private bool $isCommandHandler, + private bool $isFactoryMethod, private ?string $aggregateVersionProperty, ) { - Assert::allInstanceOfType($parameterConverters, ParameterConverter::class); - Assert::allInstanceOfType($aroundMethodInterceptors, AroundMethodInterceptor::class); - - $this->isFactoryMethod = $this->interfaceToCall->isFactoryMethod() ?? false; } public function call(Message $message): ?Message { - $resultMessage = MessageBuilder::fromMessage($message); + $resultMessage = $this->aggregateMethodInvoker->execute($message); $calledAggregate = $message->getHeaders()->containsKey(AggregateMessage::CALLED_AGGREGATE_OBJECT) ? $message->getHeaders()->get(AggregateMessage::CALLED_AGGREGATE_OBJECT) : null; $versionBeforeHandling = $message->getHeaders()->containsKey(AggregateMessage::TARGET_VERSION) ? $message->getHeaders()->get(AggregateMessage::TARGET_VERSION) : null; @@ -62,41 +41,6 @@ public function call(Message $message): ?Message $resultMessage = $resultMessage->setHeader(AggregateMessage::TARGET_VERSION, $versionBeforeHandling); } - $methodInvoker = new MethodInvoker( - objectToInvokeOn: $calledAggregate ?: $this->interfaceToCall->getInterfaceType()?->toString(), - objectMethodName: $this->interfaceToCall->getMethodName(), - methodParameterConverters: $this->parameterConverters, - methodParameterNames: $this->interfaceToCall->getInterfaceParametersNames(), - canInterceptorReplaceArguments: false - ); - - if ($this->aroundMethodInterceptors) { - $methodInvokerChainProcessor = new AroundMethodInvocation(requestMessage: $message, aroundMethodInterceptors: $this->aroundMethodInterceptors, interceptedMessageProcessor: $methodInvoker); - $result = $methodInvokerChainProcessor->proceed(); - } else { - $result = $methodInvoker->executeEndpoint($message); - } - - $resultType = TypeDescriptor::createFromVariable($result); - if ($resultType->isIterable() && $this->interfaceToCall->getReturnType()?->isCollection()) { - $resultType = $this->interfaceToCall->getReturnType(); - } - - if (! is_null($result)) { - if ($this->isCommandHandler) { - $resultMessage = $resultMessage->setHeader(AggregateMessage::CALLED_AGGREGATE_OBJECT, $calledAggregate); - } - - $resultMessage = $resultMessage - ->setContentType(MediaType::createApplicationXPHPWithTypeParameter($resultType->toString())) - ->setPayload($result) - ; - } - - if ($this->isCommandHandler || ! is_null($result)) { - return $resultMessage->build(); - } - - return null; + return $resultMessage->build(); } } diff --git a/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallStateBasedAggregateService.php b/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallStateBasedAggregateService.php index dae0e6614..78407e414 100644 --- a/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallStateBasedAggregateService.php +++ b/packages/Ecotone/src/Modelling/AggregateFlow/CallAggregate/CallStateBasedAggregateService.php @@ -4,17 +4,7 @@ namespace Ecotone\Modelling\AggregateFlow\CallAggregate; -use Ecotone\Messaging\Conversion\MediaType; -use Ecotone\Messaging\Handler\InterfaceToCall; -use Ecotone\Messaging\Handler\ParameterConverter; -use Ecotone\Messaging\Handler\Processor\MethodInvoker\AroundMethodInterceptor; -use Ecotone\Messaging\Handler\Processor\MethodInvoker\AroundMethodInvocation; -use Ecotone\Messaging\Handler\Processor\MethodInvoker\MethodInvoker; -use Ecotone\Messaging\Handler\TypeDescriptor; use Ecotone\Messaging\Message; -use Ecotone\Messaging\Support\Assert; -use Ecotone\Messaging\Support\MessageBuilder; -use Ecotone\Modelling\AggregateMessage; use Ecotone\Modelling\CallAggregateService; /** @@ -22,65 +12,13 @@ */ final class CallStateBasedAggregateService implements CallAggregateService { - /** - * @param array $parameterConverters - * @param array $aroundMethodInterceptors - */ public function __construct( - private InterfaceToCall $interfaceToCall, - private array $parameterConverters, - private array $aroundMethodInterceptors, - private bool $isCommandHandler, + private AggregateMethodInvoker $aggregateMethodInvoker, ) { - Assert::allInstanceOfType($parameterConverters, ParameterConverter::class); - Assert::allInstanceOfType($aroundMethodInterceptors, AroundMethodInterceptor::class); } public function call(Message $message): ?Message { - $resultMessage = MessageBuilder::fromMessage($message); - - $calledAggregate = $message->getHeaders()->containsKey(AggregateMessage::CALLED_AGGREGATE_OBJECT) ? $message->getHeaders()->get(AggregateMessage::CALLED_AGGREGATE_OBJECT) : null; - - $methodInvoker = new MethodInvoker( - objectToInvokeOn: $calledAggregate ?: $this->interfaceToCall->getInterfaceType()->toString(), - objectMethodName: $this->interfaceToCall->getMethodName(), - methodParameterConverters: $this->parameterConverters, - methodParameterNames: $this->interfaceToCall->getInterfaceParametersNames(), - canInterceptorReplaceArguments: false - ); - - if ($this->aroundMethodInterceptors) { - $methodInvokerChainProcessor = new AroundMethodInvocation($message, $this->aroundMethodInterceptors, $methodInvoker); - $result = $methodInvokerChainProcessor->proceed(); - } else { - $result = $methodInvoker->executeEndpoint($message); - } - - $resultType = TypeDescriptor::createFromVariable($result); - if ($resultType->isIterable() && $this->interfaceToCall->getReturnType()?->isCollection()) { - $resultType = $this->interfaceToCall->getReturnType(); - } - - if (! is_null($result)) { - if ($this->isCommandHandler) { - $resultMessage = $resultMessage->setHeader(AggregateMessage::CALLED_AGGREGATE_OBJECT, $calledAggregate); - } - - $resultMessage = $resultMessage - ->setContentType(MediaType::createApplicationXPHPWithTypeParameter($resultType->toString())) - ->setPayload($result) - ; - } - - if ($this->isCommandHandler && is_null($result)) { - $resultMessage = $resultMessage->setHeader(AggregateMessage::NULL_EXECUTION_RESULT, true); - } - - if ($this->isCommandHandler || ! is_null($result)) { - return $resultMessage->build(); - } - - return null; + return $this->aggregateMethodInvoker->execute($message)?->build(); } }