diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index eedf40b10b..161c9f8e70 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2023.3.0", + "version": "2023.3.2", "commands": [ "jb" ] diff --git a/PackageReadme.md b/PackageReadme.md index 439129ab88..2826035513 100644 --- a/PackageReadme.md +++ b/PackageReadme.md @@ -7,6 +7,3 @@ Features include: - Service discovery with [Eureka](https://spring.io/projects/spring-cloud-netflix) and [HashiCorp Consul](https://www.consul.io/) - External (encrypted) configuration using [Spring Cloud Config Server](https://docs.spring.io/spring-cloud-config/docs/current/reference/html/) - Single sign-on with [Cloud Foundry](https://www.cloudfoundry.org/) -- An easy-to-use API for [RabbitMQ message exchange](https://www.rabbitmq.com/features.html), providing [RabbitTemplate](https://spring.io/guides/gs/messaging-rabbitmq/) and annotation-driven listeners -- Streaming with [Spring Cloud Data Flow](https://spring.io/projects/spring-cloud-dataflow), enabling orchestration with Java stream apps -- Steeltoe converts [Spring Expression Language (SpEL)](https://docs.spring.io/spring-framework/docs/3.0.x/reference/expressions.html) to .NET compiled code diff --git a/README.md b/README.md index 9a0034121f..089239a539 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Are you looking to create .NET microservices? Modernizing existing applications? Moving apps to containers? Steeltoe can help you! -[Steeltoe](https://steeltoe.io) is an open source project that provides a collection of libraries that helps users build production-grade cloud-native applications using externalized configuration, service discovery, distributed tracing, application management, and more. Steeltoe also provides a seamless way to build, configure, and run event-driven microservice applications and stream-based data processing applications. +[Steeltoe](https://steeltoe.io) is an open source project that provides a collection of libraries that helps users build production-grade cloud-native applications using externalized configuration, service discovery, distributed tracing, application management, and more. We have also built several tools to get you started: diff --git a/build/integration.yml b/build/integration.yml deleted file mode 100644 index 6b65eb48fb..0000000000 --- a/build/integration.yml +++ /dev/null @@ -1,19 +0,0 @@ -pr: - paths: - exclude: - - build - - src - include: - - .editorconfig - - stylecop.json - - '*.props' - - '*.ruleset' - - .config/dotnet-tools.json - - build/templates - - build/integration.yml - - src/Integration - -jobs: - - template: templates/component-build.yaml - parameters: - component: Integration diff --git a/build/messaging.yml b/build/messaging.yml deleted file mode 100644 index 912258c97d..0000000000 --- a/build/messaging.yml +++ /dev/null @@ -1,20 +0,0 @@ -pr: - paths: - exclude: - - build - - src - include: - - .editorconfig - - stylecop.json - - '*.props' - - '*.ruleset' - - .config/dotnet-tools.json - - build/templates - - build/messaging.yml - - src/Messaging - -jobs: - - template: templates/component-build.yaml - parameters: - component: Messaging - runRabbitMQ: true diff --git a/build/stream.yml b/build/stream.yml deleted file mode 100644 index 8056c2742d..0000000000 --- a/build/stream.yml +++ /dev/null @@ -1,21 +0,0 @@ -pr: - paths: - exclude: - - build - - src - include: - - .editorconfig - - stylecop.json - - '*.props' - - '*.ruleset' - - .config/dotnet-tools.json - - build/templates - - build/stream.yml - - src/Stream - -jobs: - - template: templates/component-build.yaml - parameters: - component: Stream - runRabbitMQ: true - skipFilter: --filter "Category!=SkipOnLinux" diff --git a/build/verify-code-style.yml b/build/verify-code-style.yml index 58fa9599b7..42cd527048 100644 --- a/build/verify-code-style.yml +++ b/build/verify-code-style.yml @@ -60,5 +60,5 @@ jobs: if ($LastExitCode -ne 0) { throw "Command 'git rev-parse (2)' failed with exit code $LastExitCode." } Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash in pull request." - dotnet regitlint -s src/Steeltoe.All.sln --print-command --skip-tool-check --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="Steeltoe Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN --fail-on-diff --print-diff -f commits -a $headCommitHash -b $baseCommitHash + dotnet regitlint -s src/Steeltoe.All.sln --print-command --skip-tool-check --max-runs=5 --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="Steeltoe Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN --fail-on-diff --print-diff -f commits -a $headCommitHash -b $baseCommitHash } diff --git a/cleanupcode.ps1 b/cleanupcode.ps1 index 2c140ddac0..414f0de28d 100644 --- a/cleanupcode.ps1 +++ b/cleanupcode.ps1 @@ -28,12 +28,12 @@ if ($revision) { if ($baseCommitHash -eq $headCommitHash) { Write-Output "Running code cleanup on staged/unstaged files." - dotnet regitlint -s src/Steeltoe.All.sln --print-command --skip-tool-check --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="Steeltoe Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified + dotnet regitlint -s src/Steeltoe.All.sln --print-command --skip-tool-check --max-runs=5 --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="Steeltoe Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified VerifySuccessExitCode } else { Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash, including staged/unstaged files." - dotnet regitlint -s src/Steeltoe.All.sln --print-command --skip-tool-check --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="Steeltoe Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified,commits -a $headCommitHash -b $baseCommitHash + dotnet regitlint -s src/Steeltoe.All.sln --print-command --skip-tool-check --max-runs=5 --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="Steeltoe Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified,commits -a $headCommitHash -b $baseCommitHash VerifySuccessExitCode } } diff --git a/src/Common/src/Abstractions/Attributes/ServiceAttribute.cs b/src/Common/src/Abstractions/Attributes/ServiceAttribute.cs deleted file mode 100644 index 8a3e666088..0000000000 --- a/src/Common/src/Abstractions/Attributes/ServiceAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Attributes; - -[AttributeUsage(AttributeTargets.Method)] -public sealed class ServiceAttribute : Attribute -{ - public string Name { get; } = string.Empty; - - public ServiceAttribute() - { - } - - public ServiceAttribute(string name) - { - Name = name; - } -} diff --git a/src/Common/src/Abstractions/Contexts/IApplicationContext.cs b/src/Common/src/Abstractions/Contexts/IApplicationContext.cs deleted file mode 100644 index f3aca1351c..0000000000 --- a/src/Common/src/Abstractions/Contexts/IApplicationContext.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Steeltoe.Common.Expression.Internal.Contexts; - -namespace Steeltoe.Common.Contexts; - -public interface IApplicationContext : IDisposable -{ - IConfiguration Configuration { get; } - - IServiceProvider ServiceProvider { get; } - - IServiceExpressionResolver ServiceExpressionResolver { get; set; } - - object GetService(string name); - - object GetService(string name, Type serviceType); - - T GetService(string name); - - T GetService(); - - object GetService(Type serviceType); - - IEnumerable GetServices(); - - bool ContainsService(string name); - - bool ContainsService(string name, Type serviceType); - - bool ContainsService(string name); - - void Register(string name, object instance); - - object Deregister(string name); - - string ResolveEmbeddedValue(string value); -} diff --git a/src/Common/src/Abstractions/Converter/IConditionalConverter.cs b/src/Common/src/Abstractions/Converter/IConditionalConverter.cs deleted file mode 100644 index 461af1c42c..0000000000 --- a/src/Common/src/Abstractions/Converter/IConditionalConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -/// -/// Allows a converter to conditionally execute based on the types of the source and target. -/// -public interface IConditionalConverter -{ - /// - /// Can the conversion from source type to target type be performed by this converter. - /// - /// - /// the type of the source object to convert from. - /// - /// - /// the type of the target object to convert to. - /// - /// - /// true if the conversion can be performed. - /// - bool Matches(Type sourceType, Type targetType); -} diff --git a/src/Common/src/Abstractions/Converter/IConditionalGenericConverter.cs b/src/Common/src/Abstractions/Converter/IConditionalGenericConverter.cs deleted file mode 100644 index 17d49d2b87..0000000000 --- a/src/Common/src/Abstractions/Converter/IConditionalGenericConverter.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -/// -/// A generic converter that may conditionally execute based on the source type and target types. -/// -public interface IConditionalGenericConverter : IConditionalConverter, IGenericConverter -{ -} diff --git a/src/Common/src/Abstractions/Converter/IConversionService.cs b/src/Common/src/Abstractions/Converter/IConversionService.cs deleted file mode 100644 index 93782e12cc..0000000000 --- a/src/Common/src/Abstractions/Converter/IConversionService.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -/// -/// A service interface for type conversions. -/// -public interface IConversionService -{ - /// - /// Returns true if objects of the source type can be converted to the target type. - /// - /// - /// the type of the source object. - /// - /// - /// the type of the target object. - /// - /// - /// returns true if the conversion can be performed. - /// - bool CanConvert(Type sourceType, Type targetType); - - /// - /// Determine whether the conversion between source type and destination type can be bypassed. - /// - /// - /// the source type. - /// - /// - /// the target type. - /// - /// - /// returns true if it can be bypassed. - /// - bool CanBypassConvert(Type sourceType, Type targetType); - - /// - /// Convert the given source to the target. - /// - /// - /// the target type to convert to. - /// - /// - /// the source object to convert. - /// - /// - /// the converted object. - /// - T Convert(object source); - - /// - /// Convert the given source to the specified target type. - /// - /// - /// the object to convert; may be null. - /// - /// - /// the source objects type. - /// - /// - /// the target type to convert to. - /// - /// - /// the converted object. - /// - object Convert(object source, Type sourceType, Type targetType); -} diff --git a/src/Common/src/Abstractions/Converter/IConverter.cs b/src/Common/src/Abstractions/Converter/IConverter.cs deleted file mode 100644 index 2538fd6d19..0000000000 --- a/src/Common/src/Abstractions/Converter/IConverter.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -/// -/// A converter converts from a source object of type S to a target object of type T. -/// -/// -/// type of the source object. -/// -/// -/// type of the target object. -/// -public interface IConverter -{ - /// - /// Convert the source object of type S to a target object of type T. - /// - /// - /// the source object to convert; can not be null. - /// - /// - /// the converted object which must be type T. - /// - T Convert(TSource source); -} diff --git a/src/Common/src/Abstractions/Converter/IConverterRegistry.cs b/src/Common/src/Abstractions/Converter/IConverterRegistry.cs deleted file mode 100644 index e98613c40f..0000000000 --- a/src/Common/src/Abstractions/Converter/IConverterRegistry.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -/// -/// For registering converters with a type conversion system. -/// -public interface IConverterRegistry -{ - /// - /// Adds a generic converter to this registry. - /// - /// - /// the converter to add. - /// - void AddConverter(IGenericConverter converter); -} diff --git a/src/Common/src/Abstractions/Converter/IGenericConverter.cs b/src/Common/src/Abstractions/Converter/IGenericConverter.cs deleted file mode 100644 index 0bfd58ba42..0000000000 --- a/src/Common/src/Abstractions/Converter/IGenericConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -/// -/// Generic converter interface for converting between two or more types. -/// -public interface IGenericConverter -{ - /// - /// Gets the source and target types this converter can convert between. - /// - ISet<(Type SourceType, Type TargetType)> ConvertibleTypes { get; } - - /// - /// Convert the source object to the target type. - /// - /// - /// the object to convert; the source can be null. - /// - /// - /// the type of the source that should be used during the conversion. Can be null, and defaults to the type of the source object. - /// - /// - /// the type we are converting to. - /// - /// - /// the converted object. - /// - object Convert(object source, Type sourceType, Type targetType); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/Contexts/IServiceExpressionContext.cs b/src/Common/src/Abstractions/Expression/Internal/Contexts/IServiceExpressionContext.cs deleted file mode 100644 index 4597300dd3..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/Contexts/IServiceExpressionContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Common.Expression.Internal.Contexts; - -public interface IServiceExpressionContext -{ - IApplicationContext ApplicationContext { get; } - - bool ContainsService(string serviceName); - - bool ContainsService(string serviceName, Type serviceType); - - object GetService(string serviceName); - - object GetService(string serviceName, Type serviceType); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/Contexts/IServiceExpressionResolver.cs b/src/Common/src/Abstractions/Expression/Internal/Contexts/IServiceExpressionResolver.cs deleted file mode 100644 index 3ede5c1c3c..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/Contexts/IServiceExpressionResolver.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Contexts; - -public interface IServiceExpressionResolver -{ - object Evaluate(string value, IServiceExpressionContext evalContext); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IConstructorExecutor.cs b/src/Common/src/Abstractions/Expression/Internal/IConstructorExecutor.cs deleted file mode 100644 index 11c2f72b03..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IConstructorExecutor.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface IConstructorExecutor -{ - ITypedValue Execute(IEvaluationContext context, params object[] arguments); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IConstructorResolver.cs b/src/Common/src/Abstractions/Expression/Internal/IConstructorResolver.cs deleted file mode 100644 index 6aaf5fb617..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IConstructorResolver.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface IConstructorResolver -{ - IConstructorExecutor Resolve(IEvaluationContext context, string typeName, List argumentTypes); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IEvaluationContext.cs b/src/Common/src/Abstractions/Expression/Internal/IEvaluationContext.cs deleted file mode 100644 index 57f002cc60..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IEvaluationContext.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -/// -/// Expressions are executed in an evaluation context. It is in this context that references are resolved when encountered during expression evaluation. -/// -public interface IEvaluationContext -{ - ITypedValue RootObject { get; } - - List PropertyAccessors { get; } - - List ConstructorResolvers { get; } - - List MethodResolvers { get; } - - IServiceResolver ServiceResolver { get; } - - ITypeLocator TypeLocator { get; } - - ITypeConverter TypeConverter { get; } - - ITypeComparator TypeComparator { get; } - - IOperatorOverloader OperatorOverloader { get; } - - void SetVariable(string name, object value); - - object LookupVariable(string name); - - T LookupVariable(string name); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IExpression.cs b/src/Common/src/Abstractions/Expression/Internal/IExpression.cs deleted file mode 100644 index bf85451f83..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IExpression.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -/// -/// An expression capable of evaluating itself against context objects. Encapsulates the details of a previously parsed expression string. Provides a -/// common abstraction for expression evaluation. -/// -public interface IExpression -{ - string ExpressionString { get; } - - object GetValue(); - - object GetValue(Type desiredResultType); - - T GetValue(); - - object GetValue(object rootObject); - - object GetValue(object rootObject, Type desiredResultType); - - T GetValue(object rootObject); - - object GetValue(IEvaluationContext context); - - object GetValue(IEvaluationContext context, object rootObject); - - object GetValue(IEvaluationContext context, Type desiredResultType); - - T GetValue(IEvaluationContext context); - - object GetValue(IEvaluationContext context, object rootObject, Type desiredResultType); - - T GetValue(IEvaluationContext context, object rootObject); - - Type GetValueType(); - - Type GetValueType(object rootObject); - - Type GetValueType(IEvaluationContext context); - - Type GetValueType(IEvaluationContext context, object rootObject); - - bool IsWritable(object rootObject); - - bool IsWritable(IEvaluationContext context); - - bool IsWritable(IEvaluationContext context, object rootObject); - - void SetValue(object rootObject, object value); - - void SetValue(IEvaluationContext context, object value); - - void SetValue(IEvaluationContext context, object rootObject, object value); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IExpressionParser.cs b/src/Common/src/Abstractions/Expression/Internal/IExpressionParser.cs deleted file mode 100644 index 00d3799b47..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IExpressionParser.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -/// -/// Parses expression strings into compiled expressions that can be evaluated. Supports parsing templates as well as standard expression strings. -/// -public interface IExpressionParser -{ - IExpression ParseExpression(string expressionString); - - IExpression ParseExpression(string expressionString, IParserContext context); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IMethodExecutor.cs b/src/Common/src/Abstractions/Expression/Internal/IMethodExecutor.cs deleted file mode 100644 index 821a07c5c9..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IMethodExecutor.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface IMethodExecutor -{ - ITypedValue Execute(IEvaluationContext context, object target, params object[] arguments); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IMethodFilter.cs b/src/Common/src/Abstractions/Expression/Internal/IMethodFilter.cs deleted file mode 100644 index d65857537a..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IMethodFilter.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Expression.Internal; - -public interface IMethodFilter -{ - List Filter(List methods); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IMethodResolver.cs b/src/Common/src/Abstractions/Expression/Internal/IMethodResolver.cs deleted file mode 100644 index bd49b9a2b4..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IMethodResolver.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface IMethodResolver -{ - IMethodExecutor Resolve(IEvaluationContext context, object targetObject, string name, List argumentTypes); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IOperatorOverloader.cs b/src/Common/src/Abstractions/Expression/Internal/IOperatorOverloader.cs deleted file mode 100644 index ff6d714c2a..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IOperatorOverloader.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface IOperatorOverloader -{ - bool OverridesOperation(Operation operation, object leftOperand, object rightOperand); - - object Operate(Operation operation, object leftOperand, object rightOperand); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IParserContext.cs b/src/Common/src/Abstractions/Expression/Internal/IParserContext.cs deleted file mode 100644 index 3e38122324..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IParserContext.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -/// -/// Context that gets passed along a bean definition parsing process, encapsulating all relevant configuration as well as state. -/// -public interface IParserContext -{ - bool IsTemplate { get; } - - string ExpressionPrefix { get; } - - string ExpressionSuffix { get; } -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IPropertyAccessor.cs b/src/Common/src/Abstractions/Expression/Internal/IPropertyAccessor.cs deleted file mode 100644 index d35cd48183..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IPropertyAccessor.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface IPropertyAccessor -{ - IList GetSpecificTargetClasses(); - - bool CanRead(IEvaluationContext context, object target, string name); - - ITypedValue Read(IEvaluationContext context, object target, string name); - - bool CanWrite(IEvaluationContext context, object target, string name); - - void Write(IEvaluationContext context, object target, string name, object newValue); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/IServiceResolver.cs b/src/Common/src/Abstractions/Expression/Internal/IServiceResolver.cs deleted file mode 100644 index 5a41a9f8cc..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/IServiceResolver.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface IServiceResolver -{ - object Resolve(IEvaluationContext context, string serviceName); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/ITypeComparator.cs b/src/Common/src/Abstractions/Expression/Internal/ITypeComparator.cs deleted file mode 100644 index 8f57a5c8f3..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/ITypeComparator.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface ITypeComparator -{ - bool CanCompare(object firstObject, object secondObject); - - int Compare(object firstObject, object secondObject); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/ITypeConverter.cs b/src/Common/src/Abstractions/Expression/Internal/ITypeConverter.cs deleted file mode 100644 index 40ab1697db..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/ITypeConverter.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Converter; - -namespace Steeltoe.Common.Expression.Internal; - -/// -/// A type converter can convert values between different types encountered during expression evaluation. -/// -public interface ITypeConverter -{ - public IConversionService ConversionService { get; set; } - - bool CanConvert(Type sourceType, Type targetType); - - object ConvertValue(object value, Type sourceType, Type targetType); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/ITypeLocator.cs b/src/Common/src/Abstractions/Expression/Internal/ITypeLocator.cs deleted file mode 100644 index 8ed473ee03..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/ITypeLocator.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface ITypeLocator -{ - Type FindType(string typeName); -} diff --git a/src/Common/src/Abstractions/Expression/Internal/ITypedValue.cs b/src/Common/src/Abstractions/Expression/Internal/ITypedValue.cs deleted file mode 100644 index 315494bd23..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/ITypedValue.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public interface ITypedValue -{ - object Value { get; } - - Type TypeDescriptor { get; } -} diff --git a/src/Common/src/Abstractions/Expression/Internal/Operation.cs b/src/Common/src/Abstractions/Expression/Internal/Operation.cs deleted file mode 100644 index 166c617cc3..0000000000 --- a/src/Common/src/Abstractions/Expression/Internal/Operation.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public enum Operation -{ - Add, - Subtract, - Divide, - Multiply, - Modulus, - Power -} diff --git a/src/Common/src/Abstractions/Lifecycle/ILifecycle.cs b/src/Common/src/Abstractions/Lifecycle/ILifecycle.cs deleted file mode 100644 index d04df2e407..0000000000 --- a/src/Common/src/Abstractions/Lifecycle/ILifecycle.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Lifecycle; - -/// -/// A common interface defining methods for start/stop lifecycle control. -/// -public interface ILifecycle -{ - /// - /// Gets a value indicating whether its running. - /// - bool IsRunning { get; } - - /// - /// Start this component. - /// - /// - /// a task to signal completion. - /// - Task StartAsync(); - - /// - /// Stop this component. - /// - /// - /// a task to signal completion. - /// - Task StopAsync(); -} diff --git a/src/Common/src/Abstractions/Lifecycle/ILifecycleProcessor.cs b/src/Common/src/Abstractions/Lifecycle/ILifecycleProcessor.cs deleted file mode 100644 index 0ac66fb97c..0000000000 --- a/src/Common/src/Abstractions/Lifecycle/ILifecycleProcessor.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Lifecycle; - -/// -/// Interface for processing lifecycle based services. -/// -public interface ILifecycleProcessor : IDisposable -{ - /// - /// Gets a value indicating whether its running. - /// - bool IsRunning { get; } - - /// - /// Start this component. - /// - /// - /// a task to signal completion. - /// - Task StartAsync(); - - /// - /// Stop this component. - /// - /// - /// a task to signal completion. - /// - Task StopAsync(); - - /// - /// Call to refresh the lifecycle processor. - /// - /// - /// a task to signal completion. - /// - Task OnRefreshAsync(); - - /// - /// Call to shutdown the lifecycle processor. - /// - /// - /// a task to signal completion. - /// - Task OnCloseAsync(); -} diff --git a/src/Common/src/Abstractions/Lifecycle/IPhased.cs b/src/Common/src/Abstractions/Lifecycle/IPhased.cs deleted file mode 100644 index 0c6eaf1789..0000000000 --- a/src/Common/src/Abstractions/Lifecycle/IPhased.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Lifecycle; - -/// -/// An interface for objects that participate in a phased lifecycle. -/// -public interface IPhased -{ - /// - /// Gets the phase of this object. - /// - int Phase { get; } -} diff --git a/src/Common/src/Abstractions/Lifecycle/ISmartLifecycle.cs b/src/Common/src/Abstractions/Lifecycle/ISmartLifecycle.cs deleted file mode 100644 index 296a3be41a..0000000000 --- a/src/Common/src/Abstractions/Lifecycle/ISmartLifecycle.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Lifecycle; - -/// -/// An extension of the Lifecycle interface that provides auto startup and a call back stop action. -/// -public interface ISmartLifecycle : ILifecycle, IPhased -{ - /// - /// Gets a value indicating whether the auto startup is set for this object. - /// - bool IsAutoStartup { get; } - - /// - /// Stop the component and issue the callback when complete. - /// - /// - /// the callback action to invoke when complete. - /// - /// - /// a task for completion. - /// - Task StopAsync(Action callback); -} diff --git a/src/Common/src/Abstractions/Order/IOrdered.cs b/src/Common/src/Abstractions/Order/IOrdered.cs deleted file mode 100644 index ff147ad756..0000000000 --- a/src/Common/src/Abstractions/Order/IOrdered.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Order; - -/// -/// An interface that can be implemented by objects that should be orderable. -/// -public interface IOrdered -{ - /// - /// Gets the order of this object. - /// - int Order { get; } -} diff --git a/src/Common/src/Abstractions/Order/IPriorityOrdered.cs b/src/Common/src/Abstractions/Order/IPriorityOrdered.cs deleted file mode 100644 index 5c8ab5e534..0000000000 --- a/src/Common/src/Abstractions/Order/IPriorityOrdered.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Order; - -/// -/// Extension of the ordered interface expressing a priority order. Order values expressed by these objects come before others. -/// -public interface IPriorityOrdered : IOrdered -{ -} diff --git a/src/Common/src/Abstractions/Retry/IRecoveryCallback.cs b/src/Common/src/Abstractions/Retry/IRecoveryCallback.cs deleted file mode 100644 index b3ca7f133b..0000000000 --- a/src/Common/src/Abstractions/Retry/IRecoveryCallback.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Retry; - -/// -/// Callback for stateful retry after all tries are exhausted. -/// -public interface IRecoveryCallback -{ - /// - /// The callback that is issued. - /// - /// - /// the current retry context. - /// - /// - /// an object that can be used to replace the callback result that failed. - /// - object Recover(IRetryContext context); -} - -/// -/// Typed callback for stateful retry after all tries are exhausted. -/// -/// -/// the type returned from callback. -/// -public interface IRecoveryCallback : IRecoveryCallback -{ - /// - /// The callback that is issued. - /// - /// - /// the current retry context. - /// - /// - /// an object that can be used to replace the callback result that failed. - /// - new T Recover(IRetryContext context); -} diff --git a/src/Common/src/Abstractions/Retry/IRetryContext.cs b/src/Common/src/Abstractions/Retry/IRetryContext.cs deleted file mode 100644 index b7d137fded..0000000000 --- a/src/Common/src/Abstractions/Retry/IRetryContext.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Retry; - -/// -/// Low-level access to ongoing retry operation. Normally not needed by clients, but can be used to alter the course of the retry, e.g.force an early -/// termination. -/// -public interface IRetryContext : IAttributeAccessor -{ - /// - /// Gets the last exception that caused the retry. - /// - Exception LastException { get; } - - /// - /// Gets the number of retry attempts. - /// - int RetryCount { get; } - - /// - /// Gets the parent context if present. - /// - IRetryContext Parent { get; } -} diff --git a/src/Common/src/Abstractions/Retry/IRetryListener.cs b/src/Common/src/Abstractions/Retry/IRetryListener.cs deleted file mode 100644 index 6d06eabf8b..0000000000 --- a/src/Common/src/Abstractions/Retry/IRetryListener.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Retry; - -/// -/// Interface for listener that can be used to add behaviour to a retry. Implementations of RetryOperations can chose to issue callbacks to an -/// interceptor during the retry lifecycle. -/// -public interface IRetryListener -{ - /// - /// Called before the first attempt in a retry. - /// - /// - /// the current retry context. - /// - /// - /// true if the retry should proceed. - /// - bool Open(IRetryContext context); - - /// - /// Called after the final attempt (successful or not). - /// - /// - /// the current retry context. - /// - /// - /// the last exception that was thrown during retry. - /// - void Close(IRetryContext context, Exception exception); - - /// - /// Called after every unsuccessful attempt at a retry. - /// - /// - /// the current retry context. - /// - /// - /// the last exception that was thrown during retry. - /// - void OnError(IRetryContext context, Exception exception); -} diff --git a/src/Common/src/Abstractions/Retry/IRetryOperation.cs b/src/Common/src/Abstractions/Retry/IRetryOperation.cs deleted file mode 100644 index 5281d50671..0000000000 --- a/src/Common/src/Abstractions/Retry/IRetryOperation.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Retry; - -/// -/// Defines the basic set of operations to execute operations with configurable retry behaviour. -/// -public interface IRetryOperation -{ - /// - /// Execute the supplied RetryCallback with the configured retry semantics. - /// - /// - /// the type of return value. - /// - /// - /// the callback. - /// - /// - /// result of operation. - /// - T Execute(Func retryCallback); - - /// - /// Execute the supplied RetryCallback with the configured retry semantics. When retry is exhausted, call the recoverycallback. - /// - /// - /// the type of return value. - /// - /// - /// the callback. - /// - /// - /// the callback after retries are exhausted. - /// - /// - /// result of the operation. - /// - T Execute(Func retryCallback, IRecoveryCallback recoveryCallback); - - /// - /// Execute the supplied RetryCallback with the configured retry semantics. When retry is exhausted, call the recoverycallback. - /// - /// - /// the type of return value. - /// - /// - /// the callback. - /// - /// - /// the callback after retries are exhausted. - /// - /// - /// result of the operation. - /// - T Execute(Func retryCallback, Func recoveryCallback); - - /// - /// Execute the supplied RetryCallback with the configured retry semantics. - /// - /// - /// the callback. - /// - void Execute(Action retryCallback); - - /// - /// Execute the supplied RetryCallback with the configured retry semantics. When retry is exhausted, call the recoverycallback. - /// - /// - /// the callback. - /// - /// - /// the callback after retries are exhausted. - /// - void Execute(Action retryCallback, IRecoveryCallback recoveryCallback); - - /// - /// Execute the supplied RetryCallback with the configured retry semantics. When retry is exhausted, call the recoverycallback. - /// - /// - /// the callback. - /// - /// - /// the callback after retries are exhausted. - /// - void Execute(Action retryCallback, Action recoveryCallback); -} diff --git a/src/Common/src/Abstractions/Services/IServiceNameAware.cs b/src/Common/src/Abstractions/Services/IServiceNameAware.cs deleted file mode 100644 index b9cf9efd6d..0000000000 --- a/src/Common/src/Abstractions/Services/IServiceNameAware.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Services; - -public interface IServiceNameAware -{ - string ServiceName { get; set; } -} diff --git a/src/Common/src/Abstractions/Transaction/IPlatformTransactionManager.cs b/src/Common/src/Abstractions/Transaction/IPlatformTransactionManager.cs deleted file mode 100644 index 3197678c4d..0000000000 --- a/src/Common/src/Abstractions/Transaction/IPlatformTransactionManager.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface IPlatformTransactionManager -{ - ITransactionStatus GetTransaction(ITransactionDefinition definition); - - void Commit(ITransactionStatus status); - - void Rollback(ITransactionStatus status); -} diff --git a/src/Common/src/Abstractions/Transaction/IResourceHolder.cs b/src/Common/src/Abstractions/Transaction/IResourceHolder.cs deleted file mode 100644 index 89b7f068ca..0000000000 --- a/src/Common/src/Abstractions/Transaction/IResourceHolder.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface IResourceHolder -{ - /// - /// Gets a value indicating whether this holder is considered void, leftover from previous thread. - /// - bool IsVoid { get; } - - /// - /// Reset the transactional state. - /// - void Reset(); - - /// - /// Notify holder that it has been unbound from transaction. - /// - void Unbound(); -} diff --git a/src/Common/src/Abstractions/Transaction/IResourceTransactionManager.cs b/src/Common/src/Abstractions/Transaction/IResourceTransactionManager.cs deleted file mode 100644 index 3e5277555f..0000000000 --- a/src/Common/src/Abstractions/Transaction/IResourceTransactionManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface IResourceTransactionManager : IPlatformTransactionManager -{ - object ResourceFactory { get; } -} diff --git a/src/Common/src/Abstractions/Transaction/ISavepointManager.cs b/src/Common/src/Abstractions/Transaction/ISavepointManager.cs deleted file mode 100644 index 9f5091be51..0000000000 --- a/src/Common/src/Abstractions/Transaction/ISavepointManager.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface ISavepointManager -{ - object CreateSavepoint(); - - void RollbackToSavepoint(object savepoint); - - void ReleaseSavepoint(object savepoint); -} diff --git a/src/Common/src/Abstractions/Transaction/ISmartTransactionObject.cs b/src/Common/src/Abstractions/Transaction/ISmartTransactionObject.cs deleted file mode 100644 index 883de4998f..0000000000 --- a/src/Common/src/Abstractions/Transaction/ISmartTransactionObject.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface ISmartTransactionObject -{ - bool IsRollbackOnly { get; } - - void Flush(); -} diff --git a/src/Common/src/Abstractions/Transaction/ITransactionAttribute.cs b/src/Common/src/Abstractions/Transaction/ITransactionAttribute.cs deleted file mode 100644 index 145f112440..0000000000 --- a/src/Common/src/Abstractions/Transaction/ITransactionAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface ITransactionAttribute : ITransactionDefinition -{ - string Qualifier { get; } - - bool RollbackOn(Exception exception); -} diff --git a/src/Common/src/Abstractions/Transaction/ITransactionCallback.cs b/src/Common/src/Abstractions/Transaction/ITransactionCallback.cs deleted file mode 100644 index 60bf1877f2..0000000000 --- a/src/Common/src/Abstractions/Transaction/ITransactionCallback.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface ITransactionCallback -{ - T DoInTransaction(ITransactionStatus status); -} diff --git a/src/Common/src/Abstractions/Transaction/ITransactionDefinition.cs b/src/Common/src/Abstractions/Transaction/ITransactionDefinition.cs deleted file mode 100644 index 019a5f2d71..0000000000 --- a/src/Common/src/Abstractions/Transaction/ITransactionDefinition.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface ITransactionDefinition -{ - int PropagationBehavior { get; } - - int IsolationLevel { get; } - - int Timeout { get; } - - bool IsReadOnly { get; } - - string Name { get; } -} diff --git a/src/Common/src/Abstractions/Transaction/ITransactionExecution.cs b/src/Common/src/Abstractions/Transaction/ITransactionExecution.cs deleted file mode 100644 index a5bdeec734..0000000000 --- a/src/Common/src/Abstractions/Transaction/ITransactionExecution.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface ITransactionExecution -{ - bool IsCompleted { get; } - - bool IsRollbackOnly { get; } - - bool IsNewTransaction { get; } - - void SetRollbackOnly(); -} diff --git a/src/Common/src/Abstractions/Transaction/ITransactionOperations.cs b/src/Common/src/Abstractions/Transaction/ITransactionOperations.cs deleted file mode 100644 index fea2a44534..0000000000 --- a/src/Common/src/Abstractions/Transaction/ITransactionOperations.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface ITransactionOperations -{ - T Execute(Func action); - - void ExecuteWithoutResult(Action action); -} diff --git a/src/Common/src/Abstractions/Transaction/ITransactionStatus.cs b/src/Common/src/Abstractions/Transaction/ITransactionStatus.cs deleted file mode 100644 index 1ff8dfc233..0000000000 --- a/src/Common/src/Abstractions/Transaction/ITransactionStatus.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface ITransactionStatus : ITransactionExecution, ISavepointManager -{ - bool HasSavepoint { get; } - - void Flush(); -} diff --git a/src/Common/src/Abstractions/Transaction/ITransactionSynchronization.cs b/src/Common/src/Abstractions/Transaction/ITransactionSynchronization.cs deleted file mode 100644 index bf04963cd3..0000000000 --- a/src/Common/src/Abstractions/Transaction/ITransactionSynchronization.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public interface ITransactionSynchronization -{ - void Suspend(); - - void Resume(); - - void Flush(); - - void BeforeCommit(bool readOnly); - - void BeforeCompletion(); - - void AfterCommit(); - - void AfterCompletion(int status); -} diff --git a/src/Common/src/Abstractions/Util/AtomicBoolean.cs b/src/Common/src/Abstractions/Util/AtomicBoolean.cs deleted file mode 100644 index 7d090b55c4..0000000000 --- a/src/Common/src/Abstractions/Util/AtomicBoolean.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public class AtomicBoolean -{ - private volatile int _value; - - public bool Value - { - get => _value != 0; - set => _value = value ? 1 : 0; - } - - public AtomicBoolean() - : this(false) - { - } - - public AtomicBoolean(bool value) - { - Value = value; - } - - public bool CompareAndSet(bool expected, bool update) - { - int expectedInt = expected ? 1 : 0; - int updateInt = update ? 1 : 0; - return Interlocked.CompareExchange(ref _value, updateInt, expectedInt) == expectedInt; - } - - public bool GetAndSet(bool newValue) - { - int newValueInt = newValue ? 1 : 0; - int previous = Interlocked.Exchange(ref _value, newValueInt); - return previous == 1; - } -} diff --git a/src/Common/src/Abstractions/Util/AtomicInteger.cs b/src/Common/src/Abstractions/Util/AtomicInteger.cs deleted file mode 100644 index a8343f00f0..0000000000 --- a/src/Common/src/Abstractions/Util/AtomicInteger.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public class AtomicInteger -{ - protected volatile int value; - - public int Value - { - get => value; - set => this.value = value; - } - - public AtomicInteger() - : this(0) - { - } - - public AtomicInteger(int value) - { - this.value = value; - } - - public bool CompareAndSet(int expected, int update) - { - return Interlocked.CompareExchange(ref value, update, expected) == expected; - } - - public int IncrementAndGet() - { - return Interlocked.Increment(ref value); - } - - public int DecrementAndGet() - { - return Interlocked.Decrement(ref value); - } - - public int GetAndIncrement() - { - return Interlocked.Increment(ref value) - 1; - } - - public int AddAndGet(int value) - { - return Interlocked.Add(ref this.value, value); - } -} diff --git a/src/Common/src/Abstractions/Util/AtomicIntegerArray.cs b/src/Common/src/Abstractions/Util/AtomicIntegerArray.cs deleted file mode 100644 index 78c4a56f17..0000000000 --- a/src/Common/src/Abstractions/Util/AtomicIntegerArray.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public class AtomicIntegerArray : AtomicReferenceArray -{ - public AtomicIntegerArray(int length) - : base(length) - { - } -} diff --git a/src/Common/src/Abstractions/Util/AtomicLong.cs b/src/Common/src/Abstractions/Util/AtomicLong.cs deleted file mode 100644 index 00a40f7b51..0000000000 --- a/src/Common/src/Abstractions/Util/AtomicLong.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public class AtomicLong -{ - private long _value; - - public long Value - { - get => Interlocked.Read(ref _value); - set => Interlocked.Exchange(ref _value, value); - } - - public AtomicLong() - : this(0) - { - } - - public AtomicLong(long value) - { - _value = value; - } - - public bool CompareAndSet(long expected, long update) - { - return Interlocked.CompareExchange(ref _value, update, expected) == expected; - } - - public long GetAndSet(long value) - { - return Interlocked.Exchange(ref _value, value); - } - - public long AddAndGet(long value) - { - return Interlocked.Add(ref _value, value); - } -} diff --git a/src/Common/src/Abstractions/Util/AtomicReference.cs b/src/Common/src/Abstractions/Util/AtomicReference.cs deleted file mode 100644 index 5a2ffb8729..0000000000 --- a/src/Common/src/Abstractions/Util/AtomicReference.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public class AtomicReference - where T : class -{ - private volatile T _value; - - public T Value - { - get => _value; - set => _value = value; - } - - public AtomicReference() - : this(default) - { - } - - public AtomicReference(T value) - { - _value = value; - } - - public bool CompareAndSet(T expected, T update) - { - return Interlocked.CompareExchange(ref _value, update, expected) == expected; - } - - public T GetAndSet(T value) - { - return Interlocked.Exchange(ref _value, value); - } -} diff --git a/src/Common/src/Abstractions/Util/AtomicReferenceArray.cs b/src/Common/src/Abstractions/Util/AtomicReferenceArray.cs deleted file mode 100644 index 9d22495d6f..0000000000 --- a/src/Common/src/Abstractions/Util/AtomicReferenceArray.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public class AtomicReferenceArray -{ - private readonly T[] _array; - - public int Length - { - get - { - lock (_array) - { - return _array.Length; - } - } - } - - public T this[int index] - { - get - { - lock (_array) - { - return _array[index]; - } - } - set - { - lock (_array) - { - _array[index] = value; - } - } - } - - public AtomicReferenceArray(int length) - { - _array = new T[length]; - } - - public T[] ToArray() - { - lock (_array) - { - return (T[])_array.Clone(); - } - } -} diff --git a/src/Common/src/Abstractions/Util/IAsyncRunnable.cs b/src/Common/src/Abstractions/Util/IAsyncRunnable.cs deleted file mode 100644 index 90100fd01f..0000000000 --- a/src/Common/src/Abstractions/Util/IAsyncRunnable.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -/// -/// An object that can be invoked asynchronously via a run method. -/// -public interface IAsyncRunnable -{ - /// - /// Run this component. - /// - /// - /// return a task to signal completion. - /// - Task RunAsync(); -} diff --git a/src/Common/src/Abstractions/Util/IAttributeAccessor.cs b/src/Common/src/Abstractions/Util/IAttributeAccessor.cs deleted file mode 100644 index 08183702af..0000000000 --- a/src/Common/src/Abstractions/Util/IAttributeAccessor.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface IAttributeAccessor -{ - string[] AttributeNames { get; } - - void SetAttribute(string name, object value); - - object GetAttribute(string name); - - object RemoveAttribute(string name); - - bool HasAttribute(string name); -} diff --git a/src/Common/src/Abstractions/Util/IBackOff.cs b/src/Common/src/Abstractions/Util/IBackOff.cs deleted file mode 100644 index 1dbb9b222f..0000000000 --- a/src/Common/src/Abstractions/Util/IBackOff.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface IBackOff -{ - IBackOffExecution Start(); -} diff --git a/src/Common/src/Abstractions/Util/IBackOffExecution.cs b/src/Common/src/Abstractions/Util/IBackOffExecution.cs deleted file mode 100644 index 2dfdc8be4e..0000000000 --- a/src/Common/src/Abstractions/Util/IBackOffExecution.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface IBackOffExecution -{ - int NextBackOff(); -} diff --git a/src/Common/src/Abstractions/Util/IClassifier.cs b/src/Common/src/Abstractions/Util/IClassifier.cs deleted file mode 100644 index a4bd22a766..0000000000 --- a/src/Common/src/Abstractions/Util/IClassifier.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface IClassifier -{ - TTarget Classify(TSource classifiable); -} diff --git a/src/Common/src/Abstractions/Util/IErrorHandler.cs b/src/Common/src/Abstractions/Util/IErrorHandler.cs deleted file mode 100644 index 6d2e554100..0000000000 --- a/src/Common/src/Abstractions/Util/IErrorHandler.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Common.Util; - -public interface IErrorHandler : IServiceNameAware -{ - bool HandleError(Exception exception); -} diff --git a/src/Common/src/Abstractions/Util/IIDGenerator.cs b/src/Common/src/Abstractions/Util/IIDGenerator.cs deleted file mode 100644 index 8ac7c6a952..0000000000 --- a/src/Common/src/Abstractions/Util/IIDGenerator.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface IIdGenerator -{ - string GenerateId(); -} diff --git a/src/Common/src/Abstractions/Util/IPathMatcher.cs b/src/Common/src/Abstractions/Util/IPathMatcher.cs deleted file mode 100644 index 5e02e91427..0000000000 --- a/src/Common/src/Abstractions/Util/IPathMatcher.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface IPathMatcher -{ - bool IsPattern(string path); - - bool Match(string pattern, string path); - - bool MatchStart(string pattern, string path); - - string ExtractPathWithinPattern(string pattern, string path); - - IDictionary ExtractUriTemplateVariables(string pattern, string path); - - IComparer GetPatternComparer(string path); - - string Combine(string pattern1, string pattern2); -} diff --git a/src/Common/src/Abstractions/Util/IRoute.cs b/src/Common/src/Abstractions/Util/IRoute.cs deleted file mode 100644 index 24950f5b0f..0000000000 --- a/src/Common/src/Abstractions/Util/IRoute.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface IRoute -{ - string Value { get; } -} diff --git a/src/Common/src/Abstractions/Util/IRouteMatcher.cs b/src/Common/src/Abstractions/Util/IRouteMatcher.cs deleted file mode 100644 index 9228695d24..0000000000 --- a/src/Common/src/Abstractions/Util/IRouteMatcher.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface IRouteMatcher -{ - IRoute ParseRoute(string routeValue); - - bool IsPattern(string routeValue); - - string Combine(string pattern1, string pattern2); - - bool Match(string pattern, IRoute route); - - IDictionary MatchAndExtract(string pattern, IRoute route); - - IComparer GetPatternComparer(IRoute route); -} diff --git a/src/Common/src/Abstractions/Util/IRunnable.cs b/src/Common/src/Abstractions/Util/IRunnable.cs deleted file mode 100644 index 4f361f64d5..0000000000 --- a/src/Common/src/Abstractions/Util/IRunnable.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -/// -/// An object that can be invoked synchronously via a run method. -/// -public interface IRunnable -{ - /// - /// Run this object. - /// - /// - /// returns success or failure. - /// - bool Run(); -} diff --git a/src/Common/src/Abstractions/Util/IStringValueResolver.cs b/src/Common/src/Abstractions/Util/IStringValueResolver.cs deleted file mode 100644 index b6212fa272..0000000000 --- a/src/Common/src/Abstractions/Util/IStringValueResolver.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface IStringValueResolver -{ - string ResolveStringValue(string strVal); -} diff --git a/src/Common/src/Abstractions/Util/ITimerListener.cs b/src/Common/src/Abstractions/Util/ITimerListener.cs deleted file mode 100644 index 052a968b01..0000000000 --- a/src/Common/src/Abstractions/Util/ITimerListener.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public interface ITimerListener -{ - int IntervalTimeInMilliseconds { get; } - - void Tick(); -} diff --git a/src/Common/src/Common.Expression/Internal/AccessException.cs b/src/Common/src/Common.Expression/Internal/AccessException.cs deleted file mode 100644 index 267fc19913..0000000000 --- a/src/Common/src/Common.Expression/Internal/AccessException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public class AccessException : Exception -{ - public AccessException(string message) - : base(message) - { - } - - public AccessException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common.Expression/Internal/Contexts/ConfigurationAccessor.cs b/src/Common/src/Common.Expression/Internal/Contexts/ConfigurationAccessor.cs deleted file mode 100644 index 5dcd892eb4..0000000000 --- a/src/Common/src/Common.Expression/Internal/Contexts/ConfigurationAccessor.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; - -namespace Steeltoe.Common.Expression.Internal.Contexts; - -public class ConfigurationAccessor : IPropertyAccessor -{ - public bool CanRead(IEvaluationContext context, object target, string name) - { - return target is IConfiguration; - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return false; - } - - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(IConfiguration) - }; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - if (target is not IConfiguration configuration) - { - throw new ArgumentException($"Target must be of type {nameof(IConfiguration)}", nameof(target)); - } - - return new TypedValue(configuration[name]); - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - // Empty - } -} diff --git a/src/Common/src/Common.Expression/Internal/Contexts/DictionaryAccessor.cs b/src/Common/src/Common.Expression/Internal/Contexts/DictionaryAccessor.cs deleted file mode 100644 index 3391ebf2fc..0000000000 --- a/src/Common/src/Common.Expression/Internal/Contexts/DictionaryAccessor.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring; - -namespace Steeltoe.Common.Expression.Internal.Contexts; - -public class DictionaryAccessor : ICompilablePropertyAccessor -{ - private static readonly MethodInfo GetItem = typeof(IDictionary).GetMethod("get_Item", new[] - { - typeof(object) - }); - - public bool CanRead(IEvaluationContext context, object target, string name) - { - return target is IDictionary dictionary && dictionary.Contains(name); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return true; - } - - public Type GetPropertyType() - { - return typeof(object); - } - - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(IDictionary) - }; - } - - public bool IsCompilable() - { - return true; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - if (target is not IDictionary dictionary) - { - throw new ArgumentException($"Target must be of type {nameof(IDictionary)}", nameof(target)); - } - - if (dictionary.Contains(name)) - { - return new TypedValue(dictionary[name]); - } - - throw new DictionaryAccessException(name); - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - if (target is not IDictionary dictionary) - { - throw new ArgumentException($"Target must be of type {nameof(IDictionary)}", nameof(target)); - } - - dictionary[name] = newValue; - } - - public void GenerateCode(string propertyName, ILGenerator gen, CodeFlow cf) - { - TypeDescriptor descriptor = cf.LastDescriptor(); - - if (descriptor == null || descriptor.Value != typeof(IDictionary)) - { - if (descriptor == null) - { - CodeFlow.LoadTarget(gen); - } - - gen.Emit(OpCodes.Castclass, typeof(IDictionary)); - } - - gen.Emit(OpCodes.Ldstr, propertyName); - gen.Emit(OpCodes.Callvirt, GetItem); - } - - private sealed class DictionaryAccessException : AccessException - { - private readonly string _key; - - public override string Message => $"Dictionary does not contain a value for key '{_key}'"; - - public DictionaryAccessException(string key) - : base(string.Empty) - { - _key = key; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Contexts/ServiceExpressionContext.cs b/src/Common/src/Common.Expression/Internal/Contexts/ServiceExpressionContext.cs deleted file mode 100644 index 69dee97788..0000000000 --- a/src/Common/src/Common.Expression/Internal/Contexts/ServiceExpressionContext.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Common.Expression.Internal.Contexts; - -// Used as root context with StandardServiceExpressionResolver -public class ServiceExpressionContext : IServiceExpressionContext -{ - private const string EnvironmentName = "configuration"; - - public IApplicationContext ApplicationContext { get; } - - public ServiceExpressionContext(IApplicationContext applicationContext) - { - ArgumentGuard.NotNull(applicationContext); - - ApplicationContext = applicationContext; - } - - public bool ContainsService(string serviceName, Type serviceType) - { - if (serviceName == EnvironmentName) - { - return true; - } - - return ApplicationContext.ContainsService(serviceName, serviceType); - } - - public bool ContainsService(string serviceName) - { - if (serviceName == EnvironmentName) - { - return true; - } - - return ApplicationContext.ContainsService(serviceName); - } - - public object GetService(string serviceName) - { - if (serviceName == EnvironmentName) - { - return ApplicationContext.Configuration; - } - - if (ApplicationContext.ContainsService(serviceName)) - { - return ApplicationContext.GetService(serviceName); - } - - return null; - } - - public object GetService(string serviceName, Type serviceType) - { - if (serviceName == EnvironmentName) - { - return ApplicationContext.Configuration; - } - - if (ApplicationContext.ContainsService(serviceName, serviceType)) - { - return ApplicationContext.GetService(serviceName, serviceType); - } - - return null; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not ServiceExpressionContext otherContext) - { - return false; - } - - return ApplicationContext == otherContext.ApplicationContext; - } - - public override int GetHashCode() - { - return ApplicationContext.GetHashCode(); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Contexts/ServiceExpressionContextAccessor.cs b/src/Common/src/Common.Expression/Internal/Contexts/ServiceExpressionContextAccessor.cs deleted file mode 100644 index 2d7afae115..0000000000 --- a/src/Common/src/Common.Expression/Internal/Contexts/ServiceExpressionContextAccessor.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Contexts; - -public class ServiceExpressionContextAccessor : IPropertyAccessor -{ - public bool CanRead(IEvaluationContext context, object target, string name) - { - (Type serviceType, string lookupName) = ServiceFactoryResolver.GetServiceNameAndType(context, name); - - if (serviceType != null) - { - return target is IServiceExpressionContext context1 && context1.ContainsService(lookupName, serviceType); - } - - return target is IServiceExpressionContext context2 && context2.ContainsService(name); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return false; - } - - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(IServiceExpressionContext) - }; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - if (target is not IServiceExpressionContext targetContext) - { - throw new InvalidOperationException($"{nameof(target)} must be of type {nameof(IServiceExpressionContext)}."); - } - - (Type serviceType, string lookupName) = ServiceFactoryResolver.GetServiceNameAndType(context, name); - - if (serviceType != null) - { - return new TypedValue(targetContext.GetService(lookupName, serviceType)); - } - - return new TypedValue(targetContext.GetService(name)); - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - throw new AccessException("Services are read-only"); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Contexts/ServiceFactoryAccessor.cs b/src/Common/src/Common.Expression/Internal/Contexts/ServiceFactoryAccessor.cs deleted file mode 100644 index e57d6c4200..0000000000 --- a/src/Common/src/Common.Expression/Internal/Contexts/ServiceFactoryAccessor.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Common.Expression.Internal.Contexts; - -public class ServiceFactoryAccessor : IPropertyAccessor -{ - public bool CanRead(IEvaluationContext context, object target, string name) - { - (Type serviceType, string lookupName) = ServiceFactoryResolver.GetServiceNameAndType(context, name); - - if (serviceType != null) - { - return target is IApplicationContext context1 && context1.ContainsService(lookupName, serviceType); - } - - return target is IApplicationContext context2 && context2.ContainsService(name); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return false; - } - - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(IApplicationContext) - }; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - if (target is not IApplicationContext targetContext) - { - throw new InvalidOperationException($"{nameof(target)} must be of type {nameof(IApplicationContext)}."); - } - - (Type serviceType, string lookupName) = ServiceFactoryResolver.GetServiceNameAndType(context, name); - - if (serviceType != null) - { - return new TypedValue(targetContext.GetService(lookupName, serviceType)); - } - - return new TypedValue(targetContext.GetService(name)); - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - throw new AccessException($"Services in an {nameof(IApplicationContext)} are read-only."); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Contexts/ServiceFactoryResolver.cs b/src/Common/src/Common.Expression/Internal/Contexts/ServiceFactoryResolver.cs deleted file mode 100644 index 650c2ca980..0000000000 --- a/src/Common/src/Common.Expression/Internal/Contexts/ServiceFactoryResolver.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Common.Expression.Internal.Contexts; - -public class ServiceFactoryResolver : IServiceResolver -{ - private readonly IApplicationContext _applicationContext; - - public ServiceFactoryResolver(IApplicationContext applicationContext) - { - ArgumentGuard.NotNull(applicationContext); - - _applicationContext = applicationContext; - } - - public static (Type Type, string Name) GetServiceNameAndType(IEvaluationContext context, string serviceName) - { - Type result = null; - string name = serviceName; - - if (serviceName.StartsWith("T(", StringComparison.Ordinal) && context.TypeLocator != null) - { - int endParen = serviceName.LastIndexOf(')'); - - if (endParen > 0) - { - string serviceTypeName = serviceName.Substring(2, endParen - 2); - result = context.TypeLocator.FindType(serviceTypeName); - name = serviceName.Substring(endParen + 1); - } - } - - return (result, name); - } - - public object Resolve(IEvaluationContext context, string serviceName) - { - try - { - (Type serviceType, string lookupName) = GetServiceNameAndType(context, serviceName); - object result = serviceType != null ? _applicationContext.GetService(lookupName, serviceType) : _applicationContext.GetService(serviceName); - - if (result == null) - { - throw new AccessException($"Could not resolve service reference '{serviceName}' against application context"); - } - - return result; - } - catch (Exception ex) - { - throw new AccessException("Could not resolve service reference against application context", ex); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Contexts/StandardServiceExpressionResolver.cs b/src/Common/src/Common.Expression/Internal/Contexts/StandardServiceExpressionResolver.cs deleted file mode 100644 index e83933c7bb..0000000000 --- a/src/Common/src/Common.Expression/Internal/Contexts/StandardServiceExpressionResolver.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Contexts; - -public class StandardServiceExpressionResolver : IServiceExpressionResolver -{ - public const string DefaultExpressionPrefix = "#{"; - public const string DefaultExpressionSuffix = "}"; - - private readonly ConcurrentDictionary _expressionCache = new(); - private readonly ConcurrentDictionary _evaluationCache = new(); - private readonly IParserContext _serviceExpressionParserContext; - private IExpressionParser _expressionParser; - private string _expressionPrefix = DefaultExpressionPrefix; - private string _expressionSuffix = DefaultExpressionSuffix; - - public string ExpressionPrefix - { - get => _expressionPrefix; - set - { - ArgumentGuard.NotNullOrEmpty(value); - - _expressionPrefix = value; - } - } - - public string ExpressionSuffix - { - get => _expressionSuffix; - set - { - ArgumentGuard.NotNullOrEmpty(value); - - _expressionSuffix = value; - } - } - - public IExpressionParser ExpressionParser - { - get => _expressionParser; - set - { - ArgumentGuard.NotNull(value); - - _expressionParser = value; - } - } - - public StandardServiceExpressionResolver() - { - _expressionParser = new SpelExpressionParser(); - _serviceExpressionParserContext = new ServiceExpressionParserContext(this); - } - - public object Evaluate(string value, IServiceExpressionContext evalContext) - { - if (string.IsNullOrEmpty(value)) - { - return value; - } - - try - { - _expressionCache.TryGetValue(value, out IExpression expr); - - if (expr == null) - { - expr = _expressionParser.ParseExpression(value, _serviceExpressionParserContext); - _expressionCache.TryAdd(value, expr); - } - - _evaluationCache.TryGetValue(evalContext, out IEvaluationContext sec); - - if (sec == null) - { - var sec2 = new StandardEvaluationContext(evalContext); - sec2.AddPropertyAccessor(new ServiceExpressionContextAccessor()); - sec2.AddPropertyAccessor(new ServiceFactoryAccessor()); - sec2.AddPropertyAccessor(new DictionaryAccessor()); - sec2.AddPropertyAccessor(new ConfigurationAccessor()); - sec2.ServiceResolver = new ServiceFactoryResolver(evalContext.ApplicationContext); - sec2.TypeLocator = new StandardTypeLocator(); - var conversionService = evalContext.ApplicationContext.GetService(); - - if (conversionService != null) - { - sec2.TypeConverter = new StandardTypeConverter(conversionService); - } - - CustomizeEvaluationContext(sec2); - _evaluationCache.TryAdd(evalContext, sec2); - sec = sec2; - } - - return expr.GetValue(sec); - } - catch (Exception ex) - { - throw new ExpressionException("Expression parsing failed", ex); - } - } - - protected virtual void CustomizeEvaluationContext(IEvaluationContext evalContext) - { - } - - private sealed class ServiceExpressionParserContext : IParserContext - { - private readonly StandardServiceExpressionResolver _resolver; - - public bool IsTemplate => true; - - public string ExpressionPrefix => _resolver.ExpressionPrefix; - - public string ExpressionSuffix => _resolver.ExpressionSuffix; - - public ServiceExpressionParserContext(StandardServiceExpressionResolver resolver) - { - _resolver = resolver; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/EvaluationException.cs b/src/Common/src/Common.Expression/Internal/EvaluationException.cs deleted file mode 100644 index 0b901aedce..0000000000 --- a/src/Common/src/Common.Expression/Internal/EvaluationException.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public class EvaluationException : ExpressionException -{ - public EvaluationException(string message) - : base(message) - { - } - - public EvaluationException(string message, Exception innerException) - : base(message, innerException) - { - } - - public EvaluationException(int position, string message) - : base(position, message) - { - } - - public EvaluationException(string expressionString, string message) - : base(expressionString, message) - { - } - - public EvaluationException(int position, string message, Exception innerException) - : base(position, message, innerException) - { - } -} diff --git a/src/Common/src/Common.Expression/Internal/ExpressionException.cs b/src/Common/src/Common.Expression/Internal/ExpressionException.cs deleted file mode 100644 index fe1fa97c3c..0000000000 --- a/src/Common/src/Common.Expression/Internal/ExpressionException.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Expression.Internal; - -public class ExpressionException : Exception -{ - public string ExpressionString { get; } - public int Position { get; set; } - public override string Message => ToDetailedString(); - public string SimpleMessage => base.Message; - - public ExpressionException(string message) - : base(message) - { - ExpressionString = null; - Position = 0; - } - - public ExpressionException(string message, Exception innerException) - : base(message, innerException) - { - ExpressionString = null; - Position = 0; - } - - public ExpressionException(string expressionString, string message) - : base(message) - { - ExpressionString = expressionString; - Position = -1; - } - - public ExpressionException(string expressionString, int position, string message) - : base(message) - { - ExpressionString = expressionString; - Position = position; - } - - public ExpressionException(int position, string message) - : base(message) - { - ExpressionString = null; - Position = position; - } - - public ExpressionException(int position, string message, Exception innerException) - : base(message, innerException) - { - ExpressionString = null; - Position = position; - } - - public string ToDetailedString() - { - if (ExpressionString != null) - { - var output = new StringBuilder(); - output.Append("Expression ["); - output.Append(ExpressionString); - output.Append(']'); - - if (Position >= 0) - { - output.Append(" @"); - output.Append(Position); - } - - output.Append(": "); - output.Append(SimpleMessage); - return output.ToString(); - } - - return SimpleMessage; - } -} diff --git a/src/Common/src/Common.Expression/Internal/ExpressionInvocationTargetException.cs b/src/Common/src/Common.Expression/Internal/ExpressionInvocationTargetException.cs deleted file mode 100644 index c3e4b25ffd..0000000000 --- a/src/Common/src/Common.Expression/Internal/ExpressionInvocationTargetException.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public class ExpressionInvocationTargetException : EvaluationException -{ - public ExpressionInvocationTargetException(string message) - : base(message) - { - } - - public ExpressionInvocationTargetException(string message, Exception innerException) - : base(message, innerException) - { - } - - public ExpressionInvocationTargetException(int position, string message) - : base(position, message) - { - } - - public ExpressionInvocationTargetException(int position, string message, Exception innerException) - : base(position, message, innerException) - { - } - - public ExpressionInvocationTargetException(string expressionString, string message) - : base(expressionString, message) - { - } -} diff --git a/src/Common/src/Common.Expression/Internal/ParseException.cs b/src/Common/src/Common.Expression/Internal/ParseException.cs deleted file mode 100644 index b627fb28ff..0000000000 --- a/src/Common/src/Common.Expression/Internal/ParseException.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public class ParseException : ExpressionException -{ - public ParseException(int position, string message) - : base(position, message) - { - } - - public ParseException(int position, string message, Exception innerException) - : base(position, message, innerException) - { - } - - public ParseException(string expressionString, int position, string message) - : base(expressionString, position, message) - { - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/Assign.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/Assign.cs deleted file mode 100644 index b923d3a58c..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/Assign.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class Assign : SpelNode -{ - public Assign(int startPos, int endPos, params SpelNode[] operands) - : base(startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - ITypedValue newValue = children[1].GetValueInternal(state); - GetChild(0).SetValue(state, newValue.Value); - return newValue; - } - - public override string ToStringAst() - { - return $"{GetChild(0).ToStringAst()}={GetChild(1).ToStringAst()}"; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/AstUtils.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/AstUtils.cs deleted file mode 100644 index 6d1d9630d2..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/AstUtils.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public static class AstUtils -{ - public static List GetPropertyAccessorsToTry(Type targetType, List propertyAccessors) - { - var specificAccessors = new List(); - var generalAccessors = new List(); - - foreach (IPropertyAccessor resolver in propertyAccessors) - { - IList targets = resolver.GetSpecificTargetClasses(); - - if (targets == null) - { - // generic resolver that says it can be used for any type - generalAccessors.Add(resolver); - } - else - { - if (targetType != null) - { - int pos = 0; - - foreach (Type type in targets) - { - if (type == targetType) - { - // put exact matches on the front to be tried first? - specificAccessors.Insert(pos++, resolver); - } - else if (type.IsAssignableFrom(targetType)) - { - // put supertype matches at the end of the - // specificAccessor list - generalAccessors.Add(resolver); - } - } - } - } - } - - var resolvers = new List(specificAccessors.Count + generalAccessors.Count); - resolvers.AddRange(specificAccessors); - resolvers.AddRange(generalAccessors); - return resolvers; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/BooleanLiteral.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/BooleanLiteral.cs deleted file mode 100644 index 0abe2b1cef..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/BooleanLiteral.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class BooleanLiteral : Literal -{ - private readonly BooleanTypedValue _value; - - public BooleanLiteral(string payload, int startPos, int endPos, bool value) - : base(payload, startPos, endPos) - { - _value = BooleanTypedValue.ForValue(value); - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetLiteralValue() - { - return _value; - } - - public override bool IsCompilable() - { - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - LocalBuilder result = gen.DeclareLocal(typeof(bool)); - gen.Emit(_value.Equals(BooleanTypedValue.True) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - - gen.Emit(OpCodes.Stloc, result); - gen.Emit(OpCodes.Ldloc, result); - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/CompoundExpression.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/CompoundExpression.cs deleted file mode 100644 index 1616272b2f..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/CompoundExpression.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class CompoundExpression : SpelNode -{ - public CompoundExpression(int startPos, int endPos, params SpelNode[] expressionComponents) - : base(startPos, endPos, expressionComponents) - { - if (expressionComponents.Length < 2) - { - throw new InvalidOperationException($"Do not build compound expressions with less than two entries: {expressionComponents.Length}"); - } - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - IValueRef valueRef = GetValueRef(state); - ITypedValue result = valueRef.GetValue(); - exitTypeDescriptor = children[^1].ExitDescriptor; - return result; - } - - public override void SetValue(ExpressionState state, object newValue) - { - GetValueRef(state).SetValue(newValue); - } - - public override bool IsWritable(ExpressionState state) - { - return GetValueRef(state).IsWritable; - } - - public override string ToStringAst() - { - var strings = new List(); - - for (int i = 0; i < ChildCount; i++) - { - strings.Add(GetChild(i).ToStringAst()); - } - - return string.Join(".", strings); - } - - public override bool IsCompilable() - { - foreach (SpelNode child in children) - { - if (!child.IsCompilable()) - { - return false; - } - } - - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - foreach (SpelNode child in children) - { - child.GenerateCode(gen, cf); - } - - cf.PushDescriptor(exitTypeDescriptor); - } - - protected internal override IValueRef GetValueRef(ExpressionState state) - { - if (ChildCount == 1) - { - return children[0].GetValueRef(state); - } - - SpelNode nextNode = children[0]; - - try - { - ITypedValue result = nextNode.GetValueInternal(state); - int cc = ChildCount; - - for (int i = 1; i < cc - 1; i++) - { - try - { - state.PushActiveContextObject(result); - nextNode = children[i]; - - result = nextNode.GetValueInternal(state); - } - finally - { - state.PopActiveContextObject(); - } - } - - try - { - state.PushActiveContextObject(result); - nextNode = children[cc - 1]; - return nextNode.GetValueRef(state); - } - finally - { - state.PopActiveContextObject(); - } - } - catch (SpelEvaluationException ex) - { - // Correct the position for the error before re-throwing - ex.Position = nextNode.StartPosition; - throw; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/ConstructorReference.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/ConstructorReference.cs deleted file mode 100644 index c7eb9bbef5..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/ConstructorReference.cs +++ /dev/null @@ -1,510 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using Steeltoe.Common.Expression.Internal.Spring.Common; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class ConstructorReference : SpelNode -{ - private readonly bool _isArrayConstructor; - - private readonly SpelNode[] _dimensions; - - // Is this caching safe - passing the expression around will mean this executor is also being passed around - // The cached executor that may be reused on subsequent evaluations. - private volatile IConstructorExecutor _cachedExecutor; - - private bool HasInitializer => ChildCount > 1; - - public ConstructorReference(int startPos, int endPos, params SpelNode[] arguments) - : base(startPos, endPos, arguments) - { - _isArrayConstructor = false; - } - - public ConstructorReference(int startPos, int endPos, SpelNode[] dimensions, params SpelNode[] arguments) - : base(startPos, endPos, arguments) - { - _isArrayConstructor = true; - _dimensions = dimensions; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - if (_isArrayConstructor) - { - return CreateArray(state); - } - - return CreateNewInstance(state); - } - - public override bool IsCompilable() - { - if (_cachedExecutor is not ReflectiveConstructorExecutor executor || exitTypeDescriptor == null) - { - return false; - } - - if (ChildCount > 1) - { - for (int c = 1, max = ChildCount; c < max; c++) - { - if (!children[c].IsCompilable()) - { - return false; - } - } - } - - ConstructorInfo constructor = executor.Constructor; - return constructor.IsPublic && ReflectionHelper.IsPublic(constructor.DeclaringType); - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - var executor = (ReflectiveConstructorExecutor)_cachedExecutor; - - if (executor == null) - { - throw new InvalidOperationException("No cached executor"); - } - - ConstructorInfo constructor = executor.Constructor; - - // children[0] is the type of the constructor, don't want to include that in argument processing - var arguments = new SpelNode[children.Length - 1]; - Array.Copy(children, 1, arguments, 0, children.Length - 1); - GenerateCodeForArguments(gen, cf, constructor, arguments); - gen.Emit(OpCodes.Newobj, constructor); - cf.PushDescriptor(exitTypeDescriptor); - } - - public override string ToStringAst() - { - var sb = new StringBuilder("new "); - int index = 0; - sb.Append(GetChild(index++).ToStringAst()); - sb.Append('('); - - for (int i = index; i < ChildCount; i++) - { - if (i > index) - { - sb.Append(','); - } - - sb.Append(GetChild(i).ToStringAst()); - } - - sb.Append(')'); - return sb.ToString(); - } - - private ITypedValue CreateNewInstance(ExpressionState state) - { - object[] arguments = new object[ChildCount - 1]; - var argumentTypes = new List(ChildCount - 1); - - for (int i = 0; i < arguments.Length; i++) - { - ITypedValue childValue = children[i + 1].GetValueInternal(state); - object value = childValue.Value; - arguments[i] = value; - Type valueType = value?.GetType(); - argumentTypes.Add(valueType); - } - - IConstructorExecutor executorToUse = _cachedExecutor; - - if (executorToUse != null) - { - try - { - return executorToUse.Execute(state.EvaluationContext, arguments); - } - catch (AccessException ex) - { - // Two reasons this can occur: - // 1. the method invoked actually threw a real exception - // 2. the method invoked was not passed the arguments it expected and has become 'stale' - - // In the first case we should not retry, in the second case we should see if there is a - // better suited method. - - // To determine which situation it is, the AccessException will contain a cause. - // If the cause is an InvocationTargetException, a user exception was thrown inside the constructor. - // Otherwise the constructor could not be invoked. - if (ex.InnerException is TargetInvocationException) - { - // User exception was the root cause - exit now - Exception rootCause = ex.InnerException.InnerException; - - if (rootCause is SystemException) - { - throw rootCause; - } - - string name = (string)children[0].GetValueInternal(state).Value; - - throw new SpelEvaluationException(StartPosition, rootCause, SpelMessage.ConstructorInvocationProblem, name, - FormatHelper.FormatMethodForMessage(string.Empty, argumentTypes)); - } - - // At this point we know it wasn't a user problem so worth a retry if a better candidate can be found - _cachedExecutor = null; - } - } - - // Either there was no accessor or it no longer exists - string typeName = (string)children[0].GetValueInternal(state).Value; - - if (typeName == null) - { - throw new InvalidOperationException("No type name"); - } - - executorToUse = FindExecutorForConstructor(typeName, argumentTypes, state); - - try - { - _cachedExecutor = executorToUse; - - if (executorToUse is ReflectiveConstructorExecutor executor) - { - exitTypeDescriptor = CodeFlow.ToDescriptor(executor.Constructor.DeclaringType); - } - - return executorToUse.Execute(state.EvaluationContext, arguments); - } - catch (AccessException ex) - { - throw new SpelEvaluationException(StartPosition, ex, SpelMessage.ConstructorInvocationProblem, typeName, - FormatHelper.FormatMethodForMessage(string.Empty, argumentTypes)); - } - } - - private IConstructorExecutor FindExecutorForConstructor(string typeName, List argumentTypes, ExpressionState state) - { - IEvaluationContext evalContext = state.EvaluationContext; - List ctorResolvers = evalContext.ConstructorResolvers; - - foreach (IConstructorResolver ctorResolver in ctorResolvers) - { - try - { - IConstructorExecutor ce = ctorResolver.Resolve(state.EvaluationContext, typeName, argumentTypes); - - if (ce != null) - { - return ce; - } - } - catch (AccessException ex) - { - throw new SpelEvaluationException(StartPosition, ex, SpelMessage.ConstructorInvocationProblem, typeName, - FormatHelper.FormatMethodForMessage(string.Empty, argumentTypes)); - } - } - - throw new SpelEvaluationException(StartPosition, SpelMessage.ConstructorNotFound, typeName, - FormatHelper.FormatMethodForMessage(string.Empty, argumentTypes)); - } - - private TypedValue CreateArray(ExpressionState state) - { - // First child gives us the array type which will either be a primitive or reference type - object intendedArrayType = GetChild(0).GetValue(state); - - if (intendedArrayType is not string type) - { - throw new SpelEvaluationException(GetChild(0).StartPosition, SpelMessage.TypeNameExpectedForArrayConstruction, - FormatHelper.FormatClassNameForMessage(intendedArrayType?.GetType())); - } - - SpelTypeCode arrayTypeCode = SpelTypeCode.ForName(type); - Type componentType = arrayTypeCode == SpelTypeCode.Object ? state.FindType(type) : arrayTypeCode.Type; - - object newArray; - - if (!HasInitializer) - { - // Confirm all dimensions were specified (for example [3][][5] is missing the 2nd dimension) - if (_dimensions != null) - { - foreach (SpelNode dimension in _dimensions) - { - if (dimension == null) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.MissingArrayDimension); - } - } - } - else - { - throw new SpelEvaluationException(StartPosition, SpelMessage.MissingArrayDimension); - } - - ITypeConverter typeConverter = state.EvaluationContext.TypeConverter; - - // Shortcut for 1 dimensional - if (_dimensions.Length == 1) - { - ITypedValue o = _dimensions[0].GetTypedValue(state); - int arraySize = ExpressionUtils.ToInt(typeConverter, o); - newArray = Array.CreateInstance(componentType, arraySize); - } - else - { - // Multi-dimensional - hold onto your hat! - int[] dims = new int[_dimensions.Length]; - - for (int d = 0; d < _dimensions.Length; d++) - { - ITypedValue o = _dimensions[d].GetTypedValue(state); - dims[d] = ExpressionUtils.ToInt(typeConverter, o); - } - - newArray = Array.CreateInstance(componentType, dims); - } - } - else - { - // There is an initializer - if (_dimensions == null || _dimensions.Length > 1) - { - // There is an initializer but this is a multi-dimensional array (e.g. new int[][]{{1,2},{3,4}}) - this - // is not currently supported - throw new SpelEvaluationException(StartPosition, SpelMessage.MultidimensionalArrayInitializerNotSupported); - } - - ITypeConverter typeConverter = state.EvaluationContext.TypeConverter; - var initializer = (InlineList)GetChild(1); - - // If a dimension was specified, check it matches the initializer length - if (_dimensions[0] != null) - { - ITypedValue dValue = _dimensions[0].GetTypedValue(state); - int i = ExpressionUtils.ToInt(typeConverter, dValue); - - if (i != initializer.ChildCount) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.InitializerLengthIncorrect); - } - } - - // Build the array and populate it - int arraySize = initializer.ChildCount; - newArray = Array.CreateInstance(componentType, arraySize); - - if (arrayTypeCode == SpelTypeCode.Object) - { - PopulateReferenceTypeArray(state, newArray, typeConverter, initializer, componentType); - } - else if (arrayTypeCode == SpelTypeCode.Boolean) - { - PopulateBooleanArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Byte) - { - PopulateByteArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Sbyte) - { - PopulateSByteArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Char) - { - PopulateCharArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Double) - { - PopulateDoubleArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Float) - { - PopulateFloatArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Int) - { - PopulateIntArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Uint) - { - PopulateUIntArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Long) - { - PopulateLongArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Ulong) - { - PopulateULongArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Short) - { - PopulateShortArray(state, newArray, typeConverter, initializer); - } - else if (arrayTypeCode == SpelTypeCode.Ushort) - { - PopulateUShortArray(state, newArray, typeConverter, initializer); - } - else - { - throw new InvalidOperationException(arrayTypeCode.Name); - } - } - - return new TypedValue(newArray); - } - - private void PopulateReferenceTypeArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer, Type componentType) - { - object[] newObjectArray = (object[])newArray; - - for (int i = 0; i < newObjectArray.Length; i++) - { - ISpelNode elementNode = initializer.GetChild(i); - object arrayEntry = elementNode.GetValue(state); - newObjectArray[i] = typeConverter.ConvertValue(arrayEntry, arrayEntry?.GetType(), componentType); - } - } - - private void PopulateByteArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - byte[] newByteArray = (byte[])newArray; - - for (int i = 0; i < newByteArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newByteArray[i] = ExpressionUtils.ToByte(typeConverter, typedValue); - } - } - - private void PopulateSByteArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - sbyte[] newByteArray = (sbyte[])newArray; - - for (int i = 0; i < newByteArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newByteArray[i] = ExpressionUtils.ToSByte(typeConverter, typedValue); - } - } - - private void PopulateFloatArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - float[] newFloatArray = (float[])newArray; - - for (int i = 0; i < newFloatArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newFloatArray[i] = ExpressionUtils.ToFloat(typeConverter, typedValue); - } - } - - private void PopulateDoubleArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - double[] newDoubleArray = (double[])newArray; - - for (int i = 0; i < newDoubleArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newDoubleArray[i] = ExpressionUtils.ToDouble(typeConverter, typedValue); - } - } - - private void PopulateShortArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - short[] newShortArray = (short[])newArray; - - for (int i = 0; i < newShortArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newShortArray[i] = ExpressionUtils.ToShort(typeConverter, typedValue); - } - } - - private void PopulateUShortArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - ushort[] newShortArray = (ushort[])newArray; - - for (int i = 0; i < newShortArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newShortArray[i] = ExpressionUtils.ToUShort(typeConverter, typedValue); - } - } - - private void PopulateLongArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - long[] newLongArray = (long[])newArray; - - for (int i = 0; i < newLongArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newLongArray[i] = ExpressionUtils.ToLong(typeConverter, typedValue); - } - } - - private void PopulateULongArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - ulong[] newLongArray = (ulong[])newArray; - - for (int i = 0; i < newLongArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newLongArray[i] = ExpressionUtils.ToULong(typeConverter, typedValue); - } - } - - private void PopulateCharArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - char[] newCharArray = (char[])newArray; - - for (int i = 0; i < newCharArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newCharArray[i] = ExpressionUtils.ToChar(typeConverter, typedValue); - } - } - - private void PopulateBooleanArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - bool[] newBooleanArray = (bool[])newArray; - - for (int i = 0; i < newBooleanArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newBooleanArray[i] = ExpressionUtils.ToBoolean(typeConverter, typedValue); - } - } - - private void PopulateIntArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - int[] newIntArray = (int[])newArray; - - for (int i = 0; i < newIntArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newIntArray[i] = ExpressionUtils.ToInt(typeConverter, typedValue); - } - } - - private void PopulateUIntArray(ExpressionState state, object newArray, ITypeConverter typeConverter, InlineList initializer) - { - uint[] newIntArray = (uint[])newArray; - - for (int i = 0; i < newIntArray.Length; i++) - { - ITypedValue typedValue = initializer.GetChild(i).GetTypedValue(state); - newIntArray[i] = ExpressionUtils.ToUInt(typeConverter, typedValue); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/Elvis.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/Elvis.cs deleted file mode 100644 index 3db82f33b7..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/Elvis.cs +++ /dev/null @@ -1,164 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class Elvis : SpelNode -{ - private static readonly MethodInfo EqualsMethod = typeof(object).GetMethod(nameof(Equals), new[] - { - typeof(object) - }); - - public Elvis(int startPos, int endPos, params SpelNode[] args) - : base(startPos, endPos, args) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - ITypedValue value = children[0].GetValueInternal(state); - - // If this check is changed, the generateCode method will need changing too - if (!(value.Value == null || string.Empty.Equals(value.Value))) - { - return value; - } - - ITypedValue result = children[1].GetValueInternal(state); - ComputeExitTypeDescriptor(); - return result; - } - - public override string ToStringAst() - { - return $"{GetChild(0).ToStringAst()} ?: {GetChild(1).ToStringAst()}"; - } - - public override bool IsCompilable() - { - SpelNode condition = children[0]; - SpelNode ifNullValue = children[1]; - return condition.IsCompilable() && ifNullValue.IsCompilable() && condition.ExitDescriptor != null && ifNullValue.ExitDescriptor != null; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - // exit type descriptor can be null if both components are literal expressions - ComputeExitTypeDescriptor(); - cf.EnterCompilationScope(); - - children[0].GenerateCode(gen, cf); - TypeDescriptor lastDesc = cf.LastDescriptor(); - - if (lastDesc == null) - { - throw new InvalidOperationException("No last descriptor"); - } - - // if primitive result, boxed will be on stack - CodeFlow.InsertBoxIfNecessary(gen, lastDesc); - cf.ExitCompilationScope(); - - LocalBuilder ifResult = gen.DeclareLocal(typeof(bool)); - LocalBuilder finalResult = gen.DeclareLocal(exitTypeDescriptor.Value); - Label loadFinalResult = gen.DefineLabel(); - - // Save off child1 result - LocalBuilder child1Result = gen.DeclareLocal(typeof(object)); - gen.Emit(OpCodes.Stloc, child1Result); - - Label child1IsNull = gen.DefineLabel(); - gen.Emit(OpCodes.Ldloc, child1Result); - - // br if child1 null - gen.Emit(OpCodes.Brfalse, child1IsNull); - - // Check for empty string - gen.Emit(OpCodes.Ldstr, string.Empty); - gen.Emit(OpCodes.Ldloc, child1Result); - gen.Emit(OpCodes.Callvirt, EqualsMethod); - gen.Emit(OpCodes.Ldc_I4_0); - gen.Emit(OpCodes.Ceq); - - // save empty string result - gen.Emit(OpCodes.Stloc, ifResult); - Label loadCheckIfResults = gen.DefineLabel(); - gen.Emit(OpCodes.Br, loadCheckIfResults); - - // Child1 null, load false for if result - gen.MarkLabel(child1IsNull); - gen.Emit(OpCodes.Ldc_I4_0); - gen.Emit(OpCodes.Stloc, ifResult); - - // Fall thru to check if results - // Mark Check if Results - gen.MarkLabel(loadCheckIfResults); - - // Load if results - gen.Emit(OpCodes.Ldloc, ifResult); - Label callChild2 = gen.DefineLabel(); - - // If failed, call child2 for results - gen.Emit(OpCodes.Brfalse, callChild2); - - // Final result is child 1, save final - gen.Emit(OpCodes.Ldloc, child1Result); - gen.Emit(OpCodes.Stloc, finalResult); - gen.Emit(OpCodes.Br, loadFinalResult); - - gen.MarkLabel(callChild2); - cf.EnterCompilationScope(); - children[1].GenerateCode(gen, cf); - - if (!CodeFlow.IsValueType(exitTypeDescriptor)) - { - lastDesc = cf.LastDescriptor(); - - if (lastDesc == null) - { - throw new InvalidOperationException("No last descriptor"); - } - - if (lastDesc == TypeDescriptor.V) - { - gen.Emit(OpCodes.Ldnull); - } - else - { - CodeFlow.InsertBoxIfNecessary(gen, lastDesc); - } - } - - cf.ExitCompilationScope(); - gen.Emit(OpCodes.Stloc, finalResult); - - // Load final result on stack - gen.MarkLabel(loadFinalResult); - gen.Emit(OpCodes.Ldloc, finalResult); - cf.PushDescriptor(exitTypeDescriptor); - } - - private void ComputeExitTypeDescriptor() - { - if (exitTypeDescriptor == null && children[0].ExitDescriptor != null && children[1].ExitDescriptor != null) - { - TypeDescriptor conditionDescriptor = children[0].ExitDescriptor; - TypeDescriptor ifNullValueDescriptor = children[1].ExitDescriptor; - - if (Equals(conditionDescriptor, ifNullValueDescriptor)) - { - exitTypeDescriptor = conditionDescriptor; - } - else - { - // Use the easiest to compute common super type - exitTypeDescriptor = TypeDescriptor.Object; - } - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/FloatLiteral.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/FloatLiteral.cs deleted file mode 100644 index e50c340fe4..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/FloatLiteral.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class FloatLiteral : Literal -{ - private readonly ITypedValue _value; - - public FloatLiteral(string payload, int startPos, int endPos, float value) - : base(payload, startPos, endPos) - { - _value = new TypedValue(value); - exitTypeDescriptor = TypeDescriptor.F; - } - - public override ITypedValue GetLiteralValue() - { - return _value; - } - - public override bool IsCompilable() - { - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - gen.Emit(OpCodes.Ldc_R4, (float)_value.Value); - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/FormatHelper.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/FormatHelper.cs deleted file mode 100644 index 4d03605280..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/FormatHelper.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public static class FormatHelper -{ - public static string FormatMethodForMessage(string name, IList argumentTypes) - { - var items = new List(); - - foreach (Type typeDescriptor in argumentTypes) - { - items.Add(typeDescriptor != null ? FormatClassNameForMessage(typeDescriptor) : FormatClassNameForMessage(null)); - } - - return $"{name}({string.Join(",", items)})"; - } - - public static string FormatClassNameForMessage(Type type) - { - return type != null ? type.FullName : "null"; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/FunctionReference.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/FunctionReference.cs deleted file mode 100644 index 490be77482..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/FunctionReference.cs +++ /dev/null @@ -1,168 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class FunctionReference : SpelNode -{ - private readonly string _name; - - // Captures the most recently used method for the function invocation *if* the method - // can safely be used for compilation (i.e. no argument conversion is going on) - private volatile MethodInfo _method; - - public FunctionReference(string functionName, int startPos, int endPos, params SpelNode[] arguments) - : base(startPos, endPos, arguments) - { - _name = functionName; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - ITypedValue value = state.LookupVariable(_name); - - if (Equals(value, TypedValue.Null)) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.FunctionNotDefined, _name); - } - - if (value.Value is not MethodInfo method) - { - // Possibly a static method registered as a function - throw new SpelEvaluationException(SpelMessage.FunctionReferenceCannotBeInvoked, _name, value.GetType()); - } - - try - { - return ExecuteFunctionJlrMethod(state, method); - } - catch (SpelEvaluationException ex) - { - ex.Position = StartPosition; - throw; - } - } - - public override string ToStringAst() - { - var items = new List(); - - for (int i = 0; i < ChildCount; i++) - { - items.Add(GetChild(i).ToStringAst()); - } - - return $"#{_name}({string.Join(",", items)})"; - } - - public override bool IsCompilable() - { - if (_method == null) - { - return false; - } - - if (!_method.IsStatic || !_method.IsPublic || !ReflectionHelper.IsPublic(_method.DeclaringType)) - { - return false; - } - - foreach (SpelNode child in children) - { - if (!child.IsCompilable()) - { - return false; - } - } - - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - MethodInfo method = _method; - - if (method == null) - { - throw new InvalidOperationException("No method handle"); - } - - GenerateCodeForArguments(gen, cf, method, children); - gen.Emit(OpCodes.Call, method); - cf.PushDescriptor(exitTypeDescriptor); - } - - private object[] GetArguments(ExpressionState state) - { - // Compute arguments to the function - object[] arguments = new object[ChildCount]; - - for (int i = 0; i < arguments.Length; i++) - { - arguments[i] = children[i].GetValueInternal(state).Value; - } - - return arguments; - } - - private TypedValue ExecuteFunctionJlrMethod(ExpressionState state, MethodInfo method) - { - object[] functionArgs = GetArguments(state); - - if (!method.IsVarArgs()) - { - int declaredParamCount = method.GetParameters().Length; - - if (declaredParamCount != functionArgs.Length) - { - throw new SpelEvaluationException(SpelMessage.IncorrectNumberOfArgumentsToFunction, functionArgs.Length, declaredParamCount); - } - } - - if (!method.IsStatic) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.FunctionMustBeStatic, ClassUtils.GetQualifiedMethodName(method), _name); - } - - // Convert arguments if necessary and remap them for varargs if required - ITypeConverter converter = state.EvaluationContext.TypeConverter; - bool argumentConversionOccurred = ReflectionHelper.ConvertAllArguments(converter, functionArgs, method); - - if (method.IsVarArgs()) - { - functionArgs = ReflectionHelper.SetupArgumentsForVarargsInvocation(ClassUtils.GetParameterTypes(method), functionArgs); - } - - bool compilable = false; - - try - { - object result = method.Invoke(method.GetType(), functionArgs); - compilable = !argumentConversionOccurred; - return new TypedValue(result, result?.GetType() ?? method.ReturnType); - } - catch (Exception ex) - { - throw new SpelEvaluationException(StartPosition, ex, SpelMessage.ExceptionDuringFunctionCall, _name, ex.Message); - } - finally - { - if (compilable) - { - exitTypeDescriptor = CodeFlow.ToDescriptor(method.ReturnType); - _method = method; - } - else - { - exitTypeDescriptor = null; - _method = null; - } - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/IValueRef.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/IValueRef.cs deleted file mode 100644 index 62a3e6099d..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/IValueRef.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public interface IValueRef -{ - bool IsWritable { get; } - - ITypedValue GetValue(); - - void SetValue(object newValue); -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/Identifier.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/Identifier.cs deleted file mode 100644 index ef269bcdb9..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/Identifier.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class Identifier : SpelNode -{ - private readonly ITypedValue _id; - - public Identifier(string payload, int startPos, int endPos) - : base(startPos, endPos) - { - _id = new TypedValue(payload); - } - - public override string ToStringAst() - { - return _id.Value != null ? _id.Value.ToString() : "null"; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - return _id; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/Indexer.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/Indexer.cs deleted file mode 100644 index 1716681b49..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/Indexer.cs +++ /dev/null @@ -1,842 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Globalization; -using System.Reflection; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class Indexer : SpelNode -{ - private static readonly MethodInfo ListGetItemMethod = typeof(IList).GetMethods().Single(m => m.Name == "get_Item"); - private static readonly MethodInfo DictionaryGetItemMethod = typeof(IDictionary).GetMethods().Single(m => m.Name == "get_Item"); - - // These fields are used when the indexer is being used as a property read accessor. - // If the name and target type match these cached values then the cachedReadAccessor - // is used to read the property. If they do not match, the correct accessor is - // discovered and then cached for later use. - private string _cachedReadName; - - private Type _cachedReadTargetType; - - private IPropertyAccessor _cachedReadAccessor; - - // These fields are used when the indexer is being used as a property write accessor. - // If the name and target type match these cached values then the cachedWriteAccessor - // is used to write the property. If they do not match, the correct accessor is - // discovered and then cached for later use. - private string _cachedWriteName; - - private Type _cachedWriteTargetType; - - private IPropertyAccessor _cachedWriteAccessor; - - private IndexedType _indexedType; - - public Indexer(int startPos, int endPos, SpelNode expr) - : base(startPos, endPos, expr) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - return GetValueRef(state).GetValue(); - } - - public override void SetValue(ExpressionState state, object newValue) - { - GetValueRef(state).SetValue(newValue); - } - - public override bool IsWritable(ExpressionState state) - { - return true; - } - - public override bool IsCompilable() - { - if (_indexedType == IndexedType.Array) - { - return exitTypeDescriptor != null; - } - - if (_indexedType == IndexedType.List) - { - return children[0].IsCompilable(); - } - - if (_indexedType == IndexedType.Map) - { - return children[0] is PropertyOrFieldReference || children[0].IsCompilable(); - } - - if (_indexedType == IndexedType.Object) - { - // If the string name is changing the accessor is clearly going to change (so no compilation possible) - return _cachedReadAccessor is ReflectivePropertyAccessor.OptimalPropertyAccessor && GetChild(0) is StringLiteral; - } - - return false; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - TypeDescriptor descriptor = cf.LastDescriptor(); - - if (descriptor == null) - { - CodeFlow.LoadTarget(gen); - } - - if (_indexedType == IndexedType.Array) - { - Type arrayType = exitTypeDescriptor.Value.MakeArrayType(); - gen.Emit(OpCodes.Castclass, arrayType); - SpelNode child = children[0]; - cf.EnterCompilationScope(); - child.GenerateCode(gen, cf); - cf.ExitCompilationScope(); - gen.Emit(GetLdElemInsn(exitTypeDescriptor.Value)); - } - else if (_indexedType == IndexedType.List) - { - gen.Emit(OpCodes.Castclass, typeof(IList)); - cf.EnterCompilationScope(); - children[0].GenerateCode(gen, cf); - cf.ExitCompilationScope(); - gen.Emit(OpCodes.Callvirt, ListGetItemMethod); - } - else if (_indexedType == IndexedType.Map) - { - gen.Emit(OpCodes.Castclass, typeof(IDictionary)); - - // Special case when the key is an unquoted string literal that will be parsed as - // a property/field reference - if (children[0] is PropertyOrFieldReference reference) - { - string mapKeyName = reference.Name; - gen.Emit(OpCodes.Ldstr, mapKeyName); - } - else - { - cf.EnterCompilationScope(); - children[0].GenerateCode(gen, cf); - cf.ExitCompilationScope(); - } - - gen.Emit(OpCodes.Callvirt, DictionaryGetItemMethod); - } - else if (_indexedType == IndexedType.Object) - { - if (_cachedReadAccessor is not ReflectivePropertyAccessor.OptimalPropertyAccessor accessor) - { - throw new InvalidOperationException("No cached read accessor"); - } - - var method = accessor.Member as MethodInfo; - var field = accessor.Member as FieldInfo; - bool isStatic = method != null ? method.IsStatic : field.IsStatic; - - Type targetType = accessor.Member.DeclaringType; - - if (!isStatic && (descriptor == null || targetType != descriptor.Value)) - { - gen.Emit(OpCodes.Castclass, targetType); - } - - if (method != null) - { - gen.Emit(isStatic ? OpCodes.Call : OpCodes.Callvirt, method); - } - else - { - gen.Emit(isStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, field); - } - } - - cf.PushDescriptor(exitTypeDescriptor); - } - - public override string ToStringAst() - { - var sj = new List(); - - for (int i = 0; i < ChildCount; i++) - { - sj.Add(GetChild(i).ToStringAst()); - } - - return $"[{string.Join(",", sj)}]"; - } - - protected internal override IValueRef GetValueRef(ExpressionState state) - { - ITypedValue context = state.GetActiveContextObject(); - object target = context.Value; - Type targetDescriptor = context.TypeDescriptor; - ITypedValue indexValue; - object index; - - // This first part of the if clause prevents a 'double dereference' of the property (SPR-5847) - if (target is IDictionary && children[0] is PropertyOrFieldReference reference1) - { - PropertyOrFieldReference reference = reference1; - index = reference.Name; - indexValue = new TypedValue(index); - } - else - { - // In case the map key is unqualified, we want it evaluated against the root object - // so temporarily push that on whilst evaluating the key - try - { - state.PushActiveContextObject(state.RootContextObject); - indexValue = children[0].GetValueInternal(state); - index = indexValue.Value; - - if (index == null) - { - throw new InvalidOperationException("No index"); - } - } - finally - { - state.PopActiveContextObject(); - } - } - - // Raise a proper exception in case of a null target - if (target == null) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.CannotIndexIntoNullValue); - } - - // At this point, we need a TypeDescriptor for a non-null target object - if (targetDescriptor == null) - { - throw new InvalidOperationException("No type descriptor"); - } - - // Indexing into a Map - if (target is IDictionary dictionary) - { - object key = index; - Type mapKeyType = ReflectionHelper.GetMapKeyTypeDescriptor(targetDescriptor); - - if (mapKeyType != null) - { - key = state.ConvertValue(key, mapKeyType); - } - - _indexedType = IndexedType.Map; - return new MapIndexingValueRef(this, state.TypeConverter, dictionary, key, targetDescriptor); - } - - // If the object is something that looks indexable by an integer, - // attempt to treat the index value as a number - if (target is Array || target is IList || target is string) - { - int idx = (int)state.ConvertValue(index, typeof(int)); - - if (target is Array) - { - _indexedType = IndexedType.Array; - return new ArrayIndexingValueRef(this, state.TypeConverter, target, idx, targetDescriptor); - } - - if (target is IList list) - { - _indexedType = IndexedType.List; - - return new CollectionIndexingValueRef(this, list, idx, targetDescriptor, state.TypeConverter, state.Configuration.AutoGrowCollections, - state.Configuration.MaximumAutoGrowSize); - } - - _indexedType = IndexedType.String; - return new StringIndexingLValue(this, (string)target, idx, targetDescriptor); - } - - // Try and treat the index value as a property of the context object - // Could call the conversion service to convert the value to a String - Type valueType = indexValue.TypeDescriptor; - - if (valueType != null && typeof(string) == valueType) - { - _indexedType = IndexedType.Object; - return new PropertyIndexingValueRef(this, target, (string)index, state.EvaluationContext, targetDescriptor); - } - - throw new SpelEvaluationException(StartPosition, SpelMessage.IndexingNotSupportedForType, targetDescriptor); - } - - private void CheckAccess(int arrayLength, int index) - { - if (index >= arrayLength) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.ArrayIndexOutOfBounds, arrayLength, index); - } - } - - private T ConvertValue(ITypeConverter converter, object value) - { - Type targetType = typeof(T); - object result = converter.ConvertValue(value, value == null ? typeof(object) : value.GetType(), targetType); - - if (result is not T) - { - throw new InvalidOperationException($"Failed conversion result for index [{value}]"); - } - - return (T)result; - } - - private object AccessArrayElement(object ctx, int idx) - { - Type arrayComponentType = ctx.GetType().GetElementType(); - - if (arrayComponentType == typeof(bool)) - { - bool[] array = (bool[])ctx; - CheckAccess(array.Length, idx); - exitTypeDescriptor = TypeDescriptor.Z; - return array[idx]; - } - - if (arrayComponentType == typeof(byte)) - { - byte[] array = (byte[])ctx; - CheckAccess(array.Length, idx); - - exitTypeDescriptor = TypeDescriptor.B; - return array[idx]; - } - - if (arrayComponentType == typeof(char)) - { - char[] array = (char[])ctx; - CheckAccess(array.Length, idx); - - exitTypeDescriptor = TypeDescriptor.C; - return array[idx]; - } - - if (arrayComponentType == typeof(double)) - { - double[] array = (double[])ctx; - CheckAccess(array.Length, idx); - - exitTypeDescriptor = TypeDescriptor.D; - return array[idx]; - } - - if (arrayComponentType == typeof(float)) - { - float[] array = (float[])ctx; - CheckAccess(array.Length, idx); - - exitTypeDescriptor = TypeDescriptor.F; - return array[idx]; - } - - if (arrayComponentType == typeof(int)) - { - int[] array = (int[])ctx; - CheckAccess(array.Length, idx); - - exitTypeDescriptor = TypeDescriptor.I; - return array[idx]; - } - - if (arrayComponentType == typeof(long)) - { - long[] array = (long[])ctx; - CheckAccess(array.Length, idx); - - exitTypeDescriptor = TypeDescriptor.J; - return array[idx]; - } - - if (arrayComponentType == typeof(short)) - { - short[] array = (short[])ctx; - CheckAccess(array.Length, idx); - - exitTypeDescriptor = TypeDescriptor.S; - return array[idx]; - } - else - { - object[] array = (object[])ctx; - CheckAccess(array.Length, idx); - object retValue = array[idx]; - - exitTypeDescriptor = CodeFlow.ToDescriptor(arrayComponentType); - return retValue; - } - } - - private void SetArrayElement(ITypeConverter converter, object ctx, int idx, object newValue, Type arrayComponentType) - { - if (arrayComponentType == typeof(bool)) - { - bool[] array = (bool[])ctx; - CheckAccess(array.Length, idx); - array[idx] = ConvertValue(converter, newValue); - } - else if (arrayComponentType == typeof(byte)) - { - byte[] array = (byte[])ctx; - CheckAccess(array.Length, idx); - array[idx] = ConvertValue(converter, newValue); - } - else if (arrayComponentType == typeof(char)) - { - char[] array = (char[])ctx; - CheckAccess(array.Length, idx); - array[idx] = ConvertValue(converter, newValue); - } - else if (arrayComponentType == typeof(double)) - { - double[] array = (double[])ctx; - CheckAccess(array.Length, idx); - array[idx] = ConvertValue(converter, newValue); - } - else if (arrayComponentType == typeof(float)) - { - float[] array = (float[])ctx; - CheckAccess(array.Length, idx); - array[idx] = ConvertValue(converter, newValue); - } - else if (arrayComponentType == typeof(int)) - { - int[] array = (int[])ctx; - CheckAccess(array.Length, idx); - array[idx] = ConvertValue(converter, newValue); - } - else if (arrayComponentType == typeof(long)) - { - long[] array = (long[])ctx; - CheckAccess(array.Length, idx); - array[idx] = ConvertValue(converter, newValue); - } - else if (arrayComponentType == typeof(short)) - { - short[] array = (short[])ctx; - CheckAccess(array.Length, idx); - array[idx] = ConvertValue(converter, newValue); - } - else - { - object[] array = (object[])ctx; - CheckAccess(array.Length, idx); - array[idx] = ConvertValue(converter, newValue); - } - } - - private enum IndexedType - { - Array, - List, - Map, - String, - Object - } - - private sealed class ArrayIndexingValueRef : IValueRef - { - private readonly ITypeConverter _typeConverter; - private readonly object _array; - private readonly int _index; - private readonly Type _typeDescriptor; - private readonly Indexer _indexer; - - public bool IsWritable => true; - - public ArrayIndexingValueRef(Indexer indexer, ITypeConverter typeConverter, object array, int index, Type typeDescriptor) - { - _indexer = indexer; - _typeConverter = typeConverter; - _array = array; - _index = index; - _typeDescriptor = typeDescriptor; - } - - public ITypedValue GetValue() - { - object arrayElement = _indexer.AccessArrayElement(_array, _index); - Type type = arrayElement == null ? _typeDescriptor : arrayElement.GetType(); - return new TypedValue(arrayElement, type); - } - - public void SetValue(object newValue) - { - Type elementType = _typeDescriptor.GetElementType(); - - if (elementType == null) - { - throw new InvalidOperationException("No element type"); - } - - _indexer.SetArrayElement(_typeConverter, _array, _index, newValue, elementType); - } - } - - private sealed class MapIndexingValueRef : IValueRef - { - private readonly Indexer _indexer; - - private readonly ITypeConverter _typeConverter; - - private readonly IDictionary _map; - - private readonly object _key; - - private readonly Type _mapEntryDescriptor; - - public bool IsWritable => true; - - public MapIndexingValueRef(Indexer indexer, ITypeConverter typeConverter, IDictionary map, object key, Type mapEntryDescriptor) - { - _indexer = indexer; - _typeConverter = typeConverter; - _map = map; - _key = key; - _mapEntryDescriptor = mapEntryDescriptor; - } - - public ITypedValue GetValue() - { - object value = _map[_key]; - _indexer.exitTypeDescriptor = CodeFlow.ToDescriptor(typeof(object)); - return new TypedValue(value, ReflectionHelper.GetMapValueTypeDescriptor(_mapEntryDescriptor, value)); - } - - public void SetValue(object newValue) - { - Type mapValType = ReflectionHelper.GetMapValueTypeDescriptor(_mapEntryDescriptor); - - if (mapValType != null) - { - newValue = _typeConverter.ConvertValue(newValue, newValue == null ? typeof(object) : newValue.GetType(), mapValType); - } - - _map[_key] = newValue; - } - } - - private sealed class PropertyIndexingValueRef : IValueRef - { - private readonly object _targetObject; - - private readonly string _name; - - private readonly IEvaluationContext _evaluationContext; - - private readonly Type _targetObjectTypeDescriptor; - private readonly Indexer _indexer; - - public bool IsWritable => true; - - public PropertyIndexingValueRef(Indexer indexer, object targetObject, string value, IEvaluationContext evaluationContext, - Type targetObjectTypeDescriptor) - { - _indexer = indexer; - _targetObject = targetObject; - _name = value; - _evaluationContext = evaluationContext; - _targetObjectTypeDescriptor = targetObjectTypeDescriptor; - } - - public ITypedValue GetValue() - { - Type targetObjectRuntimeClass = _indexer.GetObjectType(_targetObject); - - try - { - if (_indexer._cachedReadName != null && _indexer._cachedReadName == _name && _indexer._cachedReadTargetType != null && - _indexer._cachedReadTargetType == targetObjectRuntimeClass) - { - // It is OK to use the cached accessor - IPropertyAccessor accessor = _indexer._cachedReadAccessor; - - if (accessor == null) - { - throw new InvalidOperationException("No cached read accessor"); - } - - return accessor.Read(_evaluationContext, _targetObject, _name); - } - - List accessorsToTry = AstUtils.GetPropertyAccessorsToTry(targetObjectRuntimeClass, _evaluationContext.PropertyAccessors); - - foreach (IPropertyAccessor acc in accessorsToTry) - { - IPropertyAccessor accessor = acc; - - if (accessor.CanRead(_evaluationContext, _targetObject, _name)) - { - if (accessor is ReflectivePropertyAccessor accessor1) - { - accessor = accessor1.CreateOptimalAccessor(_evaluationContext, _targetObject, _name); - } - - _indexer._cachedReadAccessor = accessor; - _indexer._cachedReadName = _name; - _indexer._cachedReadTargetType = targetObjectRuntimeClass; - - if (accessor is ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor) - { - MemberInfo member = optimalAccessor.Member; - _indexer.exitTypeDescriptor = CodeFlow.ToDescriptor(member is MethodInfo info ? info.ReturnType : ((FieldInfo)member).FieldType); - } - - return accessor.Read(_evaluationContext, _targetObject, _name); - } - } - } - catch (AccessException ex) - { - throw new SpelEvaluationException(_indexer.StartPosition, ex, SpelMessage.IndexingNotSupportedForType, _targetObjectTypeDescriptor.ToString()); - } - - throw new SpelEvaluationException(_indexer.StartPosition, SpelMessage.IndexingNotSupportedForType, _targetObjectTypeDescriptor.ToString()); - } - - public void SetValue(object newValue) - { - Type contextObjectClass = _indexer.GetObjectType(_targetObject); - - try - { - if (_indexer._cachedWriteName != null && _indexer._cachedWriteName == _name && _indexer._cachedWriteTargetType != null && - _indexer._cachedWriteTargetType == contextObjectClass) - { - // It is OK to use the cached accessor - IPropertyAccessor accessor = _indexer._cachedWriteAccessor; - - if (accessor == null) - { - throw new InvalidOperationException("No cached write accessor"); - } - - accessor.Write(_evaluationContext, _targetObject, _name, newValue); - return; - } - - List accessorsToTry = AstUtils.GetPropertyAccessorsToTry(contextObjectClass, _evaluationContext.PropertyAccessors); - - foreach (IPropertyAccessor acc in accessorsToTry) - { - IPropertyAccessor accessor = acc; - - if (accessor.CanWrite(_evaluationContext, _targetObject, _name)) - { - _indexer._cachedWriteName = _name; - _indexer._cachedWriteTargetType = contextObjectClass; - _indexer._cachedWriteAccessor = accessor; - accessor.Write(_evaluationContext, _targetObject, _name, newValue); - return; - } - } - } - catch (AccessException ex) - { - throw new SpelEvaluationException(_indexer.StartPosition, ex, SpelMessage.ExceptionDuringPropertyWrite, _name, ex.Message); - } - } - } - - private sealed class CollectionIndexingValueRef : IValueRef - { - private readonly IList _collection; - private readonly int _index; - private readonly Type _collectionEntryDescriptor; - private readonly ITypeConverter _typeConverter; - private readonly bool _growCollection; - private readonly int _maximumSize; - private readonly Indexer _indexer; - - public bool IsWritable => true; - - public CollectionIndexingValueRef(Indexer indexer, IList collection, int index, Type collectionEntryDescriptor, ITypeConverter typeConverter, - bool growCollection, int maximumSize) - { - _indexer = indexer; - _collection = collection; - _index = index; - _collectionEntryDescriptor = collectionEntryDescriptor; - _typeConverter = typeConverter; - _growCollection = growCollection; - _maximumSize = maximumSize; - } - - public ITypedValue GetValue() - { - GrowCollectionIfNecessary(); - - if (_collection != null) - { - object o = _collection[_index]; - _indexer.exitTypeDescriptor = CodeFlow.ToDescriptor(typeof(object)); - return new TypedValue(o, ReflectionHelper.GetElementTypeDescriptor(_collectionEntryDescriptor, o)); - } - - throw new InvalidOperationException($"Failed to find indexed element {_index}: {_collection}"); - } - - public void SetValue(object newValue) - { - GrowCollectionIfNecessary(); - - if (_collection != null) - { - IList list = _collection; - Type elemTypeDesc = ReflectionHelper.GetElementTypeDescriptor(_collectionEntryDescriptor); - - if (elemTypeDesc != null) - { - newValue = _typeConverter.ConvertValue(newValue, newValue == null ? typeof(object) : newValue.GetType(), elemTypeDesc); - } - - list[_index] = newValue; - } - else - { - throw new SpelEvaluationException(_indexer.StartPosition, SpelMessage.IndexingNotSupportedForType, _collectionEntryDescriptor.ToString()); - } - } - - private void GrowCollectionIfNecessary() - { - if (_index >= _collection.Count) - { - if (!_growCollection) - { - throw new SpelEvaluationException(_indexer.StartPosition, SpelMessage.CollectionIndexOutOfBounds, _collection.Count, _index); - } - - if (_index >= _maximumSize) - { - throw new SpelEvaluationException(_indexer.StartPosition, SpelMessage.UnableToGrowCollection); - } - - Type elemTypeDesc = ReflectionHelper.GetElementTypeDescriptor(_collectionEntryDescriptor); - - if (elemTypeDesc == null) - { - throw new SpelEvaluationException(_indexer.StartPosition, SpelMessage.UnableToGrowCollectionUnknownElementType); - } - - try - { - int newElements = _index - _collection.Count; - - while (newElements >= 0) - { - // Insert a default value if the element type does not have a default constructor. - _collection.Add(GetDefaultValue(elemTypeDesc)); - newElements--; - } - } - catch (Exception ex) - { - throw new SpelEvaluationException(_indexer.StartPosition, ex, SpelMessage.UnableToGrowCollection); - } - } - } - - private object GetDefaultValue(Type elemTypeDesc) - { - if (elemTypeDesc == typeof(string)) - { - return string.Empty; - } - - if (elemTypeDesc == typeof(int)) - { - return 0; - } - - if (elemTypeDesc == typeof(short)) - { - return (short)0; - } - - if (elemTypeDesc == typeof(long)) - { - return 0L; - } - - if (elemTypeDesc == typeof(uint)) - { - return 0U; - } - - if (elemTypeDesc == typeof(ushort)) - { - return (ushort)0; - } - - if (elemTypeDesc == typeof(ulong)) - { - return 0UL; - } - - if (elemTypeDesc == typeof(byte)) - { - return (byte)0; - } - - if (elemTypeDesc == typeof(sbyte)) - { - return (sbyte)0; - } - - if (elemTypeDesc == typeof(char)) - { - return (char)0; - } - - return Activator.CreateInstance(elemTypeDesc); - } - } - - private sealed class StringIndexingLValue : IValueRef - { - private readonly string _target; - - private readonly int _index; - - private readonly Type _typeDescriptor; - private readonly Indexer _indexer; - - public bool IsWritable => true; - - public StringIndexingLValue(Indexer indexer, string target, int index, Type typeDescriptor) - { - _indexer = indexer; - _target = target; - _index = index; - _typeDescriptor = typeDescriptor; - } - - public ITypedValue GetValue() - { - if (_index >= _target.Length) - { - throw new SpelEvaluationException(_indexer.StartPosition, SpelMessage.StringIndexOutOfBounds, _target.Length, _index); - } - - return new TypedValue(_target[_index].ToString(CultureInfo.InvariantCulture)); - } - - public void SetValue(object newValue) - { - throw new SpelEvaluationException(_indexer.StartPosition, SpelMessage.IndexingNotSupportedForType, _typeDescriptor.ToString()); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/InlineList.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/InlineList.cs deleted file mode 100644 index d1bc0773d8..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/InlineList.cs +++ /dev/null @@ -1,212 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class InlineList : SpelNode -{ - private static readonly FieldInfo FieldInfo = typeof(CompiledExpression).GetField("DynamicFields", BindingFlags.NonPublic | BindingFlags.Instance); - private static readonly MethodInfo GetItemMethod = typeof(Dictionary).GetMethod("get_Item", BindingFlags.Public | BindingFlags.Instance); - - private static readonly MethodInfo AddMethod = typeof(IList).GetMethod(nameof(IList.Add), new[] - { - typeof(object) - }); - - private static readonly ConstructorInfo ListConstructor = typeof(List).GetConstructor(Type.EmptyTypes); - - // If the list is purely literals, it is a constant value and can be computed and cached - private ITypedValue _constant; - - public bool IsConstant => _constant != null; - - public InlineList(int startPos, int endPos, params SpelNode[] args) - : base(startPos, endPos, args) - { - CheckIfConstant(); - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - if (_constant != null) - { - return _constant; - } - - int childCount = ChildCount; - var returnValue = new List(childCount); - - for (int c = 0; c < childCount; c++) - { - returnValue.Add(GetChild(c).GetValue(state)); - } - - return new TypedValue(returnValue); - } - - public override string ToStringAst() - { - // String ast matches input string, not the 'toString()' of the resultant collection, which would use [] - var sj = new List(); - int count = ChildCount; - - for (int c = 0; c < count; c++) - { - sj.Add(GetChild(c).ToStringAst()); - } - - return $"{{{string.Join(",", sj)}}}"; - } - - public IList GetConstantValue() - { - if (_constant == null) - { - throw new InvalidOperationException("No constant"); - } - - return (IList)_constant.Value; - } - - public override bool IsCompilable() - { - return IsConstant; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - string constantFieldName = $"inlineList${cf.NextFieldId()}"; - cf.RegisterNewField(constantFieldName, new List()); - - cf.RegisterNewInitGenerator((initGenerator, flow) => - { - GenerateInitCode(constantFieldName, initGenerator, flow); - }); - - GenerateLoadListCode(gen, constantFieldName); - cf.PushDescriptor(new TypeDescriptor(typeof(IList))); - } - - public void GenerateInitCode(string constantFieldName, ILGenerator gen, CodeFlow codeFlow, bool nested = false) - { - LocalBuilder listLocal = null; - - if (!nested) - { - // Get list on stack - GenerateLoadListCode(gen, constantFieldName); - - // Save to local for easy access - listLocal = gen.DeclareLocal(typeof(IList)); - gen.Emit(OpCodes.Stloc, listLocal); - } - else - { - // Create nested list to work with - gen.Emit(OpCodes.Newobj, ListConstructor); - gen.Emit(OpCodes.Castclass, typeof(IList)); - } - - int childCount = ChildCount; - - for (int c = 0; c < childCount; c++) - { - if (!nested) - { - gen.Emit(OpCodes.Ldloc, listLocal); - } - else - { - gen.Emit(OpCodes.Dup); - } - - // The children might be further lists if they are not constants. In this - // situation do not call back into generateCode() because it will register another client adder. - // Instead, directly build the list here: - if (children[c] is InlineList list) - { - list.GenerateInitCode(constantFieldName, gen, codeFlow, true); - } - else - { - children[c].GenerateCode(gen, codeFlow); - TypeDescriptor lastDesc = codeFlow.LastDescriptor(); - - if (CodeFlow.IsValueType(lastDesc)) - { - CodeFlow.InsertBoxIfNecessary(gen, lastDesc); - } - } - - gen.Emit(OpCodes.Callvirt, AddMethod); - - // Ignore int return - gen.Emit(OpCodes.Pop); - } - } - - private void GenerateLoadListCode(ILGenerator gen, string constantFieldName) - { - // Load SpelCompiledExpression - gen.Emit(OpCodes.Ldarg_0); - - // Get Dictionary from CompiledExpression - gen.Emit(OpCodes.Ldfld, FieldInfo); - - // Get registered Field out of dictionary - gen.Emit(OpCodes.Ldstr, constantFieldName); - gen.Emit(OpCodes.Callvirt, GetItemMethod); - } - - private void CheckIfConstant() - { - bool isConstant = true; - - for (int c = 0, max = ChildCount; c < max; c++) - { - ISpelNode child = GetChild(c); - - if (child is not Literal) - { - if (child is InlineList inlineList) - { - if (!inlineList.IsConstant) - { - isConstant = false; - } - } - else - { - isConstant = false; - } - } - } - - if (isConstant) - { - var constantList = new List(); - int childCount = ChildCount; - - for (int c = 0; c < childCount; c++) - { - ISpelNode child = GetChild(c); - - if (child is Literal literal) - { - constantList.Add(literal.GetLiteralValue().Value); - } - else if (child is InlineList list) - { - constantList.Add(list.GetConstantValue()); - } - } - - _constant = new TypedValue(constantList.AsReadOnly()); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/InlineMap.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/InlineMap.cs deleted file mode 100644 index 74015236e8..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/InlineMap.cs +++ /dev/null @@ -1,164 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.ObjectModel; -using System.Text; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class InlineMap : SpelNode -{ - // If the map is purely literals, it is a constant value and can be computed and cached - private ITypedValue _constant; - - public bool IsConstant => _constant != null; - - public InlineMap(int startPos, int endPos, params SpelNode[] args) - : base(startPos, endPos, args) - { - CheckIfConstant(); - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - if (_constant != null) - { - return _constant; - } - - var returnValue = new Dictionary(); - int childCount = ChildCount; - - for (int c = 0; c < childCount; c++) - { - // Allow for key being PropertyOrFieldReference like Indexer on maps - ISpelNode keyChild = GetChild(c++); - object key = null; - - if (keyChild is PropertyOrFieldReference reference) - { - key = reference.Name; - } - else - { - key = keyChild.GetValue(state); - } - - object value = GetChild(c).GetValue(state); - returnValue[key] = value; - } - - return new TypedValue(returnValue); - } - - public override string ToStringAst() - { - var sb = new StringBuilder("{"); - int count = ChildCount; - - for (int c = 0; c < count; c++) - { - if (c > 0) - { - sb.Append(','); - } - - sb.Append(GetChild(c++).ToStringAst()); - sb.Append(':'); - sb.Append(GetChild(c).ToStringAst()); - } - - sb.Append('}'); - return sb.ToString(); - } - - public IDictionary GetConstantValue() - { - if (_constant == null) - { - throw new InvalidOperationException("No constant"); - } - - return (IDictionary)_constant.Value; - } - - private void CheckIfConstant() - { - bool isConstant = true; - - for (int c = 0, max = ChildCount; c < max; c++) - { - ISpelNode child = GetChild(c); - - if (child is not Literal) - { - if (child is InlineList inlineList) - { - if (!inlineList.IsConstant) - { - isConstant = false; - break; - } - } - else if (child is InlineMap inlineMap) - { - if (!inlineMap.IsConstant) - { - isConstant = false; - break; - } - } - else if (!(c % 2 == 0 && child is PropertyOrFieldReference)) - { - isConstant = false; - break; - } - } - } - - if (isConstant) - { - var constantMap = new Dictionary(); - int childCount = ChildCount; - - for (int c = 0; c < childCount; c++) - { - ISpelNode keyChild = GetChild(c++); - ISpelNode valueChild = GetChild(c); - object key = null; - object value = null; - - if (keyChild is Literal literal) - { - key = literal.GetLiteralValue().Value; - } - else if (keyChild is PropertyOrFieldReference reference) - { - key = reference.Name; - } - else - { - return; - } - - if (valueChild is Literal literal1) - { - value = literal1.GetLiteralValue().Value; - } - else if (valueChild is InlineList list) - { - value = list.GetConstantValue(); - } - else if (valueChild is InlineMap map) - { - value = map.GetConstantValue(); - } - - constantMap[key] = value; - } - - _constant = new TypedValue(new ReadOnlyDictionary(constantMap)); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/IntLiteral.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/IntLiteral.cs deleted file mode 100644 index 294761d495..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/IntLiteral.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class IntLiteral : Literal -{ - private readonly ITypedValue _value; - - public IntLiteral(string payload, int startPos, int endPos, int value) - : base(payload, startPos, endPos) - { - _value = new TypedValue(value); - exitTypeDescriptor = TypeDescriptor.I; - } - - public override ITypedValue GetLiteralValue() - { - return _value; - } - - public override bool IsCompilable() - { - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - object intVal = _value.Value; - - if (intVal == null) - { - throw new InvalidOperationException("No int value"); - } - - int intValue = (int)intVal; - - if (intValue == -1) - { - // Not sure we can get here because -1 is OpMinus - gen.Emit(OpCodes.Ldc_I4_M1); - } - else - { - gen.Emit(OpCodes.Ldc_I4, intValue); - } - - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/Literal.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/Literal.cs deleted file mode 100644 index e78c37149b..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/Literal.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public abstract class Literal : SpelNode -{ - public string OriginalValue { get; } - - protected Literal(string originalValue, int startPos, int endPos) - : base(startPos, endPos) - { - OriginalValue = originalValue; - } - - public static Literal GetIntLiteral(string numberToken, int startPos, int endPos, NumberStyles radix) - { - try - { - int value = int.Parse(numberToken, radix, CultureInfo.InvariantCulture); - - if (radix == NumberStyles.HexNumber && value < 0) - { - throw new InternalParseException( - new SpelParseException(startPos, new FormatException("Hex parse error"), SpelMessage.NotAnInteger, numberToken)); - } - - return new IntLiteral(numberToken, startPos, endPos, value); - } - catch (FormatException ex) - { - throw new InternalParseException(new SpelParseException(startPos, ex, SpelMessage.NotAnInteger, numberToken)); - } - } - - public static Literal GetLongLiteral(string numberToken, int startPos, int endPos, NumberStyles radix) - { - try - { - long value = long.Parse(numberToken, radix, CultureInfo.InvariantCulture); - - if (radix == NumberStyles.HexNumber && value < 0) - { - throw new InternalParseException(new SpelParseException(startPos, new FormatException("Hex parse error"), SpelMessage.NotALong, numberToken)); - } - - return new LongLiteral(numberToken, startPos, endPos, value); - } - catch (FormatException ex) - { - throw new InternalParseException(new SpelParseException(startPos, ex, SpelMessage.NotALong, numberToken)); - } - } - - public static Literal GetRealLiteral(string numberToken, int startPos, int endPos, bool isFloat) - { - try - { - string toParse = GetNumberLiteral(numberToken); - - if (isFloat) - { - float value = float.Parse(toParse, CultureInfo.InvariantCulture); - return new FloatLiteral(numberToken, startPos, endPos, value); - } - else - { - double value = double.Parse(toParse, CultureInfo.InvariantCulture); - return new RealLiteral(numberToken, startPos, endPos, value); - } - } - catch (FormatException ex) - { - throw new InternalParseException(new SpelParseException(startPos, ex, SpelMessage.NotAReal, numberToken)); - } - } - - public static string GetNumberLiteral(string numberToken) - { - if (numberToken[^1] == 'd' || numberToken[^1] == 'D' || numberToken[^1] == 'f' || numberToken[^1] == 'F') - { - return numberToken.Substring(0, numberToken.Length - 1); - } - - return numberToken; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - return GetLiteralValue(); - } - - public override string ToString() - { - object value = GetLiteralValue().Value; - - if (value is IFormattable formattable) - { - return formattable.ToString(null, CultureInfo.InvariantCulture); - } - - return value.ToString(); - } - - public override string ToStringAst() - { - return ToString(); - } - - public abstract ITypedValue GetLiteralValue(); -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/LongLiteral.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/LongLiteral.cs deleted file mode 100644 index ee84152ef5..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/LongLiteral.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class LongLiteral : Literal -{ - private readonly ITypedValue _value; - - public LongLiteral(string payload, int startPos, int endPos, long value) - : base(payload, startPos, endPos) - { - _value = new TypedValue(value); - exitTypeDescriptor = TypeDescriptor.J; - } - - public override ITypedValue GetLiteralValue() - { - return _value; - } - - public override bool IsCompilable() - { - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - gen.Emit(OpCodes.Ldc_I8, (long)_value.Value); - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/MethodReference.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/MethodReference.cs deleted file mode 100644 index 66fc9a1b0f..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/MethodReference.cs +++ /dev/null @@ -1,507 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class MethodReference : SpelNode -{ - private readonly bool _nullSafe; - - private TypeDescriptor _originalPrimitiveExitTypeDescriptor; - - private volatile CachedMethodExecutor _cachedExecutor; - - public string Name { get; } - - public MethodReference(bool nullSafe, string methodName, int startPos, int endPos, params SpelNode[] arguments) - : base(startPos, endPos, arguments) - { - Name = methodName; - _nullSafe = nullSafe; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - IEvaluationContext evaluationContext = state.EvaluationContext; - object value = state.GetActiveContextObject().Value; - Type targetType = state.GetActiveContextObject().TypeDescriptor; - object[] arguments = GetArguments(state); - ITypedValue result = GetValueInternal(evaluationContext, value, targetType, arguments); - UpdateExitTypeDescriptor(result.Value); - return result; - } - - public override string ToStringAst() - { - var sj = new List(); - - for (int i = 0; i < ChildCount; i++) - { - sj.Add(GetChild(i).ToStringAst()); - } - - return $"{Name}({string.Join(",", sj)})"; - } - - public override bool IsCompilable() - { - CachedMethodExecutor executorToCheck = _cachedExecutor; - - if (executorToCheck == null || executorToCheck.HasProxyTarget || executorToCheck.Get() is not ReflectiveMethodExecutor) - { - return false; - } - - foreach (SpelNode child in children) - { - if (!child.IsCompilable()) - { - return false; - } - } - - var executor = (ReflectiveMethodExecutor)executorToCheck.Get(); - - if (executor.DidArgumentConversionOccur) - { - return false; - } - - Type type = executor.Method.DeclaringType; - - if (!ReflectionHelper.IsPublic(type) && executor.GetPublicDeclaringClass() == null) - { - return false; - } - - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - MethodInfo method = GetTargetMethodAndType(out Type classType); - - if (method.IsStatic) - { - GenerateStaticMethodCode(gen, cf, method); - } - else - { - GenerateInstanceMethodCode(gen, cf, method, classType); - } - - cf.PushDescriptor(exitTypeDescriptor); - } - - protected internal override IValueRef GetValueRef(ExpressionState state) - { - object[] arguments = GetArguments(state); - - if (state.GetActiveContextObject().Value == null) - { - ThrowIfNotNullSafe(GetArgumentTypes(arguments)); - return NullValueRef.Instance; - } - - return new MethodValueRef(this, state, arguments); - } - - protected internal TypeDescriptor ComputeExitDescriptor(object result, Type propertyReturnType) - { - if (propertyReturnType.IsValueType) - { - return CodeFlow.ToDescriptor(propertyReturnType); - } - - return CodeFlow.ToDescriptorFromObject(result); - } - - private void GenerateStaticMethodCode(ILGenerator gen, CodeFlow cf, MethodInfo method) - { - TypeDescriptor stackDescriptor = cf.LastDescriptor(); - Label? skipIfNullTarget = null; - - if (_nullSafe) - { - skipIfNullTarget = GenerateNullCheckCode(gen); - } - - if (stackDescriptor != null) - { - // Something on the stack when nothing is needed - gen.Emit(OpCodes.Pop); - } - - GenerateCodeForArguments(gen, cf, method, children); - gen.Emit(OpCodes.Call, method); - - if (_originalPrimitiveExitTypeDescriptor != null) - { - // The output of the accessor will be a primitive but from the block above it might be null, - // so to have a 'common stack' element at skipIfNull target we need to box the primitive - CodeFlow.InsertBoxIfNecessary(gen, _originalPrimitiveExitTypeDescriptor); - } - - if (skipIfNullTarget.HasValue) - { - gen.MarkLabel(skipIfNullTarget.Value); - } - } - - private void GenerateInstanceMethodCode(ILGenerator gen, CodeFlow cf, MethodInfo targetMethod, Type targetType) - { - TypeDescriptor stackDescriptor = cf.LastDescriptor(); - - if (stackDescriptor == null) - { - // Nothing on the stack but something is needed - CodeFlow.LoadTarget(gen); - stackDescriptor = TypeDescriptor.Object; - } - - Label? skipIfNullTarget = null; - - if (_nullSafe) - { - skipIfNullTarget = GenerateNullCheckCode(gen); - } - - if (targetType.IsValueType) - { - if (stackDescriptor.IsBoxed || stackDescriptor.IsReferenceType) - { - gen.Emit(OpCodes.Unbox_Any, targetType); - } - - LocalBuilder local = gen.DeclareLocal(targetType); - gen.Emit(OpCodes.Stloc, local); - gen.Emit(OpCodes.Ldloca, local); - } - else - { - if (stackDescriptor.Value != targetType) - { - CodeFlow.InsertCastClass(gen, new TypeDescriptor(targetType)); - } - } - - GenerateCodeForArguments(gen, cf, targetMethod, children); - gen.Emit(targetType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, targetMethod); - - if (_originalPrimitiveExitTypeDescriptor != null) - { - // The output of the accessor will be a primitive but from the block above it might be null, - // so to have a 'common stack' element at skipIfNull target we need to box the primitive - CodeFlow.InsertBoxIfNecessary(gen, _originalPrimitiveExitTypeDescriptor); - } - - if (skipIfNullTarget.HasValue) - { - gen.MarkLabel(skipIfNullTarget.Value); - } - } - - private Label GenerateNullCheckCode(ILGenerator gen) - { - Label skipIfNullTarget = gen.DefineLabel(); - Label continueTarget = gen.DefineLabel(); - gen.Emit(OpCodes.Dup); - gen.Emit(OpCodes.Ldnull); - gen.Emit(OpCodes.Cgt_Un); - gen.Emit(OpCodes.Brtrue, continueTarget); - - // cast null on stack to result type - CodeFlow.InsertCastClass(gen, exitTypeDescriptor); - gen.Emit(OpCodes.Br, skipIfNullTarget); - gen.MarkLabel(continueTarget); - return skipIfNullTarget; - } - - private ITypedValue GetValueInternal(IEvaluationContext evaluationContext, object value, Type targetType, object[] arguments) - { - List argumentTypes = GetArgumentTypes(arguments); - - if (value == null) - { - ThrowIfNotNullSafe(argumentTypes); - return TypedValue.Null; - } - - IMethodExecutor executorToUse = GetCachedExecutor(evaluationContext, value, targetType, argumentTypes); - - if (executorToUse != null) - { - try - { - return executorToUse.Execute(evaluationContext, value, arguments); - } - catch (AccessException ex) - { - // Two reasons this can occur: - // 1. the method invoked actually threw a real exception - // 2. the method invoked was not passed the arguments it expected and - // has become 'stale' - - // In the first case we should not retry, in the second case we should see - // if there is a better suited method. - - // To determine the situation, the AccessException will contain a cause. - // If the cause is an InvocationTargetException, a user exception was - // thrown inside the method. Otherwise the method could not be invoked. - ThrowSimpleExceptionIfPossible(value, ex); - - // At this point we know it wasn't a user problem so worth a retry if a - // better candidate can be found. - _cachedExecutor = null; - } - } - - // either there was no accessor or it no longer existed - executorToUse = FindAccessorForMethod(argumentTypes, value, evaluationContext); - _cachedExecutor = new CachedMethodExecutor(executorToUse, value as Type, targetType, argumentTypes); - - try - { - return executorToUse.Execute(evaluationContext, value, arguments); - } - catch (AccessException ex) - { - // Same unwrapping exception handling as above in above catch block - ThrowSimpleExceptionIfPossible(value, ex); - throw new SpelEvaluationException(StartPosition, ex, SpelMessage.ExceptionDuringMethodInvocation, Name, value.GetType().FullName, ex.Message); - } - } - - private void ThrowIfNotNullSafe(IList argumentTypes) - { - if (!_nullSafe) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.MethodCallOnNullObjectNotAllowed, - FormatHelper.FormatMethodForMessage(Name, argumentTypes)); - } - } - - private void ThrowSimpleExceptionIfPossible(object value, AccessException ex) - { - if (ex.InnerException is TargetInvocationException) - { - Exception rootCause = ex.InnerException.InnerException; - - if (rootCause is SystemException exception) - { - throw exception; - } - - throw new ExpressionInvocationTargetException(StartPosition, - $"A problem occurred when trying to execute method '{Name}' on object of type [{value.GetType().FullName}]", rootCause); - } - } - - private object[] GetArguments(ExpressionState state) - { - object[] arguments = new object[ChildCount]; - - for (int i = 0; i < arguments.Length; i++) - { - // Make the root object the active context again for evaluating the parameter expressions - try - { - state.PushActiveContextObject(state.GetScopeRootContextObject()); - arguments[i] = children[i].GetValueInternal(state).Value; - } - finally - { - state.PopActiveContextObject(); - } - } - - return arguments; - } - - private List GetArgumentTypes(params object[] arguments) - { - var descriptors = new List(arguments.Length); - - foreach (object argument in arguments) - { - descriptors.Add(argument?.GetType()); - } - - return descriptors; - } - - private IMethodExecutor GetCachedExecutor(IEvaluationContext evaluationContext, object value, Type targetType, IList argumentTypes) - { - List methodResolvers = evaluationContext.MethodResolvers; - - if (methodResolvers.Count != 1 || methodResolvers[0] is not ReflectiveMethodResolver) - { - // Not a default ReflectiveMethodResolver - don't know whether caching is valid - return null; - } - - CachedMethodExecutor executorToCheck = _cachedExecutor; - - if (executorToCheck != null && executorToCheck.IsSuitable(value, targetType, argumentTypes)) - { - return executorToCheck.Get(); - } - - _cachedExecutor = null; - return null; - } - - private IMethodExecutor FindAccessorForMethod(List argumentTypes, object targetObject, IEvaluationContext evaluationContext) - { - AccessException accessException = null; - List methodResolvers = evaluationContext.MethodResolvers; - - foreach (IMethodResolver methodResolver in methodResolvers) - { - try - { - IMethodExecutor methodExecutor = methodResolver.Resolve(evaluationContext, targetObject, Name, argumentTypes); - - if (methodExecutor != null) - { - return methodExecutor; - } - } - catch (AccessException ex) - { - accessException = ex; - break; - } - } - - string method = FormatHelper.FormatMethodForMessage(Name, argumentTypes); - string className = FormatHelper.FormatClassNameForMessage(targetObject as Type ?? targetObject.GetType()); - - if (accessException != null) - { - throw new SpelEvaluationException(StartPosition, accessException, SpelMessage.ProblemLocatingMethod, method, className); - } - - throw new SpelEvaluationException(StartPosition, SpelMessage.MethodNotFound, method, className); - } - - private void UpdateExitTypeDescriptor(object result) - { - CachedMethodExecutor executorToCheck = _cachedExecutor; - - if (executorToCheck != null && executorToCheck.Get() is ReflectiveMethodExecutor executor) - { - MethodInfo method = executor.Method; - TypeDescriptor descriptor = ComputeExitDescriptor(result, method.ReturnType); - - if (_nullSafe && CodeFlow.IsValueType(descriptor)) - { - _originalPrimitiveExitTypeDescriptor = descriptor; - exitTypeDescriptor = CodeFlow.ToBoxedDescriptor(descriptor); - } - else - { - exitTypeDescriptor = descriptor; - } - } - } - - private MethodInfo GetTargetMethodAndType(out Type targetType) - { - var methodExecutor = (ReflectiveMethodExecutor)_cachedExecutor?.Get(); - - if (methodExecutor == null) - { - throw new InvalidOperationException($"No applicable cached executor found: {_cachedExecutor}"); - } - - MethodInfo method = methodExecutor.Method; - targetType = GetMethodTargetType(method, methodExecutor); - return method; - } - - private Type GetMethodTargetType(MethodInfo method, ReflectiveMethodExecutor methodExecutor) - { - if (ReflectionHelper.IsPublic(method.DeclaringType)) - { - return method.DeclaringType; - } - - return methodExecutor.GetPublicDeclaringClass(); - } - - private sealed class MethodValueRef : IValueRef - { - private readonly IEvaluationContext _evaluationContext; - private readonly object _value; - private readonly Type _targetType; - private readonly object[] _arguments; - private readonly MethodReference _methodReference; - - public bool IsWritable => false; - - public MethodValueRef(MethodReference methodReference, ExpressionState state, object[] arguments) - { - _methodReference = methodReference; - _evaluationContext = state.EvaluationContext; - _value = state.GetActiveContextObject().Value; - _targetType = state.GetActiveContextObject().TypeDescriptor; - _arguments = arguments; - } - - public ITypedValue GetValue() - { - ITypedValue result = _methodReference.GetValueInternal(_evaluationContext, _value, _targetType, _arguments); - _methodReference.UpdateExitTypeDescriptor(result.Value); - return result; - } - - public void SetValue(object newValue) - { - throw new InvalidOperationException(); - } - } - - private sealed class CachedMethodExecutor - { - private readonly IMethodExecutor _methodExecutor; - private readonly Type _staticClass; - private readonly Type _targetType; - private readonly List _argumentTypes; - - public bool HasProxyTarget => false; - - public CachedMethodExecutor(IMethodExecutor methodExecutor, Type staticClass, Type targetType, List argumentTypes) - { - _methodExecutor = methodExecutor; - _staticClass = staticClass; - _targetType = targetType; - _argumentTypes = argumentTypes; - } - - public bool IsSuitable(object value, Type targetType, IList argumentTypes) - { - return (_staticClass == null || _staticClass.Equals(value)) && _targetType == targetType && AreEqual(_argumentTypes, argumentTypes); - } - - public IMethodExecutor Get() - { - return _methodExecutor; - } - - private static bool AreEqual(IList list1, IList list2) - { - if (ReferenceEquals(list1, list2)) - { - return true; - } - - return list1 is not null && list1.SequenceEqual(list2); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/NullLiteral.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/NullLiteral.cs deleted file mode 100644 index 930ecd926d..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/NullLiteral.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class NullLiteral : Literal -{ - public NullLiteral(int startPos, int endPos) - : base(null, startPos, endPos) - { - exitTypeDescriptor = TypeDescriptor.Object; - } - - public override ITypedValue GetLiteralValue() - { - return TypedValue.Null; - } - - public override string ToString() - { - return "null"; - } - - public override bool IsCompilable() - { - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - gen.Emit(OpCodes.Ldnull); - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/NullValueRef.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/NullValueRef.cs deleted file mode 100644 index b073cc397d..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/NullValueRef.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class NullValueRef : IValueRef -{ - public static readonly NullValueRef Instance = new(); - - public bool IsWritable => false; - - public ITypedValue GetValue() - { - return TypedValue.Null; - } - - public void SetValue(object newValue) - { - // The exception position '0' isn't right but the overhead of creating - // instances of this per node (where the node is solely for error reporting) - // would be unfortunate. - throw new SpelEvaluationException(0, SpelMessage.NotAssignable, "null"); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpAnd.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpAnd.cs deleted file mode 100644 index 27052cbc41..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpAnd.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpAnd : Operator -{ - public OpAnd(int startPos, int endPos, params SpelNode[] operands) - : base("and", startPos, endPos, operands) - { - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - if (!GetBooleanValue(state, LeftOperand)) - { - // no need to evaluate right operand - return BooleanTypedValue.False; - } - - return BooleanTypedValue.ForValue(GetBooleanValue(state, RightOperand)); - } - - public override bool IsCompilable() - { - SpelNode left = LeftOperand; - SpelNode right = RightOperand; - - return left.IsCompilable() && right.IsCompilable() && CodeFlow.IsBooleanCompatible(left.ExitDescriptor) && - CodeFlow.IsBooleanCompatible(right.ExitDescriptor); - } - - /// - /// Pseudo: - /// - /// - /// - /// - /// - /// IL generator. - /// - /// - /// Code flow. - /// - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - Label elseTarget = gen.DefineLabel(); - Label endIfTarget = gen.DefineLabel(); - LocalBuilder result = gen.DeclareLocal(typeof(bool)); - - cf.EnterCompilationScope(); - LeftOperand.GenerateCode(gen, cf); - cf.UnboxBooleanIfNecessary(gen); - cf.ExitCompilationScope(); - gen.Emit(OpCodes.Brtrue, elseTarget); - gen.Emit(OpCodes.Ldc_I4_0); - gen.Emit(OpCodes.Stloc, result); - gen.Emit(OpCodes.Br, endIfTarget); - gen.MarkLabel(elseTarget); - cf.EnterCompilationScope(); - RightOperand.GenerateCode(gen, cf); - cf.UnboxBooleanIfNecessary(gen); - cf.ExitCompilationScope(); - gen.Emit(OpCodes.Stloc, result); - gen.MarkLabel(endIfTarget); - gen.Emit(OpCodes.Ldloc, result); - cf.PushDescriptor(exitTypeDescriptor); - } - - private bool GetBooleanValue(ExpressionState state, SpelNode operand) - { - try - { - bool value = operand.GetValue(state); - return value; - } - catch (SpelEvaluationException ex) - { - ex.Position = operand.StartPosition; - throw; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpDec.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpDec.cs deleted file mode 100644 index cf5cd476c3..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpDec.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpDec : Operator -{ - private readonly bool _postfix; // false means prefix - - public override SpelNode RightOperand => throw new InvalidOperationException("No right operand"); - - public OpDec(int startPos, int endPos, bool postfix, params SpelNode[] operands) - : base("--", startPos, endPos, operands) - { - _postfix = postfix; - - if (operands == null || operands.Length == 0) - { - throw new InvalidOperationException("Operands can not be empty"); - } - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - SpelNode operand = LeftOperand; - - // The operand is going to be read and then assigned to, we don't want to evaluate it twice. - IValueRef lvalue = operand.GetValueRef(state); - - ITypedValue operandTypedValue = lvalue.GetValue(); - object operandValue = operandTypedValue.Value; - ITypedValue returnValue = operandTypedValue; - ITypedValue newValue = null; - - if (IsNumber(operandValue)) - { - newValue = operandValue switch - { - decimal val => new TypedValue(val - 1M, operandTypedValue.TypeDescriptor), - double val => new TypedValue(val - 1.0d, operandTypedValue.TypeDescriptor), - float val => new TypedValue(val - 1.0f, operandTypedValue.TypeDescriptor), - long val => new TypedValue(val - 1L, operandTypedValue.TypeDescriptor), - int val => new TypedValue(val - 1, operandTypedValue.TypeDescriptor), - short val => new TypedValue((short)(val - 1), operandTypedValue.TypeDescriptor), - byte val => new TypedValue((byte)(val - 1), operandTypedValue.TypeDescriptor), - ulong val => new TypedValue(val - 1L, operandTypedValue.TypeDescriptor), - uint val => new TypedValue(val - 1, operandTypedValue.TypeDescriptor), - ushort val => new TypedValue((ushort)(val - 1), operandTypedValue.TypeDescriptor), - sbyte val => new TypedValue((sbyte)(val - 1), operandTypedValue.TypeDescriptor), - _ => null - }; - } - - if (newValue == null) - { - try - { - newValue = state.Operate(Operation.Subtract, returnValue.Value, 1); - } - catch (SpelEvaluationException ex) - { - if (Equals(ex.MessageCode, SpelMessage.OperatorNotSupportedBetweenTypes)) - { - // This means the operand is not decrementable - throw new SpelEvaluationException(operand.StartPosition, SpelMessage.OperandNotDecrementable, operand.ToStringAst()); - } - - throw; - } - } - - // set the new value - try - { - lvalue.SetValue(newValue.Value); - } - catch (SpelEvaluationException see) - { - // if unable to set the value the operand is not writable (e.g. 1-- ) - if (Equals(see.MessageCode, SpelMessage.SetValueNotSupported)) - { - throw new SpelEvaluationException(operand.StartPosition, SpelMessage.OperandNotDecrementable); - } - - throw; - } - - if (!_postfix) - { - // the return value is the new value, not the original value - returnValue = newValue; - } - - return returnValue; - } - - public override string ToStringAst() - { - return $"{LeftOperand.ToStringAst()}--"; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpDivide.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpDivide.cs deleted file mode 100644 index 12a39cc4bb..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpDivide.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpDivide : Operator -{ - public OpDivide(int startPos, int endPos, params SpelNode[] operands) - : base("/", startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object leftOperand = LeftOperand.GetValueInternal(state).Value; - object rightOperand = RightOperand.GetValueInternal(state).Value; - - if (IsNumber(leftOperand) && IsNumber(rightOperand)) - { - var leftConv = (IConvertible)leftOperand; - var rightConv = (IConvertible)rightOperand; - - if (leftOperand is decimal || rightOperand is decimal) - { - decimal leftVal = leftConv.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightConv.ToDecimal(CultureInfo.InvariantCulture); - return new TypedValue(leftVal / rightVal); - } - - if (leftOperand is double || rightOperand is double) - { - double leftVal = leftConv.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightConv.ToDouble(CultureInfo.InvariantCulture); - exitTypeDescriptor = TypeDescriptor.D; - return new TypedValue(leftVal / rightVal); - } - - if (leftOperand is float || rightOperand is float) - { - float leftVal = leftConv.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightConv.ToSingle(CultureInfo.InvariantCulture); - exitTypeDescriptor = TypeDescriptor.F; - return new TypedValue(leftVal / rightVal); - } - - // Look at need to add support for .NET types not present in Java, e.g. ulong, ushort, byte, uint - - if (leftOperand is long || rightOperand is long) - { - long leftVal = leftConv.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightConv.ToInt64(CultureInfo.InvariantCulture); - exitTypeDescriptor = TypeDescriptor.J; - return new TypedValue(leftVal / rightVal); - } - - if (CodeFlow.IsIntegerForNumericOp(leftOperand) || CodeFlow.IsIntegerForNumericOp(rightOperand)) - { - int leftVal = leftConv.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightConv.ToInt32(CultureInfo.InvariantCulture); - exitTypeDescriptor = TypeDescriptor.I; - return new TypedValue(leftVal / rightVal); - } - else - { - double leftVal = leftConv.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightConv.ToDouble(CultureInfo.InvariantCulture); - - // Unknown Number subtypes -> best guess is double division - // Look at need to add support for .NET types not present in Java, e.g. ulong, ushort, byte, uint - return new TypedValue(leftVal / rightVal); - } - } - - return state.Operate(Operation.Divide, leftOperand, rightOperand); - } - - public override bool IsCompilable() - { - if (!LeftOperand.IsCompilable()) - { - return false; - } - - if (children.Length > 1 && !RightOperand.IsCompilable()) - { - return false; - } - - return exitTypeDescriptor != null; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - LeftOperand.GenerateCode(gen, cf); - TypeDescriptor leftDesc = LeftOperand.ExitDescriptor; - TypeDescriptor exitDesc = exitTypeDescriptor; - - if (exitDesc == null) - { - throw new InvalidOperationException("No exit type descriptor"); - } - - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, leftDesc, exitDesc); - - if (children.Length > 1) - { - cf.EnterCompilationScope(); - RightOperand.GenerateCode(gen, cf); - TypeDescriptor rightDesc = RightOperand.ExitDescriptor; - cf.ExitCompilationScope(); - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, rightDesc, exitDesc); - - if (exitDesc != TypeDescriptor.I && exitDesc != TypeDescriptor.J && exitDesc != TypeDescriptor.F && exitDesc != TypeDescriptor.D) - { - throw new InvalidOperationException($"Unrecognized exit type descriptor: '{exitTypeDescriptor}'"); - } - - gen.Emit(OpCodes.Div); - } - - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpEQ.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpEQ.cs deleted file mode 100644 index 3510957ef5..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpEQ.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpEq : Operator -{ - public OpEq(int startPos, int endPos, params SpelNode[] operands) - : base("==", startPos, endPos, operands) - { - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object left = LeftOperand.GetValueInternal(state).Value; - object right = RightOperand.GetValueInternal(state).Value; - leftActualDescriptor = CodeFlow.ToDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.ToDescriptorFromObject(right); - return BooleanTypedValue.ForValue(EqualityCheck(state.EvaluationContext, left, right)); - } - - // This check is different to the one in the other numeric operators (OpLt/etc) - // because it allows for simple object comparison - public override bool IsCompilable() - { - SpelNode left = LeftOperand; - SpelNode right = RightOperand; - - if (!left.IsCompilable() || !right.IsCompilable()) - { - return false; - } - - TypeDescriptor leftDesc = left.ExitDescriptor; - TypeDescriptor rightDesc = right.ExitDescriptor; - DescriptorComparison dc = DescriptorComparison.CheckNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor); - return !dc.AreNumbers || dc.AreCompatible; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - CodeFlow.LoadEvaluationContext(gen); - TypeDescriptor leftDesc = LeftOperand.ExitDescriptor; - TypeDescriptor rightDesc = RightOperand.ExitDescriptor; - bool leftPrim = CodeFlow.IsValueType(leftDesc); - bool rightPrim = CodeFlow.IsValueType(rightDesc); - - cf.EnterCompilationScope(); - LeftOperand.GenerateCode(gen, cf); - cf.ExitCompilationScope(); - - if (leftPrim) - { - CodeFlow.InsertBoxIfNecessary(gen, leftDesc); - } - - cf.EnterCompilationScope(); - RightOperand.GenerateCode(gen, cf); - cf.ExitCompilationScope(); - - if (rightPrim) - { - CodeFlow.InsertBoxIfNecessary(gen, rightDesc); - } - - gen.Emit(OpCodes.Call, EqualityCheckMethod); - cf.PushDescriptor(TypeDescriptor.Z); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpGE.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpGE.cs deleted file mode 100644 index 8071c2581b..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpGE.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpGe : Operator -{ - public OpGe(int startPos, int endPos, params SpelNode[] operands) - : base(">=", startPos, endPos, operands) - { - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object left = LeftOperand.GetValueInternal(state).Value; - object right = RightOperand.GetValueInternal(state).Value; - - leftActualDescriptor = CodeFlow.ToDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.ToDescriptorFromObject(right); - - if (IsNumber(left) && IsNumber(right)) - { - var leftConv = (IConvertible)left; - var rightConv = (IConvertible)right; - - if (left is decimal || right is decimal) - { - decimal leftVal = leftConv.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightConv.ToDecimal(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal.CompareTo(rightVal) >= 0); - } - - if (left is double || right is double) - { - double leftVal = leftConv.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightConv.ToDouble(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - - if (left is float || right is float) - { - float leftVal = leftConv.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightConv.ToSingle(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - - if (left is long || right is long) - { - long leftVal = leftConv.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightConv.ToInt64(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - - if (left is int || right is int) - { - int leftVal = leftConv.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightConv.ToInt32(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - - if (left is short || right is short) - { - short leftVal = leftConv.ToInt16(CultureInfo.InvariantCulture); - short rightVal = rightConv.ToInt16(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - - if (left is byte || right is byte) - { - byte leftVal = leftConv.ToByte(CultureInfo.InvariantCulture); - byte rightVal = rightConv.ToByte(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - - if (left is ulong || right is ulong) - { - ulong leftVal = leftConv.ToUInt64(CultureInfo.InvariantCulture); - ulong rightVal = rightConv.ToUInt64(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - - if (left is uint || right is uint) - { - uint leftVal = leftConv.ToUInt32(CultureInfo.InvariantCulture); - uint rightVal = rightConv.ToUInt32(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - - if (left is ushort || right is ushort) - { - ushort leftVal = leftConv.ToUInt16(CultureInfo.InvariantCulture); - ushort rightVal = rightConv.ToUInt16(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - - if (left is sbyte || right is sbyte) - { - sbyte leftVal = leftConv.ToSByte(CultureInfo.InvariantCulture); - sbyte rightVal = rightConv.ToSByte(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal >= rightVal); - } - } - - return BooleanTypedValue.ForValue(state.TypeComparator.Compare(left, right) >= 0); - } - - public override bool IsCompilable() - { - return IsCompilableOperatorUsingNumerics(); - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - GenerateComparisonCode(gen, cf, OpCodes.Blt); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpGT.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpGT.cs deleted file mode 100644 index dd4adc1834..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpGT.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpGt : Operator -{ - public OpGt(int startPos, int endPos, params SpelNode[] operands) - : base(">", startPos, endPos, operands) - { - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object left = LeftOperand.GetValueInternal(state).Value; - object right = RightOperand.GetValueInternal(state).Value; - - leftActualDescriptor = CodeFlow.ToDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.ToDescriptorFromObject(right); - - if (IsNumber(left) && IsNumber(right)) - { - var leftConv = (IConvertible)left; - var rightConv = (IConvertible)right; - - if (left is decimal || right is decimal) - { - decimal leftVal = leftConv.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightConv.ToDecimal(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal.CompareTo(rightVal) > 0); - } - - if (left is double || right is double) - { - double leftVal = leftConv.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightConv.ToDouble(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - - if (left is float || right is float) - { - float leftVal = leftConv.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightConv.ToSingle(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - - if (left is long || right is long) - { - long leftVal = leftConv.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightConv.ToInt64(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - - if (left is int || right is int) - { - int leftVal = leftConv.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightConv.ToInt32(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - - if (left is short || right is short) - { - short leftVal = leftConv.ToInt16(CultureInfo.InvariantCulture); - short rightVal = rightConv.ToInt16(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - - if (left is byte || right is byte) - { - byte leftVal = leftConv.ToByte(CultureInfo.InvariantCulture); - byte rightVal = rightConv.ToByte(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - - if (left is ulong || right is ulong) - { - ulong leftVal = leftConv.ToUInt64(CultureInfo.InvariantCulture); - ulong rightVal = rightConv.ToUInt64(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - - if (left is uint || right is uint) - { - uint leftVal = leftConv.ToUInt32(CultureInfo.InvariantCulture); - uint rightVal = rightConv.ToUInt32(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - - if (left is ushort || right is ushort) - { - ushort leftVal = leftConv.ToUInt16(CultureInfo.InvariantCulture); - ushort rightVal = rightConv.ToUInt16(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - - if (left is sbyte || right is sbyte) - { - sbyte leftVal = leftConv.ToSByte(CultureInfo.InvariantCulture); - sbyte rightVal = rightConv.ToSByte(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal > rightVal); - } - } - - return BooleanTypedValue.ForValue(state.TypeComparator.Compare(left, right) > 0); - } - - public override bool IsCompilable() - { - return IsCompilableOperatorUsingNumerics(); - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - GenerateComparisonCode(gen, cf, OpCodes.Ble); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpInc.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpInc.cs deleted file mode 100644 index af07de6101..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpInc.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpInc : Operator -{ - private readonly bool _postfix; // false means prefix - - public override SpelNode RightOperand => throw new InvalidOperationException("No right operand"); - - public OpInc(int startPos, int endPos, bool postfix, params SpelNode[] operands) - : base("++", startPos, endPos, operands) - { - _postfix = postfix; - - if (operands == null || operands.Length == 0) - { - throw new InvalidOperationException("Operands must not be empty"); - } - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - SpelNode operand = LeftOperand; - IValueRef valueRef = operand.GetValueRef(state); - - ITypedValue typedValue = valueRef.GetValue(); - object value = typedValue.Value; - ITypedValue returnValue = typedValue; - ITypedValue newValue = null; - - if (IsNumber(value)) - { - newValue = (IConvertible)value switch - { - decimal val => new TypedValue(val + 1M, typedValue.TypeDescriptor), - double val => new TypedValue(val + 1.0d, typedValue.TypeDescriptor), - float val => new TypedValue(val + 1.0f, typedValue.TypeDescriptor), - long val => new TypedValue(val + 1L, typedValue.TypeDescriptor), - int val => new TypedValue(val + 1, typedValue.TypeDescriptor), - short val => new TypedValue((short)(val + 1), typedValue.TypeDescriptor), - byte val => new TypedValue((byte)(val + 1), typedValue.TypeDescriptor), - ulong val => new TypedValue(val + 1UL, typedValue.TypeDescriptor), - uint val => new TypedValue(val + 1U, typedValue.TypeDescriptor), - ushort val => new TypedValue((ushort)(val + 1), typedValue.TypeDescriptor), - sbyte val => new TypedValue((sbyte)(val + 1), typedValue.TypeDescriptor), - _ => null - }; - } - - if (newValue == null) - { - try - { - newValue = state.Operate(Operation.Add, returnValue.Value, 1); - } - catch (SpelEvaluationException ex) - { - if (Equals(ex.MessageCode, SpelMessage.OperatorNotSupportedBetweenTypes)) - { - // This means the operand is not incrementable - throw new SpelEvaluationException(operand.StartPosition, SpelMessage.OperandNotIncrementable, operand.ToStringAst()); - } - - throw; - } - } - - // set the name value - try - { - valueRef.SetValue(newValue.Value); - } - catch (SpelEvaluationException see) - { - // If unable to set the value the operand is not writable (e.g. unary increment) - if (Equals(see.MessageCode, SpelMessage.SetValueNotSupported)) - { - throw new SpelEvaluationException(operand.StartPosition, SpelMessage.OperandNotIncrementable); - } - - throw; - } - - if (!_postfix) - { - // The return value is the new value, not the original value - returnValue = newValue; - } - - return returnValue; - } - - public override string ToStringAst() - { - return $"{LeftOperand.ToStringAst()}++"; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpLE.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpLE.cs deleted file mode 100644 index 36c517ac24..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpLE.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpLe : Operator -{ - public OpLe(int startPos, int endPos, params SpelNode[] operands) - : base("<=", startPos, endPos, operands) - { - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object left = LeftOperand.GetValueInternal(state).Value; - object right = RightOperand.GetValueInternal(state).Value; - - leftActualDescriptor = CodeFlow.ToDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.ToDescriptorFromObject(right); - - if (IsNumber(left) && IsNumber(right)) - { - var leftConv = (IConvertible)left; - var rightConv = (IConvertible)right; - - if (left is decimal || right is decimal) - { - decimal leftVal = leftConv.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightConv.ToDecimal(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal.CompareTo(rightVal) <= 0); - } - - if (left is double || right is double) - { - double leftVal = leftConv.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightConv.ToDouble(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - - if (left is float || right is float) - { - float leftVal = leftConv.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightConv.ToSingle(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - - if (left is long || right is long) - { - long leftVal = leftConv.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightConv.ToInt64(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - - if (left is int || right is int) - { - int leftVal = leftConv.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightConv.ToInt32(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - - if (left is short || right is short) - { - short leftVal = leftConv.ToInt16(CultureInfo.InvariantCulture); - short rightVal = rightConv.ToInt16(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - - if (left is byte || right is byte) - { - byte leftVal = leftConv.ToByte(CultureInfo.InvariantCulture); - byte rightVal = rightConv.ToByte(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - - if (left is ulong || right is ulong) - { - ulong leftVal = leftConv.ToUInt64(CultureInfo.InvariantCulture); - ulong rightVal = rightConv.ToUInt64(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - - if (left is uint || right is uint) - { - uint leftVal = leftConv.ToUInt32(CultureInfo.InvariantCulture); - uint rightVal = rightConv.ToUInt32(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - - if (left is ushort || right is ushort) - { - ushort leftVal = leftConv.ToUInt16(CultureInfo.InvariantCulture); - ushort rightVal = rightConv.ToUInt16(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - - if (left is sbyte || right is sbyte) - { - sbyte leftVal = leftConv.ToSByte(CultureInfo.InvariantCulture); - sbyte rightVal = rightConv.ToSByte(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal <= rightVal); - } - } - - return BooleanTypedValue.ForValue(state.TypeComparator.Compare(left, right) <= 0); - } - - public override bool IsCompilable() - { - return IsCompilableOperatorUsingNumerics(); - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - GenerateComparisonCode(gen, cf, OpCodes.Bgt); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpLT.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpLT.cs deleted file mode 100644 index e5da68a03e..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpLT.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpLt : Operator -{ - public OpLt(int startPos, int endPos, params SpelNode[] operands) - : base("<", startPos, endPos, operands) - { - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object left = LeftOperand.GetValueInternal(state).Value; - object right = RightOperand.GetValueInternal(state).Value; - - leftActualDescriptor = CodeFlow.ToDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.ToDescriptorFromObject(right); - - if (IsNumber(left) && IsNumber(right)) - { - var leftConv = (IConvertible)left; - var rightConv = (IConvertible)right; - - if (left is decimal || right is decimal) - { - decimal leftVal = leftConv.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightConv.ToDecimal(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal.CompareTo(rightVal) < 0); - } - - if (left is double || right is double) - { - double leftVal = leftConv.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightConv.ToDouble(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - - if (left is float || right is float) - { - float leftVal = leftConv.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightConv.ToSingle(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - - if (left is long || right is long) - { - long leftVal = leftConv.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightConv.ToInt64(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - - if (left is int || right is int) - { - int leftVal = leftConv.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightConv.ToInt32(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - - if (left is short || right is short) - { - short leftVal = leftConv.ToInt16(CultureInfo.InvariantCulture); - short rightVal = rightConv.ToInt16(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - - if (left is byte || right is byte) - { - byte leftVal = leftConv.ToByte(CultureInfo.InvariantCulture); - byte rightVal = rightConv.ToByte(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - - if (left is ulong || right is ulong) - { - ulong leftVal = leftConv.ToUInt64(CultureInfo.InvariantCulture); - ulong rightVal = rightConv.ToUInt64(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - - if (left is uint || right is uint) - { - uint leftVal = leftConv.ToUInt32(CultureInfo.InvariantCulture); - uint rightVal = rightConv.ToUInt32(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - - if (left is ushort || right is ushort) - { - ushort leftVal = leftConv.ToUInt16(CultureInfo.InvariantCulture); - ushort rightVal = rightConv.ToUInt16(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - - if (left is sbyte || right is sbyte) - { - sbyte leftVal = leftConv.ToSByte(CultureInfo.InvariantCulture); - sbyte rightVal = rightConv.ToSByte(CultureInfo.InvariantCulture); - return BooleanTypedValue.ForValue(leftVal < rightVal); - } - } - - return BooleanTypedValue.ForValue(state.TypeComparator.Compare(left, right) < 0); - } - - public override bool IsCompilable() - { - return IsCompilableOperatorUsingNumerics(); - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - GenerateComparisonCode(gen, cf, OpCodes.Bge); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpMinus.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpMinus.cs deleted file mode 100644 index 433cdd250b..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpMinus.cs +++ /dev/null @@ -1,199 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpMinus : Operator -{ - public override SpelNode RightOperand - { - get - { - if (children.Length < 2) - { - throw new InvalidOperationException("No right operand"); - } - - return children[1]; - } - } - - public OpMinus(int startPos, int endPos, params SpelNode[] operands) - : base("-", startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - SpelNode leftOp = LeftOperand; - - if (children.Length < 2) - { - // if only one operand, then this is unary minus - object operand = leftOp.GetValueInternal(state).Value; - - if (IsNumber(operand)) - { - switch (operand) - { - case decimal val: - return new TypedValue(0M - val); - case double val: - exitTypeDescriptor = TypeDescriptor.D; - return new TypedValue(0d - val); - case float val: - exitTypeDescriptor = TypeDescriptor.F; - return new TypedValue(0f - val); - case long val: - exitTypeDescriptor = TypeDescriptor.J; - return new TypedValue(0L - val); - case int val: - exitTypeDescriptor = TypeDescriptor.I; - return new TypedValue(0 - val); - case short val: - return new TypedValue(0 - val); - case byte val: - return new TypedValue(0 - val); - case ulong val: - return new TypedValue(0UL - val); - case uint val: - return new TypedValue(0U - val); - case ushort val: - return new TypedValue(0 - val); - case sbyte val: - return new TypedValue(0 - val); - default: - return state.Operate(Operation.Subtract, operand, null); - } - } - - return state.Operate(Operation.Subtract, operand, null); - } - - object left = leftOp.GetValueInternal(state).Value; - object right = RightOperand.GetValueInternal(state).Value; - - if (IsNumber(left) && IsNumber(right)) - { - var leftNumber = (IConvertible)left; - var rightNumber = (IConvertible)right; - - if (leftNumber is decimal || rightNumber is decimal) - { - decimal leftVal = leftNumber.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightNumber.ToDecimal(CultureInfo.InvariantCulture); - return new TypedValue(leftVal - rightVal); - } - - if (leftNumber is double || rightNumber is double) - { - exitTypeDescriptor = TypeDescriptor.D; - double leftVal = leftNumber.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightNumber.ToDouble(CultureInfo.InvariantCulture); - return new TypedValue(leftVal - rightVal); - } - - if (leftNumber is float || rightNumber is float) - { - exitTypeDescriptor = TypeDescriptor.F; - float leftVal = leftNumber.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightNumber.ToSingle(CultureInfo.InvariantCulture); - return new TypedValue(leftVal - rightVal); - } - - if (leftNumber is long || rightNumber is long) - { - exitTypeDescriptor = TypeDescriptor.J; - long leftVal = leftNumber.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightNumber.ToInt64(CultureInfo.InvariantCulture); - return new TypedValue(leftVal - rightVal); - } - - if (CodeFlow.IsIntegerForNumericOp(leftNumber) || CodeFlow.IsIntegerForNumericOp(rightNumber)) - { - exitTypeDescriptor = TypeDescriptor.I; - int leftVal = leftNumber.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightNumber.ToInt32(CultureInfo.InvariantCulture); - return new TypedValue(leftVal - rightVal); - } - else - { - // Unknown Number subtypes -> best guess is double subtraction - double leftVal = leftNumber.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightNumber.ToDouble(CultureInfo.InvariantCulture); - return new TypedValue(leftVal - rightVal); - } - } - - if (left is string str && right is int integer && str.Length == 1) - { - string theString = str; - int theInteger = integer; - - // Implements character - int (ie. b - 1 = a) - return new TypedValue(((char)(theString[0] - theInteger)).ToString(CultureInfo.InvariantCulture)); - } - - return state.Operate(Operation.Subtract, left, right); - } - - public override string ToStringAst() - { - if (children.Length < 2) - { - // unary minus - return $"-{LeftOperand.ToStringAst()}"; - } - - return base.ToStringAst(); - } - - public override bool IsCompilable() - { - if (!LeftOperand.IsCompilable()) - { - return false; - } - - if (children.Length > 1 && !RightOperand.IsCompilable()) - { - return false; - } - - return exitTypeDescriptor != null; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - LeftOperand.GenerateCode(gen, cf); - TypeDescriptor leftDesc = LeftOperand.ExitDescriptor; - TypeDescriptor exitDesc = exitTypeDescriptor; - - if (exitDesc == null) - { - throw new InvalidOperationException("No exit type descriptor"); - } - - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, leftDesc, exitDesc); - - if (children.Length > 1) - { - cf.EnterCompilationScope(); - RightOperand.GenerateCode(gen, cf); - TypeDescriptor rightDesc = RightOperand.ExitDescriptor; - cf.ExitCompilationScope(); - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, rightDesc, exitDesc); - gen.Emit(OpCodes.Sub); - } - else - { - gen.Emit(OpCodes.Neg); - } - - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpModulus.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpModulus.cs deleted file mode 100644 index b4a26220cd..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpModulus.cs +++ /dev/null @@ -1,117 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpModulus : Operator -{ - public OpModulus(int startPos, int endPos, params SpelNode[] operands) - : base("%", startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object leftOperand = LeftOperand.GetValueInternal(state).Value; - object rightOperand = RightOperand.GetValueInternal(state).Value; - - if (IsNumber(leftOperand) && IsNumber(rightOperand)) - { - var leftNumber = (IConvertible)leftOperand; - var rightNumber = (IConvertible)rightOperand; - - if (leftNumber is decimal || rightNumber is decimal) - { - decimal leftVal = leftNumber.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightNumber.ToDecimal(CultureInfo.InvariantCulture); - return new TypedValue(leftVal % rightVal); - } - - if (leftNumber is double || rightNumber is double) - { - exitTypeDescriptor = TypeDescriptor.D; - double leftVal = leftNumber.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightNumber.ToDouble(CultureInfo.InvariantCulture); - return new TypedValue(leftVal % rightVal); - } - - if (leftNumber is float || rightNumber is float) - { - exitTypeDescriptor = TypeDescriptor.F; - float leftVal = leftNumber.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightNumber.ToSingle(CultureInfo.InvariantCulture); - return new TypedValue(leftVal % rightVal); - } - - if (leftNumber is long || rightNumber is long) - { - exitTypeDescriptor = TypeDescriptor.J; - long leftVal = leftNumber.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightNumber.ToInt64(CultureInfo.InvariantCulture); - return new TypedValue(leftVal % rightVal); - } - - if (CodeFlow.IsIntegerForNumericOp(leftNumber) || CodeFlow.IsIntegerForNumericOp(rightNumber)) - { - exitTypeDescriptor = TypeDescriptor.I; - int leftVal = leftNumber.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightNumber.ToInt32(CultureInfo.InvariantCulture); - return new TypedValue(leftVal % rightVal); - } - else - { - // Unknown Number subtypes -> best guess is double division - double leftVal = leftNumber.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightNumber.ToDouble(CultureInfo.InvariantCulture); - return new TypedValue(leftVal % rightVal); - } - } - - return state.Operate(Operation.Modulus, leftOperand, rightOperand); - } - - public override bool IsCompilable() - { - if (!LeftOperand.IsCompilable()) - { - return false; - } - - if (children.Length > 1 && !RightOperand.IsCompilable()) - { - return false; - } - - return exitTypeDescriptor != null; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - LeftOperand.GenerateCode(gen, cf); - TypeDescriptor leftDesc = LeftOperand.ExitDescriptor; - TypeDescriptor exitDesc = exitTypeDescriptor; - - if (exitDesc == null) - { - throw new InvalidOperationException("No exit type descriptor"); - } - - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, leftDesc, exitDesc); - - if (children.Length > 1) - { - cf.EnterCompilationScope(); - RightOperand.GenerateCode(gen, cf); - TypeDescriptor rightDesc = RightOperand.ExitDescriptor; - cf.ExitCompilationScope(); - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, rightDesc, exitDesc); - gen.Emit(OpCodes.Rem); - } - - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpMultiply.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpMultiply.cs deleted file mode 100644 index 75e84966de..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpMultiply.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection.Emit; -using System.Text; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpMultiply : Operator -{ - public OpMultiply(int startPos, int endPos, params SpelNode[] operands) - : base("*", startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object leftOperand = LeftOperand.GetValueInternal(state).Value; - object rightOperand = RightOperand.GetValueInternal(state).Value; - - if (IsNumber(leftOperand) && IsNumber(rightOperand)) - { - var leftNumber = (IConvertible)leftOperand; - var rightNumber = (IConvertible)rightOperand; - - if (leftNumber is decimal || rightNumber is decimal) - { - decimal leftVal = leftNumber.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightNumber.ToDecimal(CultureInfo.InvariantCulture); - return new TypedValue(leftVal * rightVal); - } - - if (leftNumber is double || rightNumber is double) - { - exitTypeDescriptor = TypeDescriptor.D; - double leftVal = leftNumber.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightNumber.ToDouble(CultureInfo.InvariantCulture); - return new TypedValue(leftVal * rightVal); - } - - if (leftNumber is float || rightNumber is float) - { - exitTypeDescriptor = TypeDescriptor.F; - float leftVal = leftNumber.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightNumber.ToSingle(CultureInfo.InvariantCulture); - return new TypedValue(leftVal * rightVal); - } - - if (leftNumber is long || rightNumber is long) - { - exitTypeDescriptor = TypeDescriptor.J; - long leftVal = leftNumber.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightNumber.ToInt64(CultureInfo.InvariantCulture); - return new TypedValue(leftVal * rightVal); - } - - if (CodeFlow.IsIntegerForNumericOp(leftNumber) || CodeFlow.IsIntegerForNumericOp(rightNumber)) - { - exitTypeDescriptor = TypeDescriptor.I; - int leftVal = leftNumber.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightNumber.ToInt32(CultureInfo.InvariantCulture); - return new TypedValue(leftVal * rightVal); - } - else - { - // Unknown Number subtypes -> best guess is double multiplication - double leftVal = leftNumber.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightNumber.ToDouble(CultureInfo.InvariantCulture); - return new TypedValue(leftVal * rightVal); - } - } - - if (leftOperand is string && rightOperand is int integer) - { - int repeats = integer; - var result = new StringBuilder(); - - for (int i = 0; i < repeats; i++) - { - result.Append(leftOperand); - } - - return new TypedValue(result.ToString()); - } - - return state.Operate(Operation.Multiply, leftOperand, rightOperand); - } - - public override bool IsCompilable() - { - if (!LeftOperand.IsCompilable()) - { - return false; - } - - if (children.Length > 1 && !RightOperand.IsCompilable()) - { - return false; - } - - return exitTypeDescriptor != null; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - LeftOperand.GenerateCode(gen, cf); - TypeDescriptor leftDesc = LeftOperand.ExitDescriptor; - TypeDescriptor exitDesc = exitTypeDescriptor; - - if (exitDesc == null) - { - throw new InvalidOperationException("No exit type descriptor"); - } - - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, leftDesc, exitDesc); - - if (children.Length > 1) - { - cf.EnterCompilationScope(); - RightOperand.GenerateCode(gen, cf); - TypeDescriptor rightDesc = RightOperand.ExitDescriptor; - cf.ExitCompilationScope(); - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, rightDesc, exitDesc); - gen.Emit(OpCodes.Mul); - } - - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpNE.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpNE.cs deleted file mode 100644 index 067d399d99..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpNE.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpNe : Operator -{ - public OpNe(int startPos, int endPos, params SpelNode[] operands) - : base("!=", startPos, endPos, operands) - { - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object leftValue = LeftOperand.GetValueInternal(state).Value; - object rightValue = RightOperand.GetValueInternal(state).Value; - leftActualDescriptor = CodeFlow.ToDescriptorFromObject(leftValue); - rightActualDescriptor = CodeFlow.ToDescriptorFromObject(rightValue); - return BooleanTypedValue.ForValue(!EqualityCheck(state.EvaluationContext, leftValue, rightValue)); - } - - // This check is different to the one in the other numeric operators (OpLt/etc) - // because we allow simple object comparison - public override bool IsCompilable() - { - SpelNode left = LeftOperand; - SpelNode right = RightOperand; - - if (!left.IsCompilable() || !right.IsCompilable()) - { - return false; - } - - TypeDescriptor leftDesc = left.ExitDescriptor; - TypeDescriptor rightDesc = right.ExitDescriptor; - DescriptorComparison dc = DescriptorComparison.CheckNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor); - return !dc.AreNumbers || dc.AreCompatible; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - CodeFlow.LoadEvaluationContext(gen); - TypeDescriptor leftDesc = LeftOperand.ExitDescriptor; - TypeDescriptor rightDesc = RightOperand.ExitDescriptor; - bool leftPrim = CodeFlow.IsValueType(leftDesc); - bool rightPrim = CodeFlow.IsValueType(rightDesc); - - cf.EnterCompilationScope(); - LeftOperand.GenerateCode(gen, cf); - cf.ExitCompilationScope(); - - if (leftPrim) - { - CodeFlow.InsertBoxIfNecessary(gen, leftDesc); - } - - cf.EnterCompilationScope(); - RightOperand.GenerateCode(gen, cf); - cf.ExitCompilationScope(); - - if (rightPrim) - { - CodeFlow.InsertBoxIfNecessary(gen, rightDesc); - } - - // returns bool - gen.Emit(OpCodes.Call, EqualityCheckMethod); - - // Invert the boolean - LocalBuilder result = gen.DeclareLocal(typeof(bool)); - gen.Emit(OpCodes.Ldc_I4_0); - gen.Emit(OpCodes.Ceq); - gen.Emit(OpCodes.Stloc, result); - gen.Emit(OpCodes.Ldloc, result); - - cf.PushDescriptor(TypeDescriptor.Z); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpOr.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpOr.cs deleted file mode 100644 index b71ae43edb..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpOr.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpOr : Operator -{ - public OpOr(int startPos, int endPos, params SpelNode[] operands) - : base("or", startPos, endPos, operands) - { - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - if (GetBooleanValue(state, LeftOperand)) - { - // no need to evaluate right operand - return BooleanTypedValue.True; - } - - return BooleanTypedValue.ForValue(GetBooleanValue(state, RightOperand)); - } - - public override bool IsCompilable() - { - SpelNode left = LeftOperand; - SpelNode right = RightOperand; - - return left.IsCompilable() && right.IsCompilable() && CodeFlow.IsBooleanCompatible(left.ExitDescriptor) && - CodeFlow.IsBooleanCompatible(right.ExitDescriptor); - } - - /// - /// Pseudo: - /// - /// - /// - /// - /// - /// IL generator. - /// - /// - /// Code flow. - /// - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - Label elseTarget = gen.DefineLabel(); - Label endIfTarget = gen.DefineLabel(); - LocalBuilder result = gen.DeclareLocal(typeof(bool)); - - cf.EnterCompilationScope(); - LeftOperand.GenerateCode(gen, cf); - cf.UnboxBooleanIfNecessary(gen); - cf.ExitCompilationScope(); - gen.Emit(OpCodes.Brfalse, elseTarget); - gen.Emit(OpCodes.Ldc_I4_1); - gen.Emit(OpCodes.Stloc, result); - gen.Emit(OpCodes.Br, endIfTarget); - gen.MarkLabel(elseTarget); - cf.EnterCompilationScope(); - RightOperand.GenerateCode(gen, cf); - cf.UnboxBooleanIfNecessary(gen); - cf.ExitCompilationScope(); - gen.Emit(OpCodes.Stloc, result); - gen.MarkLabel(endIfTarget); - gen.Emit(OpCodes.Ldloc, result); - cf.PushDescriptor(exitTypeDescriptor); - } - - private bool GetBooleanValue(ExpressionState state, SpelNode operand) - { - try - { - bool value = operand.GetValue(state); - return value; - } - catch (SpelEvaluationException ee) - { - ee.Position = operand.StartPosition; - throw; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpPlus.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OpPlus.cs deleted file mode 100644 index c1367afab9..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OpPlus.cs +++ /dev/null @@ -1,248 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OpPlus : Operator -{ - private static readonly MethodInfo AppendStringMethod = typeof(StringBuilder).GetMethod(nameof(StringBuilder.Append), new[] - { - typeof(string) - }); - - private static readonly MethodInfo ToStringMethod = typeof(StringBuilder).GetMethod(nameof(StringBuilder.ToString), Type.EmptyTypes); - private static readonly ConstructorInfo StringBuilderConstructor = typeof(StringBuilder).GetConstructor(Type.EmptyTypes); - - public override SpelNode RightOperand - { - get - { - if (children.Length < 2) - { - throw new InvalidOperationException("No right operand"); - } - - return children[1]; - } - } - - public OpPlus(int startPos, int endPos, params SpelNode[] operands) - : base("+", startPos, endPos, operands) - { - ArgumentGuard.NotNullOrEmpty(operands); - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - SpelNode leftOp = LeftOperand; - - if (children.Length < 2) - { - // if only one operand, then this is unary plus - object operandOne = leftOp.GetValueInternal(state).Value; - - if (IsNumber(operandOne)) - { - if (operandOne is double) - { - exitTypeDescriptor = TypeDescriptor.D; - } - else if (operandOne is float) - { - exitTypeDescriptor = TypeDescriptor.F; - } - else if (operandOne is long) - { - exitTypeDescriptor = TypeDescriptor.J; - } - else if (operandOne is int) - { - exitTypeDescriptor = TypeDescriptor.I; - } - - return new TypedValue(operandOne); - } - - return state.Operate(Operation.Add, operandOne, null); - } - - ITypedValue operandOneValue = leftOp.GetValueInternal(state); - object leftOperand = operandOneValue.Value; - ITypedValue operandTwoValue = RightOperand.GetValueInternal(state); - object rightOperand = operandTwoValue.Value; - - if (IsNumber(leftOperand) && IsNumber(rightOperand)) - { - var leftNumber = (IConvertible)leftOperand; - var rightNumber = (IConvertible)rightOperand; - - if (leftNumber is decimal || rightNumber is decimal) - { - decimal leftVal = leftNumber.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightNumber.ToDecimal(CultureInfo.InvariantCulture); - return new TypedValue(leftVal + rightVal); - } - - if (leftNumber is double || rightNumber is double) - { - exitTypeDescriptor = TypeDescriptor.D; - double leftVal = leftNumber.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightNumber.ToDouble(CultureInfo.InvariantCulture); - return new TypedValue(leftVal + rightVal); - } - - if (leftNumber is float || rightNumber is float) - { - exitTypeDescriptor = TypeDescriptor.F; - float leftVal = leftNumber.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightNumber.ToSingle(CultureInfo.InvariantCulture); - return new TypedValue(leftVal + rightVal); - } - - if (leftNumber is long || rightNumber is long) - { - exitTypeDescriptor = TypeDescriptor.J; - long leftVal = leftNumber.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightNumber.ToInt64(CultureInfo.InvariantCulture); - return new TypedValue(leftVal + rightVal); - } - - if (CodeFlow.IsIntegerForNumericOp(leftNumber) || CodeFlow.IsIntegerForNumericOp(rightNumber)) - { - exitTypeDescriptor = TypeDescriptor.I; - int leftVal = leftNumber.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightNumber.ToInt32(CultureInfo.InvariantCulture); - return new TypedValue(leftVal + rightVal); - } - else - { - // Unknown Number subtypes -> best guess is double addition - double leftVal = leftNumber.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightNumber.ToDouble(CultureInfo.InvariantCulture); - return new TypedValue(leftVal + rightVal); - } - } - - if (leftOperand is string strLeft && rightOperand is string strRight) - { - exitTypeDescriptor = TypeDescriptor.String; - return new TypedValue(strLeft + strRight); - } - - if (leftOperand is string) - { - return new TypedValue(leftOperand + (rightOperand == null ? "null" : ConvertTypedValueToString(operandTwoValue, state))); - } - - if (rightOperand is string) - { - return new TypedValue((leftOperand == null ? "null" : ConvertTypedValueToString(operandOneValue, state)) + rightOperand); - } - - return state.Operate(Operation.Add, leftOperand, rightOperand); - } - - public override string ToStringAst() - { - if (children.Length < 2) - { - // unary plus - return $"+{LeftOperand.ToStringAst()}"; - } - - return base.ToStringAst(); - } - - public override bool IsCompilable() - { - if (!LeftOperand.IsCompilable()) - { - return false; - } - - if (children.Length > 1 && !RightOperand.IsCompilable()) - { - return false; - } - - return exitTypeDescriptor != null; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - if (exitTypeDescriptor == TypeDescriptor.String) - { - gen.Emit(OpCodes.Newobj, StringBuilderConstructor); - Walk(gen, cf, LeftOperand); - Walk(gen, cf, RightOperand); - gen.Emit(OpCodes.Callvirt, ToStringMethod); - } - else - { - children[0].GenerateCode(gen, cf); - TypeDescriptor leftDesc = children[0].ExitDescriptor; - TypeDescriptor exitDesc = exitTypeDescriptor; - - if (exitDesc == null) - { - throw new InvalidOperationException("No exit type descriptor"); - } - - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, leftDesc, exitDesc); - - if (children.Length > 1) - { - cf.EnterCompilationScope(); - children[1].GenerateCode(gen, cf); - TypeDescriptor rightDesc = children[1].ExitDescriptor; - cf.ExitCompilationScope(); - CodeFlow.InsertNumericUnboxOrPrimitiveTypeCoercion(gen, rightDesc, exitDesc); - gen.Emit(OpCodes.Add); - } - } - - cf.PushDescriptor(exitTypeDescriptor); - } - - private static string ConvertTypedValueToString(ITypedValue value, ExpressionState state) - { - ITypeConverter typeConverter = state.EvaluationContext.TypeConverter; - Type typeDescriptor = typeof(string); - - if (typeConverter.CanConvert(value.TypeDescriptor, typeDescriptor)) - { - object val = typeConverter.ConvertValue(value.Value, value.TypeDescriptor, typeDescriptor); - return val == null ? "null" : val.ToString(); - } - - return value.Value == null ? "null" : value.Value.ToString(); - } - - private void Walk(ILGenerator gen, CodeFlow cf, SpelNode operand) - { - if (operand is OpPlus plus) - { - Walk(gen, cf, plus.LeftOperand); - Walk(gen, cf, plus.RightOperand); - } - else if (operand != null) - { - cf.EnterCompilationScope(); - operand.GenerateCode(gen, cf); - - if (cf.LastDescriptor() != TypeDescriptor.String) - { - gen.Emit(OpCodes.Castclass, typeof(string)); - } - - cf.ExitCompilationScope(); - gen.Emit(OpCodes.Callvirt, AppendStringMethod); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/Operator.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/Operator.cs deleted file mode 100644 index dfb651a9a3..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/Operator.cs +++ /dev/null @@ -1,429 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public abstract class Operator : SpelNode -{ - private static readonly ISet NumericTypeCodes = new[] - { - TypeCode.Byte, - TypeCode.Decimal, - TypeCode.Double, - TypeCode.Int16, - TypeCode.Int32, - TypeCode.Int64, - TypeCode.SByte, - TypeCode.Single, - TypeCode.UInt16, - TypeCode.UInt32, - TypeCode.UInt64 - }.ToHashSet(); - - protected static readonly MethodInfo EqualityCheckMethod = typeof(Operator).GetMethod("EqualityCheck", new[] - { - typeof(IEvaluationContext), - typeof(object), - typeof(object) - }); - - protected readonly string InnerOperatorName; - - // The descriptors of the runtime operand values are used if the discovered declared - // descriptors are not providing enough information (for example a generic type - // whose accessors seem to only be returning 'Object' - the actual descriptors may - // indicate 'int') - protected TypeDescriptor leftActualDescriptor; - - protected TypeDescriptor rightActualDescriptor; - - public virtual SpelNode LeftOperand => children[0]; - - public virtual SpelNode RightOperand => children[1]; - - public virtual string OperatorName => InnerOperatorName; - - protected Operator(string payload, int startPos, int endPos, params SpelNode[] operands) - : base(startPos, endPos, operands) - { - InnerOperatorName = payload; - } - - public static bool IsNumber(object target) - { - if (target is not IConvertible targetConv) - { - return false; - } - - TypeCode typeCode = targetConv.GetTypeCode(); - return NumericTypeCodes.Contains(typeCode); - } - - public static bool EqualityCheck(IEvaluationContext context, object left, object right) - { - if (IsNumber(left) && IsNumber(right)) - { - var leftConv = (IConvertible)left; - var rightConv = (IConvertible)right; - - if (left is decimal || right is decimal) - { - decimal leftVal = leftConv.ToDecimal(CultureInfo.InvariantCulture); - decimal rightVal = rightConv.ToDecimal(CultureInfo.InvariantCulture); - return leftVal == rightVal; - } - - if (left is double || right is double) - { - double leftVal = leftConv.ToDouble(CultureInfo.InvariantCulture); - double rightVal = rightConv.ToDouble(CultureInfo.InvariantCulture); -#pragma warning disable S1244 // Floating point numbers should not be tested for equality - return leftVal == rightVal; -#pragma warning restore S1244 // Floating point numbers should not be tested for equality - } - - if (left is float || right is float) - { - float leftVal = leftConv.ToSingle(CultureInfo.InvariantCulture); - float rightVal = rightConv.ToSingle(CultureInfo.InvariantCulture); -#pragma warning disable S1244 // Floating point numbers should not be tested for equality - return leftVal == rightVal; -#pragma warning restore S1244 // Floating point numbers should not be tested for equality - } - - if (left is long || right is long) - { - long leftVal = leftConv.ToInt64(CultureInfo.InvariantCulture); - long rightVal = rightConv.ToInt64(CultureInfo.InvariantCulture); - return leftVal == rightVal; - } - - if (left is int || right is int) - { - int leftVal = leftConv.ToInt32(CultureInfo.InvariantCulture); - int rightVal = rightConv.ToInt32(CultureInfo.InvariantCulture); - return leftVal == rightVal; - } - - if (left is short || right is short) - { - short leftVal = leftConv.ToInt16(CultureInfo.InvariantCulture); - short rightVal = rightConv.ToInt16(CultureInfo.InvariantCulture); - return leftVal == rightVal; - } - - if (left is byte || right is byte) - { - byte leftVal = leftConv.ToByte(CultureInfo.InvariantCulture); - byte rightVal = rightConv.ToByte(CultureInfo.InvariantCulture); - return leftVal == rightVal; - } - - if (left is sbyte || right is sbyte) - { - sbyte leftVal = leftConv.ToSByte(CultureInfo.InvariantCulture); - sbyte rightVal = rightConv.ToSByte(CultureInfo.InvariantCulture); - return leftVal == rightVal; - } - - if (left is uint || right is uint) - { - uint leftVal = leftConv.ToUInt32(CultureInfo.InvariantCulture); - uint rightVal = rightConv.ToUInt32(CultureInfo.InvariantCulture); - return leftVal == rightVal; - } - - if (left is ushort || right is ushort) - { - ushort leftVal = leftConv.ToUInt16(CultureInfo.InvariantCulture); - ushort rightVal = rightConv.ToUInt16(CultureInfo.InvariantCulture); - return leftVal == rightVal; - } - - if (left is ulong || right is ulong) - { - ulong leftVal = leftConv.ToUInt64(CultureInfo.InvariantCulture); - ulong rightVal = rightConv.ToUInt64(CultureInfo.InvariantCulture); - return leftVal == rightVal; - } - } - - if (left is string leftString && right is string rightString) - { - return leftString == rightString; - } - - if (left is bool leftBool && right is bool rightBool) - { - return leftBool == rightBool; - } - - if (ObjectEquality.ObjectOrCollectionEquals(left, right)) - { - return true; - } - - if (left is IComparable && right is IComparable) - { - Type ancestor = ClassUtils.DetermineCommonAncestor(left.GetType(), right.GetType()); - - if (ancestor != null && typeof(IComparable).IsAssignableFrom(ancestor)) - { - return context.TypeComparator.Compare(left, right) == 0; - } - } - - return false; - } - - public override string ToStringAst() - { - var sb = new StringBuilder("("); - sb.Append(GetChild(0).ToStringAst()); - - for (int i = 1; i < ChildCount; i++) - { - sb.Append(" ").Append(OperatorName).Append(" "); - sb.Append(GetChild(i).ToStringAst()); - } - - sb.Append(")"); - return sb.ToString(); - } - - protected virtual bool IsCompilableOperatorUsingNumerics() - { - SpelNode left = LeftOperand; - SpelNode right = RightOperand; - - if (!left.IsCompilable() || !right.IsCompilable()) - { - return false; - } - - // Supported operand types for equals (at the moment) - TypeDescriptor leftDesc = left.ExitDescriptor; - TypeDescriptor rightDesc = right.ExitDescriptor; - DescriptorComparison dc = DescriptorComparison.CheckNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor); - return dc.AreNumbers && dc.AreCompatible; - } - - protected void GenerateComparisonCode(ILGenerator gen, CodeFlow cf, OpCode brToElseInstruction) - { - SpelNode left = LeftOperand; - SpelNode right = RightOperand; - TypeDescriptor leftDesc = left.ExitDescriptor; - TypeDescriptor rightDesc = right.ExitDescriptor; - - Label elseTarget = gen.DefineLabel(); - Label endOfIfTarget = gen.DefineLabel(); - - bool unboxLeft = !CodeFlow.IsValueType(leftDesc); - bool unboxRight = !CodeFlow.IsValueType(rightDesc); - - cf.EnterCompilationScope(); - left.GenerateCode(gen, cf); - cf.ExitCompilationScope(); - - if (CodeFlow.IsValueType(leftDesc)) - { - gen.Emit(OpCodes.Box, leftDesc.Value); - unboxLeft = true; - } - - cf.EnterCompilationScope(); - right.GenerateCode(gen, cf); - cf.ExitCompilationScope(); - - if (CodeFlow.IsValueType(rightDesc)) - { - gen.Emit(OpCodes.Box, rightDesc.Value); - unboxRight = true; - } - - LocalBuilder leftLocal = gen.DeclareLocal(typeof(object)); - LocalBuilder rightLocal = gen.DeclareLocal(typeof(object)); - gen.Emit(OpCodes.Stloc, rightLocal); - gen.Emit(OpCodes.Stloc, leftLocal); - - gen.Emit(OpCodes.Ldloc, leftLocal); - gen.Emit(OpCodes.Ldloc, rightLocal); - - // This code block checks whether the left or right operand is null and handles - // those cases before letting the original code (that only handled actual numbers) run - Label rightIsNonNullTarget = gen.DefineLabel(); - - // stack: left/right - gen.Emit(OpCodes.Brtrue, rightIsNonNullTarget); - - // stack: left - // here: RIGHT==null LEFT==unknown - Label leftNotNullRightIsNullTarget = gen.DefineLabel(); - gen.Emit(OpCodes.Brtrue, leftNotNullRightIsNullTarget); - - // stack: empty - // here: RIGHT==null LEFT==null - // load 0 or 1 depending on comparison instruction - if (brToElseInstruction == OpCodes.Bge || brToElseInstruction == OpCodes.Ble) - { - gen.Emit(OpCodes.Ldc_I4_0); - } - else if (brToElseInstruction == OpCodes.Bgt || brToElseInstruction == OpCodes.Blt) - { - gen.Emit(OpCodes.Ldc_I4_1); - } - else - { - throw new InvalidOperationException($"Unsupported: {brToElseInstruction}"); - } - - gen.Emit(OpCodes.Br, endOfIfTarget); - gen.MarkLabel(leftNotNullRightIsNullTarget); - - // stack: empty - // RIGHT==null LEFT!=null - // load 0 or 1 depending on comparison instruction - if (brToElseInstruction == OpCodes.Bge || brToElseInstruction == OpCodes.Bgt) - { - gen.Emit(OpCodes.Ldc_I4_0); - } - else if (brToElseInstruction == OpCodes.Ble || brToElseInstruction == OpCodes.Blt) - { - gen.Emit(OpCodes.Ldc_I4_1); - } - else - { - throw new InvalidOperationException($"Unsupported: {brToElseInstruction}"); - } - - gen.Emit(OpCodes.Br, endOfIfTarget); - gen.MarkLabel(rightIsNonNullTarget); - - // stack: left - // here: RIGHT!=null LEFT==unknown - Label neitherRightNorLeftAreNullTarget = gen.DefineLabel(); - gen.Emit(OpCodes.Brtrue, neitherRightNorLeftAreNullTarget); - - // stack: empty - // here: RIGHT!=null LEFT==null - if (brToElseInstruction == OpCodes.Bge || brToElseInstruction == OpCodes.Bgt) - { - gen.Emit(OpCodes.Ldc_I4_1); - } - else if (brToElseInstruction == OpCodes.Ble || brToElseInstruction == OpCodes.Blt) - { - gen.Emit(OpCodes.Ldc_I4_0); - } - else - { - throw new InvalidOperationException($"Unsupported: {brToElseInstruction}"); - } - - gen.Emit(OpCodes.Br, endOfIfTarget); - gen.MarkLabel(neitherRightNorLeftAreNullTarget); - - // stack: empty - // neither were null so unbox and proceed with numeric comparison - gen.Emit(OpCodes.Ldloc, leftLocal); - - if (unboxLeft) - { - gen.Emit(OpCodes.Unbox_Any, leftDesc.Value); - } - - // stack: left - gen.Emit(OpCodes.Ldloc, rightLocal); - - if (unboxRight) - { - gen.Emit(OpCodes.Unbox_Any, rightDesc.Value); - } - - // stack: left, right - // Br instruction - gen.Emit(brToElseInstruction, elseTarget); - - // Stack: Empty - gen.Emit(OpCodes.Ldc_I4_1); - gen.Emit(OpCodes.Br, endOfIfTarget); - gen.MarkLabel(elseTarget); - - // Stack: Empty - gen.Emit(OpCodes.Ldc_I4_0); - gen.MarkLabel(endOfIfTarget); - - // Stack: result on stack, convert to bool - LocalBuilder result = gen.DeclareLocal(typeof(bool)); - gen.Emit(OpCodes.Stloc, result); - gen.Emit(OpCodes.Ldloc, result); - cf.PushDescriptor(TypeDescriptor.Z); - } - - protected class DescriptorComparison - { - protected static readonly DescriptorComparison NotNumbers = new(false, false, TypeDescriptor.V); - protected static readonly DescriptorComparison IncompatibleNumbers = new(true, false, TypeDescriptor.V); - - protected readonly bool InnerAreNumbers; // Were the two compared descriptor both for numbers? - - protected readonly bool InnerAreCompatible; // If they were numbers, were they compatible? - - protected readonly TypeDescriptor InnerCompatibleType; // When compatible, what is the descriptor of the common type - - public bool AreNumbers => InnerAreNumbers; - - public bool AreCompatible => InnerAreCompatible; - - public TypeDescriptor CompatibleType => InnerCompatibleType; - - public DescriptorComparison(bool areNumbers, bool areCompatible, TypeDescriptor compatibleType) - { - InnerAreNumbers = areNumbers; - InnerAreCompatible = areCompatible; - InnerCompatibleType = compatibleType; - } - - public static DescriptorComparison CheckNumericCompatibility(TypeDescriptor leftDeclaredDescriptor, TypeDescriptor rightDeclaredDescriptor, - TypeDescriptor leftActualDescriptor, TypeDescriptor rightActualDescriptor) - { - TypeDescriptor ld = leftDeclaredDescriptor; - TypeDescriptor rd = rightDeclaredDescriptor; - - bool leftNumeric = CodeFlow.IsPrimitiveOrUnboxableSupportedNumberOrBoolean(ld); - bool rightNumeric = CodeFlow.IsPrimitiveOrUnboxableSupportedNumberOrBoolean(rd); - - // If the declared descriptors aren't providing the information, try the actual descriptors - if (!leftNumeric && !Equals(ld, leftActualDescriptor)) - { - ld = leftActualDescriptor; - leftNumeric = CodeFlow.IsPrimitiveOrUnboxableSupportedNumberOrBoolean(ld); - } - - if (!rightNumeric && !Equals(rd, rightActualDescriptor)) - { - rd = rightActualDescriptor; - rightNumeric = CodeFlow.IsPrimitiveOrUnboxableSupportedNumberOrBoolean(rd); - } - - if (leftNumeric && rightNumeric) - { - if (CodeFlow.AreBoxingCompatible(ld, rd)) - { - return new DescriptorComparison(true, true, CodeFlow.ToPrimitiveTargetDescriptor(ld)); - } - - return IncompatibleNumbers; - } - - return NotNumbers; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorBetween.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorBetween.cs deleted file mode 100644 index d4c1cdf52e..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorBetween.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OperatorBetween : Operator -{ - public OperatorBetween(int startPos, int endPos, params SpelNode[] operands) - : base("between", startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - object left = LeftOperand.GetValueInternal(state).Value; - object right = RightOperand.GetValueInternal(state).Value; - - if (right is not IList list || list.Count != 2) - { - throw new SpelEvaluationException(RightOperand.StartPosition, SpelMessage.BetweenRightOperandMustBeTwoElementList); - } - - object low = list[0]; - object high = list[1]; - ITypeComparator comp = state.TypeComparator; - - try - { - return BooleanTypedValue.ForValue(comp.Compare(left, low) >= 0 && comp.Compare(left, high) <= 0); - } - catch (SpelEvaluationException ex) - { - ex.Position = StartPosition; - throw; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorInstanceof.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorInstanceof.cs deleted file mode 100644 index fe970c5d0f..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorInstanceof.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OperatorInstanceOf : Operator -{ - private Type _type; - - public OperatorInstanceOf(int startPos, int endPos, params SpelNode[] operands) - : base("instanceof", startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - SpelNode rightOperand = RightOperand; - ITypedValue left = LeftOperand.GetValueInternal(state); - ITypedValue right = rightOperand.GetValueInternal(state); - object leftValue = left.Value; - object rightValue = right.Value; - BooleanTypedValue result; - - if (rightValue is not Type rightClass) - { - throw new SpelEvaluationException(RightOperand.StartPosition, SpelMessage.InstanceOfOperatorNeedsClassOperand, - rightValue == null ? "null" : rightValue.GetType().FullName); - } - - if (leftValue == null) - { - result = BooleanTypedValue.False; // null is not an instance of anything - } - else - { - result = BooleanTypedValue.ForValue(rightClass.IsInstanceOfType(leftValue)); - } - - _type = rightClass; - - if (rightOperand is TypeReference) - { - // Can only generate bytecode where the right operand is a direct type reference, - // not if it is indirect (for example when right operand is a variable reference) - exitTypeDescriptor = TypeDescriptor.Z; - } - - return result; - } - - public override bool IsCompilable() - { - return exitTypeDescriptor != null && LeftOperand.IsCompilable(); - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - LeftOperand.GenerateCode(gen, cf); - CodeFlow.InsertBoxIfNecessary(gen, cf.LastDescriptor()); - - if (_type == null) - { - throw new InvalidOperationException("No type available"); - } - - LocalBuilder convert = gen.DeclareLocal(typeof(bool)); - gen.Emit(OpCodes.Isinst, _type); - gen.Emit(OpCodes.Ldnull); - gen.Emit(OpCodes.Cgt_Un); - gen.Emit(OpCodes.Stloc, convert); - gen.Emit(OpCodes.Ldloc, convert); - - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorMatches.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorMatches.cs deleted file mode 100644 index 8a8a2ffbc8..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorMatches.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Text.RegularExpressions; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OperatorMatches : Operator -{ - private readonly ConcurrentDictionary _patternCache = new(); - - public OperatorMatches(int startPos, int endPos, params SpelNode[] operands) - : base("matches", startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - SpelNode leftOp = LeftOperand; - SpelNode rightOp = RightOperand; - string left = leftOp.GetValue(state); - object right = RightOperand.GetValue(state); - - if (left == null) - { - throw new SpelEvaluationException(leftOp.StartPosition, SpelMessage.InvalidFirstOperandForMatchesOperator, (object)null); - } - - if (right is not string rightString) - { - throw new SpelEvaluationException(rightOp.StartPosition, SpelMessage.InvalidSecondOperandForMatchesOperator, right); - } - - try - { - _patternCache.TryGetValue(rightString, out Regex pattern); - - if (pattern == null) - { - pattern = new Regex(rightString, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); - _patternCache.TryAdd(rightString, pattern); - } - - return BooleanTypedValue.ForValue(pattern.IsMatch(left)); - } - catch (ArgumentException ex) - { - throw new SpelEvaluationException(rightOp.StartPosition, ex, SpelMessage.InvalidPattern, rightString); - } - catch (RegexMatchTimeoutException ex) - { - throw new SpelEvaluationException(rightOp.StartPosition, ex, SpelMessage.FlawedPattern, rightString); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorNot.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorNot.cs deleted file mode 100644 index 115af0ca00..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorNot.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OperatorNot : SpelNode -{ - public OperatorNot(int startPos, int endPos, SpelNode operand) - : base(startPos, endPos, operand) - { - exitTypeDescriptor = TypeDescriptor.Z; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - try - { - bool value = children[0].GetValue(state); - return BooleanTypedValue.ForValue(!value); - } - catch (SpelEvaluationException ex) - { - ex.Position = GetChild(0).StartPosition; - throw; - } - catch (Exception ex) - { - throw new SpelEvaluationException(SpelMessage.TypeConversionError, ex, "null", "System.Boolean"); - } - } - - public override string ToStringAst() - { - return $"!{GetChild(0).ToStringAst()}"; - } - - public override bool IsCompilable() - { - SpelNode child = children[0]; - return child.IsCompilable() && CodeFlow.IsBooleanCompatible(child.ExitDescriptor); - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - Label elseTarget = gen.DefineLabel(); - Label endIfTarget = gen.DefineLabel(); - LocalBuilder result = gen.DeclareLocal(typeof(bool)); - - SpelNode child = children[0]; - child.GenerateCode(gen, cf); - cf.UnboxBooleanIfNecessary(gen); - gen.Emit(OpCodes.Brtrue, elseTarget); - gen.Emit(OpCodes.Ldc_I4_1); - gen.Emit(OpCodes.Stloc, result); - gen.Emit(OpCodes.Br, endIfTarget); - gen.MarkLabel(elseTarget); - gen.Emit(OpCodes.Ldc_I4_0); - gen.Emit(OpCodes.Stloc, result); - gen.MarkLabel(endIfTarget); - gen.Emit(OpCodes.Ldloc, result); - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorPower.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorPower.cs deleted file mode 100644 index fc2bb48f11..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/OperatorPower.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class OperatorPower : Operator -{ - public OperatorPower(int startPos, int endPos, params SpelNode[] operands) - : base("^", startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - SpelNode leftOp = LeftOperand; - SpelNode rightOp = RightOperand; - - object leftOperand = leftOp.GetValueInternal(state).Value; - object rightOperand = rightOp.GetValueInternal(state).Value; - - if (IsNumber(leftOperand) && IsNumber(rightOperand)) - { - var leftConv = (IConvertible)leftOperand; - var rightConv = (IConvertible)rightOperand; - - if (leftOperand is decimal) - { - int rightVal = rightConv.ToInt32(CultureInfo.InvariantCulture); - double leftVal = leftConv.ToDouble(CultureInfo.InvariantCulture); - var result = Math.Pow(leftVal, rightVal) as IConvertible; - return new TypedValue(result.ToDecimal(CultureInfo.InvariantCulture)); - } - - if (leftOperand is double || rightOperand is double) - { - double rightVal = rightConv.ToDouble(CultureInfo.InvariantCulture); - double leftVal = leftConv.ToDouble(CultureInfo.InvariantCulture); - return new TypedValue(Math.Pow(leftVal, rightVal)); - } - - if (leftOperand is float || rightOperand is float) - { - float rightVal = rightConv.ToSingle(CultureInfo.InvariantCulture); - float leftVal = leftConv.ToSingle(CultureInfo.InvariantCulture); - return new TypedValue(Math.Pow(leftVal, rightVal)); - } - - double r = rightConv.ToDouble(CultureInfo.InvariantCulture); - double l = leftConv.ToDouble(CultureInfo.InvariantCulture); - - double d = Math.Pow(l, r); - long asLong = (long)d; - - if (asLong > int.MaxValue || leftOperand is long || rightOperand is long) - { - return new TypedValue(asLong); - } - - return new TypedValue((int)asLong); - } - - return state.Operate(Operation.Power, leftOperand, rightOperand); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/Projection.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/Projection.cs deleted file mode 100644 index f6ac5f984f..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/Projection.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class Projection : SpelNode -{ - private readonly bool _nullSafe; - - public Projection(bool nullSafe, int startPos, int endPos, SpelNode expression) - : base(startPos, endPos, expression) - { - _nullSafe = nullSafe; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - return GetValueRef(state).GetValue(); - } - - public override string ToStringAst() - { - return $"![{GetChild(0).ToStringAst()}]"; - } - - protected internal override IValueRef GetValueRef(ExpressionState state) - { - ITypedValue op = state.GetActiveContextObject(); - - object operand = op.Value; - var operandAsArray = operand as Array; - - // When the input is a map, we push a special context object on the stack - // before calling the specified operation. This special context object - // has two fields 'key' and 'value' that refer to the map entries key - // and value, and they can be referenced in the operation - // eg. {'a':'y','b':'n'}.![value=='y'?key:null]" == ['a', null] - if (operand is IDictionary mapData) - { - var result = new List(); - - foreach (object entry in mapData) - { - try - { - state.PushActiveContextObject(new TypedValue(entry)); - state.EnterScope(); - result.Add(children[0].GetValueInternal(state).Value); - } - finally - { - state.PopActiveContextObject(); - state.ExitScope(); - } - } - - return new TypedValueHolderValueRef(new TypedValue(result), this); - } - - if (operand is IEnumerable data) - { - var result = new List(); - Type arrayElementType = null; - - foreach (object element in data) - { - try - { - state.PushActiveContextObject(new TypedValue(element)); - state.EnterScope("index", result.Count); - object value = children[0].GetValueInternal(state).Value; - - if (value != null && operandAsArray != null) - { - arrayElementType = DetermineCommonType(arrayElementType, value.GetType()); - } - - result.Add(value); - } - finally - { - state.ExitScope(); - state.PopActiveContextObject(); - } - } - - if (operandAsArray != null) - { - arrayElementType ??= typeof(object); - - var resultArray = Array.CreateInstance(arrayElementType, result.Count); - Array.Copy(result.ToArray(), 0, resultArray, 0, result.Count); - return new TypedValueHolderValueRef(new TypedValue(resultArray), this); - } - - return new TypedValueHolderValueRef(new TypedValue(result), this); - } - - if (operand == null) - { - if (_nullSafe) - { - return NullValueRef.Instance; - } - - throw new SpelEvaluationException(StartPosition, SpelMessage.ProjectionNotSupportedOnType, "null"); - } - - throw new SpelEvaluationException(StartPosition, SpelMessage.ProjectionNotSupportedOnType, operand.GetType().FullName); - } - - private Type DetermineCommonType(Type oldType, Type newType) - { - if (oldType == null) - { - return newType; - } - - if (oldType.IsAssignableFrom(newType)) - { - return oldType; - } - - Type nextType = newType; - - while (nextType != typeof(object)) - { - if (nextType.IsAssignableFrom(oldType)) - { - return nextType; - } - - nextType = nextType.BaseType; - } - - Type[] interfaces = newType.FindInterfaces((_, _) => true, null); - - foreach (Type nextInterface in interfaces) - { - if (nextInterface.IsAssignableFrom(oldType)) - { - return nextInterface; - } - } - - return typeof(object); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/PropertyOrFieldReference.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/PropertyOrFieldReference.cs deleted file mode 100644 index b0e54477b7..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/PropertyOrFieldReference.cs +++ /dev/null @@ -1,440 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class PropertyOrFieldReference : SpelNode -{ - private TypeDescriptor _originalPrimitiveExitTypeDescriptor; - private volatile IPropertyAccessor _cachedReadAccessor; - private volatile IPropertyAccessor _cachedWriteAccessor; - - public bool IsNullSafe { get; } - - public string Name { get; } - - public PropertyOrFieldReference(bool nullSafe, string propertyOrFieldName, int startPos, int endPos) - : base(startPos, endPos) - { - IsNullSafe = nullSafe; - Name = propertyOrFieldName; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - ITypedValue tv = GetValueInternal(state.GetActiveContextObject(), state.EvaluationContext, state.Configuration.AutoGrowNullReferences); - IPropertyAccessor accessorToUse = _cachedReadAccessor; - - if (accessorToUse is ICompilablePropertyAccessor accessor) - { - TypeDescriptor descriptor = ComputeExitDescriptor(tv.Value, accessor.GetPropertyType()); - SetExitTypeDescriptor(descriptor); - } - - return tv; - } - - public override void SetValue(ExpressionState state, object newValue) - { - WriteProperty(state.GetActiveContextObject(), state.EvaluationContext, Name, newValue); - } - - public override bool IsWritable(ExpressionState state) - { - return IsWritableProperty(Name, state.GetActiveContextObject(), state.EvaluationContext); - } - - public override string ToStringAst() - { - return Name; - } - - public bool IsWritableProperty(string name, ITypedValue contextObject, IEvaluationContext evalContext) - { - object value = contextObject.Value; - - if (value != null) - { - IList accessorsToTry = GetPropertyAccessorsToTry(contextObject.Value, evalContext.PropertyAccessors); - - foreach (IPropertyAccessor accessor in accessorsToTry) - { - try - { - if (accessor.CanWrite(evalContext, value, name)) - { - return true; - } - } - catch (AccessException) - { - // let others try - } - } - } - - return false; - } - - public override bool IsCompilable() - { - IPropertyAccessor accessorToUse = _cachedReadAccessor; - return accessorToUse is ICompilablePropertyAccessor accessor && accessor.IsCompilable(); - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - if (_cachedReadAccessor is not ICompilablePropertyAccessor accessorToUse) - { - throw new InvalidOperationException($"Property accessor is not compilable: {_cachedReadAccessor}"); - } - - Label? skipIfNullLabel = null; - - if (IsNullSafe) - { - skipIfNullLabel = gen.DefineLabel(); - gen.Emit(OpCodes.Dup); - gen.Emit(OpCodes.Ldnull); - gen.Emit(OpCodes.Cgt_Un); - gen.Emit(OpCodes.Brfalse, skipIfNullLabel.Value); - } - - accessorToUse.GenerateCode(Name, gen, cf); - cf.PushDescriptor(exitTypeDescriptor); - - if (_originalPrimitiveExitTypeDescriptor != null) - { - // The output of the accessor is a primitive but from the block above it might be null, - // so to have a common stack element type at skipIfNull target it is necessary - // to box the primitive - CodeFlow.InsertBoxIfNecessary(gen, _originalPrimitiveExitTypeDescriptor); - } - - if (skipIfNullLabel.HasValue) - { - gen.MarkLabel(skipIfNullLabel.Value); - } - } - - protected internal override IValueRef GetValueRef(ExpressionState state) - { - return new AccessorLValue(this, state.GetActiveContextObject(), state.EvaluationContext, state.Configuration.AutoGrowNullReferences); - } - - protected internal TypeDescriptor ComputeExitDescriptor(object result, Type propertyReturnType) - { - if (propertyReturnType.IsValueType) - { - return CodeFlow.ToDescriptor(propertyReturnType); - } - - return CodeFlow.ToDescriptorFromObject(result); - } - - protected internal void SetExitTypeDescriptor(TypeDescriptor descriptor) - { - // If this property or field access would return a primitive - and yet - // it is also marked null safe - then the exit type descriptor must be - // promoted to the box type to allow a null value to be passed on - if (IsNullSafe && CodeFlow.IsValueType(descriptor)) - { - _originalPrimitiveExitTypeDescriptor = descriptor; - exitTypeDescriptor = CodeFlow.ToBoxedDescriptor(descriptor); - } - else - { - exitTypeDescriptor = descriptor; - } - } - - private ITypedValue GetValueInternal(ITypedValue contextObject, IEvaluationContext evalContext, bool isAutoGrowNullReferences) - { - ITypedValue result = ReadProperty(contextObject, evalContext, Name); - - // Dynamically create the objects if the user has requested that optional behavior - if (result.Value == null && isAutoGrowNullReferences && NextChildIs(typeof(Indexer), typeof(PropertyOrFieldReference))) - { - Type resultDescriptor = ClassUtils.GetGenericTypeDefinition(result.TypeDescriptor); - - if (resultDescriptor == null) - { - throw new InvalidOperationException("No result type"); - } - - // Create a new collection or map ready for the indexer - if (typeof(List<>) == resultDescriptor || typeof(IList<>) == resultDescriptor) - { - if (IsWritableProperty(Name, contextObject, evalContext)) - { - object newList = Activator.CreateInstance(typeof(List<>).MakeGenericType(result.TypeDescriptor.GetGenericArguments())); - - WriteProperty(contextObject, evalContext, Name, newList); - result = ReadProperty(contextObject, evalContext, Name); - } - } - else if (typeof(Dictionary<,>) == resultDescriptor || typeof(IDictionary<,>) == resultDescriptor) - { - if (IsWritableProperty(Name, contextObject, evalContext)) - { - object newMap = Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(result.TypeDescriptor.GetGenericArguments())); - - WriteProperty(contextObject, evalContext, Name, newMap); - result = ReadProperty(contextObject, evalContext, Name); - } - } - else if (typeof(IDictionary) == resultDescriptor) - { - if (IsWritableProperty(Name, contextObject, evalContext)) - { - var newMap = new Dictionary(); - WriteProperty(contextObject, evalContext, Name, newMap); - result = ReadProperty(contextObject, evalContext, Name); - } - } - else if (typeof(IList) == resultDescriptor) - { - if (IsWritableProperty(Name, contextObject, evalContext)) - { - var newList = new ArrayList(); - WriteProperty(contextObject, evalContext, Name, newList); - result = ReadProperty(contextObject, evalContext, Name); - } - } - else - { - // 'simple' object - try - { - if (IsWritableProperty(Name, contextObject, evalContext)) - { - Type type = result.TypeDescriptor; - object newObject = ReflectionHelper.GetAccessibleConstructor(type).Invoke(Array.Empty()); - WriteProperty(contextObject, evalContext, Name, newObject); - result = ReadProperty(contextObject, evalContext, Name); - } - } - catch (TargetInvocationException ex) - { - throw new SpelEvaluationException(StartPosition, ex.InnerException, SpelMessage.UnableToDynamicallyCreateObject, result.TypeDescriptor); - } - catch (Exception ex) - { - throw new SpelEvaluationException(StartPosition, ex, SpelMessage.UnableToDynamicallyCreateObject, result.TypeDescriptor); - } - } - } - - return result; - } - - private ITypedValue ReadProperty(ITypedValue contextObject, IEvaluationContext evalContext, string name) - { - object targetObject = contextObject.Value; - - if (targetObject == null && IsNullSafe) - { - return TypedValue.Null; - } - - IPropertyAccessor accessorToUse = _cachedReadAccessor; - - if (accessorToUse != null) - { - if (accessorToUse is ReflectivePropertyAccessor.OptimalPropertyAccessor || evalContext.PropertyAccessors.Contains(accessorToUse)) - { - try - { - return accessorToUse.Read(evalContext, contextObject.Value, name); - } - catch (Exception) - { - // This is OK - it may have gone stale due to a class change, - // let's try to get a new one and call it before giving up... - } - } - - _cachedReadAccessor = null; - } - - IList accessorsToTry = GetPropertyAccessorsToTry(contextObject.Value, evalContext.PropertyAccessors); - - // Go through the accessors that may be able to resolve it. If they are a cacheable accessor then - // get the accessor and use it. If they are not cacheable but report they can read the property - // then ask them to read it - try - { - foreach (IPropertyAccessor acc in accessorsToTry) - { - IPropertyAccessor accessor = acc; - - if (accessor.CanRead(evalContext, contextObject.Value, name)) - { - if (accessor is ReflectivePropertyAccessor accessor1) - { - accessor = accessor1.CreateOptimalAccessor(evalContext, contextObject.Value, name); - } - - _cachedReadAccessor = accessor; - return accessor.Read(evalContext, contextObject.Value, name); - } - } - } - catch (Exception ex) - { - throw new SpelEvaluationException(ex, SpelMessage.ExceptionDuringPropertyRead, name, ex.Message); - } - - if (contextObject.Value == null) - { - throw new SpelEvaluationException(SpelMessage.PropertyOrFieldNotReadableOnNull, name); - } - - throw new SpelEvaluationException(StartPosition, SpelMessage.PropertyOrFieldNotReadable, Name, - FormatHelper.FormatClassNameForMessage(GetObjectType(contextObject.Value))); - } - - private void WriteProperty(ITypedValue contextObject, IEvaluationContext evalContext, string name, object newValue) - { - if (contextObject.Value == null && IsNullSafe) - { - return; - } - - if (contextObject.Value == null) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.PropertyOrFieldNotWritableOnNull, name); - } - - IPropertyAccessor accessorToUse = _cachedWriteAccessor; - - if (accessorToUse != null) - { - if (accessorToUse is ReflectivePropertyAccessor.OptimalPropertyAccessor || evalContext.PropertyAccessors.Contains(accessorToUse)) - { - try - { - accessorToUse.Write(evalContext, contextObject.Value, name, newValue); - return; - } - catch (Exception) - { - // This is OK - it may have gone stale due to a class change, - // let's try to get a new one and call it before giving up... - } - } - - _cachedWriteAccessor = null; - } - - IList accessorsToTry = GetPropertyAccessorsToTry(contextObject.Value, evalContext.PropertyAccessors); - - try - { - foreach (IPropertyAccessor accessor in accessorsToTry) - { - if (accessor.CanWrite(evalContext, contextObject.Value, name)) - { - _cachedWriteAccessor = accessor; - accessor.Write(evalContext, contextObject.Value, name, newValue); - return; - } - } - } - catch (AccessException ex) - { - throw new SpelEvaluationException(StartPosition, ex, SpelMessage.ExceptionDuringPropertyWrite, name, ex.Message); - } - - throw new SpelEvaluationException(StartPosition, SpelMessage.PropertyOrFieldNotWritable, name, - FormatHelper.FormatClassNameForMessage(GetObjectType(contextObject.Value))); - } - - private IList GetPropertyAccessorsToTry(object contextObject, IList propertyAccessors) - { - Type targetType = contextObject?.GetType(); - - var specificAccessors = new List(); - var generalAccessors = new List(); - - foreach (IPropertyAccessor resolver in propertyAccessors) - { - IList targetTypes = resolver.GetSpecificTargetClasses(); - - if (targetTypes == null) - { - // generic resolver that says it can be used for any type - generalAccessors.Add(resolver); - } - else if (targetType != null) - { - foreach (Type type in targetTypes) - { - if (type == targetType) - { - specificAccessors.Add(resolver); - break; - } - - if (type.IsAssignableFrom(targetType)) - { - generalAccessors.Add(resolver); - } - } - } - } - - var resolvers = new List(specificAccessors); - generalAccessors.RemoveAll(a => specificAccessors.Contains(a)); - resolvers.AddRange(generalAccessors); - return resolvers; - } - - private sealed class AccessorLValue : IValueRef - { - private readonly PropertyOrFieldReference _ref; - - private readonly ITypedValue _contextObject; - - private readonly IEvaluationContext _evalContext; - - private readonly bool _autoGrowNullReferences; - - public bool IsWritable => _ref.IsWritableProperty(_ref.Name, _contextObject, _evalContext); - - public AccessorLValue(PropertyOrFieldReference propertyOrFieldReference, ITypedValue activeContextObject, IEvaluationContext evalContext, - bool autoGrowNullReferences) - { - _ref = propertyOrFieldReference; - _contextObject = activeContextObject; - _evalContext = evalContext; - _autoGrowNullReferences = autoGrowNullReferences; - } - - public ITypedValue GetValue() - { - ITypedValue value = _ref.GetValueInternal(_contextObject, _evalContext, _autoGrowNullReferences); - IPropertyAccessor accessorToUse = _ref._cachedReadAccessor; - - if (accessorToUse is ICompilablePropertyAccessor accessor) - { - TypeDescriptor descriptor = _ref.ComputeExitDescriptor(value.Value, accessor.GetPropertyType()); - _ref.SetExitTypeDescriptor(descriptor); - } - - return value; - } - - public void SetValue(object newValue) - { - _ref.WriteProperty(_contextObject, _evalContext, _ref.Name, newValue); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/QualifiedIdentifier.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/QualifiedIdentifier.cs deleted file mode 100644 index 2c1b15917d..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/QualifiedIdentifier.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class QualifiedIdentifier : SpelNode -{ - private ITypedValue _value; - - public QualifiedIdentifier(int startPos, int endPos, params SpelNode[] operands) - : base(startPos, endPos, operands) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - // Cache the concatenation of child identifiers - if (_value == null) - { - var sb = new StringBuilder(); - - for (int i = 0; i < ChildCount; i++) - { - object value = children[i].GetValueInternal(state).Value; - - if (i > 0 && (value == null || !value.ToString().StartsWith('$'))) - { - sb.Append('.'); - } - - sb.Append(value); - } - - _value = new TypedValue(sb.ToString()); - } - - return _value; - } - - public override string ToStringAst() - { - var sb = new StringBuilder(); - - if (_value != null) - { - sb.Append(_value.Value); - } - else - { - for (int i = 0; i < ChildCount; i++) - { - if (i > 0) - { - sb.Append('.'); - } - - sb.Append(GetChild(i).ToStringAst()); - } - } - - return sb.ToString(); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/RealLiteral.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/RealLiteral.cs deleted file mode 100644 index 70d0f8040f..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/RealLiteral.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class RealLiteral : Literal -{ - private readonly ITypedValue _value; - - public RealLiteral(string payload, int startPos, int endPos, double value) - : base(payload, startPos, endPos) - { - _value = new TypedValue(value); - exitTypeDescriptor = TypeDescriptor.D; - } - - public override ITypedValue GetLiteralValue() - { - return _value; - } - - public override bool IsCompilable() - { - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - gen.Emit(OpCodes.Ldc_R8, (double)_value.Value); - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/Selection.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/Selection.cs deleted file mode 100644 index acbc3910a9..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/Selection.cs +++ /dev/null @@ -1,199 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class Selection : SpelNode -{ - public const int All = 0; - public const int First = 1; - public const int Last = 2; - - private readonly int _variant; - - private readonly bool _nullSafe; - - public Selection(bool nullSafe, int variant, int startPos, int endPos, SpelNode expression) - : base(startPos, endPos, expression) - { - _nullSafe = nullSafe; - _variant = variant; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - return GetValueRef(state).GetValue(); - } - - public override string ToStringAst() - { - return $"{GetPrefix()}{GetChild(0).ToStringAst()}]"; - } - - protected internal override IValueRef GetValueRef(ExpressionState state) - { - ITypedValue op = state.GetActiveContextObject(); - object operand = op.Value; - SpelNode selectionCriteria = children[0]; - - if (operand is IDictionary mapData) - { - // Don't lose generic info for the new map - var result = new Dictionary(); - object lastKey = null; - - foreach (DictionaryEntry entry in mapData) - { - try - { - var kvPair = new TypedValue(entry); - state.PushActiveContextObject(kvPair); - state.EnterScope(); - object val = selectionCriteria.GetValueInternal(state).Value; - - if (val is bool boolean) - { - if (boolean) - { - if (_variant == First) - { - result[entry.Key] = entry.Value; - return new TypedValueHolderValueRef(new TypedValue(result), this); - } - - result[entry.Key] = entry.Value; - lastKey = entry.Key; - } - } - else - { - throw new SpelEvaluationException(selectionCriteria.StartPosition, SpelMessage.ResultOfSelectionCriteriaIsNotBoolean); - } - } - finally - { - state.PopActiveContextObject(); - state.ExitScope(); - } - } - - if ((_variant == First || _variant == Last) && result.Count == 0) - { - return new TypedValueHolderValueRef(new TypedValue(null), this); - } - - if (_variant == Last) - { - var resultMap = new Dictionary(); - result.TryGetValue(lastKey, out object lastValue); - resultMap[lastKey] = lastValue; - return new TypedValueHolderValueRef(new TypedValue(resultMap), this); - } - - return new TypedValueHolderValueRef(new TypedValue(result), this); - } - - if (operand is IEnumerable data) - { - var operandAsArray = data as Array; - - var result = new List(); - int index = 0; - - foreach (object element in data) - { - try - { - state.PushActiveContextObject(new TypedValue(element)); - state.EnterScope("index", index); - object val = selectionCriteria.GetValueInternal(state).Value; - - if (val is bool boolean) - { - if (boolean) - { - if (_variant == First) - { - return new TypedValueHolderValueRef(new TypedValue(element), this); - } - - result.Add(element); - } - } - else - { - throw new SpelEvaluationException(selectionCriteria.StartPosition, SpelMessage.ResultOfSelectionCriteriaIsNotBoolean); - } - - index++; - } - finally - { - state.ExitScope(); - state.PopActiveContextObject(); - } - } - - if ((_variant == First || _variant == Last) && result.Count == 0) - { - return NullValueRef.Instance; - } - - if (_variant == Last) - { - object lastElem = result.Count == 0 ? null : result[^1]; - return new TypedValueHolderValueRef(new TypedValue(lastElem), this); - } - - if (operandAsArray == null) - { - return new TypedValueHolderValueRef(new TypedValue(result), this); - } - - // Array - Type elementType = null; - Type typeDesc = op.TypeDescriptor; - - if (typeDesc != null) - { - elementType = ReflectionHelper.GetElementTypeDescriptor(typeDesc); - } - - if (elementType == null) - { - throw new InvalidOperationException("Unresolvable element type"); - } - - var resultArray = Array.CreateInstance(elementType, result.Count); - Array.Copy(result.ToArray(), 0, resultArray, 0, result.Count); - return new TypedValueHolderValueRef(new TypedValue(resultArray), this); - } - - if (operand == null) - { - if (_nullSafe) - { - return NullValueRef.Instance; - } - - throw new SpelEvaluationException(StartPosition, SpelMessage.InvalidTypeForSelection, "null"); - } - - throw new SpelEvaluationException(StartPosition, SpelMessage.InvalidTypeForSelection, operand.GetType().FullName); - } - - private string GetPrefix() - { - return _variant switch - { - All => "?[", - First => "^[", - Last => "$[", - _ => string.Empty - }; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/ServiceReference.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/ServiceReference.cs deleted file mode 100644 index dd9456bbcf..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/ServiceReference.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class ServiceReference : SpelNode -{ - private readonly string _serviceName; - - public ServiceReference(int startPos, int endPos, string serviceName) - : base(startPos, endPos) - { - _serviceName = serviceName; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - IServiceResolver serviceResolver = state.EvaluationContext.ServiceResolver; - - if (serviceResolver == null) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.NoServiceResolverRegistered, _serviceName); - } - - try - { - return new TypedValue(serviceResolver.Resolve(state.EvaluationContext, _serviceName)); - } - catch (AccessException ex) - { - throw new SpelEvaluationException(StartPosition, ex, SpelMessage.ExceptionDuringServiceResolution, _serviceName, ex.Message); - } - } - - public override string ToStringAst() - { - var sb = new StringBuilder(); - sb.Append('@'); - - if (!_serviceName.Contains('.')) - { - sb.Append(_serviceName); - } - else - { - sb.Append('\'').Append(_serviceName).Append('\''); - } - - return sb.ToString(); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/SpelNode.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/SpelNode.cs deleted file mode 100644 index b40e118605..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/SpelNode.cs +++ /dev/null @@ -1,267 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Common; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public abstract class SpelNode : ISpelNode -{ - private static readonly SpelNode[] NoChildren = Array.Empty(); - private SpelNode _parent; - protected SpelNode[] children = NoChildren; - protected volatile TypeDescriptor exitTypeDescriptor; - - public virtual TypeDescriptor ExitDescriptor => exitTypeDescriptor; - - public virtual int StartPosition { get; } - - public virtual int EndPosition { get; } - - public virtual int ChildCount => children.Length; - - protected SpelNode(int startPos, int endPos, params SpelNode[] operands) - { - StartPosition = startPos; - EndPosition = endPos; - - if (operands != null && operands.Any()) - { - children = operands; - - foreach (SpelNode operand in operands) - { - if (operand == null) - { - throw new InvalidOperationException("Operand must not be null"); - } - - operand._parent = this; - } - } - } - - public virtual bool IsCompilable() - { - return false; - } - - public virtual object GetValue(ExpressionState state) - { - return GetValueInternal(state).Value; - } - - public virtual ITypedValue GetTypedValue(ExpressionState state) - { - return GetValueInternal(state); - } - - public virtual bool IsWritable(ExpressionState state) - { - return false; - } - - public virtual void SetValue(ExpressionState state, object newValue) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.SetValueNotSupported, GetType()); - } - - public virtual ISpelNode GetChild(int index) - { - return children[index]; - } - - public virtual Type GetObjectType(object obj) - { - if (obj == null) - { - return null; - } - - return obj as Type ?? obj.GetType(); - } - - public virtual void GenerateCode(ILGenerator gen, CodeFlow cf) - { - throw new InvalidOperationException($"{GetType().FullName} has no GenerateCode(..) method"); - } - - public abstract ITypedValue GetValueInternal(ExpressionState state); - - public abstract string ToStringAst(); - - protected internal virtual bool NextChildIs(params Type[] types) - { - if (_parent != null) - { - SpelNode[] peers = _parent.children; - - for (int i = 0, max = peers.Length; i < max; i++) - { - if (this == peers[i]) - { - if (i + 1 >= max) - { - return false; - } - - Type peerClass = peers[i + 1].GetType(); - - foreach (Type desiredType in types) - { - if (peerClass == desiredType) - { - return true; - } - } - - return false; - } - } - } - - return false; - } - - protected internal virtual T GetValue(ExpressionState state) - { - return (T)GetValue(state, typeof(T)); - } - - protected internal virtual object GetValue(ExpressionState state, Type desiredReturnType) - { - return ExpressionUtils.ConvertTypedValue(state.EvaluationContext, GetValueInternal(state), desiredReturnType); - } - - protected internal virtual IValueRef GetValueRef(ExpressionState state) - { - throw new SpelEvaluationException(StartPosition, SpelMessage.NotAssignable, ToStringAst()); - } - - protected static void GenerateCodeForArguments(ILGenerator gen, CodeFlow cf, MethodBase member, SpelNode[] arguments) - { - TypeDescriptor[] paramDescriptors = CodeFlow.ToDescriptors(member.GetParameterTypes()); - bool isVarargs = member.IsVarArgs(); - - if (isVarargs) - { - int childCount = arguments.Length; - - // The final parameter may or may not need packaging into an array, or nothing may - // have been passed to satisfy the varargs and so something needs to be built. - int p; - - // Fulfill all the parameter requirements except the last one - for (p = 0; p < paramDescriptors.Length - 1; p++) - { - GenerateCodeForArgument(gen, cf, arguments[p], paramDescriptors[p]); - } - - SpelNode lastChild = childCount == 0 ? null : arguments[childCount - 1]; - TypeDescriptor arrayType = paramDescriptors[^1]; - - // Determine if the final passed argument is already suitably packaged in array - // form to be passed to the method - if (lastChild != null && arrayType.Equals(lastChild.ExitDescriptor)) - { - GenerateCodeForArgument(gen, cf, lastChild, paramDescriptors[p]); - } - else - { - Type arrElemType = arrayType.Value.GetElementType(); - var arrElemDesc = new TypeDescriptor(arrElemType); - - gen.Emit(OpCodes.Ldc_I4, childCount - p); - gen.Emit(OpCodes.Newarr, arrElemType); - - // Package up the remaining arguments into the array - int arrayIndex = 0; - - while (p < childCount) - { - SpelNode child = arguments[p]; - gen.Emit(OpCodes.Dup); - gen.Emit(OpCodes.Ldc_I4, arrayIndex++); - GenerateCodeForArgument(gen, cf, child, arrElemDesc); - gen.Emit(GetStelemInsn(arrElemType)); - p++; - } - } - } - else - { - for (int i = 0; i < paramDescriptors.Length; i++) - { - GenerateCodeForArgument(gen, cf, arguments[i], paramDescriptors[i]); - } - } - } - - protected static void GenerateCodeForArgument(ILGenerator gen, CodeFlow cf, SpelNode argument, TypeDescriptor paramDesc) - { - cf.EnterCompilationScope(); - argument.GenerateCode(gen, cf); - TypeDescriptor lastDesc = cf.LastDescriptor(); - - if (lastDesc == null) - { - throw new InvalidOperationException("No last descriptor"); - } - - bool valueTypeOnStack = CodeFlow.IsValueType(lastDesc); - - // Check if need to box it for the method reference? - if (valueTypeOnStack && paramDesc.IsReferenceType) - { - CodeFlow.InsertBoxIfNecessary(gen, lastDesc); - } - else if (paramDesc.IsValueType && !paramDesc.IsBoxed && !valueTypeOnStack) - { - gen.Emit(OpCodes.Unbox_Any, paramDesc.Value); - } - else - { - // This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in) - CodeFlow.InsertCastClass(gen, paramDesc); - } - - cf.ExitCompilationScope(); - } - - protected static OpCode GetLdElemInsn(Type arrElemType) - { - return arrElemType switch - { - var t when t == typeof(sbyte) => OpCodes.Ldelem_I1, - var t when t == typeof(byte) => OpCodes.Ldelem_U1, - var t when t == typeof(short) || t == typeof(char) => OpCodes.Ldelem_I2, - var t when t == typeof(ushort) => OpCodes.Ldelem_U2, - var t when t == typeof(int) => OpCodes.Ldelem_I4, - var t when t == typeof(uint) => OpCodes.Ldelem_U4, - var t when t == typeof(long) || t == typeof(ulong) => OpCodes.Ldelem_I8, - var t when t == typeof(float) => OpCodes.Ldelem_R4, - var t when t == typeof(double) => OpCodes.Ldelem_R8, - var t when t == typeof(IntPtr) || t == typeof(UIntPtr) => OpCodes.Ldelem_I, - _ => OpCodes.Ldelem_Ref - }; - } - - protected static OpCode GetStelemInsn(Type arrElemType) - { - return arrElemType switch - { - var t when t == typeof(sbyte) || t == typeof(byte) || t == typeof(bool) => OpCodes.Stelem_I1, - var t when t == typeof(short) || t == typeof(ushort) || t == typeof(char) => OpCodes.Stelem_I2, - var t when t == typeof(int) || t == typeof(uint) => OpCodes.Stelem_I4, - var t when t == typeof(long) || t == typeof(ulong) => OpCodes.Stelem_I8, - var t when t == typeof(float) => OpCodes.Stelem_R4, - var t when t == typeof(double) => OpCodes.Stelem_R8, - var t when t == typeof(IntPtr) || t == typeof(UIntPtr) => OpCodes.Stelem_I, - _ => OpCodes.Stelem_Ref - }; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/SpelTypeCode.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/SpelTypeCode.cs deleted file mode 100644 index 9442fe3e27..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/SpelTypeCode.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class SpelTypeCode -{ - private static readonly Dictionary Names = new(); - private static readonly Dictionary Types = new(); - - public static readonly SpelTypeCode Object = new("OBJECT", typeof(object)); - public static readonly SpelTypeCode Boolean = new("BOOLEAN", typeof(bool)); - public static readonly SpelTypeCode Byte = new("BYTE", typeof(byte)); - public static readonly SpelTypeCode Sbyte = new("SBYTE", typeof(sbyte)); - public static readonly SpelTypeCode Char = new("CHAR", typeof(char)); - public static readonly SpelTypeCode Double = new("DOUBLE", typeof(double)); - public static readonly SpelTypeCode Float = new("FLOAT", typeof(float)); - public static readonly SpelTypeCode Int = new("INT", typeof(int)); - public static readonly SpelTypeCode Uint = new("UINT", typeof(uint)); - public static readonly SpelTypeCode Long = new("LONG", typeof(long)); - public static readonly SpelTypeCode Ulong = new("ULONG", typeof(ulong)); - public static readonly SpelTypeCode Short = new("SHORT", typeof(short)); - public static readonly SpelTypeCode Ushort = new("USHORT", typeof(ushort)); - - public Type Type { get; } - - public string Name { get; } - - private SpelTypeCode(string name, Type type) - { - Type = type; - Name = name; - Names.Add(Name, this); - Types.Add(Type, this); - } - - public static SpelTypeCode ForName(string name) - { - if (!Names.TryGetValue(name.ToUpperInvariant(), out SpelTypeCode result)) - { - return Object; - } - - return result; - } - - public static SpelTypeCode ForType(Type type) - { - if (!Types.TryGetValue(type, out SpelTypeCode result)) - { - return Object; - } - - return result; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/StringLiteral.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/StringLiteral.cs deleted file mode 100644 index 3e34f30a81..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/StringLiteral.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class StringLiteral : Literal -{ - private readonly ITypedValue _value; - - public StringLiteral(string payload, int startPos, int endPos, string value) - : base(payload, startPos, endPos) - { - string valueWithinQuotes = value.Substring(1, value.Length - 1 - 1); - valueWithinQuotes = valueWithinQuotes.Replace("''", "'", StringComparison.Ordinal); - valueWithinQuotes = valueWithinQuotes.Replace("\"\"", "\"", StringComparison.Ordinal); - _value = new TypedValue(valueWithinQuotes); - exitTypeDescriptor = TypeDescriptor.String; - } - - public override ITypedValue GetLiteralValue() - { - return _value; - } - - public override string ToString() - { - return $"'{GetLiteralValue().Value}'"; - } - - public override bool IsCompilable() - { - return true; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - gen.Emit(OpCodes.Ldstr, (string)_value.Value); - cf.PushDescriptor(exitTypeDescriptor); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/Ternary.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/Ternary.cs deleted file mode 100644 index 72c5a25881..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/Ternary.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class Ternary : SpelNode -{ - public Ternary(int startPos, int endPos, params SpelNode[] args) - : base(startPos, endPos, args) - { - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - try - { - bool value = children[0].GetValue(state); - ITypedValue result = children[value ? 1 : 2].GetValueInternal(state); - ComputeExitTypeDescriptor(); - return result; - } - catch (Exception ex) - { - throw new SpelEvaluationException(GetChild(0).StartPosition, ex, SpelMessage.TypeConversionError, "null", "System.Boolean"); - } - } - - public override string ToStringAst() - { - return $"{GetChild(0).ToStringAst()} ? {GetChild(1).ToStringAst()} : {GetChild(2).ToStringAst()}"; - } - - public override bool IsCompilable() - { - SpelNode condition = children[0]; - SpelNode left = children[1]; - SpelNode right = children[2]; - - bool isCompilable = condition.IsCompilable() && left.IsCompilable() && right.IsCompilable(); - return isCompilable && CodeFlow.IsBooleanCompatible(condition.ExitDescriptor) && left.ExitDescriptor != null && right.ExitDescriptor != null; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - // May reach here without it computed if all elements are literals - ComputeExitTypeDescriptor(); - - cf.EnterCompilationScope(); - children[0].GenerateCode(gen, cf); - TypeDescriptor lastDesc = cf.LastDescriptor() ?? throw new InvalidOperationException("No last descriptor"); - - if (!CodeFlow.IsValueType(lastDesc)) - { - gen.Emit(OpCodes.Unbox_Any, typeof(bool)); - } - - cf.ExitCompilationScope(); - - Label elseTarget = gen.DefineLabel(); - Label endOfIfTarget = gen.DefineLabel(); - - gen.Emit(OpCodes.Brfalse, elseTarget); - cf.EnterCompilationScope(); - children[1].GenerateCode(gen, cf); - - if (!CodeFlow.IsValueType(exitTypeDescriptor)) - { - lastDesc = cf.LastDescriptor(); - - if (lastDesc == null) - { - throw new InvalidOperationException("No last descriptor"); - } - - CodeFlow.InsertBoxIfNecessary(gen, lastDesc); - } - - cf.ExitCompilationScope(); - gen.Emit(OpCodes.Br, endOfIfTarget); - - gen.MarkLabel(elseTarget); - cf.EnterCompilationScope(); - children[2].GenerateCode(gen, cf); - - if (!CodeFlow.IsValueType(exitTypeDescriptor)) - { - lastDesc = cf.LastDescriptor(); - - if (lastDesc == null) - { - throw new InvalidOperationException("No last descriptor"); - } - - CodeFlow.InsertBoxIfNecessary(gen, lastDesc); - } - - cf.ExitCompilationScope(); - gen.MarkLabel(endOfIfTarget); - cf.PushDescriptor(exitTypeDescriptor); - } - - private void ComputeExitTypeDescriptor() - { - if (exitTypeDescriptor == null && children[1].ExitDescriptor != null && children[2].ExitDescriptor != null) - { - TypeDescriptor leftDescriptor = children[1].ExitDescriptor; - TypeDescriptor rightDescriptor = children[2].ExitDescriptor; - - if (Equals(leftDescriptor, rightDescriptor)) - { - exitTypeDescriptor = leftDescriptor; - } - else - { - // Use the easiest to compute common super type - exitTypeDescriptor = TypeDescriptor.Object; - } - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/TypeReference.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/TypeReference.cs deleted file mode 100644 index d94fdfd3a3..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/TypeReference.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Reflection.Emit; -using System.Text; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class TypeReference : SpelNode -{ - private static readonly MethodInfo GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), BindingFlags.Static | BindingFlags.Public); - private readonly int _dimensions; - private Type _type; - - public TypeReference(int startPos, int endPos, SpelNode qualifiedId) - : this(startPos, endPos, qualifiedId, 0) - { - } - - public TypeReference(int startPos, int endPos, SpelNode qualifiedId, int dims) - : base(startPos, endPos, qualifiedId) - { - _dimensions = dims; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - // Possible optimization here if we cache the discovered type reference, but can we do that? - string typeName = (string)children[0].GetValueInternal(state).Value; - - if (typeName == null) - { - throw new InvalidOperationException("No type name"); - } - - if (!typeName.Contains('.') && char.IsLower(typeName[0])) - { - SpelTypeCode tc = SpelTypeCode.ForName(typeName.ToUpperInvariant()); - - if (tc != SpelTypeCode.Object) - { - // It is a primitive type - Type type = MakeArrayIfNecessary(tc.Type); - exitTypeDescriptor = TypeDescriptor.Type; - _type = type; - return new TypedValue(type); - } - } - - Type foundType = state.FindType(typeName); - foundType = MakeArrayIfNecessary(foundType); - exitTypeDescriptor = TypeDescriptor.Type; - _type = foundType; - return new TypedValue(foundType); - } - - public override string ToStringAst() - { - var sb = new StringBuilder("T("); - sb.Append(GetChild(0).ToStringAst()); - - for (int d = 0; d < _dimensions; d++) - { - sb.Append("[]"); - } - - sb.Append(')'); - return sb.ToString(); - } - - public override bool IsCompilable() - { - return exitTypeDescriptor != null; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - if (_type == null) - { - throw new InvalidOperationException("No type available"); - } - - gen.Emit(OpCodes.Ldtoken, _type); - gen.Emit(OpCodes.Call, GetTypeFromHandle); - cf.PushDescriptor(exitTypeDescriptor); - } - - private Type MakeArrayIfNecessary(Type type) - { - if (_dimensions != 0) - { - for (int i = 0; i < _dimensions; i++) - { - var array = Array.CreateInstance(type, 0); - type = array.GetType(); - } - } - - return type; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/TypedValueHolderValueRef.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/TypedValueHolderValueRef.cs deleted file mode 100644 index 736956093c..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/TypedValueHolderValueRef.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class TypedValueHolderValueRef : IValueRef -{ - private readonly ITypedValue _typedValue; - private readonly SpelNode _node; // used only for error reporting - - public bool IsWritable => false; - - public TypedValueHolderValueRef(ITypedValue typedValue, SpelNode node) - { - _typedValue = typedValue; - _node = node; - } - - public ITypedValue GetValue() - { - return _typedValue; - } - - public void SetValue(object newValue) - { - throw new SpelEvaluationException(_node.StartPosition, SpelMessage.NotAssignable, _node.ToStringAst()); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Ast/VariableReference.cs b/src/Common/src/Common.Expression/Internal/Spring/Ast/VariableReference.cs deleted file mode 100644 index e58a64a6ec..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Ast/VariableReference.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Support; - -namespace Steeltoe.Common.Expression.Internal.Spring.Ast; - -public class VariableReference : SpelNode -{ - // Well known variables: - private const string This = "this"; // currently active context object - private const string Root = "root"; // root context object - - private readonly string _name; - private MethodInfo _method; - - public VariableReference(string variableName, int startPos, int endPos) - : base(startPos, endPos) - { - _name = variableName; - } - - public override ITypedValue GetValueInternal(ExpressionState state) - { - if (_name == This) - { - return state.GetActiveContextObject(); - } - - if (_name == Root) - { - ITypedValue obj = state.RootContextObject; - exitTypeDescriptor = CodeFlow.ToDescriptorFromObject(obj.Value); - return obj; - } - - ITypedValue result = state.LookupVariable(_name); - object value = result.Value; - - if (value == null || !ReflectionHelper.IsPublic(value.GetType())) - { - // If the type is not public then when generateCode produces a checkcast to it - // then an IllegalAccessError will occur. - // If resorting to Object isn't sufficient, the hierarchy could be traversed for - // the first public type. - exitTypeDescriptor = TypeDescriptor.Object; - } - else - { - exitTypeDescriptor = CodeFlow.ToDescriptorFromObject(value); - } - - // a null value will mean either the value was null or the variable was not found - return result; - } - - public override void SetValue(ExpressionState state, object newValue) - { - state.SetVariable(_name, newValue); - } - - public override string ToStringAst() - { - return $"#{_name}"; - } - - public override bool IsWritable(ExpressionState state) - { - return !(_name == This || _name == Root); - } - - public override bool IsCompilable() - { - return exitTypeDescriptor != null; - } - - public override void GenerateCode(ILGenerator gen, CodeFlow cf) - { - if (_name == Root) - { - CodeFlow.LoadTarget(gen); - } - else - { - gen.Emit(OpCodes.Ldarg_2); - gen.Emit(OpCodes.Ldstr, _name); - gen.Emit(OpCodes.Callvirt, GetLookUpVariableMethod()); - } - - CodeFlow.InsertCastClass(gen, exitTypeDescriptor); - cf.PushDescriptor(exitTypeDescriptor); - } - - protected internal override IValueRef GetValueRef(ExpressionState state) - { - if (_name == This) - { - return new TypedValueHolderValueRef(state.GetActiveContextObject(), this); - } - - if (_name == Root) - { - return new TypedValueHolderValueRef(state.RootContextObject, this); - } - - ITypedValue result = state.LookupVariable(_name); - - // a null value will mean either the value was null or the variable was not found - return new VariableRef(_name, result, state.EvaluationContext); - } - - private MethodInfo GetLookUpVariableMethod() - { - if (_method == null) - { - _method = typeof(IEvaluationContext).GetMethods().Single(m => m.Name == "LookupVariable" && !m.IsGenericMethod); - } - - return _method; - } - - private sealed class VariableRef : IValueRef - { - private readonly string _name; - private readonly ITypedValue _value; - private readonly IEvaluationContext _evaluationContext; - - public bool IsWritable => true; - - public VariableRef(string name, ITypedValue value, IEvaluationContext evaluationContext) - { - _name = name; - _value = value; - _evaluationContext = evaluationContext; - } - - public ITypedValue GetValue() - { - return _value; - } - - public void SetValue(object newValue) - { - _evaluationContext.SetVariable(_name, newValue); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/CodeFlow.cs b/src/Common/src/Common.Expression/Internal/Spring/CodeFlow.cs deleted file mode 100644 index 5abfe98e0d..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/CodeFlow.cs +++ /dev/null @@ -1,412 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; -using Steeltoe.Common.Expression.Internal.Spring.Standard; - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public class CodeFlow -{ - private static readonly Dictionary Primitives = new(); - private readonly CompiledExpression _compiledExpression; - private readonly Stack> _compilationScopes; - private readonly List> _initGenerators = new(); - private int _nextFieldId = 1; - - static CodeFlow() - { - Primitives.Add(typeof(void), TypeDescriptor.V); - Primitives.Add(typeof(int), TypeDescriptor.I); - Primitives.Add(typeof(long), TypeDescriptor.J); - Primitives.Add(typeof(float), TypeDescriptor.F); - Primitives.Add(typeof(double), TypeDescriptor.D); - Primitives.Add(typeof(byte), TypeDescriptor.B); - Primitives.Add(typeof(char), TypeDescriptor.C); - Primitives.Add(typeof(short), TypeDescriptor.S); - Primitives.Add(typeof(bool), TypeDescriptor.Z); - Primitives.Add(typeof(sbyte), TypeDescriptor.A); - Primitives.Add(typeof(ushort), TypeDescriptor.M); - Primitives.Add(typeof(uint), TypeDescriptor.N); - Primitives.Add(typeof(ulong), TypeDescriptor.O); - Primitives.Add(typeof(IntPtr), TypeDescriptor.P); - Primitives.Add(typeof(UIntPtr), TypeDescriptor.Q); - } - - public CodeFlow(CompiledExpression compiledExpression) - { - _compiledExpression = compiledExpression; - _compilationScopes = new Stack>(); - _compilationScopes.Push(new List()); - } - - public static bool IsBooleanCompatible(TypeDescriptor descriptor) - { - return descriptor != null && (descriptor == TypeDescriptor.Z || descriptor == TypeDescriptor.Z.Boxed()); - } - - public static void InsertCastClass(ILGenerator gen, TypeDescriptor descriptor) - { - if (descriptor != null && !descriptor.IsPrimitive && !descriptor.IsValueType && descriptor != TypeDescriptor.Object) - { - gen.Emit(OpCodes.Castclass, descriptor.Value); - } - } - - public static TypeDescriptor ToDescriptor(Type type) - { - if (IsPrimitive(type)) - { - return Primitives[type]; - } - - return new TypeDescriptor(type); - } - - public static TypeDescriptor ToDescriptorFromObject(object value) - { - if (value == null) - { - return TypeDescriptor.Object; - } - - TypeDescriptor desc = ToDescriptor(value.GetType()); - - if (desc.IsBoxable) - { - return desc.Boxed(); - } - - return desc; - } - - public static bool IsPrimitive(Type type) - { - return type != null && (type.IsPrimitive || type == typeof(void)); - } - - public static bool IsValueType(TypeDescriptor descriptor) - { - return descriptor != null && descriptor.IsValueType && !descriptor.IsBoxed && !descriptor.IsVoid; - } - - public static bool IsIntegerForNumericOp(object value) - { - Type valueType = value?.GetType(); - - // Other .NET types, what about long? - return valueType == typeof(int) || valueType == typeof(short) || valueType == typeof(byte); - } - - public static bool IsPrimitiveOrUnboxableSupportedNumberOrBoolean(TypeDescriptor descriptor) - { - if (descriptor == null) - { - return false; - } - - if (IsPrimitiveOrUnboxableSupportedNumber(descriptor)) - { - return true; - } - - return descriptor == TypeDescriptor.Z || TypeDescriptor.Z.Boxed() == descriptor; - } - - public static bool IsPrimitiveOrUnboxableSupportedNumber(TypeDescriptor descriptor) - { - if (descriptor == null) - { - return false; - } - - if (descriptor.IsPrimitive) - { - return descriptor == TypeDescriptor.D || descriptor == TypeDescriptor.F || descriptor == TypeDescriptor.I || descriptor == TypeDescriptor.J; - } - - return descriptor == TypeDescriptor.D.Boxed() || descriptor == TypeDescriptor.F.Boxed() || descriptor == TypeDescriptor.I.Boxed() || - descriptor == TypeDescriptor.J.Boxed(); - } - - public static TypeDescriptor ToPrimitiveTargetDescriptor(TypeDescriptor descriptor) - { - if (descriptor.IsPrimitive) - { - return descriptor; - } - - if (descriptor.IsBoxedPrimitive) - { - return descriptor.UnBox(); - } - - throw new InvalidOperationException($"No primitive for '{descriptor}'"); - } - - public static void InsertBoxIfNecessary(ILGenerator gen, TypeDescriptor descriptor) - { - if (descriptor != null && descriptor.IsBoxable) - { - gen.Emit(OpCodes.Box, descriptor.Value); - } - } - - public static bool AreBoxingCompatible(TypeDescriptor desc1, TypeDescriptor desc2) - { - if (desc1 == desc2) - { - return true; - } - - // Primitive and not boxed - if (desc1.IsPrimitive) - { - return desc2 == desc1.Boxed(); - } - - if (desc2.IsPrimitive) - { - return desc1 == desc2.Boxed(); - } - - return false; - } - - public static void InsertNumericUnboxOrPrimitiveTypeCoercion(ILGenerator gen, TypeDescriptor stackDescriptor, TypeDescriptor targetDescriptor) - { - if (stackDescriptor.IsBoxedNumber) - { - gen.Emit(OpCodes.Unbox_Any, stackDescriptor.Value); - InsertAnyNecessaryTypeConversionBytecode(gen, targetDescriptor, stackDescriptor.UnBox()); - } - else - { - InsertAnyNecessaryTypeConversionBytecode(gen, targetDescriptor, stackDescriptor); - } - } - - public static void InsertAnyNecessaryTypeConversionBytecode(ILGenerator gen, TypeDescriptor targetDescriptor, TypeDescriptor stackDescriptor) - { - if (IsValueType(stackDescriptor)) - { - if (stackDescriptor == TypeDescriptor.I || stackDescriptor == TypeDescriptor.B || stackDescriptor == TypeDescriptor.S || - stackDescriptor == TypeDescriptor.C) - { - if (targetDescriptor == TypeDescriptor.D) - { - gen.Emit(OpCodes.Conv_R8); - } - else if (targetDescriptor == TypeDescriptor.F) - { - gen.Emit(OpCodes.Conv_R4); - } - else if (targetDescriptor == TypeDescriptor.J) - { - if (stackDescriptor == TypeDescriptor.I || stackDescriptor == TypeDescriptor.S) - { - gen.Emit(OpCodes.Conv_I8); - } - else - { - gen.Emit(OpCodes.Conv_U8); - } - } - else if (targetDescriptor == TypeDescriptor.I) - { - gen.Emit(OpCodes.Conv_I4); - } - else - { - throw new InvalidOperationException($"Cannot get from {stackDescriptor} to {targetDescriptor}"); - } - } - else if (stackDescriptor == TypeDescriptor.J) - { - if (targetDescriptor == TypeDescriptor.D) - { - gen.Emit(OpCodes.Conv_R8); - } - else if (targetDescriptor == TypeDescriptor.F) - { - gen.Emit(OpCodes.Conv_R4); - } - else if (targetDescriptor == TypeDescriptor.J) - { - // nop - } - else if (targetDescriptor == TypeDescriptor.I) - { - gen.Emit(OpCodes.Conv_I4); - } - else - { - throw new InvalidOperationException($"Cannot get from {stackDescriptor} to {targetDescriptor}"); - } - } - else if (stackDescriptor == TypeDescriptor.F) - { - if (targetDescriptor == TypeDescriptor.D) - { - gen.Emit(OpCodes.Conv_R8); - } - else if (targetDescriptor == TypeDescriptor.F) - { - // nop - } - else if (targetDescriptor == TypeDescriptor.J) - { - gen.Emit(OpCodes.Conv_I8); - } - else if (targetDescriptor == TypeDescriptor.I) - { - gen.Emit(OpCodes.Conv_I4); - } - else - { - throw new InvalidOperationException($"Cannot get from {stackDescriptor} to {targetDescriptor}"); - } - } - else if (stackDescriptor == TypeDescriptor.D) - { - if (targetDescriptor == TypeDescriptor.D) - { - // nop - } - else if (targetDescriptor == TypeDescriptor.F) - { - gen.Emit(OpCodes.Conv_R4); - } - else if (targetDescriptor == TypeDescriptor.J) - { - gen.Emit(OpCodes.Conv_I8); - } - else if (targetDescriptor == TypeDescriptor.I) - { - gen.Emit(OpCodes.Conv_I4); - } - else - { - throw new InvalidOperationException($"Cannot get from {stackDescriptor} to {targetDescriptor}"); - } - } - } - } - - public static TypeDescriptor ToBoxedDescriptor(TypeDescriptor descriptor) - { - if (descriptor.IsBoxable) - { - return descriptor.Boxed(); - } - - return descriptor; - } - - public static void LoadTarget(ILGenerator gen) - { - gen.Emit(OpCodes.Ldarg_1); - } - - public static void LoadEvaluationContext(ILGenerator gen) - { - gen.Emit(OpCodes.Ldarg_2); - } - - public static TypeDescriptor[] ToDescriptors(Type[] types) - { - int typesCount = types.Length; - var descriptors = new TypeDescriptor[typesCount]; - - for (int p = 0; p < typesCount; p++) - { - descriptors[p] = ToDescriptor(types[p]); - } - - return descriptors; - } - - public void UnboxBooleanIfNecessary(ILGenerator gen) - { - TypeDescriptor lastDescriptor = LastDescriptor(); - - if (lastDescriptor == TypeDescriptor.Z.Boxed()) - { - gen.Emit(OpCodes.Unbox_Any, lastDescriptor.Value); - } - } - - public DynamicMethod Finish(int compilationId) - { - if (_initGenerators.Count > 0) - { - string methodName = $"SpelExpressionInit{compilationId}"; - - var method = new DynamicMethod(methodName, typeof(void), new[] - { - typeof(SpelCompiledExpression), - typeof(object), - typeof(IEvaluationContext) - }, typeof(SpelCompiledExpression)); - - ILGenerator ilGenerator = method.GetILGenerator(4096); - - foreach (Action generator in _initGenerators) - { - generator(ilGenerator, this); - } - - ilGenerator.Emit(OpCodes.Ret); - - return method; - } - - return null; - } - - public void PushDescriptor(TypeDescriptor descriptor) - { - if (descriptor != null) - { - _compilationScopes.Peek().Add(descriptor); - } - } - - public void EnterCompilationScope() - { - _compilationScopes.Push(new List()); - } - - public void ExitCompilationScope() - { - _compilationScopes.Pop(); - } - - public TypeDescriptor LastDescriptor() - { - List top = _compilationScopes.Peek(); - - if (top == null || top.Count == 0) - { - return null; - } - - return top[^1]; - } - - public int NextFieldId() - { - return _nextFieldId++; - } - - public void RegisterNewInitGenerator(Action generator) - { - _initGenerators.Add(generator); - } - - public void RegisterNewField(string constantFieldName, object fieldValue) - { - _compiledExpression.DynamicFields.Add(constantFieldName, fieldValue); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Common/CompositeStringExpression.cs b/src/Common/src/Common.Expression/Internal/Spring/Common/CompositeStringExpression.cs deleted file mode 100644 index 3349df628e..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Common/CompositeStringExpression.cs +++ /dev/null @@ -1,202 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Expression.Internal.Spring.Common; - -public class CompositeStringExpression : IExpression -{ - public string ExpressionString { get; } - - public List Expressions { get; } - - public CompositeStringExpression(string expressionString, List expressions) - { - ExpressionString = expressionString; - Expressions = expressions; - } - - public string GetValue() - { - var sb = new StringBuilder(); - - foreach (IExpression expression in Expressions) - { - object value = expression.GetValue(typeof(string)); - - if (value != null) - { - sb.Append(value); - } - } - - return sb.ToString(); - } - - public T GetValue() - { - return (T)GetValue(typeof(T)); - } - - public object GetValue(Type desiredResultType) - { - string value = GetValue(); - return ExpressionUtils.ConvertTypedValue(null, new TypedValue(value), desiredResultType); - } - - public string GetValue(object rootObject) - { - var sb = new StringBuilder(); - - foreach (IExpression expression in Expressions) - { - object value = expression.GetValue(rootObject, typeof(string)); - - if (value != null) - { - sb.Append(value); - } - } - - return sb.ToString(); - } - - public T GetValue(object rootObject) - { - return (T)GetValue(rootObject, typeof(T)); - } - - public object GetValue(object rootObject, Type desiredResultType) - { - string value = GetValue(rootObject); - return ExpressionUtils.ConvertTypedValue(null, new TypedValue(value), desiredResultType); - } - - public string GetValue(IEvaluationContext context) - { - var sb = new StringBuilder(); - - foreach (IExpression expression in Expressions) - { - object value = expression.GetValue(context, typeof(string)); - - if (value != null) - { - sb.Append(value); - } - } - - return sb.ToString(); - } - - public T GetValue(IEvaluationContext context) - { - return (T)GetValue(context, typeof(T)); - } - - public object GetValue(IEvaluationContext context, Type desiredResultType) - { - string value = GetValue(context); - return ExpressionUtils.ConvertTypedValue(context, new TypedValue(value), desiredResultType); - } - - public string GetValue(IEvaluationContext context, object rootObject) - { - var sb = new StringBuilder(); - - foreach (IExpression expression in Expressions) - { - object value = expression.GetValue(context, rootObject, typeof(string)); - - if (value != null) - { - sb.Append(value); - } - } - - return sb.ToString(); - } - - public T GetValue(IEvaluationContext context, object rootObject) - { - return (T)GetValue(context, rootObject, typeof(T)); - } - - public object GetValue(IEvaluationContext context, object rootObject, Type desiredResultType) - { - string value = GetValue(context, rootObject); - return ExpressionUtils.ConvertTypedValue(context, new TypedValue(value), desiredResultType); - } - - public Type GetValueType() - { - return typeof(string); - } - - public Type GetValueType(IEvaluationContext context) - { - return typeof(string); - } - - public Type GetValueType(object rootObject) - { - return typeof(string); - } - - public Type GetValueType(IEvaluationContext context, object rootObject) - { - return typeof(string); - } - - public bool IsWritable(object rootObject) - { - return false; - } - - public bool IsWritable(IEvaluationContext context) - { - return false; - } - - public bool IsWritable(IEvaluationContext context, object rootObject) - { - return false; - } - - public void SetValue(object rootObject, object value) - { - throw new EvaluationException(ExpressionString, "Cannot call setValue on a composite expression"); - } - - public void SetValue(IEvaluationContext context, object value) - { - throw new EvaluationException(ExpressionString, "Cannot call setValue on a composite expression"); - } - - public void SetValue(IEvaluationContext context, object rootObject, object value) - { - throw new EvaluationException(ExpressionString, "Cannot call setValue on a composite expression"); - } - - object IExpression.GetValue() - { - return GetValue(); - } - - object IExpression.GetValue(object rootObject) - { - return GetValue(rootObject); - } - - object IExpression.GetValue(IEvaluationContext context) - { - return GetValue(context); - } - - object IExpression.GetValue(IEvaluationContext context, object rootObject) - { - return GetValue(context, rootObject); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Common/ExpressionUtils.cs b/src/Common/src/Common.Expression/Internal/Spring/Common/ExpressionUtils.cs deleted file mode 100644 index 3c8f41079a..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Common/ExpressionUtils.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal.Spring.Common; - -public static class ExpressionUtils -{ - public static T ConvertTypedValue(IEvaluationContext context, ITypedValue typedValue) - { - return (T)ConvertTypedValue(context, typedValue, typeof(T)); - } - - public static object ConvertTypedValue(IEvaluationContext context, ITypedValue typedValue, Type targetType) - { - object value = typedValue.Value; - - if (targetType == null) - { - return value; - } - - if (context != null) - { - return context.TypeConverter.ConvertValue(value, typedValue.TypeDescriptor, targetType); - } - - if (ClassUtils.IsAssignableValue(targetType, value)) - { - return value; - } - - throw new EvaluationException($"Cannot convert value '{value}' to type '{targetType.FullName}'"); - } - - public static int ToInt(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static uint ToUInt(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static bool ToBoolean(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static double ToDouble(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static long ToLong(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static ulong ToULong(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static char ToChar(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static short ToShort(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static ushort ToUShort(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static float ToFloat(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static byte ToByte(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - public static sbyte ToSByte(ITypeConverter typeConverter, ITypedValue typedValue) - { - return ConvertValue(typeConverter, typedValue); - } - - private static T ConvertValue(ITypeConverter typeConverter, ITypedValue typedValue) - { - object result = typeConverter.ConvertValue(typedValue.Value, typedValue.TypeDescriptor, typeof(T)); - - if (result == null) - { - throw new InvalidOperationException($"Null conversion result for value [{typedValue.Value}]"); - } - - return (T)result; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Common/LiteralExpression.cs b/src/Common/src/Common.Expression/Internal/Spring/Common/LiteralExpression.cs deleted file mode 100644 index 691eee28ae..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Common/LiteralExpression.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Common; - -public class LiteralExpression : IExpression -{ - public string ExpressionString { get; } - - public LiteralExpression(string literalValue) - { - ExpressionString = literalValue; - } - - public string GetValue() - { - return ExpressionString; - } - - public T GetValue() - { - return (T)GetValue(typeof(T)); - } - - public object GetValue(Type desiredResultType) - { - object value = GetValue(); - return ExpressionUtils.ConvertTypedValue(null, new TypedValue(value), desiredResultType); - } - - public string GetValue(object rootObject) - { - return ExpressionString; - } - - public T GetValue(object rootObject) - { - return (T)GetValue(rootObject, typeof(T)); - } - - public object GetValue(object rootObject, Type desiredResultType) - { - object value = GetValue(rootObject); - return ExpressionUtils.ConvertTypedValue(null, new TypedValue(value), desiredResultType); - } - - public string GetValue(IEvaluationContext context) - { - return ExpressionString; - } - - public object GetValue(IEvaluationContext context, Type desiredResultType) - { - object value = GetValue(context); - return ExpressionUtils.ConvertTypedValue(context, new TypedValue(value), desiredResultType); - } - - public T GetValue(IEvaluationContext context) - { - return (T)GetValue(context, typeof(T)); - } - - public string GetValue(IEvaluationContext context, object rootObject) - { - return ExpressionString; - } - - public T GetValue(IEvaluationContext context, object rootObject) - { - return (T)GetValue(context, rootObject, typeof(T)); - } - - public object GetValue(IEvaluationContext context, object rootObject, Type desiredResultType) - { - object value = GetValue(context, rootObject); - return ExpressionUtils.ConvertTypedValue(context, new TypedValue(value), desiredResultType); - } - - public Type GetValueType() - { - return typeof(string); - } - - public Type GetValueType(object rootObject) - { - return typeof(string); - } - - public Type GetValueType(IEvaluationContext context, object rootObject) - { - return typeof(string); - } - - public Type GetValueType(IEvaluationContext context) - { - return typeof(string); - } - - public bool IsWritable(object rootObject) - { - return false; - } - - public bool IsWritable(IEvaluationContext context) - { - return false; - } - - public bool IsWritable(IEvaluationContext context, object rootObject) - { - return false; - } - - public void SetValue(object rootObject, object value) - { - throw new EvaluationException(ExpressionString, "Cannot call SetValue() on a LiteralExpression"); - } - - public void SetValue(IEvaluationContext context, object value) - { - throw new EvaluationException(ExpressionString, "Cannot call SetValue() on a LiteralExpression"); - } - - public void SetValue(IEvaluationContext context, object rootObject, object value) - { - throw new EvaluationException(ExpressionString, "Cannot call SetValue() on a LiteralExpression"); - } - - object IExpression.GetValue() - { - return GetValue(); - } - - object IExpression.GetValue(object rootObject) - { - return GetValue(rootObject); - } - - object IExpression.GetValue(IEvaluationContext context) - { - return GetValue(context); - } - - object IExpression.GetValue(IEvaluationContext context, object rootObject) - { - return GetValue(context, rootObject); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Common/TemplateAwareExpressionParser.cs b/src/Common/src/Common.Expression/Internal/Spring/Common/TemplateAwareExpressionParser.cs deleted file mode 100644 index 607d8ef7cf..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Common/TemplateAwareExpressionParser.cs +++ /dev/null @@ -1,262 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Common; - -public abstract class TemplateAwareExpressionParser : IExpressionParser -{ - public IExpression ParseExpression(string expressionString) - { - return ParseExpression(expressionString, null); - } - - public IExpression ParseExpression(string expressionString, IParserContext context) - { - if (context != null && context.IsTemplate) - { - return ParseTemplate(expressionString, context); - } - - return DoParseExpression(expressionString, context); - } - - protected internal abstract IExpression DoParseExpression(string expressionString, IParserContext context); - - private IExpression ParseTemplate(string expressionString, IParserContext context) - { - if (expressionString.Length == 0) - { - return new LiteralExpression(string.Empty); - } - - List expressions = ParseExpressions(expressionString, context); - - if (expressions.Count == 1) - { - return expressions[0]; - } - - return new CompositeStringExpression(expressionString, expressions); - } - - private List ParseExpressions(string expressionString, IParserContext context) - { - var expressions = new List(); - string prefix = context.ExpressionPrefix; - string suffix = context.ExpressionSuffix; - int startIdx = 0; - - while (startIdx < expressionString.Length) - { - int prefixIndex = expressionString.IndexOf(prefix, startIdx, StringComparison.Ordinal); - - if (prefixIndex >= startIdx) - { - // an inner expression was found - this is a composite - if (prefixIndex > startIdx) - { - expressions.Add(new LiteralExpression(expressionString.Substring(startIdx, prefixIndex - startIdx))); - } - - int afterPrefixIndex = prefixIndex + prefix.Length; - int suffixIndex = SkipToCorrectEndSuffix(suffix, expressionString, afterPrefixIndex); - - if (suffixIndex == -1) - { - throw new ParseException(expressionString, prefixIndex, - $"No ending suffix '{suffix}' for expression starting at character {prefixIndex}: {expressionString.Substring(prefixIndex)}"); - } - - if (suffixIndex == afterPrefixIndex) - { - throw new ParseException(expressionString, prefixIndex, - $"No expression defined within delimiter '{prefix}{suffix}' at character {prefixIndex}"); - } - - int startIndex = prefixIndex + prefix.Length; - string expr = expressionString.Substring(startIndex, suffixIndex - startIndex); - expr = expr.Trim(); - - if (expr.Length == 0) - { - throw new ParseException(expressionString, prefixIndex, - $"No expression defined within delimiter '{prefix}{suffix}' at character {prefixIndex}"); - } - - expressions.Add(DoParseExpression(expr, context)); - startIdx = suffixIndex + suffix.Length; - } - else - { - // no more ${expressions} found in string, add rest as static text - expressions.Add(new LiteralExpression(expressionString.Substring(startIdx))); - startIdx = expressionString.Length; - } - } - - return expressions; - } - - private bool IsSuffixHere(string expressionString, int pos, string suffix) - { - int suffixPosition = 0; - - for (int i = 0; i < suffix.Length && pos < expressionString.Length; i++) - { - if (expressionString[pos++] != suffix[suffixPosition++]) - { - return false; - } - } - - if (suffixPosition != suffix.Length) - { - // the expressionString ran out before the suffix could entirely be found - return false; - } - - return true; - } - - private int SkipToCorrectEndSuffix(string suffix, string expressionString, int afterPrefixIndex) - { - // Chew on the expression text - relying on the rules: - // brackets must be in pairs - // string literals are "..." or '...' and these may contain unmatched brackets - int pos = afterPrefixIndex; - int maxLength = expressionString.Length; - int nextSuffix = expressionString.IndexOf(suffix, afterPrefixIndex, StringComparison.Ordinal); - - if (nextSuffix == -1) - { - return -1; // the suffix is missing - } - - var stack = new Stack(); - - while (pos < maxLength) - { - if (IsSuffixHere(expressionString, pos, suffix) && stack.Count == 0) - { - break; - } - - char ch = expressionString[pos]; - - switch (ch) - { - case '{': - case '[': - case '(': - stack.Push(new Bracket(ch, pos)); - break; - case '}': - case ']': - case ')': - if (stack.Count == 0) - { - throw new ParseException(expressionString, pos, - $"Found closing '{ch}' at position {pos} without an opening '{Bracket.TheOpenBracketFor(ch)}'"); - } - - Bracket p = stack.Pop(); - - if (!p.CompatibleWithCloseBracket(ch)) - { - throw new ParseException(expressionString, pos, - $"Found closing '{ch}' at position {pos} but most recent opening is '{p.BracketChar}' at position {p.Pos}"); - } - - break; - case '\'': - case '"': - // jump to the end of the literal - int endLiteral = expressionString.IndexOf(ch, pos + 1); - - if (endLiteral == -1) - { - throw new ParseException(expressionString, pos, $"Found non terminating string literal starting at position {pos}"); - } - - pos = endLiteral; - break; - } - - pos++; - } - - if (stack.Count > 0) - { - Bracket p = stack.Pop(); - - throw new ParseException(expressionString, p.Pos, - $"Missing closing '{Bracket.TheCloseBracketFor(p.BracketChar)}' for '{p.BracketChar}' at position {p.Pos}"); - } - - if (!IsSuffixHere(expressionString, pos, suffix)) - { - return -1; - } - - return pos; - } - - private sealed class Bracket - { - public char BracketChar { get; } - - public int Pos { get; } - - public Bracket(char bracket, int pos) - { - BracketChar = bracket; - Pos = pos; - } - - public static char TheOpenBracketFor(char closeBracket) - { - if (closeBracket == '}') - { - return '{'; - } - - if (closeBracket == ']') - { - return '['; - } - - return '('; - } - - public static char TheCloseBracketFor(char openBracket) - { - if (openBracket == '{') - { - return '}'; - } - - if (openBracket == '[') - { - return ']'; - } - - return ')'; - } - - public bool CompatibleWithCloseBracket(char closeBracket) - { - if (BracketChar == '{') - { - return closeBracket == '}'; - } - - if (BracketChar == '[') - { - return closeBracket == ']'; - } - - return closeBracket == ')'; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Common/TemplateParserContext.cs b/src/Common/src/Common.Expression/Internal/Spring/Common/TemplateParserContext.cs deleted file mode 100644 index 4649f96756..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Common/TemplateParserContext.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Common; - -public class TemplateParserContext : IParserContext -{ - public bool IsTemplate { get; } = true; - - public string ExpressionPrefix { get; } - - public string ExpressionSuffix { get; } - - public TemplateParserContext() - : this("#{", "}") - { - } - - public TemplateParserContext(string expressionPrefix, string expressionSuffix) - { - ExpressionPrefix = expressionPrefix; - ExpressionSuffix = expressionSuffix; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/CompiledExpression.cs b/src/Common/src/Common.Expression/Internal/Spring/CompiledExpression.cs deleted file mode 100644 index 254b401db9..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/CompiledExpression.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public abstract class CompiledExpression -{ - internal readonly Dictionary DynamicFields = new(); - - internal Delegate MethodDelegate { get; set; } - - internal Delegate InitDelegate { get; set; } - - public virtual object GetValue(object target, IEvaluationContext context) - { - return null; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/DefaultParseContext.cs b/src/Common/src/Common.Expression/Internal/Spring/DefaultParseContext.cs deleted file mode 100644 index 4b26c8ee21..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/DefaultParseContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public class DefaultParseContext : IParserContext -{ - public static readonly IParserContext TemplateExpression = new DefaultParseContext(); - - public bool IsTemplate => true; - - public string ExpressionPrefix => "#{"; - - public string ExpressionSuffix => "}"; -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/ExpressionState.cs b/src/Common/src/Common.Expression/Internal/Spring/ExpressionState.cs deleted file mode 100644 index 47e6ec5c48..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/ExpressionState.cs +++ /dev/null @@ -1,236 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public class ExpressionState -{ - private Stack _contextObjects; - - private Stack _variableScopes; - - private Stack _scopeRootObjects; - - public List PropertyAccessors => EvaluationContext.PropertyAccessors; - - public IEvaluationContext EvaluationContext { get; } - - public SpelParserOptions Configuration { get; } - - public ITypedValue RootContextObject { get; } - - public ITypeComparator TypeComparator => EvaluationContext.TypeComparator; - - public ITypeConverter TypeConverter => EvaluationContext.TypeConverter; - - public ExpressionState(IEvaluationContext context) - : this(context, context.RootObject, new SpelParserOptions(false, false)) - { - } - - public ExpressionState(IEvaluationContext context, SpelParserOptions configuration) - : this(context, context.RootObject, configuration) - { - } - - public ExpressionState(IEvaluationContext context, ITypedValue rootObject) - : this(context, rootObject, new SpelParserOptions(false, false)) - { - } - - public ExpressionState(IEvaluationContext context, ITypedValue rootObject, SpelParserOptions configuration) - { - ArgumentGuard.NotNull(context); - ArgumentGuard.NotNull(configuration); - - EvaluationContext = context; - RootContextObject = rootObject; - Configuration = configuration; - } - - public ITypedValue GetActiveContextObject() - { - if (_contextObjects == null || _contextObjects.Count == 0) - { - return RootContextObject; - } - - return _contextObjects.Peek(); - } - - public void PushActiveContextObject(ITypedValue obj) - { - _contextObjects ??= new Stack(); - - _contextObjects.Push(obj); - } - - public void PopActiveContextObject() - { - _contextObjects ??= new Stack(); - - _contextObjects.Pop(); - } - - public ITypedValue GetScopeRootContextObject() - { - if (_scopeRootObjects == null || _scopeRootObjects.Count == 0) - { - return RootContextObject; - } - - return _scopeRootObjects.Peek(); - } - - public void SetVariable(string name, object value) - { - EvaluationContext.SetVariable(name, value); - } - - public ITypedValue LookupVariable(string name) - { - object value = EvaluationContext.LookupVariable(name); - return value != null ? new TypedValue(value) : TypedValue.Null; - } - - public Type FindType(string type) - { - return EvaluationContext.TypeLocator.FindType(type); - } - - public object ConvertValue(object value, Type targetTypeDescriptor) - { - object result = EvaluationContext.TypeConverter.ConvertValue(value, value?.GetType(), targetTypeDescriptor); - - if (result == null) - { - throw new InvalidOperationException($"Null conversion result for value [{value}]"); - } - - return result; - } - - public object ConvertValue(ITypedValue value, Type targetTypeDescriptor) - { - object val = value.Value; - return EvaluationContext.TypeConverter.ConvertValue(val, val?.GetType(), targetTypeDescriptor); - } - - public void EnterScope(Dictionary argMap) - { - InitVariableScopes().Push(new VariableScope(argMap)); - InitScopeRootObjects().Push(GetActiveContextObject()); - } - - public void EnterScope() - { - InitVariableScopes().Push(new VariableScope(new Dictionary())); - InitScopeRootObjects().Push(GetActiveContextObject()); - } - - public void EnterScope(string name, object value) - { - InitVariableScopes().Push(new VariableScope(name, value)); - InitScopeRootObjects().Push(GetActiveContextObject()); - } - - public void ExitScope() - { - InitVariableScopes().Pop(); - InitScopeRootObjects().Pop(); - } - - public void SetLocalVariable(string name, object value) - { - InitVariableScopes().Peek().SetVariable(name, value); - } - - public object LookupLocalVariable(string name) - { - foreach (VariableScope scope in InitVariableScopes()) - { - if (scope.DefinesVariable(name)) - { - return scope.LookupVariable(name); - } - } - - return null; - } - - public ITypedValue Operate(Operation op, object left, object right) - { - IOperatorOverloader overloader = EvaluationContext.OperatorOverloader; - - if (overloader.OverridesOperation(op, left, right)) - { - object returnValue = overloader.Operate(op, left, right); - return new TypedValue(returnValue); - } - - string leftType = left == null ? "null" : left.GetType().FullName; - string rightType = right == null ? "null" : right.GetType().FullName; - throw new SpelEvaluationException(SpelMessage.OperatorNotSupportedBetweenTypes, op, leftType, rightType); - } - - private Stack InitVariableScopes() - { - if (_variableScopes == null) - { - _variableScopes = new Stack(); - - // top-level empty variable scope - _variableScopes.Push(new VariableScope()); - } - - return _variableScopes; - } - - private Stack InitScopeRootObjects() - { - _scopeRootObjects ??= new Stack(); - return _scopeRootObjects; - } - - private sealed class VariableScope - { - private readonly Dictionary _vars = new(); - - public VariableScope() - { - } - - public VariableScope(Dictionary arguments) - { - if (arguments != null) - { - foreach (KeyValuePair args in arguments) - { - _vars[args.Key] = args.Value; - } - } - } - - public VariableScope(string name, object value) - { - _vars[name] = value; - } - - public object LookupVariable(string name) - { - _vars.TryGetValue(name, out object val); - return val; - } - - public void SetVariable(string name, object value) - { - _vars[name] = value; - } - - public bool DefinesVariable(string name) - { - return _vars.ContainsKey(name); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/ICompilablePropertyAccessor.cs b/src/Common/src/Common.Expression/Internal/Spring/ICompilablePropertyAccessor.cs deleted file mode 100644 index a5d877db8c..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/ICompilablePropertyAccessor.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public interface ICompilablePropertyAccessor : IPropertyAccessor -{ - bool IsCompilable(); - - Type GetPropertyType(); - - void GenerateCode(string propertyName, ILGenerator gen, CodeFlow cf); -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/ISpelNode.cs b/src/Common/src/Common.Expression/Internal/Spring/ISpelNode.cs deleted file mode 100644 index 71126458b8..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/ISpelNode.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public interface ISpelNode -{ - int StartPosition { get; } - - int EndPosition { get; } - - int ChildCount { get; } - - bool IsCompilable(); - - object GetValue(ExpressionState state); - - ITypedValue GetTypedValue(ExpressionState state); - - bool IsWritable(ExpressionState state); - - void SetValue(ExpressionState state, object newValue); - - string ToStringAst(); - - ISpelNode GetChild(int index); - - Type GetObjectType(object obj); - - void GenerateCode(ILGenerator gen, CodeFlow cf); -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/InternalParseException.cs b/src/Common/src/Common.Expression/Internal/Spring/InternalParseException.cs deleted file mode 100644 index 45ef9139bf..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/InternalParseException.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public class InternalParseException : Exception -{ - public SpelParseException Cause => (SpelParseException)InnerException; - - public InternalParseException(SpelParseException innerException) - : base("Internal Parse Error", innerException) - { - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/SpelCompilerMode.cs b/src/Common/src/Common.Expression/Internal/Spring/SpelCompilerMode.cs deleted file mode 100644 index 4701783eb6..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/SpelCompilerMode.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public enum SpelCompilerMode -{ - Off, - Immediate, - Mixed -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/SpelEvaluationException.cs b/src/Common/src/Common.Expression/Internal/Spring/SpelEvaluationException.cs deleted file mode 100644 index 7bc34120fc..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/SpelEvaluationException.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public class SpelEvaluationException : EvaluationException -{ - public SpelMessage MessageCode { get; } - public object[] Inserts { get; } - - public SpelEvaluationException(SpelMessage message, params object[] inserts) - : base(message.FormatMessage(inserts)) - { - MessageCode = message; - Inserts = inserts; - } - - public SpelEvaluationException(int position, SpelMessage message, params object[] inserts) - : base(position, message.FormatMessage(inserts)) - { - MessageCode = message; - Inserts = inserts; - } - - public SpelEvaluationException(int position, Exception innerException, SpelMessage message, params object[] inserts) - : base(position, message.FormatMessage(inserts), innerException) - { - MessageCode = message; - Inserts = inserts; - } - - public SpelEvaluationException(Exception innerException, SpelMessage message, params object[] inserts) - : base(message.FormatMessage(inserts), innerException) - { - MessageCode = message; - Inserts = inserts; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/SpelMessage.cs b/src/Common/src/Common.Expression/Internal/Spring/SpelMessage.cs deleted file mode 100644 index 1d4aea6935..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/SpelMessage.cs +++ /dev/null @@ -1,241 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Text; - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public class SpelMessage -{ - public static readonly SpelMessage TypeConversionError = new(Kind.Error, 1001, "Type conversion problem, cannot convert from {0} to {1}"); - - public static readonly SpelMessage ConstructorNotFound = new( - Kind.Error, 1002, "Constructor call: No suitable constructor found on type {0} for arguments {1}"); - - public static readonly SpelMessage ConstructorInvocationProblem = new(Kind.Error, 1003, - "A problem occurred whilst attempting to construct an object of type ''{0}'' using arguments ''{1}''"); - - public static readonly SpelMessage MethodNotFound = new(Kind.Error, 1004, "Method call: Method {0} cannot be found on type {1}"); - - public static readonly SpelMessage TypeNotFound = new(Kind.Error, 1005, "Type cannot be found ''{0}''"); - - public static readonly SpelMessage FunctionNotDefined = new(Kind.Error, 1006, "Function ''{0}'' could not be found"); - - public static readonly SpelMessage PropertyOrFieldNotReadableOnNull = new(Kind.Error, 1007, "Property or field ''{0}'' cannot be found on null"); - - public static readonly SpelMessage PropertyOrFieldNotReadable = new(Kind.Error, 1008, - "Property or field ''{0}'' cannot be found on object of type ''{1}'' - maybe not public or not valid?"); - - public static readonly SpelMessage PropertyOrFieldNotWritableOnNull = new(Kind.Error, 1009, "Property or field ''{0}'' cannot be set on null"); - - public static readonly SpelMessage PropertyOrFieldNotWritable = new(Kind.Error, 1010, - "Property or field ''{0}'' cannot be set on object of type ''{1}'' - maybe not public or not writable?"); - - public static readonly SpelMessage MethodCallOnNullObjectNotAllowed = new( - Kind.Error, 1011, "Method call: Attempted to call method {0} on null context object"); - - public static readonly SpelMessage CannotIndexIntoNullValue = new(Kind.Error, 1012, "Cannot index into a null value"); - - public static readonly SpelMessage NotComparable = new(Kind.Error, 1013, "Cannot compare instances of {0} and {1}"); - - public static readonly SpelMessage IncorrectNumberOfArgumentsToFunction = new( - Kind.Error, 1014, "Incorrect number of arguments for function, {0} supplied but function takes {1}"); - - public static readonly SpelMessage InvalidTypeForSelection = new(Kind.Error, 1015, "Cannot perform selection on input data of type ''{0}''"); - - public static readonly SpelMessage ResultOfSelectionCriteriaIsNotBoolean = new(Kind.Error, 1016, "Result of selection criteria is not boolean"); - - public static readonly SpelMessage BetweenRightOperandMustBeTwoElementList = new( - Kind.Error, 1017, "Right operand for the 'between' operator has to be a two-element list"); - - public static readonly SpelMessage InvalidPattern = new(Kind.Error, 1018, "Pattern is not valid ''{0}''"); - - public static readonly SpelMessage ProjectionNotSupportedOnType = new(Kind.Error, 1019, "Projection is not supported on the type ''{0}''"); - - public static readonly SpelMessage ArgListShouldNotBeEvaluated = new( - Kind.Error, 1020, "The argument list of a lambda expression should never have getValue() called upon it"); - - public static readonly SpelMessage ExceptionDuringPropertyRead = new( - Kind.Error, 1021, "A problem occurred whilst attempting to access the property ''{0}'': ''{1}''"); - - public static readonly SpelMessage FunctionReferenceCannotBeInvoked = new( - Kind.Error, 1022, "The function ''{0}'' mapped to an object of type ''{1}'' which cannot be invoked"); - - public static readonly SpelMessage ExceptionDuringFunctionCall = new( - Kind.Error, 1023, "A problem occurred whilst attempting to invoke the function ''{0}'': ''{1}''"); - - public static readonly SpelMessage ArrayIndexOutOfBounds = new(Kind.Error, 1024, "The array has ''{0}'' elements, index ''{1}'' is invalid"); - - public static readonly SpelMessage CollectionIndexOutOfBounds = new(Kind.Error, 1025, "The collection has ''{0}'' elements, index ''{1}'' is invalid"); - - public static readonly SpelMessage StringIndexOutOfBounds = new(Kind.Error, 1026, "The string has ''{0}'' characters, index ''{1}'' is invalid"); - - public static readonly SpelMessage IndexingNotSupportedForType = new(Kind.Error, 1027, "Indexing into type ''{0}'' is not supported"); - - public static readonly SpelMessage InstanceOfOperatorNeedsClassOperand = new( - Kind.Error, 1028, "The operator 'instanceof' needs the right operand to be a class, not a ''{0}''"); - - public static readonly SpelMessage ExceptionDuringMethodInvocation = new( - Kind.Error, 1029, "A problem occurred when trying to execute method ''{0}'' on object of type ''{1}'': ''{2}''"); - - public static readonly SpelMessage OperatorNotSupportedBetweenTypes = new( - Kind.Error, 1030, "The operator ''{0}'' is not supported between objects of type ''{1}'' and ''{2}''"); - - public static readonly SpelMessage ProblemLocatingMethod = new(Kind.Error, 1031, "Problem locating method {0} on type {1}"); - - public static readonly SpelMessage SetValueNotSupported = new(Kind.Error, 1032, "setValue(ExpressionState, Object) not supported for ''{0}''"); - - public static readonly SpelMessage MultiplePossibleMethods = new( - Kind.Error, 1033, "Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"); - - public static readonly SpelMessage ExceptionDuringPropertyWrite = new( - Kind.Error, 1034, "A problem occurred whilst attempting to set the property ''{0}'': {1}"); - - public static readonly SpelMessage NotAnInteger = new(Kind.Error, 1035, "The value ''{0}'' cannot be parsed as an int"); - - public static readonly SpelMessage NotALong = new(Kind.Error, 1036, "The value ''{0}'' cannot be parsed as a long"); - - public static readonly SpelMessage InvalidFirstOperandForMatchesOperator = new( - Kind.Error, 1037, "First operand to matches operator must be a string. ''{0}'' is not"); - - public static readonly SpelMessage InvalidSecondOperandForMatchesOperator = new( - Kind.Error, 1038, "Second operand to matches operator must be a string. ''{0}'' is not"); - - public static readonly SpelMessage FunctionMustBeStatic = new(Kind.Error, 1039, - "Only static methods can be called via function references. The method ''{0}'' referred to by name ''{1}'' is not static."); - - public static readonly SpelMessage NotAReal = new(Kind.Error, 1040, "The value ''{0}'' cannot be parsed as a double"); - - public static readonly SpelMessage MoreInput = new(Kind.Error, 1041, - "After parsing a valid expression, there is still more data in the expression: ''{0}''"); - - public static readonly SpelMessage RightOperandProblem = new(Kind.Error, 1042, "Problem parsing right operand"); - - public static readonly SpelMessage NotExpectedToken = new(Kind.Error, 1043, "Unexpected token. Expected ''{0}'' but was ''{1}''"); - - public static readonly SpelMessage Ood = new(Kind.Error, 1044, "Unexpectedly ran out of input"); - - public static readonly SpelMessage NonTerminatingDoubleQuotedString = new(Kind.Error, 1045, "Cannot find terminating \" for string"); - - public static readonly SpelMessage NonTerminatingQuotedString = new(Kind.Error, 1046, "Cannot find terminating '' for string"); - - public static readonly SpelMessage MissingLeadingZeroForNumber = new( - Kind.Error, 1047, "A real number must be prefixed by zero, it cannot start with just ''.''"); - - public static readonly SpelMessage RealCannotBeLong = new(Kind.Error, 1048, "Real number cannot be suffixed with a long (L or l) suffix"); - - public static readonly SpelMessage UnexpectedDataAfterDot = new(Kind.Error, 1049, "Unexpected data after ''.'': ''{0}''"); - - public static readonly SpelMessage MissingConstructorArgs = new(Kind.Error, 1050, "The arguments '(...)' for the constructor call are missing"); - - public static readonly SpelMessage RunOutOfArguments = new(Kind.Error, 1051, "Unexpectedly ran out of arguments"); - - public static readonly SpelMessage UnableToGrowCollection = new(Kind.Error, 1052, "Unable to grow collection"); - - public static readonly SpelMessage UnableToGrowCollectionUnknownElementType = new( - Kind.Error, 1053, "Unable to grow collection: unable to determine list element type"); - - public static readonly SpelMessage UnableToCreateListForIndexing = new(Kind.Error, 1054, "Unable to dynamically create a List to replace a null value"); - - public static readonly SpelMessage UnableToCreateMapForIndexing = new(Kind.Error, 1055, "Unable to dynamically create a Map to replace a null value"); - - public static readonly SpelMessage UnableToDynamicallyCreateObject = new( - Kind.Error, 1056, "Unable to dynamically create instance of ''{0}'' to replace a null value"); - - public static readonly SpelMessage NoServiceResolverRegistered = new( - Kind.Error, 1057, "No service resolver registered in the context to resolve access to service ''{0}''"); - - public static readonly SpelMessage ExceptionDuringServiceResolution = new( - Kind.Error, 1058, "A problem occurred when trying to resolve service ''{0}'':''{1}''"); - - public static readonly SpelMessage InvalidServiceReference = new(Kind.Error, 1059, "@ or & can only be followed by an identifier or a quoted name"); - - public static readonly SpelMessage TypeNameExpectedForArrayConstruction = new( - Kind.Error, 1060, "Expected the type of the new array to be specified as a String but found ''{0}''"); - - public static readonly SpelMessage IncorrectElementTypeForArray = new( - Kind.Error, 1061, "The array of type ''{0}'' cannot have an element of type ''{1}'' inserted"); - - public static readonly SpelMessage MultidimensionalArrayInitializerNotSupported = new( - Kind.Error, 1062, "Using an initializer to build a multi-dimensional array is not currently supported"); - - public static readonly SpelMessage MissingArrayDimension = new(Kind.Error, 1063, "A required array dimension has not been specified"); - - public static readonly SpelMessage InitializerLengthIncorrect = new(Kind.Error, 1064, "Array initializer size does not match array dimensions"); - - public static readonly SpelMessage UnexpectedEscapeChar = new(Kind.Error, 1065, "Unexpected escape character"); - - public static readonly SpelMessage OperandNotIncrementable = new(Kind.Error, 1066, "The expression component ''{0}'' does not support increment"); - - public static readonly SpelMessage OperandNotDecrementable = new(Kind.Error, 1067, "The expression component ''{0}'' does not support decrement"); - - public static readonly SpelMessage NotAssignable = new(Kind.Error, 1068, "The expression component ''{0}'' is not assignable"); - - public static readonly SpelMessage MissingCharacter = new(Kind.Error, 1069, "Missing expected character ''{0}''"); - - public static readonly SpelMessage LeftOperandProblem = new(Kind.Error, 1070, "Problem parsing left operand"); - - public static readonly SpelMessage MissingSelectionExpression = new(Kind.Error, 1071, "A required selection expression has not been specified"); - - public static readonly SpelMessage ExceptionRunningCompiledExpression = new( - Kind.Error, 1072, "An exception occurred whilst evaluating a compiled expression"); - - public static readonly SpelMessage FlawedPattern = new(Kind.Error, 1073, "Failed to efficiently evaluate pattern ''{0}'': consider redesigning it"); - - private readonly Kind _kind; - private readonly int _code; - private readonly string _message; - - private SpelMessage(Kind kind, int code, string message) - { - _kind = kind; - _code = code; - _message = message; - } - - public string FormatMessage(params object[] inserts) - { - var formattedMessage = new StringBuilder(); - formattedMessage.Append("EL").Append(_code); - - switch (_kind) - { - case Kind.Error: - formattedMessage.Append('E'); - break; - } - - formattedMessage.Append(": "); - formattedMessage.Append(string.Format(CultureInfo.InvariantCulture, _message, inserts)); - return formattedMessage.ToString(); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not SpelMessage asMessage) - { - return false; - } - - return _kind == asMessage._kind && _code == asMessage._code && _message == asMessage._message; - } - - public override int GetHashCode() - { - return _message.GetHashCode(); - } - - public enum Kind - { - Info, - Warning, - Error - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/SpelParseException.cs b/src/Common/src/Common.Expression/Internal/Spring/SpelParseException.cs deleted file mode 100644 index 98f94f38b9..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/SpelParseException.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public class SpelParseException : ParseException -{ - public SpelMessage MessageCode { get; } - public object[] Inserts { get; } - - public SpelParseException(int position, SpelMessage message, params object[] inserts) - : base(position, message.FormatMessage(inserts)) - { - MessageCode = message; - Inserts = inserts; - } - - public SpelParseException(string expressionString, int position, SpelMessage message, params object[] inserts) - : base(expressionString, position, message.FormatMessage(inserts)) - { - MessageCode = message; - Inserts = inserts; - } - - public SpelParseException(int position, Exception innerException, SpelMessage message, params object[] inserts) - : base(position, message.FormatMessage(inserts), innerException) - { - MessageCode = message; - Inserts = inserts; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/SpelParserOptions.cs b/src/Common/src/Common.Expression/Internal/Spring/SpelParserOptions.cs deleted file mode 100644 index c4c9ae59ea..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/SpelParserOptions.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public class SpelParserOptions -{ - public int MaximumAutoGrowSize { get; set; } - - public bool AutoGrowCollections { get; set; } - - public bool AutoGrowNullReferences { get; set; } - - public SpelCompilerMode CompilerMode { get; set; } - - public SpelParserOptions() - : this(null, false, false, int.MaxValue) - { - } - - public SpelParserOptions(SpelCompilerMode compilerMode) - : this(compilerMode, false, false, int.MaxValue) - { - } - - public SpelParserOptions(bool autoGrowNullReferences, bool autoGrowCollections) - : this(null, autoGrowNullReferences, autoGrowCollections, int.MaxValue) - { - } - - public SpelParserOptions(bool autoGrowNullReferences, bool autoGrowCollections, int maximumAutoGrowSize) - : this(null, autoGrowNullReferences, autoGrowCollections, maximumAutoGrowSize) - { - } - - public SpelParserOptions(SpelCompilerMode? compilerMode, bool autoGrowNullReferences, bool autoGrowCollections, int maximumAutoGrowSize) - { - CompilerMode = compilerMode ?? SpelCompilerMode.Off; - AutoGrowNullReferences = autoGrowNullReferences; - AutoGrowCollections = autoGrowCollections; - MaximumAutoGrowSize = maximumAutoGrowSize; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Standard/InternalSpelExpressionParser.cs b/src/Common/src/Common.Expression/Internal/Spring/Standard/InternalSpelExpressionParser.cs deleted file mode 100644 index 3a27a2df77..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Standard/InternalSpelExpressionParser.cs +++ /dev/null @@ -1,1248 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Text.RegularExpressions; -using Steeltoe.Common.Expression.Internal.Spring.Ast; -using Steeltoe.Common.Expression.Internal.Spring.Common; - -namespace Steeltoe.Common.Expression.Internal.Spring.Standard; - -public class InternalSpelExpressionParser : TemplateAwareExpressionParser -{ - private static readonly Regex ValidQualifiedIdPattern = new("[\\p{L}\\p{N}_$]+", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); - - internal Stack ConstructedNodes = new(); - - internal SpelParserOptions Configuration { get; } - - internal string ExpressionString { get; private set; } - - internal List TokenStream { get; private set; } = new(); - - internal int TokenStreamLength { get; private set; } - - internal int TokenStreamPointer { get; private set; } - - public InternalSpelExpressionParser(SpelParserOptions configuration) - { - Configuration = configuration; - } - - protected internal override IExpression DoParseExpression(string expressionString, IParserContext context) - { - try - { - ExpressionString = expressionString; - var tokenizer = new Tokenizer(expressionString); - TokenStream = tokenizer.Process(); - TokenStreamLength = TokenStream.Count; - TokenStreamPointer = 0; - ConstructedNodes.Clear(); - SpelNode ast = EatExpression(); - - if (ast == null) - { - throw new InvalidOperationException("No node"); - } - - Token t = PeekToken(); - - if (t != null) - { - throw new SpelParseException(t.StartPos, SpelMessage.MoreInput, ToString(NextToken())); - } - - if (ConstructedNodes.Count != 0) - { - throw new InvalidOperationException("At least one node expected"); - } - - return new SpelExpression(expressionString, ast, Configuration); - } - catch (InternalParseException ex) - { - throw ex.InnerException; - } - } - - private SpelNode EatExpression() - { - SpelNode expr = EatLogicalOrExpression(); - Token t = PeekToken(); - - if (t != null) - { - if (Equals(t.Kind, TokenKind.Assign)) - { - // a=b - expr ??= new NullLiteral(t.StartPos - 1, t.EndPos - 1); - - NextToken(); - SpelNode assignedValue = EatLogicalOrExpression(); - return new Assign(t.StartPos, t.EndPos, expr, assignedValue); - } - - if (Equals(t.Kind, TokenKind.Elvis)) - { - // a?:b (a if it isn't null, otherwise b) - expr ??= new NullLiteral(t.StartPos - 1, t.EndPos - 2); - - NextToken(); // elvis has left the building - SpelNode valueIfNull = EatExpression() ?? new NullLiteral(t.StartPos + 1, t.EndPos + 1); - - return new Elvis(t.StartPos, t.EndPos, expr, valueIfNull); - } - - if (Equals(t.Kind, TokenKind.QuestionMark)) - { - // a?b:c - expr ??= new NullLiteral(t.StartPos - 1, t.EndPos - 1); - - NextToken(); - SpelNode ifTrueExprValue = EatExpression(); - EatToken(TokenKind.Colon); - SpelNode ifFalseExprValue = EatExpression(); - return new Ternary(t.StartPos, t.EndPos, expr, ifTrueExprValue, ifFalseExprValue); - } - } - - return expr; - } - - private SpelNode EatLogicalOrExpression() - { - SpelNode expr = EatLogicalAndExpression(); - - while (PeekIdentifierToken("or") || PeekToken(TokenKind.SymbolicOr)) - { - // consume OR - Token t = TakeToken(); - SpelNode rhExpr = EatLogicalAndExpression(); - CheckOperands(t, expr, rhExpr); - expr = new OpOr(t.StartPos, t.EndPos, expr, rhExpr); - } - - return expr; - } - - private SpelNode EatLogicalAndExpression() - { - SpelNode expr = EatRelationalExpression(); - - while (PeekIdentifierToken("and") || PeekToken(TokenKind.SymbolicAnd)) - { - // consume 'AND' - Token t = TakeToken(); - SpelNode rhExpr = EatRelationalExpression(); - CheckOperands(t, expr, rhExpr); - expr = new OpAnd(t.StartPos, t.EndPos, expr, rhExpr); - } - - return expr; - } - - private SpelNode EatRelationalExpression() - { - SpelNode expr = EatSumExpression(); - Token relationalOperatorToken = MaybeEatRelationalOperator(); - - if (relationalOperatorToken != null) - { - // consume relational operator token - Token t = TakeToken(); - SpelNode rhExpr = EatSumExpression(); - CheckOperands(t, expr, rhExpr); - TokenKind tk = relationalOperatorToken.Kind; - - if (relationalOperatorToken.IsNumericRelationalOperator) - { - if (Equals(tk, TokenKind.Gt)) - { - return new OpGt(t.StartPos, t.EndPos, expr, rhExpr); - } - - if (Equals(tk, TokenKind.Lt)) - { - return new OpLt(t.StartPos, t.EndPos, expr, rhExpr); - } - - if (Equals(tk, TokenKind.Le)) - { - return new OpLe(t.StartPos, t.EndPos, expr, rhExpr); - } - - if (Equals(tk, TokenKind.Ge)) - { - return new OpGe(t.StartPos, t.EndPos, expr, rhExpr); - } - - if (Equals(tk, TokenKind.Eq)) - { - return new OpEq(t.StartPos, t.EndPos, expr, rhExpr); - } - - if (!Equals(tk, TokenKind.Ne)) - { - throw new InvalidOperationException("Not-equals token expected"); - } - - return new OpNe(t.StartPos, t.EndPos, expr, rhExpr); - } - - if (Equals(tk, TokenKind.InstanceOf)) - { - return new OperatorInstanceOf(t.StartPos, t.EndPos, expr, rhExpr); - } - - if (Equals(tk, TokenKind.Matches)) - { - return new OperatorMatches(t.StartPos, t.EndPos, expr, rhExpr); - } - - if (!Equals(tk, TokenKind.Between)) - { - throw new InvalidOperationException("Between token expected"); - } - - return new OperatorBetween(t.StartPos, t.EndPos, expr, rhExpr); - } - - return expr; - } - - private SpelNode EatSumExpression() - { - SpelNode expr = EatProductExpression(); - - while (PeekToken(TokenKind.Plus, TokenKind.Minus, TokenKind.Inc)) - { - // consume PLUS or MINUS or INC - Token t = TakeToken(); - SpelNode rhExpr = EatProductExpression(); - CheckRightOperand(t, rhExpr); - - if (Equals(t.Kind, TokenKind.Plus)) - { - expr = new OpPlus(t.StartPos, t.EndPos, expr, rhExpr); - } - else if (Equals(t.Kind, TokenKind.Minus)) - { - expr = new OpMinus(t.StartPos, t.EndPos, expr, rhExpr); - } - } - - return expr; - } - - private SpelNode EatProductExpression() - { - SpelNode expr = EatPowerIncDecExpression(); - - while (PeekToken(TokenKind.Star, TokenKind.Div, TokenKind.Mod)) - { - Token t = TakeToken(); // consume STAR/DIV/MOD - SpelNode rhExpr = EatPowerIncDecExpression(); - CheckOperands(t, expr, rhExpr); - - if (Equals(t.Kind, TokenKind.Star)) - { - expr = new OpMultiply(t.StartPos, t.EndPos, expr, rhExpr); - } - else if (Equals(t.Kind, TokenKind.Div)) - { - expr = new OpDivide(t.StartPos, t.EndPos, expr, rhExpr); - } - else - { - if (!Equals(t.Kind, TokenKind.Mod)) - { - throw new InvalidOperationException("Mod token expected"); - } - - expr = new OpModulus(t.StartPos, t.EndPos, expr, rhExpr); - } - } - - return expr; - } - - private SpelNode EatPowerIncDecExpression() - { - SpelNode expr = EatUnaryExpression(); - - if (PeekToken(TokenKind.Power)) - { - Token t = TakeToken(); // consume POWER - SpelNode rhExpr = EatUnaryExpression(); - CheckRightOperand(t, rhExpr); - return new OperatorPower(t.StartPos, t.EndPos, expr, rhExpr); - } - - if (expr != null && PeekToken(TokenKind.Inc, TokenKind.Dec)) - { - Token t = TakeToken(); // consume INC/DEC - - if (Equals(t.Kind, TokenKind.Inc)) - { - return new OpInc(t.StartPos, t.EndPos, true, expr); - } - - return new OpDec(t.StartPos, t.EndPos, true, expr); - } - - return expr; - } - - private SpelNode EatUnaryExpression() - { - if (PeekToken(TokenKind.Plus, TokenKind.Minus, TokenKind.Not)) - { - Token t = TakeToken(); - SpelNode expr = EatUnaryExpression(); - - if (expr == null) - { - throw new InvalidOperationException("No node"); - } - - if (Equals(t.Kind, TokenKind.Not)) - { - return new OperatorNot(t.StartPos, t.EndPos, expr); - } - - if (Equals(t.Kind, TokenKind.Plus)) - { - return new OpPlus(t.StartPos, t.EndPos, expr); - } - - if (!Equals(t.Kind, TokenKind.Minus)) - { - throw new InvalidOperationException("Minus token expected"); - } - - return new OpMinus(t.StartPos, t.EndPos, expr); - } - - if (PeekToken(TokenKind.Inc, TokenKind.Dec)) - { - Token t = TakeToken(); - SpelNode expr = EatUnaryExpression(); - - if (Equals(t.Kind, TokenKind.Inc)) - { - return new OpInc(t.StartPos, t.EndPos, false, expr); - } - - return new OpDec(t.StartPos, t.EndPos, false, expr); - } - - return EatPrimaryExpression(); - } - - private SpelNode EatPrimaryExpression() - { - SpelNode start = EatStartNode(); // always a start node - List nodes = null; - SpelNode node = EatNode(); - - while (node != null) - { - nodes ??= new List(4) - { - start - }; - - nodes.Add(node); - node = EatNode(); - } - - if (start == null || nodes == null) - { - return start; - } - - return new CompoundExpression(start.StartPosition, nodes[^1].EndPosition, nodes.ToArray()); - } - - private SpelNode EatNode() - { - return PeekToken(TokenKind.Dot, TokenKind.SafeNavigator) ? EatDottedNode() : EatNonDottedNode(); - } - - private SpelNode EatNonDottedNode() - { - if (PeekToken(TokenKind.LeftSquare) && MaybeEatIndexer()) - { - return Pop(); - } - - return null; - } - - private SpelNode EatDottedNode() - { - Token t = TakeToken(); // it was a '.' or a '?.' - bool nullSafeNavigation = Equals(t.Kind, TokenKind.SafeNavigator); - - if (MaybeEatMethodOrProperty(nullSafeNavigation) || MaybeEatFunctionOrVar() || MaybeEatProjection(nullSafeNavigation) || - MaybeEatSelection(nullSafeNavigation)) - { - return Pop(); - } - - if (PeekToken() == null) - { - // unexpectedly ran out of data - throw InternalException(t.StartPos, SpelMessage.Ood); - } - - throw InternalException(t.StartPos, SpelMessage.UnexpectedDataAfterDot, ToString(PeekToken())); - } - - private bool MaybeEatFunctionOrVar() - { - if (!PeekToken(TokenKind.Hash)) - { - return false; - } - - Token t = TakeToken(); - Token functionOrVariableName = EatToken(TokenKind.Identifier); - SpelNode[] args = MaybeEatMethodArgs(); - - if (args == null) - { - Push(new VariableReference(functionOrVariableName.StringValue, t.StartPos, functionOrVariableName.EndPos)); - return true; - } - - Push(new FunctionReference(functionOrVariableName.StringValue, t.StartPos, functionOrVariableName.EndPos, args)); - return true; - } - - private SpelNode[] MaybeEatMethodArgs() - { - if (!PeekToken(TokenKind.LeftParen)) - { - return null; - } - - var args = new List(); - ConsumeArguments(args); - EatToken(TokenKind.RightParen); - return args.ToArray(); - } - - private void EatConstructorArgs(List accumulatedArguments) - { - if (!PeekToken(TokenKind.LeftParen)) - { - throw new InternalParseException(new SpelParseException(ExpressionString, PositionOf(PeekToken()), SpelMessage.MissingConstructorArgs)); - } - - ConsumeArguments(accumulatedArguments); - EatToken(TokenKind.RightParen); - } - - private void ConsumeArguments(List accumulatedArguments) - { - Token t = PeekToken(); - - if (t == null) - { - throw new InvalidOperationException("Expected token"); - } - - int pos = t.StartPos; - Token next; - - do - { - NextToken(); // consume (first time through) or comma (subsequent times) - t = PeekToken(); - - if (t == null) - { - throw InternalException(pos, SpelMessage.RunOutOfArguments); - } - - if (!Equals(t.Kind, TokenKind.RightParen)) - { - accumulatedArguments.Add(EatExpression()); - } - - next = PeekToken(); - } - while (next != null && Equals(next.Kind, TokenKind.Comma)); - - if (next == null) - { - throw InternalException(pos, SpelMessage.RunOutOfArguments); - } - } - - private int PositionOf(Token t) - { - if (t == null) - { - // if null assume the problem is because the right token was - // not found at the end of the expression - return ExpressionString.Length; - } - - return t.StartPos; - } - - private SpelNode EatStartNode() - { - bool hasEaten = MaybeEatLiteral(); - hasEaten = hasEaten || MaybeEatParenExpression(); - hasEaten = hasEaten || MaybeEatTypeReference(); - hasEaten = hasEaten || MaybeEatNullReference(); - hasEaten = hasEaten || MaybeEatConstructorReference(); - hasEaten = hasEaten || MaybeEatMethodOrProperty(false); - hasEaten = hasEaten || MaybeEatFunctionOrVar(); - hasEaten = hasEaten || MaybeEatServiceReference(); - hasEaten = hasEaten || MaybeEatProjection(false); - hasEaten = hasEaten || MaybeEatSelection(false); - hasEaten = hasEaten || MaybeEatIndexer(); - hasEaten = hasEaten || MaybeEatInlineListOrMap(); - - return hasEaten ? Pop() : null; - } - - private bool MaybeEatServiceReference() - { - if (PeekToken(TokenKind.ServiceRef) || PeekToken(TokenKind.FactoryServiceRef)) - { - Token serviceRefToken = TakeToken(); - Token serviceNameToken = null; - string serviceName = null; - - if (PeekToken(TokenKind.Identifier)) - { - serviceNameToken = EatToken(TokenKind.Identifier); - serviceName = serviceNameToken.StringValue; - } - else if (PeekToken(TokenKind.LiteralString)) - { - serviceNameToken = EatToken(TokenKind.LiteralString); - serviceName = serviceNameToken.StringValue; - serviceName = serviceName.Substring(1, serviceName.Length - 1 - 1); - } - else - { - throw InternalException(serviceRefToken.StartPos, SpelMessage.InvalidServiceReference); - } - - ServiceReference serviceReference; - - if (Equals(serviceRefToken.Kind, TokenKind.FactoryServiceRef)) - { - string serviceNameString = new string(TokenKind.FactoryServiceRef.TokenChars) + serviceName; - serviceReference = new ServiceReference(serviceRefToken.StartPos, serviceNameToken.EndPos, serviceNameString); - } - else - { - serviceReference = new ServiceReference(serviceNameToken.StartPos, serviceNameToken.EndPos, serviceName); - } - - ConstructedNodes.Push(serviceReference); - return true; - } - - return false; - } - - private bool MaybeEatTypeReference() - { - if (PeekToken(TokenKind.Identifier)) - { - Token typeName = PeekToken(); - - if (typeName == null) - { - throw new InvalidOperationException("Expected token"); - } - - if (typeName.StringValue != "T") - { - return false; - } - - // It looks like a type reference but is T being used as a map key? - Token t = TakeToken(); - - if (PeekToken(TokenKind.RightSquare)) - { - // looks like 'T]' (T is map key) - Push(new PropertyOrFieldReference(false, t.StringValue, t.StartPos, t.EndPos)); - return true; - } - - EatToken(TokenKind.LeftParen); - SpelNode node = EatPossiblyQualifiedId(); - - // dotted qualified id - // Are there array dimensions? - int dims = 0; - - while (PeekToken(TokenKind.LeftSquare, true)) - { - EatToken(TokenKind.RightSquare); - dims++; - } - - EatToken(TokenKind.RightParen); - ConstructedNodes.Push(new TypeReference(typeName.StartPos, typeName.EndPos, node, dims)); - return true; - } - - return false; - } - - private bool MaybeEatNullReference() - { - if (PeekToken(TokenKind.Identifier)) - { - Token nullToken = PeekToken(); - - if (nullToken == null) - { - throw new InvalidOperationException("Expected token"); - } - - if (!string.Equals(nullToken.StringValue, "null", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - NextToken(); - ConstructedNodes.Push(new NullLiteral(nullToken.StartPos, nullToken.EndPos)); - return true; - } - - return false; - } - - private bool MaybeEatProjection(bool nullSafeNavigation) - { - Token t = PeekToken(); - - if (!PeekToken(TokenKind.Project, true)) - { - return false; - } - - if (t == null) - { - throw new InvalidOperationException("No token"); - } - - SpelNode expr = EatExpression(); - - if (expr == null) - { - throw new InvalidOperationException("No node"); - } - - EatToken(TokenKind.RightSquare); - ConstructedNodes.Push(new Projection(nullSafeNavigation, t.StartPos, t.EndPos, expr)); - return true; - } - - private bool MaybeEatInlineListOrMap() - { - Token t = PeekToken(); - - if (!PeekToken(TokenKind.LeftCurly, true)) - { - return false; - } - - if (t == null) - { - throw new InvalidOperationException("No token"); - } - - SpelNode expr = null; - Token closingCurly = PeekToken(); - - if (PeekToken(TokenKind.RightCurly, true)) - { - // empty list '{}' - if (closingCurly == null) - { - throw new InvalidOperationException("No token"); - } - - expr = new InlineList(t.StartPos, closingCurly.EndPos); - } - else if (PeekToken(TokenKind.Colon, true)) - { - closingCurly = EatToken(TokenKind.RightCurly); - - // empty map '{:}' - expr = new InlineMap(t.StartPos, closingCurly.EndPos); - } - else - { - SpelNode firstExpression = EatExpression(); - - // Next is either: - // '}' - end of list - // ',' - more expressions in this list - // ':' - this is a map! - if (PeekToken(TokenKind.RightCurly)) - { - // list with one item in it - var elements = new List - { - firstExpression - }; - - closingCurly = EatToken(TokenKind.RightCurly); - expr = new InlineList(t.StartPos, closingCurly.EndPos, elements.ToArray()); - } - else if (PeekToken(TokenKind.Comma, true)) - { - // multi-item list - var elements = new List - { - firstExpression - }; - - do - { - elements.Add(EatExpression()); - } - while (PeekToken(TokenKind.Comma, true)); - - closingCurly = EatToken(TokenKind.RightCurly); - expr = new InlineList(t.StartPos, closingCurly.EndPos, elements.ToArray()); - } - else if (PeekToken(TokenKind.Colon, true)) - { - // map! - var elements = new List - { - firstExpression, - EatExpression() - }; - - while (PeekToken(TokenKind.Comma, true)) - { - elements.Add(EatExpression()); - EatToken(TokenKind.Colon); - elements.Add(EatExpression()); - } - - closingCurly = EatToken(TokenKind.RightCurly); - expr = new InlineMap(t.StartPos, closingCurly.EndPos, elements.ToArray()); - } - else - { - throw InternalException(t.StartPos, SpelMessage.Ood); - } - } - - ConstructedNodes.Push(expr); - return true; - } - - private bool MaybeEatIndexer() - { - Token t = PeekToken(); - - if (!PeekToken(TokenKind.LeftSquare, true)) - { - return false; - } - - if (t == null) - { - throw new InvalidOperationException("No token"); - } - - SpelNode expr = EatExpression(); - - if (expr == null) - { - throw new InvalidOperationException("No node"); - } - - EatToken(TokenKind.RightSquare); - ConstructedNodes.Push(new Indexer(t.StartPos, t.EndPos, expr)); - return true; - } - - private bool MaybeEatSelection(bool nullSafeNavigation) - { - Token t = PeekToken(); - - if (!PeekSelectToken()) - { - return false; - } - - if (t == null) - { - throw new InvalidOperationException("No token"); - } - - NextToken(); - SpelNode expr = EatExpression(); - - if (expr == null) - { - throw InternalException(t.StartPos, SpelMessage.MissingSelectionExpression); - } - - EatToken(TokenKind.RightSquare); - - if (Equals(t.Kind, TokenKind.SelectFirst)) - { - ConstructedNodes.Push(new Selection(nullSafeNavigation, Selection.First, t.StartPos, t.EndPos, expr)); - } - else if (Equals(t.Kind, TokenKind.SelectLast)) - { - ConstructedNodes.Push(new Selection(nullSafeNavigation, Selection.Last, t.StartPos, t.EndPos, expr)); - } - else - { - ConstructedNodes.Push(new Selection(nullSafeNavigation, Selection.All, t.StartPos, t.EndPos, expr)); - } - - return true; - } - - private SpelNode EatPossiblyQualifiedId() - { - var qualifiedIdPieces = new List(); - Token node = PeekToken(); - - while (IsValidQualifiedId(node)) - { - NextToken(); - - if (!Equals(node.Kind, TokenKind.Dot)) - { - qualifiedIdPieces.Add(new Identifier(node.StringValue, node.StartPos, node.EndPos)); - } - - node = PeekToken(); - } - - if (qualifiedIdPieces.Count == 0) - { - if (node == null) - { - throw InternalException(ExpressionString.Length, SpelMessage.Ood); - } - - throw InternalException(node.StartPos, SpelMessage.NotExpectedToken, "qualified ID", FormatTokenKind(node.Kind)); - } - - return new QualifiedIdentifier(qualifiedIdPieces[0].StartPosition, qualifiedIdPieces[^1].EndPosition, qualifiedIdPieces.ToArray()); - } - - private static string FormatTokenKind(TokenKind kind) - { -#pragma warning disable S4040 // Strings should be normalized to uppercase - return kind.ToString().ToLowerInvariant(); -#pragma warning restore S4040 // Strings should be normalized to uppercase - } - - private bool IsValidQualifiedId(Token node) - { - if (node == null || Equals(node.Kind, TokenKind.LiteralString)) - { - return false; - } - - if (Equals(node.Kind, TokenKind.Dot) || Equals(node.Kind, TokenKind.Identifier)) - { - return true; - } - - string value = node.StringValue; - return !string.IsNullOrEmpty(value) && ValidQualifiedIdPattern.Matches(value).Count > 0; - } - - private bool MaybeEatMethodOrProperty(bool nullSafeNavigation) - { - if (PeekToken(TokenKind.Identifier)) - { - Token methodOrPropertyName = TakeToken(); - SpelNode[] args = MaybeEatMethodArgs(); - - if (args == null) - { - // property - Push(new PropertyOrFieldReference(nullSafeNavigation, methodOrPropertyName.StringValue, methodOrPropertyName.StartPos, - methodOrPropertyName.EndPos)); - - return true; - } - - // method reference - Push(new MethodReference(nullSafeNavigation, methodOrPropertyName.StringValue, methodOrPropertyName.StartPos, methodOrPropertyName.EndPos, args)); - - return true; - } - - return false; - } - - private bool MaybeEatConstructorReference() - { - if (PeekIdentifierToken("new")) - { - Token newToken = TakeToken(); - - // It looks like a constructor reference but is NEW being used as a map key? - if (PeekToken(TokenKind.RightSquare)) - { - // looks like 'NEW]' (so NEW used as map key) - Push(new PropertyOrFieldReference(false, newToken.StringValue, newToken.StartPos, newToken.EndPos)); - return true; - } - - SpelNode possiblyQualifiedConstructorName = EatPossiblyQualifiedId(); - - var nodes = new List - { - possiblyQualifiedConstructorName - }; - - if (PeekToken(TokenKind.LeftSquare)) - { - // array initializer - var dimensions = new List(); - - while (PeekToken(TokenKind.LeftSquare, true)) - { - dimensions.Add(!PeekToken(TokenKind.RightSquare) ? EatExpression() : null); - - EatToken(TokenKind.RightSquare); - } - - if (MaybeEatInlineListOrMap()) - { - nodes.Add(Pop()); - } - - Push(new ConstructorReference(newToken.StartPos, newToken.EndPos, dimensions.ToArray(), nodes.ToArray())); - } - else - { - // regular constructor invocation - EatConstructorArgs(nodes); - - Push(new ConstructorReference(newToken.StartPos, newToken.EndPos, nodes.ToArray())); - } - - return true; - } - - return false; - } - - private void Push(SpelNode newNode) - { - ConstructedNodes.Push(newNode); - } - - private SpelNode Pop() - { - return ConstructedNodes.Pop(); - } - - private bool MaybeEatLiteral() - { - Token t = PeekToken(); - - if (t == null) - { - return false; - } - - if (Equals(t.Kind, TokenKind.LiteralInt)) - { - Push(Literal.GetIntLiteral(t.StringValue, t.StartPos, t.EndPos, NumberStyles.Integer)); - } - else if (Equals(t.Kind, TokenKind.LiteralLong)) - { - Push(Literal.GetLongLiteral(t.StringValue, t.StartPos, t.EndPos, NumberStyles.Integer)); - } - else if (Equals(t.Kind, TokenKind.LiteralHexInt)) - { - Push(Literal.GetIntLiteral(t.StringValue, t.StartPos, t.EndPos, NumberStyles.HexNumber)); - } - else if (Equals(t.Kind, TokenKind.LiteralHexLong)) - { - Push(Literal.GetLongLiteral(t.StringValue, t.StartPos, t.EndPos, NumberStyles.HexNumber)); - } - else if (Equals(t.Kind, TokenKind.LiteralReal)) - { - Push(Literal.GetRealLiteral(t.StringValue, t.StartPos, t.EndPos, false)); - } - else if (Equals(t.Kind, TokenKind.LiteralRealFloat)) - { - Push(Literal.GetRealLiteral(t.StringValue, t.StartPos, t.EndPos, true)); - } - else if (PeekIdentifierToken("true")) - { - Push(new BooleanLiteral(t.StringValue, t.StartPos, t.EndPos, true)); - } - else if (PeekIdentifierToken("false")) - { - Push(new BooleanLiteral(t.StringValue, t.StartPos, t.EndPos, false)); - } - else if (Equals(t.Kind, TokenKind.LiteralString)) - { - Push(new StringLiteral(t.StringValue, t.StartPos, t.EndPos, t.StringValue)); - } - else - { - return false; - } - - NextToken(); - return true; - } - - private bool MaybeEatParenExpression() - { - if (PeekToken(TokenKind.LeftParen)) - { - NextToken(); - SpelNode expr = EatExpression(); - - if (expr == null) - { - throw new InvalidOperationException("No node"); - } - - EatToken(TokenKind.RightParen); - Push(expr); - return true; - } - - return false; - } - - private Token MaybeEatRelationalOperator() - { - Token t = PeekToken(); - - if (t == null) - { - return null; - } - - if (t.IsNumericRelationalOperator) - { - return t; - } - - if (t.IsIdentifier) - { - string idString = t.StringValue; - - if (idString.Equals("instanceof", StringComparison.OrdinalIgnoreCase)) - { - return t.AsInstanceOfToken(); - } - - if (idString.Equals("matches", StringComparison.OrdinalIgnoreCase)) - { - return t.AsMatchesToken(); - } - - if (idString.Equals("between", StringComparison.OrdinalIgnoreCase)) - { - return t.AsBetweenToken(); - } - } - - return null; - } - - private Token EatToken(TokenKind expectedKind) - { - Token t = NextToken(); - - if (t == null) - { - int pos = ExpressionString.Length; - throw InternalException(pos, SpelMessage.Ood); - } - - if (!Equals(t.Kind, expectedKind)) - { - throw InternalException(t.StartPos, SpelMessage.NotExpectedToken, FormatTokenKind(expectedKind), FormatTokenKind(t.Kind)); - } - - return t; - } - - private bool PeekToken(TokenKind desiredTokenKind) - { - return PeekToken(desiredTokenKind, false); - } - - private bool PeekToken(TokenKind desiredTokenKind, bool consumeIfMatched) - { - Token t = PeekToken(); - - if (t == null) - { - return false; - } - - if (Equals(t.Kind, desiredTokenKind)) - { - if (consumeIfMatched) - { - TokenStreamPointer++; - } - - return true; - } - - // Might be one of the textual forms of the operators (e.g. NE for != ) - - // in which case we can treat it as an identifier. The list is represented here: - // Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum. - if (Equals(desiredTokenKind, TokenKind.Identifier) && t.Kind.Ordinal >= TokenKind.Div.Ordinal && t.Kind.Ordinal <= TokenKind.Not.Ordinal && - t.Data != null) - { - // if t.data were null, we'd know it wasn't the textual form, it was the symbol form - return true; - } - - return false; - } - - private bool PeekToken(TokenKind possible1, TokenKind possible2) - { - Token t = PeekToken(); - - if (t == null) - { - return false; - } - - return Equals(t.Kind, possible1) || Equals(t.Kind, possible2); - } - - private bool PeekToken(TokenKind possible1, TokenKind possible2, TokenKind possible3) - { - Token t = PeekToken(); - - if (t == null) - { - return false; - } - - return Equals(t.Kind, possible1) || Equals(t.Kind, possible2) || Equals(t.Kind, possible3); - } - - private Token PeekToken() - { - if (TokenStreamPointer >= TokenStreamLength) - { - return null; - } - - return TokenStream[TokenStreamPointer]; - } - - private bool PeekIdentifierToken(string identifierString) - { - Token t = PeekToken(); - - if (t == null) - { - return false; - } - - return Equals(t.Kind, TokenKind.Identifier) && identifierString.Equals(t.StringValue, StringComparison.OrdinalIgnoreCase); - } - - private bool PeekSelectToken() - { - Token t = PeekToken(); - - if (t == null) - { - return false; - } - - return Equals(t.Kind, TokenKind.Select) || Equals(t.Kind, TokenKind.SelectFirst) || Equals(t.Kind, TokenKind.SelectLast); - } - - private Token TakeToken() - { - if (TokenStreamPointer >= TokenStreamLength) - { - throw new InvalidOperationException("No token"); - } - - return TokenStream[TokenStreamPointer++]; - } - - private Token NextToken() - { - if (TokenStreamPointer >= TokenStreamLength) - { - return null; - } - - return TokenStream[TokenStreamPointer++]; - } - - private string ToString(Token t) - { - if (t == null) - { - return string.Empty; - } - - if (t.Kind.HasPayload) - { - return t.StringValue; - } - - return FormatTokenKind(t.Kind); - } - - private void CheckOperands(Token token, SpelNode left, SpelNode right) - { - CheckLeftOperand(token, left); - CheckRightOperand(token, right); - } - - private void CheckLeftOperand(Token token, SpelNode operandExpression) - { - if (operandExpression == null) - { - throw InternalException(token.StartPos, SpelMessage.LeftOperandProblem); - } - } - - private void CheckRightOperand(Token token, SpelNode operandExpression) - { - if (operandExpression == null) - { - throw InternalException(token.StartPos, SpelMessage.RightOperandProblem); - } - } - - private InternalParseException InternalException(int startPos, SpelMessage message, params object[] inserts) - { - return new InternalParseException(new SpelParseException(ExpressionString, startPos, message, inserts)); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelCompiledExpression.cs b/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelCompiledExpression.cs deleted file mode 100644 index b22e53140d..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelCompiledExpression.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Common.Expression.Internal.Spring.Standard; - -public class SpelCompiledExpression : CompiledExpression -{ - private readonly ILogger _logger; - - private int _initialized; - - internal SpelCompiledExpression(ILoggerFactory loggerFactory) - { - _logger = loggerFactory?.CreateLogger(); - } - - public override object GetValue(object target, IEvaluationContext context) - { - var initDelegate = InitDelegate as SpelExpressionInitDelegate; - var spelDelegate = MethodDelegate as SpelExpressionDelegate; - - try - { - // One time initialization call that allows expression init if needed (e.g. InlineList uses this) - if (Interlocked.CompareExchange(ref _initialized, 1, 0) == 0) - { - initDelegate?.Invoke(this, target, context); - } - - // Invoke the compiled expression - object result = spelDelegate.Invoke(this, target, context); - return result; - } - catch (Exception ex) - { - _logger?.LogError(ex, "Compiled Expression exception"); - throw; - } - } - - internal delegate object SpelExpressionDelegate(SpelCompiledExpression expression, object target, IEvaluationContext context); - - internal delegate void SpelExpressionInitDelegate(SpelCompiledExpression expression, object target, IEvaluationContext context); -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelCompiler.cs b/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelCompiler.cs deleted file mode 100644 index 9eff06c07b..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelCompiler.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection.Emit; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Util; -using static Steeltoe.Common.Expression.Internal.Spring.Standard.SpelCompiledExpression; - -namespace Steeltoe.Common.Expression.Internal.Spring.Standard; - -public class SpelCompiler -{ - private readonly ILoggerFactory _loggerFactory; - private readonly ILogger _logger; - - // Counter suffix for generated classes within this SpelCompiler instance - private readonly AtomicInteger _suffixId = new(1); - - private SpelCompiler(ILoggerFactory loggerFactory = null) - { - _loggerFactory = loggerFactory; - _logger = _loggerFactory?.CreateLogger(); - } - - public static SpelCompiler GetCompiler(ILoggerFactory loggerFactory = null) - { - return new SpelCompiler(loggerFactory); - } - - public static void RevertToInterpreted(IExpression expression) - { - if (expression is SpelExpression expression1) - { - expression1.RevertToInterpreted(); - } - } - - public static bool Compile(IExpression expression) - { - return expression is SpelExpression expression1 && expression1.CompileExpression(); - } - - public CompiledExpression Compile(ISpelNode expression) - { - if (expression.IsCompilable()) - { - _logger?.LogDebug("SpEL: compiling {expression}", expression.ToStringAst()); - return CreateExpressionClass(expression); - } - - _logger?.LogDebug("SpEL: unable to compile {expression} ", expression.ToStringAst()); - return null; - } - - private CompiledExpression CreateExpressionClass(ISpelNode expressionToCompile) - { - var compiledExpression = new SpelCompiledExpression(_loggerFactory); - string methodName = $"SpelExpression{_suffixId.GetAndIncrement()}"; - - var method = new DynamicMethod(methodName, typeof(object), new[] - { - typeof(SpelCompiledExpression), - typeof(object), - typeof(IEvaluationContext) - }, typeof(SpelCompiledExpression)); - - ILGenerator ilGenerator = method.GetILGenerator(4096); - var cf = new CodeFlow(compiledExpression); - - try - { - expressionToCompile.GenerateCode(ilGenerator, cf); - - TypeDescriptor lastDescriptor = cf.LastDescriptor(); - CodeFlow.InsertBoxIfNecessary(ilGenerator, lastDescriptor); - - if (lastDescriptor == TypeDescriptor.V) - { - ilGenerator.Emit(OpCodes.Ldnull); - } - - ilGenerator.Emit(OpCodes.Ret); - compiledExpression.MethodDelegate = method.CreateDelegate(typeof(SpelExpressionDelegate)); - DynamicMethod initMethod = cf.Finish(_suffixId.Value); - - if (initMethod != null) - { - compiledExpression.InitDelegate = initMethod.CreateDelegate(typeof(SpelExpressionInitDelegate)); - } - - return compiledExpression; - } - catch (Exception ex) - { - _logger?.LogDebug(ex, "{name}.GenerateCode opted out of compilation: {message}", expressionToCompile.GetType().Name, ex.Message); - return null; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelExpression.cs b/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelExpression.cs deleted file mode 100644 index f42fb6413d..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelExpression.cs +++ /dev/null @@ -1,501 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring.Ast; -using Steeltoe.Common.Expression.Internal.Spring.Common; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal.Spring.Standard; - -public class SpelExpression : IExpression -{ - // Number of times to interpret an expression before compiling it - internal const int InterpretedCountThreshold = 100; - - // Number of times to try compiling an expression before giving up - internal const int FailedAttemptsThreshold = 100; - - private readonly object _lock = new(); - private readonly SpelNode _ast; - private readonly SpelParserOptions _configuration; - - // Count of many times as the expression been interpreted - can trigger compilation - // when certain limit reached - private readonly AtomicInteger _interpretedCount = new(0); - - // The number of times compilation was attempted and failed - enables us to eventually - // give up trying to compile it when it just doesn't seem to be possible. - private readonly AtomicInteger _failedAttempts = new(0); - - // The default context is used if no override is supplied by the user - private IEvaluationContext _evaluationContext; - - // Holds the compiled form of the expression (if it has been compiled) - internal volatile CompiledExpression CompiledAst; - - public IEvaluationContext EvaluationContext - { - get - { - _evaluationContext ??= new StandardEvaluationContext(); - return _evaluationContext; - } - set => _evaluationContext = value; - } - - // implementing Expression - public string ExpressionString { get; } - - public ISpelNode Ast => _ast; - - public SpelExpression(string expression, SpelNode ast, SpelParserOptions configuration) - { - ExpressionString = expression; - _ast = ast; - _configuration = configuration; - } - - public object GetValue() - { - if (CompiledAst != null) - { - try - { - IEvaluationContext context = EvaluationContext; - return CompiledAst.GetValue(context.RootObject.Value, context); - } - catch (Exception ex) - { - // If running in mixed mode, revert to interpreted - if (_configuration.CompilerMode == SpelCompilerMode.Mixed) - { - CompiledAst = null; - _interpretedCount.Value = 0; - } - else - { - // Running in SpelCompilerMode.immediate mode - propagate exception to caller - throw new SpelEvaluationException(ex, SpelMessage.ExceptionRunningCompiledExpression); - } - } - } - - var expressionState = new ExpressionState(EvaluationContext, _configuration); - object result = _ast.GetValue(expressionState); - CheckCompile(expressionState); - return result; - } - - public T GetValue() - { - return (T)GetValue(typeof(T)); - } - - public object GetValue(Type desiredResultType) - { - if (CompiledAst != null) - { - try - { - IEvaluationContext context = EvaluationContext; - object result = CompiledAst.GetValue(context.RootObject.Value, context); - - if (desiredResultType == null) - { - return result; - } - - return ExpressionUtils.ConvertTypedValue(EvaluationContext, new TypedValue(result), desiredResultType); - } - catch (Exception ex) - { - // If running in mixed mode, revert to interpreted - if (_configuration.CompilerMode == SpelCompilerMode.Mixed) - { - CompiledAst = null; - _interpretedCount.Value = 0; - } - else - { - // Running in SpelCompilerMode.immediate mode - propagate exception to caller - throw new SpelEvaluationException(ex, SpelMessage.ExceptionRunningCompiledExpression); - } - } - } - - var expressionState = new ExpressionState(EvaluationContext, _configuration); - ITypedValue typedResultValue = _ast.GetTypedValue(expressionState); - CheckCompile(expressionState); - return ExpressionUtils.ConvertTypedValue(expressionState.EvaluationContext, typedResultValue, desiredResultType); - } - - public object GetValue(object rootObject) - { - if (CompiledAst != null) - { - try - { - return CompiledAst.GetValue(rootObject, EvaluationContext); - } - catch (Exception ex) - { - // If running in mixed mode, revert to interpreted - if (_configuration.CompilerMode == SpelCompilerMode.Mixed) - { - CompiledAst = null; - _interpretedCount.Value = 0; - } - else - { - // Running in SpelCompilerMode.immediate mode - propagate exception to caller - throw new SpelEvaluationException(ex, SpelMessage.ExceptionRunningCompiledExpression); - } - } - } - - var expressionState = new ExpressionState(EvaluationContext, ToTypedValue(rootObject), _configuration); - object result = _ast.GetValue(expressionState); - CheckCompile(expressionState); - return result; - } - - public T GetValue(object rootObject) - { - return (T)GetValue(rootObject, typeof(T)); - } - - public object GetValue(object rootObject, Type desiredResultType) - { - if (CompiledAst != null) - { - try - { - object result = CompiledAst.GetValue(rootObject, EvaluationContext); - - if (desiredResultType == null) - { - return result; - } - - return ExpressionUtils.ConvertTypedValue(EvaluationContext, new TypedValue(result), desiredResultType); - } - catch (Exception ex) - { - // If running in mixed mode, revert to interpreted - if (_configuration.CompilerMode == SpelCompilerMode.Mixed) - { - CompiledAst = null; - _interpretedCount.Value = 0; - } - else - { - // Running in SpelCompilerMode.immediate mode - propagate exception to caller - throw new SpelEvaluationException(ex, SpelMessage.ExceptionRunningCompiledExpression); - } - } - } - - var expressionState = new ExpressionState(EvaluationContext, ToTypedValue(rootObject), _configuration); - ITypedValue typedResultValue = _ast.GetTypedValue(expressionState); - CheckCompile(expressionState); - return ExpressionUtils.ConvertTypedValue(expressionState.EvaluationContext, typedResultValue, desiredResultType); - } - - public object GetValue(IEvaluationContext context) - { - ArgumentGuard.NotNull(context); - - if (CompiledAst != null) - { - try - { - return CompiledAst.GetValue(context.RootObject.Value, context); - } - catch (Exception ex) - { - // If running in mixed mode, revert to interpreted - if (_configuration.CompilerMode == SpelCompilerMode.Mixed) - { - CompiledAst = null; - _interpretedCount.Value = 0; - } - else - { - // Running in SpelCompilerMode.immediate mode - propagate exception to caller - throw new SpelEvaluationException(ex, SpelMessage.ExceptionRunningCompiledExpression); - } - } - } - - var expressionState = new ExpressionState(context, _configuration); - object result = _ast.GetValue(expressionState); - CheckCompile(expressionState); - return result; - } - - public T GetValue(IEvaluationContext context) - { - return (T)GetValue(context, typeof(T)); - } - - public object GetValue(IEvaluationContext context, Type desiredResultType) - { - ArgumentGuard.NotNull(context); - - if (CompiledAst != null) - { - try - { - object result = CompiledAst.GetValue(context.RootObject.Value, context); - - if (desiredResultType != null) - { - return ExpressionUtils.ConvertTypedValue(context, new TypedValue(result), desiredResultType); - } - - return result; - } - catch (Exception ex) - { - // If running in mixed mode, revert to interpreted - if (_configuration.CompilerMode == SpelCompilerMode.Mixed) - { - CompiledAst = null; - _interpretedCount.Value = 0; - } - else - { - // Running in SpelCompilerMode.immediate mode - propagate exception to caller - throw new SpelEvaluationException(ex, SpelMessage.ExceptionRunningCompiledExpression); - } - } - } - - var expressionState = new ExpressionState(context, _configuration); - ITypedValue typedResultValue = _ast.GetTypedValue(expressionState); - CheckCompile(expressionState); - return ExpressionUtils.ConvertTypedValue(context, typedResultValue, desiredResultType); - } - - public object GetValue(IEvaluationContext context, object rootObject) - { - ArgumentGuard.NotNull(context); - - if (CompiledAst != null) - { - try - { - return CompiledAst.GetValue(rootObject, context); - } - catch (Exception ex) - { - // If running in mixed mode, revert to interpreted - if (_configuration.CompilerMode == SpelCompilerMode.Mixed) - { - CompiledAst = null; - _interpretedCount.Value = 0; - } - else - { - // Running in SpelCompilerMode.immediate mode - propagate exception to caller - throw new SpelEvaluationException(ex, SpelMessage.ExceptionRunningCompiledExpression); - } - } - } - - var expressionState = new ExpressionState(context, ToTypedValue(rootObject), _configuration); - object result = _ast.GetValue(expressionState); - CheckCompile(expressionState); - return result; - } - - public T GetValue(IEvaluationContext context, object rootObject) - { - return (T)GetValue(context, rootObject, typeof(T)); - } - - public object GetValue(IEvaluationContext context, object rootObject, Type desiredResultType) - { - ArgumentGuard.NotNull(context); - - if (CompiledAst != null) - { - try - { - object result = CompiledAst.GetValue(rootObject, context); - - if (desiredResultType != null) - { - return ExpressionUtils.ConvertTypedValue(context, new TypedValue(result), desiredResultType); - } - - return result; - } - catch (Exception ex) - { - // If running in mixed mode, revert to interpreted - if (_configuration.CompilerMode == SpelCompilerMode.Mixed) - { - CompiledAst = null; - _interpretedCount.Value = 0; - } - else - { - // Running in SpelCompilerMode.immediate mode - propagate exception to caller - throw new SpelEvaluationException(ex, SpelMessage.ExceptionRunningCompiledExpression); - } - } - } - - var expressionState = new ExpressionState(context, ToTypedValue(rootObject), _configuration); - ITypedValue typedResultValue = _ast.GetTypedValue(expressionState); - CheckCompile(expressionState); - return ExpressionUtils.ConvertTypedValue(context, typedResultValue, desiredResultType); - } - - public Type GetValueType() - { - return GetValueType(EvaluationContext); - } - - public Type GetValueType(object rootObject) - { - return GetValueType(EvaluationContext, rootObject); - } - - public Type GetValueType(IEvaluationContext context) - { - ArgumentGuard.NotNull(context); - - var expressionState = new ExpressionState(context, _configuration); - return _ast.GetValueInternal(expressionState).TypeDescriptor; - } - - public Type GetValueType(IEvaluationContext context, object rootObject) - { - var expressionState = new ExpressionState(context, ToTypedValue(rootObject), _configuration); - return _ast.GetValueInternal(expressionState).TypeDescriptor; - } - - public bool IsWritable(object rootObject) - { - return _ast.IsWritable(new ExpressionState(EvaluationContext, ToTypedValue(rootObject), _configuration)); - } - - public bool IsWritable(IEvaluationContext context) - { - ArgumentGuard.NotNull(context); - - return _ast.IsWritable(new ExpressionState(context, _configuration)); - } - - public bool IsWritable(IEvaluationContext context, object rootObject) - { - ArgumentGuard.NotNull(context); - - return _ast.IsWritable(new ExpressionState(context, ToTypedValue(rootObject), _configuration)); - } - - public void SetValue(object rootObject, object value) - { - _ast.SetValue(new ExpressionState(EvaluationContext, ToTypedValue(rootObject), _configuration), value); - } - - public void SetValue(IEvaluationContext context, object value) - { - ArgumentGuard.NotNull(context); - - _ast.SetValue(new ExpressionState(context, _configuration), value); - } - - public void SetValue(IEvaluationContext context, object rootObject, object value) - { - ArgumentGuard.NotNull(context); - - _ast.SetValue(new ExpressionState(context, ToTypedValue(rootObject), _configuration), value); - } - - public bool CompileExpression() - { - CompiledExpression compiledAst = CompiledAst; - - if (compiledAst != null) - { - // Previously compiled - return true; - } - - if (_failedAttempts.Value > FailedAttemptsThreshold) - { - // Don't try again - return false; - } - - lock (_lock) - { - if (CompiledAst != null) - { - // Compiled by another thread before this thread got into the sync block - return true; - } - - SpelCompiler compiler = SpelCompiler.GetCompiler(); - compiledAst = compiler.Compile(_ast); - - if (compiledAst != null) - { - // Successfully compiled - CompiledAst = compiledAst; - return true; - } - - // Failed to compile - _failedAttempts.IncrementAndGet(); - return false; - } - } - - public void RevertToInterpreted() - { - CompiledAst = null; - _interpretedCount.Value = 0; - _failedAttempts.Value = 0; - } - - public string ToStringAst() - { - return _ast.ToStringAst(); - } - - private void CheckCompile(ExpressionState expressionState) - { - _interpretedCount.IncrementAndGet(); - SpelCompilerMode compilerMode = expressionState.Configuration.CompilerMode; - - if (compilerMode != SpelCompilerMode.Off) - { - if (compilerMode == SpelCompilerMode.Immediate) - { - if (_interpretedCount.Value > 1) - { - CompileExpression(); - } - } - else - { - // compilerMode = SpelCompilerMode.MIXED - if (_interpretedCount.Value > InterpretedCountThreshold) - { - CompileExpression(); - } - } - } - } - - private TypedValue ToTypedValue(object obj) - { - return obj != null ? new TypedValue(obj) : TypedValue.Null; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelExpressionParser.cs b/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelExpressionParser.cs deleted file mode 100644 index 74ea4468b7..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Standard/SpelExpressionParser.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring.Common; - -namespace Steeltoe.Common.Expression.Internal.Spring.Standard; - -public class SpelExpressionParser : TemplateAwareExpressionParser -{ - private readonly SpelParserOptions _configuration; - - public SpelExpressionParser() - { - _configuration = new SpelParserOptions(); - } - - public SpelExpressionParser(SpelParserOptions configuration) - { - ArgumentGuard.NotNull(configuration); - - _configuration = configuration; - } - - public IExpression ParseRaw(string expressionString) - { - return DoParseExpression(expressionString, null); - } - - protected internal override IExpression DoParseExpression(string expressionString, IParserContext context) - { - return new InternalSpelExpressionParser(_configuration).DoParseExpression(expressionString, context); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Standard/Token.cs b/src/Common/src/Common.Expression/Internal/Spring/Standard/Token.cs deleted file mode 100644 index c2345b523e..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Standard/Token.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Expression.Internal.Spring.Standard; - -public class Token -{ - private static readonly ISet NumericOperatorKinds = new[] - { - TokenKind.Gt, - TokenKind.Ge, - TokenKind.Lt, - TokenKind.Le, - TokenKind.Eq, - TokenKind.Ne - }.ToHashSet(); - - public TokenKind Kind { get; } - - public int StartPos { get; } - - public int EndPos { get; } - - public string Data { get; } - - public bool IsIdentifier => Equals(Kind, TokenKind.Identifier); - - public bool IsNumericRelationalOperator => NumericOperatorKinds.Contains(Kind); - - public string StringValue => Data ?? string.Empty; - - public Token(TokenKind tokenKind, int startPos, int endPos) - { - Kind = tokenKind; - StartPos = startPos; - EndPos = endPos; - } - - public Token(TokenKind tokenKind, char[] tokenData, int startPos, int endPos) - : this(tokenKind, startPos, endPos) - { - Data = new string(tokenData); - } - - public Token AsInstanceOfToken() - { - return new Token(TokenKind.InstanceOf, StartPos, EndPos); - } - - public Token AsMatchesToken() - { - return new Token(TokenKind.Matches, StartPos, EndPos); - } - - public Token AsBetweenToken() - { - return new Token(TokenKind.Between, StartPos, EndPos); - } - - public override string ToString() - { - var s = new StringBuilder(); - s.Append('[').Append(Kind); - - if (Kind.HasPayload) - { - s.Append(':').Append(Data); - } - - s.Append(']'); - s.Append('(').Append(StartPos).Append(',').Append(EndPos).Append(')'); - return s.ToString(); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Standard/TokenKind.cs b/src/Common/src/Common.Expression/Internal/Spring/Standard/TokenKind.cs deleted file mode 100644 index 8aac277614..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Standard/TokenKind.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Standard; - -public class TokenKind -{ - private static readonly Dictionary Kinds = new(); - - // ordered by priority - operands first - // Adding anything in this list requires adjusting order parameter - public static readonly TokenKind LiteralInt = new(1, "LITERAL_INT"); - public static readonly TokenKind LiteralLong = new(2, "LITERAL_LONG"); - public static readonly TokenKind LiteralHexInt = new(3, "LITERAL_HEXINT"); - public static readonly TokenKind LiteralHexLong = new(4, "LITERAL_HEXLONG"); - public static readonly TokenKind LiteralString = new(5, "LITERAL_STRING"); - public static readonly TokenKind LiteralReal = new(6, "LITERAL_REAL"); - public static readonly TokenKind LiteralRealFloat = new(7, "LITERAL_REAL_FLOAT"); - public static readonly TokenKind LeftParen = new(8, "LPAREN", "("); - public static readonly TokenKind RightParen = new(9, "RPAREN", ")"); - public static readonly TokenKind Comma = new(10, "COMMA", ","); - public static readonly TokenKind Identifier = new(11, "IDENTIFIER"); - public static readonly TokenKind Colon = new(12, "COLON", ":"); - public static readonly TokenKind Hash = new(13, "HASH", "#"); - public static readonly TokenKind RightSquare = new(14, "RSQUARE", "]"); - public static readonly TokenKind LeftSquare = new(15, "LSQUARE", "["); - public static readonly TokenKind LeftCurly = new(16, "LCURLY", "{"); - public static readonly TokenKind RightCurly = new(17, "RCURLY", "}"); - public static readonly TokenKind Dot = new(18, "DOT", "."); - public static readonly TokenKind Plus = new(19, "PLUS", "+"); - public static readonly TokenKind Star = new(20, "STAR", "*"); - public static readonly TokenKind Minus = new(21, "MINUS", "-"); - public static readonly TokenKind SelectFirst = new(22, "SELECT_FIRST", "^["); - public static readonly TokenKind SelectLast = new(23, "SELECT_LAST", "$["); - public static readonly TokenKind QuestionMark = new(24, "QMARK", "?"); - public static readonly TokenKind Project = new(25, "PROJECT", "!["); - public static readonly TokenKind Div = new(26, "DIV", "/"); - public static readonly TokenKind Ge = new(27, "GE", ">="); - public static readonly TokenKind Gt = new(28, "GT", ">"); - public static readonly TokenKind Le = new(29, "LE", "<="); - public static readonly TokenKind Lt = new(30, "LT", "<"); - public static readonly TokenKind Eq = new(31, "EQ", "=="); - public static readonly TokenKind Ne = new(32, "NE", "!="); - public static readonly TokenKind Mod = new(33, "MOD", "%"); - public static readonly TokenKind Not = new(34, "NOT", "!"); - public static readonly TokenKind Assign = new(35, "ASSIGN", "="); - public static readonly TokenKind InstanceOf = new(36, "INSTANCEOF", "instanceof"); - public static readonly TokenKind Matches = new(37, "MATCHES", "matches"); - public static readonly TokenKind Between = new(38, "BETWEEN", "between"); - public static readonly TokenKind Select = new(39, "SELECT", "?["); - public static readonly TokenKind Power = new(40, "POWER", "^"); - public static readonly TokenKind Elvis = new(41, "ELVIS", "?:"); - public static readonly TokenKind SafeNavigator = new(42, "SAFE_NAVI", "?."); - public static readonly TokenKind ServiceRef = new(43, "SERVICE_REF", "@"); - public static readonly TokenKind FactoryServiceRef = new(44, "FACTORY_SERVICE_REF", "&"); - public static readonly TokenKind SymbolicOr = new(45, "SYMBOLIC_OR", "||"); - public static readonly TokenKind SymbolicAnd = new(46, "SYMBOLIC_AND", "&&"); - public static readonly TokenKind Inc = new(47, "INC", "++"); - public static readonly TokenKind Dec = new(48, "DEC", "--"); - - public bool HasPayload { get; } - - public int Length => TokenChars.Length; - - public string Name { get; } - - public char[] TokenChars { get; } - - public int Ordinal { get; } - - private TokenKind(int order, string name, string tokenString) - { - Ordinal = order; - Name = name; - TokenChars = tokenString?.ToCharArray(); - HasPayload = TokenChars?.Length == 0; - Kinds.Add(Name, this); - } - - private TokenKind(int order, string name) - : this(order, name, string.Empty) - { - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not TokenKind other) - { - return false; - } - - return Name == other.Name; - } - - public override int GetHashCode() - { - return Name.GetHashCode(); - } - - public override string ToString() - { - return Name + (TokenChars.Length != 0 ? $"({new string(TokenChars)})" : string.Empty); - } - - internal static TokenKind ValueOf(string name) - { - if (!Kinds.TryGetValue(name, out TokenKind kind)) - { - throw new InvalidOperationException($"Invalid TokenKind name: {name}"); - } - - return kind; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Standard/Tokenizer.cs b/src/Common/src/Common.Expression/Internal/Spring/Standard/Tokenizer.cs deleted file mode 100644 index 591c1b7d5e..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Standard/Tokenizer.cs +++ /dev/null @@ -1,685 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Standard; -#pragma warning disable S125 // Sections of code should not be commented out - -internal sealed class Tokenizer -{ - private const byte IsDigitFlag = 0x01; - private const byte IsHexDigitFlag = 0x02; - private const byte IsAlphaFlag = 0x04; - - // If this gets changed, it must remain sorted... - private static readonly string[] AlternativeOperatorNames = - { - "DIV", - "EQ", - "GE", - "GT", - "LE", - "LT", - "MOD", - "NE", - "NOT" - }; - - private static readonly byte[] Flags = new byte[256]; - - private readonly int _max; - private readonly List _tokens = new(); - private readonly string _expressionString; - private readonly char[] _charsToProcess; - - private int _pos; - - static Tokenizer() - { - for (int ch = '0'; ch <= '9'; ch++) - { - Flags[ch] |= IsDigitFlag | IsHexDigitFlag; - } - - for (int ch = 'A'; ch <= 'F'; ch++) - { - Flags[ch] |= IsHexDigitFlag; - } - - for (int ch = 'a'; ch <= 'f'; ch++) - { - Flags[ch] |= IsHexDigitFlag; - } - - for (int ch = 'A'; ch <= 'Z'; ch++) - { - Flags[ch] |= IsAlphaFlag; - } - - for (int ch = 'a'; ch <= 'z'; ch++) - { - Flags[ch] |= IsAlphaFlag; - } - } - - public Tokenizer(string inputData) - { - _expressionString = inputData; - _charsToProcess = (inputData + "\0").ToCharArray(); - _max = _charsToProcess.Length; - _pos = 0; - } - - public List Process() - { - while (_pos < _max) - { - char ch = _charsToProcess[_pos]; - - if (IsAlphabetic(ch)) - { - LexIdentifier(); - } - else - { -#pragma warning disable S1479 // "switch" statements should not have too many "case" clauses - switch (ch) -#pragma warning restore S1479 // "switch" statements should not have too many "case" clauses - { - case '+': - if (IsTwoCharToken(TokenKind.Inc)) - { - PushPairToken(TokenKind.Inc); - } - else - { - PushCharToken(TokenKind.Plus); - } - - break; - case '_': // the other way to start an identifier - LexIdentifier(); - break; - case '-': - if (IsTwoCharToken(TokenKind.Dec)) - { - PushPairToken(TokenKind.Dec); - } - else - { - PushCharToken(TokenKind.Minus); - } - - break; - case ':': - PushCharToken(TokenKind.Colon); - break; - case '.': - PushCharToken(TokenKind.Dot); - break; - case ',': - PushCharToken(TokenKind.Comma); - break; - case '*': - PushCharToken(TokenKind.Star); - break; - case '/': - PushCharToken(TokenKind.Div); - break; - case '%': - PushCharToken(TokenKind.Mod); - break; - case '(': - PushCharToken(TokenKind.LeftParen); - break; - case ')': - PushCharToken(TokenKind.RightParen); - break; - case '[': - PushCharToken(TokenKind.LeftSquare); - break; - case '#': - PushCharToken(TokenKind.Hash); - break; - case ']': - PushCharToken(TokenKind.RightSquare); - break; - case '{': - PushCharToken(TokenKind.LeftCurly); - break; - case '}': - PushCharToken(TokenKind.RightCurly); - break; - case '@': - PushCharToken(TokenKind.ServiceRef); - break; - case '^': - if (IsTwoCharToken(TokenKind.SelectFirst)) - { - PushPairToken(TokenKind.SelectFirst); - } - else - { - PushCharToken(TokenKind.Power); - } - - break; - case '!': - if (IsTwoCharToken(TokenKind.Ne)) - { - PushPairToken(TokenKind.Ne); - } - else if (IsTwoCharToken(TokenKind.Project)) - { - PushPairToken(TokenKind.Project); - } - else - { - PushCharToken(TokenKind.Not); - } - - break; - case '=': - if (IsTwoCharToken(TokenKind.Eq)) - { - PushPairToken(TokenKind.Eq); - } - else - { - PushCharToken(TokenKind.Assign); - } - - break; - case '&': - if (IsTwoCharToken(TokenKind.SymbolicAnd)) - { - PushPairToken(TokenKind.SymbolicAnd); - } - else - { - PushCharToken(TokenKind.FactoryServiceRef); - } - - break; - case '|': - if (!IsTwoCharToken(TokenKind.SymbolicOr)) - { - RaiseParseException(_pos, SpelMessage.MissingCharacter, "|"); - } - - PushPairToken(TokenKind.SymbolicOr); - break; - case '?': - if (IsTwoCharToken(TokenKind.Select)) - { - PushPairToken(TokenKind.Select); - } - else if (IsTwoCharToken(TokenKind.Elvis)) - { - PushPairToken(TokenKind.Elvis); - } - else if (IsTwoCharToken(TokenKind.SafeNavigator)) - { - PushPairToken(TokenKind.SafeNavigator); - } - else - { - PushCharToken(TokenKind.QuestionMark); - } - - break; - case '$': - if (IsTwoCharToken(TokenKind.SelectLast)) - { - PushPairToken(TokenKind.SelectLast); - } - else - { - LexIdentifier(); - } - - break; - case '>': - if (IsTwoCharToken(TokenKind.Ge)) - { - PushPairToken(TokenKind.Ge); - } - else - { - PushCharToken(TokenKind.Gt); - } - - break; - case '<': - if (IsTwoCharToken(TokenKind.Le)) - { - PushPairToken(TokenKind.Le); - } - else - { - PushCharToken(TokenKind.Lt); - } - - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - LexNumericLiteral(ch == '0'); - break; - case ' ': - case '\t': - case '\r': - case '\n': - // drift over white space - _pos++; - break; - case '\'': - LexQuotedStringLiteral(); - break; - case '"': - LexDoubleQuotedStringLiteral(); - break; - case '\0': - // hit sentinel at end of value - _pos++; // will take us to the end - break; - case '\\': - RaiseParseException(_pos, SpelMessage.UnexpectedEscapeChar); - break; - default: - throw new InvalidOperationException($"Cannot handle ({(int)ch}) '{ch}'"); - } - } - } - - return _tokens; - } - - // STRING_LITERAL: '\''! (APOS|~'\'')* '\''!; - private void LexQuotedStringLiteral() - { - int start = _pos; - bool terminated = false; - - while (!terminated) - { - _pos++; - char ch = _charsToProcess[_pos]; - - if (ch == '\'') - { - // may not be the end if the char after is also a ' - if (_charsToProcess[_pos + 1] == '\'') - { - _pos++; // skip over that too, and continue - } - else - { - terminated = true; - } - } - - if (IsExhausted()) - { - RaiseParseException(start, SpelMessage.NonTerminatingQuotedString); - } - } - - _pos++; - _tokens.Add(new Token(TokenKind.LiteralString, SubArray(start, _pos), start, _pos)); - } - - // DQ_STRING_LITERAL: '"'! (~'"')* '"'!; - private void LexDoubleQuotedStringLiteral() - { - int start = _pos; - bool terminated = false; - - while (!terminated) - { - _pos++; - char ch = _charsToProcess[_pos]; - - if (ch == '"') - { - // may not be the end if the char after is also a " - if (_charsToProcess[_pos + 1] == '"') - { - _pos++; // skip over that too, and continue - } - else - { - terminated = true; - } - } - - if (IsExhausted()) - { - RaiseParseException(start, SpelMessage.NonTerminatingDoubleQuotedString); - } - } - - _pos++; - _tokens.Add(new Token(TokenKind.LiteralString, SubArray(start, _pos), start, _pos)); - } - - // REAL_LITERAL : - // ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | - // ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | - // ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) | - // ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)); - // fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' ); - // fragment HEX_DIGIT : - // '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f'; - // - // fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* - // (DECIMAL_DIGIT)+ ; - // fragment SIGN : '+' | '-' ; - // fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd'; - // INTEGER_LITERAL - // : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?; - private void LexNumericLiteral(bool firstCharIsZero) - { - bool isReal = false; - int start = _pos; - char ch = _charsToProcess[_pos + 1]; - bool isHex = ch == 'x' || ch == 'X'; - - // deal with hexadecimal - if (firstCharIsZero && isHex) - { - _pos++; - - do - { - _pos++; - } - while (IsHexadecimalDigit(_charsToProcess[_pos])); - - if (IsChar('L', 'l')) - { - PushHexIntToken(SubArray(start + 2, _pos), true, start, _pos); - _pos++; - } - else - { - PushHexIntToken(SubArray(start + 2, _pos), false, start, _pos); - } - - return; - } - - // real numbers must have leading digits - - // Consume first part of number - do - { - _pos++; - } - while (IsDigit(_charsToProcess[_pos])); - - // a '.' indicates this number is a real - ch = _charsToProcess[_pos]; - - if (ch == '.') - { - isReal = true; - int dotPos = _pos; - - // carry on consuming digits - do - { - _pos++; - } - while (IsDigit(_charsToProcess[_pos])); - - if (_pos == dotPos + 1) - { - // the number is something like '3.'. It is really an int but may be - // part of something like '3.toString()'. In this case process it as - // an int and leave the dot as a separate token. - _pos = dotPos; - PushIntToken(SubArray(start, _pos), false, start, _pos); - return; - } - } - - int endOfNumber = _pos; - - // Now there may or may not be an exponent - - // Is it a long ? - if (IsChar('L', 'l')) - { - if (isReal) - { - // 3.4L - not allowed - RaiseParseException(start, SpelMessage.RealCannotBeLong); - } - - PushIntToken(SubArray(start, endOfNumber), true, start, endOfNumber); - _pos++; - } - else if (IsExponentChar(_charsToProcess[_pos])) - { - _pos++; - char possibleSign = _charsToProcess[_pos]; - - if (IsSign(possibleSign)) - { - _pos++; - } - - // exponent digits - do - { - _pos++; - } - while (IsDigit(_charsToProcess[_pos])); - - bool isFloat = false; - - if (IsFloatSuffix(_charsToProcess[_pos])) - { - isFloat = true; - ++_pos; - } - else if (IsDoubleSuffix(_charsToProcess[_pos])) - { - ++_pos; - } - - PushRealToken(SubArray(start, _pos), isFloat, start, _pos); - } - else - { - ch = _charsToProcess[_pos]; - bool isFloat = false; - - if (IsFloatSuffix(ch)) - { - isReal = true; - isFloat = true; - endOfNumber = ++_pos; - } - else if (IsDoubleSuffix(ch)) - { - isReal = true; - endOfNumber = ++_pos; - } - - if (isReal) - { - PushRealToken(SubArray(start, endOfNumber), isFloat, start, endOfNumber); - } - else - { - PushIntToken(SubArray(start, endOfNumber), false, start, endOfNumber); - } - } - } - - private void LexIdentifier() - { - int start = _pos; - - do - { - _pos++; - } - while (IsIdentifier(_charsToProcess[_pos])); - - char[] subArray = SubArray(start, _pos); - - // Check if this is the alternative (textual) representation of an operator (see - // alternativeOperatorNames) - if (_pos - start == 2 || _pos - start == 3) - { - string asString = new string(subArray).ToUpperInvariant(); - int idx = Array.BinarySearch(AlternativeOperatorNames, asString); - - if (idx >= 0) - { - PushOneCharOrTwoCharToken(TokenKind.ValueOf(asString), start, subArray); - return; - } - } - - _tokens.Add(new Token(TokenKind.Identifier, subArray, start, _pos)); - } - - private void PushIntToken(char[] data, bool isLong, int start, int end) - { - _tokens.Add(isLong ? new Token(TokenKind.LiteralLong, data, start, end) : new Token(TokenKind.LiteralInt, data, start, end)); - } - - private void PushHexIntToken(char[] data, bool isLong, int start, int end) - { - if (data.Length == 0) - { - if (isLong) - { - RaiseParseException(start, SpelMessage.NotALong, _expressionString.Substring(start, end + 1 - start)); - } - else - { - RaiseParseException(start, SpelMessage.NotAnInteger, _expressionString.Substring(start, end - start)); - } - } - - _tokens.Add(isLong ? new Token(TokenKind.LiteralHexLong, data, start, end) : new Token(TokenKind.LiteralHexInt, data, start, end)); - } - - private void PushRealToken(char[] data, bool isFloat, int start, int end) - { - _tokens.Add(isFloat ? new Token(TokenKind.LiteralRealFloat, data, start, end) : new Token(TokenKind.LiteralReal, data, start, end)); - } - - private char[] SubArray(int start, int end) - { - char[] result = new char[end - start]; - Array.Copy(_charsToProcess, start, result, 0, end - start); - return result; - } - - private bool IsTwoCharToken(TokenKind kind) - { - return kind.TokenChars.Length == 2 && _charsToProcess[_pos] == kind.TokenChars[0] && _charsToProcess[_pos + 1] == kind.TokenChars[1]; - } - - private void PushCharToken(TokenKind kind) - { - _tokens.Add(new Token(kind, _pos, _pos + 1)); - _pos++; - } - - private void PushPairToken(TokenKind kind) - { - _tokens.Add(new Token(kind, _pos, _pos + 2)); - _pos += 2; - } - - private void PushOneCharOrTwoCharToken(TokenKind kind, int pos, char[] data) - { - _tokens.Add(new Token(kind, data, pos, pos + kind.Length)); - } - - // ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*; - private bool IsIdentifier(char ch) - { - return IsAlphabetic(ch) || IsDigit(ch) || ch == '_' || ch == '$'; - } - - private bool IsChar(char a, char b) - { - char ch = _charsToProcess[_pos]; - return ch == a || ch == b; - } - - private bool IsExponentChar(char ch) - { - return ch == 'e' || ch == 'E'; - } - - private bool IsFloatSuffix(char ch) - { - return ch == 'f' || ch == 'F'; - } - - private bool IsDoubleSuffix(char ch) - { - return ch == 'd' || ch == 'D'; - } - - private bool IsSign(char ch) - { - return ch == '+' || ch == '-'; - } - - private bool IsDigit(char ch) - { - if (ch > 255) - { - return false; - } - - return (Flags[ch] & IsDigitFlag) != 0; - } - - private bool IsAlphabetic(char ch) - { - if (ch > 255) - { - return false; - } - - return (Flags[ch] & IsAlphaFlag) != 0; - } - - private bool IsHexadecimalDigit(char ch) - { - if (ch > 255) - { - return false; - } - - return (Flags[ch] & IsHexDigitFlag) != 0; - } - - private bool IsExhausted() - { - return _pos == _max - 1; - } - - private void RaiseParseException(int start, SpelMessage msg, params object[] inserts) - { - throw new InternalParseException(new SpelParseException(_expressionString, start, msg, inserts)); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/ArgumentsMatchInfo.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/ArgumentsMatchInfo.cs deleted file mode 100644 index e4960e0a3d..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/ArgumentsMatchInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class ArgumentsMatchInfo -{ - private readonly ArgumentsMatchKind _kind; - - public bool IsExactMatch => _kind == ArgumentsMatchKind.Exact; - - public bool IsCloseMatch => _kind == ArgumentsMatchKind.Close; - - public bool IsMatchRequiringConversion => _kind == ArgumentsMatchKind.RequiresConversion; - - public ArgumentsMatchInfo(ArgumentsMatchKind kind) - { - _kind = kind; - } - - public override string ToString() - { - return $"ArgumentMatchInfo: {_kind}"; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/ArgumentsMatchKind.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/ArgumentsMatchKind.cs deleted file mode 100644 index 8bacef557c..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/ArgumentsMatchKind.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public enum ArgumentsMatchKind -{ - Exact, - Close, - RequiresConversion -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/BooleanTypedValue.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/BooleanTypedValue.cs deleted file mode 100644 index 579d384e1e..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/BooleanTypedValue.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class BooleanTypedValue : TypedValue -{ - public static readonly BooleanTypedValue True = new(true); - - public static readonly BooleanTypedValue False = new(false); - - private BooleanTypedValue(bool b) - : base(b) - { - } - - public static BooleanTypedValue ForValue(bool b) - { - return b ? True : False; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/DataBindingMethodResolver.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/DataBindingMethodResolver.cs deleted file mode 100644 index 9bf7835351..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/DataBindingMethodResolver.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class DataBindingMethodResolver : ReflectiveMethodResolver -{ - private DataBindingMethodResolver() - { - } - - public static DataBindingMethodResolver ForInstanceMethodInvocation() - { - return new DataBindingMethodResolver(); - } - - public override IMethodExecutor Resolve(IEvaluationContext context, object targetObject, string name, List argumentTypes) - { - if (targetObject is Type) - { - throw new ArgumentException($"{nameof(DataBindingMethodResolver)} does not support {typeof(Type)} targets.", nameof(targetObject)); - } - - return base.Resolve(context, targetObject, name, argumentTypes); - } - - protected override bool IsCandidateForInvocation(MethodInfo method, Type targetClass) - { - if (method.IsStatic) - { - return false; - } - - Type type = method.DeclaringType; - return type != typeof(object) && type != typeof(Type); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/DataBindingPropertyAccessor.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/DataBindingPropertyAccessor.cs deleted file mode 100644 index c731610c97..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/DataBindingPropertyAccessor.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class DataBindingPropertyAccessor : ReflectivePropertyAccessor -{ - private DataBindingPropertyAccessor(bool allowWrite) - : base(allowWrite) - { - } - - public static DataBindingPropertyAccessor ForReadOnlyAccess() - { - return new DataBindingPropertyAccessor(false); - } - - public static DataBindingPropertyAccessor ForReadWriteAccess() - { - return new DataBindingPropertyAccessor(true); - } - - protected override bool IsCandidateForProperty(MethodInfo method, Type targetClass) - { - Type type = method.DeclaringType; - return type != typeof(object) && type != typeof(Type); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/MethodBaseExtensions.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/MethodBaseExtensions.cs deleted file mode 100644 index 05377f757c..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/MethodBaseExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public static class MethodBaseExtensions -{ - public static bool IsVarArgs(this MethodBase method) - { - if (method == null) - { - return false; - } - - ParameterInfo[] parameters = method.GetParameters(); - - if (parameters.Length == 0) - { - return false; - } - - ParameterInfo lastParam = parameters[^1]; - return lastParam.GetCustomAttribute() != null; - } - - public static Type[] GetParameterTypes(this MethodBase method) - { - ParameterInfo[] param = method.GetParameters(); - var result = new Type[param.Length]; - - for (int i = 0; i < param.Length; i++) - { - result[i] = param[i].ParameterType; - } - - return result; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/PrimitiveConstructorExecutor.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/PrimitiveConstructorExecutor.cs deleted file mode 100644 index 9cd78d8a02..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/PrimitiveConstructorExecutor.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class PrimitiveConstructorExecutor : IConstructorExecutor -{ - private readonly Type _primitiveType; - - public PrimitiveConstructorExecutor(Type primitiveType) - { - _primitiveType = primitiveType; - } - - public ITypedValue Execute(IEvaluationContext context, params object[] arguments) - { - if (arguments.Length != 1 || arguments[0] == null) - { - throw new AccessException($"Invalid argument for primitive type:{_primitiveType}"); - } - - Type argType = arguments[0].GetType(); - - if (argType != _primitiveType) - { - object value = context.TypeConverter.ConvertValue(arguments[0], argType, _primitiveType); - return new TypedValue(value, _primitiveType); - } - - return new TypedValue(arguments[0], _primitiveType); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectionHelper.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectionHelper.cs deleted file mode 100644 index 43a2a2d0ba..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectionHelper.cs +++ /dev/null @@ -1,492 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public static class ReflectionHelper -{ - public static ArgumentsMatchInfo CompareArguments(List expectedArgTypes, List suppliedArgTypes, ITypeConverter typeConverter) - { - if (expectedArgTypes.Count != suppliedArgTypes.Count) - { - throw new InvalidOperationException("Expected argument types and supplied argument types should be arrays of same length"); - } - - ArgumentsMatchKind? match = ArgumentsMatchKind.Exact; - - for (int i = 0; i < expectedArgTypes.Count && match != null; i++) - { - Type suppliedArg = suppliedArgTypes[i]; - Type expectedArg = expectedArgTypes[i]; - - // The user may supply null - and that will be ok unless a primitive is expected - if (suppliedArg == null) - { - if (expectedArg.IsPrimitive) - { - match = null; - } - } - else if (expectedArg != suppliedArg) - { - // if (suppliedArg.IsAssignableTo(expectedArg)) - if (expectedArg.IsAssignableFrom(suppliedArg)) - { - if (match != ArgumentsMatchKind.RequiresConversion) - { - match = ArgumentsMatchKind.Close; - } - } - else if (typeConverter.CanConvert(suppliedArg, expectedArg)) - { - match = ArgumentsMatchKind.RequiresConversion; - } - else - { - match = null; - } - } - } - - return match != null ? new ArgumentsMatchInfo(match.Value) : null; - } - - public static int GetTypeDifferenceWeight(List paramTypes, List argTypes) - { - int result = 0; - - for (int i = 0; i < paramTypes.Count; i++) - { - Type paramType = paramTypes[i]; - Type argType = i < argTypes.Count ? argTypes[i] : null; - - if (argType == null) - { - if (paramType.IsPrimitive) - { - return int.MaxValue; - } - } - else - { - Type currentParamType = paramType; - - if (!ClassUtils.IsAssignable(currentParamType, argType)) - { - return int.MaxValue; - } - - if (currentParamType.IsPrimitive) - { - currentParamType = typeof(object); - } - - Type superClass = argType.BaseType; - - while (superClass != null) - { - if (currentParamType == superClass) - { - result += 2; - superClass = null; - } - else if (ClassUtils.IsAssignable(currentParamType, superClass)) - { - result += 2; - superClass = superClass.BaseType; - } - else - { - superClass = null; - } - } - - if (currentParamType.IsInterface) - { - result++; - } - } - } - - return result; - } - - public static ArgumentsMatchInfo CompareArgumentsVarargs(List expectedArgTypes, List suppliedArgTypes, ITypeConverter typeConverter) - { - if (expectedArgTypes == null || expectedArgTypes.Count == 0) - { - throw new InvalidOperationException("Expected arguments must at least include one array (the varargs parameter)"); - } - - if (!expectedArgTypes[^1].IsArray) - { - throw new InvalidOperationException("Final expected argument should be array type (the varargs parameter)"); - } - - ArgumentsMatchKind? match = ArgumentsMatchKind.Exact; - - // Check up until the varargs argument: - - // Deal with the arguments up to 'expected number' - 1 (that is everything but the varargs argument) - int argCountUpToVarargs = expectedArgTypes.Count - 1; - - for (int i = 0; i < argCountUpToVarargs && match != null; i++) - { - Type suppliedArg = suppliedArgTypes[i]; - Type expectedArg = expectedArgTypes[i]; - - if (suppliedArg == null) - { - if (expectedArg.IsPrimitive) - { - match = null; - } - } - else - { - if (expectedArg != suppliedArg) - { - if (expectedArg.IsAssignableFrom(suppliedArg)) - { - if (match != ArgumentsMatchKind.RequiresConversion) - { - match = ArgumentsMatchKind.Close; - } - } - else if (typeConverter.CanConvert(suppliedArg, expectedArg)) - { - match = ArgumentsMatchKind.RequiresConversion; - } - else - { - match = null; - } - } - } - } - - // If already confirmed it cannot be a match, then return - if (match == null) - { - return null; - } - - if (suppliedArgTypes.Count == expectedArgTypes.Count && expectedArgTypes[^1] == suppliedArgTypes[^1]) - { - // Special case: there is one parameter left and it is an array and it matches the varargs - // expected argument - that is a match, the caller has already built the array. Proceed with it. - } - else - { - // Now... we have the final argument in the method we are checking as a match and we have 0 - // or more other arguments left to pass to it. - Type varargsDesc = expectedArgTypes[^1]; - - if (!varargsDesc.HasElementType) - { - throw new InvalidOperationException("No element type"); - } - - Type varargsParamType = varargsDesc.GetElementType(); - - // All remaining parameters must be of this type or convertible to this type - for (int i = expectedArgTypes.Count - 1; i < suppliedArgTypes.Count; i++) - { - Type suppliedArg = suppliedArgTypes[i]; - - if (suppliedArg == null) - { - if (varargsParamType.IsPrimitive) - { - match = null; - } - } - else - { - if (varargsParamType != suppliedArg) - { - if (ClassUtils.IsAssignable(varargsParamType, suppliedArg)) - { - if (match != ArgumentsMatchKind.RequiresConversion) - { - match = ArgumentsMatchKind.Close; - } - } - else if (typeConverter.CanConvert(suppliedArg, varargsParamType)) - { - match = ArgumentsMatchKind.RequiresConversion; - } - else - { - match = null; - } - } - } - } - } - - return match != null ? new ArgumentsMatchInfo(match.Value) : null; - } - - public static ConstructorInfo GetAccessibleConstructor(Type type, params Type[] paramTypes) - { - paramTypes ??= Type.EmptyTypes; - - return type.GetConstructor(paramTypes); - } - - public static bool ConvertAllArguments(ITypeConverter converter, object[] arguments, MethodInfo method) - { - int? varargsPosition = method.IsVarArgs() ? method.GetParameters().Length - 1 : null; - return ConvertArguments(converter, arguments, method, varargsPosition); - } - - public static object[] SetupArgumentsForVarargsInvocation(Type[] requiredParameterTypes, params object[] args) - { - // Check if array already built for final argument - int parameterCount = requiredParameterTypes.Length; - int argumentCount = args.Length; - - // Check if repackaging is needed... - if (parameterCount != args.Length || requiredParameterTypes[parameterCount - 1] != args[argumentCount - 1]?.GetType()) - { - int arraySize = 0; // zero size array if nothing to pass as the varargs parameter - - if (argumentCount >= parameterCount) - { - arraySize = argumentCount - (parameterCount - 1); - } - - // Create an array for the varargs arguments - object[] newArgs = new object[parameterCount]; - Array.Copy(args, 0, newArgs, 0, newArgs.Length - 1); - - // Now sort out the final argument, which is the varargs one. Before entering this method, - // the arguments should have been converted to the box form of the required type. - Type componentType = requiredParameterTypes[parameterCount - 1].GetElementType(); - var repackagedArgs = Array.CreateInstance(componentType, arraySize); - - for (int i = 0; i < arraySize; i++) - { - repackagedArgs.SetValue(args[parameterCount - 1 + i], i); - } - - newArgs[^1] = repackagedArgs; - return newArgs; - } - - return args; - } - - public static bool ConvertArguments(ITypeConverter converter, object[] arguments, MethodBase executable, int? varargsPosition) - { - bool conversionOccurred = false; - - if (varargsPosition == null) - { - for (int i = 0; i < arguments.Length; i++) - { - Type targetType = executable.GetParameters()[i].ParameterType; - object argument = arguments[i]; - arguments[i] = converter.ConvertValue(argument, argument == null ? typeof(object) : argument.GetType(), targetType); - conversionOccurred |= argument != arguments[i]; - } - } - else - { - // Convert everything up to the varargs position - for (int i = 0; i < varargsPosition; i++) - { - Type targetType = executable.GetParameters()[i].ParameterType; - object argument = arguments[i]; - arguments[i] = converter.ConvertValue(argument, argument == null ? typeof(object) : argument.GetType(), targetType); - conversionOccurred |= argument != arguments[i]; - } - - int vPos = varargsPosition.Value; - ParameterInfo methodParam = executable.GetParameters()[vPos]; - - if (vPos == arguments.Length - 1) - { - // If the target is varargs and there is just one more argument - // then convert it here - Type targetType = methodParam.ParameterType; - object argument = arguments[vPos]; - Type sourceType = argument == null ? typeof(object) : argument.GetType(); - arguments[vPos] = converter.ConvertValue(argument, sourceType, targetType); - - // Three outcomes of that previous line: - // 1) the input argument was already compatible (ie. array of valid type) and nothing was done - // 2) the input argument was correct type but not in an array so it was made into an array - // 3) the input argument was the wrong type and got converted and put into an array - if (argument != arguments[vPos] && !IsFirstEntryInArray(argument, arguments[vPos])) - { - conversionOccurred = true; // case 3 - } - } - else - { - // Convert remaining arguments to the varargs element type - Type targetType = methodParam.ParameterType.GetElementType(); - - if (targetType == null) - { - throw new InvalidOperationException("No element type"); - } - - for (int i = vPos; i < arguments.Length; i++) - { - object argument = arguments[i]; - arguments[i] = converter.ConvertValue(argument, argument == null ? typeof(object) : argument.GetType(), targetType); - conversionOccurred |= argument != arguments[i]; - } - } - } - - return conversionOccurred; - } - - public static Type GetMapValueTypeDescriptor(Type targetDescriptor) - { - if (!typeof(IDictionary).IsAssignableFrom(targetDescriptor)) - { - throw new InvalidOperationException("Not a IDictionary"); - } - - if (!targetDescriptor.IsGenericType) - { - return null; - } - - return targetDescriptor.GetGenericArguments()[1]; - } - - public static Type GetMapValueTypeDescriptor(Type targetDescriptor, object mapValue) - { - Type type = GetMapValueTypeDescriptor(targetDescriptor); - - if (type != null) - { - if (mapValue == null) - { - return type; - } - - return mapValue.GetType(); - } - - if (mapValue != null) - { - return mapValue.GetType(); - } - - return null; - } - - public static Type GetMapKeyTypeDescriptor(Type targetDescriptor) - { - if (!typeof(IDictionary).IsAssignableFrom(targetDescriptor)) - { - throw new InvalidOperationException("Not a IDictionary"); - } - - if (!targetDescriptor.IsGenericType) - { - return null; - } - - return targetDescriptor.GetGenericArguments()[0]; - } - - public static Type GetMapKeyTypeDescriptor(Type targetDescriptor, object mapKey) - { - Type type = GetMapKeyTypeDescriptor(targetDescriptor); - - if (type != null) - { - if (mapKey == null) - { - return type; - } - - return mapKey.GetType(); - } - - if (mapKey != null) - { - return mapKey.GetType(); - } - - return null; - } - - public static Type GetElementTypeDescriptor(Type type) - { - if (type.IsArray) - { - return type.GetElementType(); - } - - if (type.IsGenericType) - { - return type.GetGenericArguments()[0]; - } - - return null; - } - - public static Type GetElementTypeDescriptor(Type type, object obj) - { - Type elemType = GetElementTypeDescriptor(type); - - if (elemType != null) - { - if (obj == null) - { - return type; - } - - return obj.GetType(); - } - - if (obj != null) - { - return obj.GetType(); - } - - return null; - } - - public static bool IsPublic(Type type) - { - if (type.IsNested) - { - return type.DeclaringType.IsPublic && type.IsNestedPublic; - } - - return type.IsPublic; - } - - private static bool IsFirstEntryInArray(object value, object possibleArray) - { - if (possibleArray == null) - { - return false; - } - - Type type = possibleArray.GetType(); - - if (!type.IsArray || ((Array)possibleArray).GetLength(0) == 0 || !ClassUtils.IsAssignableValue(type.GetElementType(), value)) - { - return false; - } - - object arrayValue = ((Array)possibleArray).GetValue(0); - return type.GetElementType().IsPrimitive ? arrayValue.Equals(value) : arrayValue == value; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveConstructorExecutor.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveConstructorExecutor.cs deleted file mode 100644 index 391907f4c2..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveConstructorExecutor.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class ReflectiveConstructorExecutor : IConstructorExecutor -{ - private readonly int? _varargsPosition; - - public ConstructorInfo Constructor { get; } - - public ReflectiveConstructorExecutor(ConstructorInfo ctor) - { - Constructor = ctor; - - if (ctor.IsVarArgs()) - { - _varargsPosition = ctor.GetParameters().Length - 1; - } - else - { - _varargsPosition = null; - } - } - - public ITypedValue Execute(IEvaluationContext context, params object[] arguments) - { - try - { - ReflectionHelper.ConvertArguments(context.TypeConverter, arguments, Constructor, _varargsPosition); - - if (Constructor.IsVarArgs()) - { - arguments = ReflectionHelper.SetupArgumentsForVarargsInvocation(ClassUtils.GetParameterTypes(Constructor), arguments); - } - - return new TypedValue(Constructor.Invoke(arguments)); - } - catch (Exception ex) - { - throw new AccessException($"Problem invoking constructor: {Constructor}", ex); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveConstructorResolver.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveConstructorResolver.cs deleted file mode 100644 index 3c1a540081..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveConstructorResolver.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class ReflectiveConstructorResolver : IConstructorResolver -{ - public IConstructorExecutor Resolve(IEvaluationContext context, string typeName, List argumentTypes) - { - try - { - ITypeConverter typeConverter = context.TypeConverter; - Type type = context.TypeLocator.FindType(typeName); - - if (IsPrimitive(type)) - { - return new PrimitiveConstructorExecutor(type); - } - - ConstructorInfo[] constructors = type.GetConstructors(); - - Array.Sort(constructors, (c1, c2) => - { - int c1Pl = c1.GetParameters().Length; - int c2Pl = c2.GetParameters().Length; - return c1Pl.CompareTo(c2Pl); - }); - - ConstructorInfo closeMatch = null; - ConstructorInfo matchRequiringConversion = null; - - foreach (ConstructorInfo ctor in constructors) - { - ParameterInfo[] parameters = ctor.GetParameters(); - int paramCount = parameters.Length; - var paramDescriptors = new List(paramCount); - - for (int i = 0; i < paramCount; i++) - { - paramDescriptors.Add(parameters[i].ParameterType); - } - - ArgumentsMatchInfo matchInfo = null; - - if (ctor.IsVarArgs() && argumentTypes.Count >= paramCount - 1) - { - // *sigh* complicated - // Basically.. we have to have all parameters match up until the varargs one, then the rest of what is - // being provided should be - // the same type whilst the final argument to the method must be an array of that (oh, how easy...not) - - // or the final parameter - // we are supplied does match exactly (it is an array already). - matchInfo = ReflectionHelper.CompareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter); - } - else if (paramCount == argumentTypes.Count) - { - // worth a closer look - matchInfo = ReflectionHelper.CompareArguments(paramDescriptors, argumentTypes, typeConverter); - } - - if (matchInfo != null) - { - if (matchInfo.IsExactMatch) - { - return new ReflectiveConstructorExecutor(ctor); - } - - if (matchInfo.IsCloseMatch) - { - closeMatch = ctor; - } - else if (matchInfo.IsMatchRequiringConversion) - { - matchRequiringConversion = ctor; - } - } - } - - if (closeMatch != null) - { - return new ReflectiveConstructorExecutor(closeMatch); - } - - if (matchRequiringConversion != null) - { - return new ReflectiveConstructorExecutor(matchRequiringConversion); - } - - return null; - } - catch (EvaluationException ex) - { - throw new AccessException("Failed to resolve constructor", ex); - } - } - - private bool IsPrimitive(Type type) - { - if (type.IsPrimitive || type == typeof(decimal)) - { - return true; - } - - return false; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveMethodExecutor.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveMethodExecutor.cs deleted file mode 100644 index 29b3679df5..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveMethodExecutor.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class ReflectiveMethodExecutor : IMethodExecutor -{ - private readonly MethodInfo _methodToInvoke; - - private readonly int? _varargsPosition; - - private bool _computedPublicDeclaringClass; - - private Type _publicDeclaringClass; - - public MethodInfo Method { get; } - - public bool DidArgumentConversionOccur { get; private set; } - - public ReflectiveMethodExecutor(MethodInfo method) - { - Method = method; - _methodToInvoke = ClassUtils.GetInterfaceMethodIfPossible(method); - - if (method.IsVarArgs()) - { - _varargsPosition = method.GetParameters().Length - 1; - } - else - { - _varargsPosition = null; - } - } - - public Type GetPublicDeclaringClass() - { - if (!_computedPublicDeclaringClass) - { - _publicDeclaringClass = DiscoverPublicDeclaringClass(Method, Method.DeclaringType); - _computedPublicDeclaringClass = true; - } - - return _publicDeclaringClass; - } - - private Type DiscoverPublicDeclaringClass(MethodInfo method, Type type) - { - if (ReflectionHelper.IsPublic(type)) - { - try - { - type.GetMethod(method.Name, ClassUtils.GetParameterTypes(method)); - return type; - } - catch (Exception) - { - // Continue below... - } - } - - if (type.BaseType != null) - { - return DiscoverPublicDeclaringClass(method, type.BaseType); - } - - return null; - } - - public ITypedValue Execute(IEvaluationContext context, object target, params object[] arguments) - { - try - { - DidArgumentConversionOccur = ReflectionHelper.ConvertArguments(context.TypeConverter, arguments, Method, _varargsPosition); - - if (Method.IsVarArgs()) - { - arguments = ReflectionHelper.SetupArgumentsForVarargsInvocation(ClassUtils.GetParameterTypes(Method), arguments); - } - - object value = _methodToInvoke.Invoke(target, arguments); - return new TypedValue(value, value?.GetType() ?? Method.ReturnType); - } - catch (Exception ex) - { - throw new AccessException($"Problem invoking method: {_methodToInvoke}", ex); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveMethodResolver.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveMethodResolver.cs deleted file mode 100644 index 0d569f9f72..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectiveMethodResolver.cs +++ /dev/null @@ -1,234 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class ReflectiveMethodResolver : IMethodResolver -{ - private readonly bool _useDistance; - private Dictionary _filters; - - public ReflectiveMethodResolver() - { - _useDistance = true; - } - - public ReflectiveMethodResolver(bool useDistance) - { - _useDistance = useDistance; - } - - public virtual void RegisterMethodFilter(Type type, IMethodFilter filter) - { - _filters ??= new Dictionary(); - - if (filter != null) - { - _filters[type] = filter; - } - else - { - _filters.Remove(type); - } - } - - public virtual IMethodExecutor Resolve(IEvaluationContext context, object targetObject, string name, List argumentTypes) - { - try - { - ITypeConverter typeConverter = context.TypeConverter; - Type type = targetObject as Type ?? targetObject.GetType(); - var methods = new List(GetMethods(type, targetObject)); - - // If a filter is registered for this type, call it - IMethodFilter filter = null; - _filters?.TryGetValue(type, out filter); - - if (filter != null) - { - methods = filter.Filter(methods); - } - - // Sort methods into a sensible order - if (methods.Count > 1) - { - methods.Sort((m1, m2) => - { - int m1Pl = m1.GetParameters().Length; - int m2Pl = m2.GetParameters().Length; - - // vararg methods go last - if (m1Pl == m2Pl) - { - if (!m1.IsVarArgs() && m2.IsVarArgs()) - { - return -1; - } - - if (m1.IsVarArgs() && !m2.IsVarArgs()) - { - return 1; - } - - return 0; - } - - return m1Pl.CompareTo(m2Pl); - }); - } - - // Remove duplicate methods (possible due to resolved bridge methods) - var methodsToIterate = new HashSet(methods); - - MethodInfo closeMatch = null; - int closeMatchDistance = int.MaxValue; - MethodInfo matchRequiringConversion = null; - bool multipleOptions = false; - - foreach (MethodInfo method in methodsToIterate) - { - if (method.Name == name) - { - ParameterInfo[] parameters = method.GetParameters(); - int paramCount = parameters.Length; - - var paramDescriptors = new List(paramCount); - - for (int i = 0; i < paramCount; i++) - { - paramDescriptors.Add(parameters[i].ParameterType); - } - - ArgumentsMatchInfo matchInfo = null; - - if (method.IsVarArgs() && argumentTypes.Count >= paramCount - 1) - { - // *sigh* complicated - matchInfo = ReflectionHelper.CompareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter); - } - else if (paramCount == argumentTypes.Count) - { - // Name and parameter number match, check the arguments - matchInfo = ReflectionHelper.CompareArguments(paramDescriptors, argumentTypes, typeConverter); - } - - if (matchInfo != null) - { - if (matchInfo.IsExactMatch) - { - return new ReflectiveMethodExecutor(method); - } - - if (matchInfo.IsCloseMatch) - { - if (_useDistance) - { - int matchDistance = ReflectionHelper.GetTypeDifferenceWeight(paramDescriptors, argumentTypes); - - if (closeMatch == null || matchDistance < closeMatchDistance) - { - // This is a better match... - closeMatch = method; - closeMatchDistance = matchDistance; - } - } - else - { - // Take this as a close match if there isn't one already - if (closeMatch == null) - { - closeMatch = method; - } - } - } - else if (matchInfo.IsMatchRequiringConversion) - { - if (matchRequiringConversion != null) - { - multipleOptions = true; - } - - matchRequiringConversion = method; - } - } - } - } - - if (closeMatch != null) - { - return new ReflectiveMethodExecutor(closeMatch); - } - - if (matchRequiringConversion != null) - { - if (multipleOptions) - { - throw new SpelEvaluationException(SpelMessage.MultiplePossibleMethods, name); - } - - return new ReflectiveMethodExecutor(matchRequiringConversion); - } - - return null; - } - catch (EvaluationException ex) - { - throw new AccessException("Failed to resolve method", ex); - } - } - - protected virtual MethodInfo[] GetMethods(Type type) - { - return type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); - } - - protected virtual bool IsCandidateForInvocation(MethodInfo method, Type targetClass) - { - return true; - } - - private ISet GetMethods(Type type, object targetObject) - { - if (targetObject is Type) - { - var result = new HashSet(); - - // Add these so that static methods are invocable on the type: e.g. Float.valueOf(..) - MethodInfo[] methods = GetMethods(type); - - foreach (MethodInfo method in methods) - { - if (method.IsStatic) - { - result.Add(method); - } - } - - // Also expose methods from System.Type itself - foreach (MethodInfo m in GetMethods(typeof(Type))) - { - result.Add(m); - } - - return result; - } - else - { - var result = new HashSet(); - MethodInfo[] methods = GetMethods(type); - - foreach (MethodInfo method in methods) - { - if (IsCandidateForInvocation(method, type)) - { - result.Add(method); - } - } - - return result; - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectivePropertyAccessor.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectivePropertyAccessor.cs deleted file mode 100644 index e811222e47..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/ReflectivePropertyAccessor.cs +++ /dev/null @@ -1,872 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Reflection; -using System.Reflection.Emit; -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class ReflectivePropertyAccessor : IPropertyAccessor -{ - private readonly bool _allowWrite; - private readonly ConcurrentDictionary _readerCache = new(); - private readonly ConcurrentDictionary _writerCache = new(); - private readonly ConcurrentDictionary _typeDescriptorCache = new(); - - public ReflectivePropertyAccessor() - { - _allowWrite = true; - } - - public ReflectivePropertyAccessor(bool allowWrite) - { - _allowWrite = allowWrite; - } - - public virtual IList GetSpecificTargetClasses() - { - return null; - } - - public bool CanRead(IEvaluationContext context, object target, string name) - { - if (target == null) - { - return false; - } - - Type type = target as Type ?? target.GetType(); - - if (type.IsArray && name == "Length") - { - return true; - } - - var cacheKey = new PropertyCacheKey(type, name, target is Type); - - if (_readerCache.ContainsKey(cacheKey)) - { - return true; - } - - MethodInfo method = FindGetterForProperty(name, type, target); - - if (method != null) - { - // Treat it like a property... - // The readerCache will only contain gettable properties (let's not worry about setters for now). - Type typeDescriptor = method.ReturnType; - method = ClassUtils.GetInterfaceMethodIfPossible(method); - _readerCache[cacheKey] = new InvokerPair(method, typeDescriptor); - _typeDescriptorCache[cacheKey] = typeDescriptor; - return true; - } - - FieldInfo field = FindField(name, type, target); - - if (field != null) - { - Type typeDescriptor = field.FieldType; - _readerCache[cacheKey] = new InvokerPair(field, typeDescriptor); - _typeDescriptorCache[cacheKey] = typeDescriptor; - return true; - } - - return false; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - ArgumentGuard.NotNull(target); - - Type type = target as Type ?? target.GetType(); - - if (type.IsArray && name == "Length") - { - if (target is Type) - { - throw new AccessException("Cannot access length on array class itself"); - } - - var asArray = (Array)target; - return new TypedValue(asArray.GetLength(0)); - } - - var cacheKey = new PropertyCacheKey(type, name, target is Type); - _readerCache.TryGetValue(cacheKey, out InvokerPair invoker); - - if (invoker == null || invoker.Member is MethodInfo) - { - var method = (MethodInfo)invoker?.Member; - - if (method == null) - { - method = FindGetterForProperty(name, type, target); - - if (method != null) - { - // Treat it like a property... - // The readerCache will only contain gettable properties (let's not worry about setters for now). - Type typeDescriptor = method.ReturnType; - method = ClassUtils.GetInterfaceMethodIfPossible(method); - invoker = new InvokerPair(method, typeDescriptor); - _readerCache[cacheKey] = invoker; - } - } - - if (method != null) - { - try - { - object value = method.Invoke(target, Array.Empty()); - return new TypedValue(value, value != null ? value.GetType() : invoker.TypeDescriptor); - } - catch (Exception ex) - { - throw new AccessException($"Unable to access property '{name}' through getter method", ex); - } - } - } - - if (invoker == null || invoker.Member is FieldInfo) - { - var field = (FieldInfo)invoker?.Member; - - if (field == null) - { - field = FindField(name, type, target); - - if (field != null) - { - invoker = new InvokerPair(field, field.FieldType); - _readerCache[cacheKey] = invoker; - } - } - - if (field != null) - { - try - { - object value = field.GetValue(target); - return new TypedValue(value, value != null ? value.GetType() : invoker.TypeDescriptor); - } - catch (Exception ex) - { - throw new AccessException($"Unable to access field '{name}'", ex); - } - } - } - - throw new AccessException($"Neither getter method nor field found for property '{name}'"); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - if (!_allowWrite || target == null) - { - return false; - } - - Type type = target as Type ?? target.GetType(); - var cacheKey = new PropertyCacheKey(type, name, target is Type); - - if (_writerCache.ContainsKey(cacheKey)) - { - return true; - } - - MethodInfo method = FindSetterForProperty(name, type, target); - - if (method != null) - { - // Treat it like a property - Type typeDescriptor = method.GetParameters()[0].ParameterType; - method = ClassUtils.GetInterfaceMethodIfPossible(method); - _writerCache[cacheKey] = method; - _typeDescriptorCache[cacheKey] = typeDescriptor; - return true; - } - - FieldInfo field = FindField(name, type, target); - - if (field != null) - { - _writerCache[cacheKey] = field; - _typeDescriptorCache[cacheKey] = field.FieldType; - return true; - } - - return false; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - ArgumentGuard.NotNull(target); - - if (!_allowWrite) - { - throw new AccessException($"PropertyAccessor for property '{name}' on target [{target}] does not allow write operations"); - } - - Type type = target as Type ?? target.GetType(); - - object possiblyConvertedNewValue = newValue; - Type typeDescriptor = GetTypeDescriptor(context, target, name); - - if (typeDescriptor != null) - { - try - { - possiblyConvertedNewValue = context.TypeConverter.ConvertValue(newValue, newValue?.GetType(), typeDescriptor); - } - catch (EvaluationException evaluationException) - { - throw new AccessException("Type conversion failure", evaluationException); - } - } - - var cacheKey = new PropertyCacheKey(type, name, target is Type); - _writerCache.TryGetValue(cacheKey, out MemberInfo cachedMember); - - if (cachedMember == null || cachedMember is MethodInfo) - { - var method = (MethodInfo)cachedMember; - - if (method == null) - { - method = FindSetterForProperty(name, type, target); - - if (method != null) - { - method = ClassUtils.GetInterfaceMethodIfPossible(method); - cachedMember = method; - _writerCache[cacheKey] = cachedMember; - } - } - - if (method != null) - { - try - { - method.Invoke(target, new[] - { - possiblyConvertedNewValue - }); - - return; - } - catch (Exception ex) - { - throw new AccessException($"Unable to access property '{name}' through setter method", ex); - } - } - } - - if (cachedMember == null || cachedMember is FieldInfo) - { - var field = (FieldInfo)cachedMember; - - if (field == null) - { - field = FindField(name, type, target); - - if (field != null) - { - cachedMember = field; - _writerCache[cacheKey] = cachedMember; - } - } - - if (field != null) - { - try - { - field.SetValue(target, possiblyConvertedNewValue); - return; - } - catch (Exception ex) - { - throw new AccessException($"Unable to access field '{name}'", ex); - } - } - } - - throw new AccessException($"Neither setter method nor field found for property '{name}'"); - } - - public IPropertyAccessor CreateOptimalAccessor(IEvaluationContext context, object target, string name) - { - // Don't be clever for arrays or a null target... - if (target == null) - { - return this; - } - - Type type = target as Type ?? target.GetType(); - - if (type.IsArray) - { - return this; - } - - var cacheKey = new PropertyCacheKey(type, name, target is Type); - _readerCache.TryGetValue(cacheKey, out InvokerPair invocationTarget); - - if (invocationTarget == null || invocationTarget.Member is MethodInfo) - { - var method = (MethodInfo)invocationTarget?.Member; - - if (method == null) - { - method = FindGetterForProperty(name, type, target); - - if (method != null) - { - Type typeDescriptor = method.ReturnType; - method = ClassUtils.GetInterfaceMethodIfPossible(method); - invocationTarget = new InvokerPair(method, typeDescriptor); - _readerCache[cacheKey] = invocationTarget; - } - } - - if (method != null) - { - return new OptimalPropertyAccessor(invocationTarget); - } - } - - if (invocationTarget == null || invocationTarget.Member is FieldInfo) - { - FieldInfo field = invocationTarget != null ? (FieldInfo)invocationTarget.Member : null; - - if (field == null) - { - field = FindField(name, type, target is Type); - - if (field != null) - { - invocationTarget = new InvokerPair(field, field.FieldType); - _readerCache[cacheKey] = invocationTarget; - } - } - - if (field != null) - { - return new OptimalPropertyAccessor(invocationTarget); - } - } - - return this; - } - - internal static string Capitalize(string str) - { - if (string.IsNullOrEmpty(str)) - { - return str; - } - - char baseChar = str[0]; - char updatedChar = char.ToUpperInvariant(baseChar); - - if (baseChar == updatedChar) - { - return str; - } - - char[] chars = str.ToCharArray(); - chars[0] = updatedChar; - return new string(chars); - } - - protected virtual MethodInfo FindGetterForProperty(string propertyName, Type type, bool mustBeStatic) - { - return FindMethodForProperty(propertyName, type, false, mustBeStatic); - } - - protected virtual MethodInfo FindSetterForProperty(string propertyName, Type type, bool mustBeStatic) - { - return FindMethodForProperty(propertyName, type, true, mustBeStatic); - } - - protected virtual bool IsCandidateForProperty(MethodInfo method, Type targetClass) - { - return true; - } - - protected virtual FieldInfo FindField(string name, Type type, bool mustBeStatic) - { - FieldInfo[] fields = type.GetFields(); - - foreach (FieldInfo field in fields) - { - if (field.Name == name && (!mustBeStatic || field.IsStatic)) - { - return field; - } - } - - // We'll search superclasses and implemented interfaces explicitly, - // although it shouldn't be necessary - however, see SPR-10125. - if (type.BaseType != null) - { - FieldInfo field = FindField(name, type.BaseType, mustBeStatic); - - if (field != null) - { - return field; - } - } - - foreach (Type implementedInterface in type.GetInterfaces()) - { - FieldInfo field = FindField(name, implementedInterface, mustBeStatic); - - if (field != null) - { - return field; - } - } - - return null; - } - - private FieldInfo FindField(string name, Type type, object target) - { - FieldInfo field = FindField(name, type, target is Type); - - if (field == null && target is Type) - { - field = FindField(name, target.GetType(), false); - } - - return field; - } - - private MethodInfo FindMethodForProperty(string propertyName, Type type, bool setter, bool mustBeStatic) - { - PropertyInfo propInfo = type.GetProperty(propertyName); - - if (propInfo != null) - { - MethodInfo method = null; - - if (setter) - { - if (propInfo.CanWrite) - { - method = propInfo.GetSetMethod(); - } - } - else - { - if (propInfo.CanRead) - { - method = propInfo.GetGetMethod(); - } - } - - if (method != null && IsCandidateForProperty(method, type) && (!mustBeStatic || method.IsStatic)) - { - return method; - } - } - - return null; - } - - private Type GetTypeDescriptor(IEvaluationContext context, object target, string name) - { - Type type = target as Type ?? target.GetType(); - - if (type.IsArray && name == "Length") - { - return typeof(int); - } - - var cacheKey = new PropertyCacheKey(type, name, target is Type); - _typeDescriptorCache.TryGetValue(cacheKey, out Type typeDescriptor); - - if (typeDescriptor == null) - { - // Attempt to populate the cache entry - try - { - if (CanRead(context, target, name) || CanWrite(context, target, name)) - { - _typeDescriptorCache.TryGetValue(cacheKey, out typeDescriptor); - } - } - catch (AccessException) - { - // Continue with null type descriptor - } - } - - return typeDescriptor; - } - - private MethodInfo FindGetterForProperty(string propertyName, Type type, object target) - { - MethodInfo method = FindGetterForProperty(propertyName, type, target is Type); - - if (method == null && target is Type) - { - method = FindGetterForProperty(propertyName, typeof(Type), false); - } - - return method; - } - - private MethodInfo FindSetterForProperty(string propertyName, Type type, object target) - { - MethodInfo method = FindSetterForProperty(propertyName, type, target is Type); - - if (method == null && target is Type) - { - method = FindSetterForProperty(propertyName, typeof(Type), false); - } - - return method; - } - - public class InvokerPair - { - public Type TypeDescriptor { get; } - - public MemberInfo Member { get; } - - public InvokerPair(MemberInfo member, Type typeDescriptor) - { - Member = member; - TypeDescriptor = typeDescriptor; - } - } - -#pragma warning disable S1210 // "Equals" and the comparison operators should be overridden when implementing "IComparable" - public class PropertyCacheKey : IComparable -#pragma warning restore S1210 // "Equals" and the comparison operators should be overridden when implementing "IComparable" - { - private readonly Type _type; - private readonly string _property; - private readonly bool _targetIsClass; - - public PropertyCacheKey(Type type, string name, bool targetIsClass) - { - _type = type; - _property = name; - _targetIsClass = targetIsClass; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not PropertyCacheKey otherKey) - { - return false; - } - - return _type == otherKey._type && _property == otherKey._property && _targetIsClass == otherKey._targetIsClass; - } - - public override int GetHashCode() - { - return HashCode.Combine(_type, _property); - } - - public override string ToString() - { - return $"CacheKey [type={_type.FullName}, property={_property}, {_property}, targetIsClass={_targetIsClass}]"; - } - - public int CompareTo(PropertyCacheKey other) - { - int result = string.Compare(_type.Name, _type.Name, StringComparison.Ordinal); - - if (result == 0) - { - result = string.Compare(_property, other._property, StringComparison.Ordinal); - } - - return result; - } - } - - public class OptimalPropertyAccessor : ICompilablePropertyAccessor - { - private static readonly ISet NumericIntegralTypes = new[] - { - typeof(int), - typeof(uint), - typeof(short), - typeof(ushort), - typeof(byte), - typeof(sbyte), - typeof(char) - }.ToHashSet(); - - public MemberInfo Member { get; } - - public Type TypeDescriptor { get; } - - public OptimalPropertyAccessor(InvokerPair target) - { - Member = target.Member; - TypeDescriptor = target.TypeDescriptor; - } - - public IList GetSpecificTargetClasses() - { - throw new InvalidOperationException("Should not be called on an OptimalPropertyAccessor"); - } - - public bool CanRead(IEvaluationContext context, object target, string name) - { - if (target == null) - { - return false; - } - - Type type = target as Type ?? target.GetType(); - - if (type.IsArray) - { - return false; - } - - if (Member is MethodInfo method) - { - string getterName = $"get_{Capitalize(name)}"; - - if (getterName == method.Name) - { - return true; - } - - return false; - } - - var field = (FieldInfo)Member; - return field.Name == name; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - if (Member is MethodInfo method) - { - try - { - object value = method.Invoke(target, Array.Empty()); - return new TypedValue(value, value != null ? value.GetType() : TypeDescriptor); - } - catch (Exception ex) - { - throw new AccessException($"Unable to access property '{name}' through getter method", ex); - } - } - - var field = (FieldInfo)Member; - - try - { - object value = field.GetValue(target); - return new TypedValue(value, value != null ? value.GetType() : TypeDescriptor); - } - catch (Exception ex) - { - throw new AccessException($"Unable to access field '{name}'", ex); - } - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - throw new InvalidOperationException("Should not be called on an OptimalPropertyAccessor"); - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - throw new InvalidOperationException("Should not be called on an OptimalPropertyAccessor"); - } - - public bool IsCompilable() - { - if (!ReflectionHelper.IsPublic(Member.DeclaringType)) - { - return false; - } - - if (Member is MethodInfo info) - { - return info.IsPublic; - } - - return ((FieldInfo)Member).IsPublic; - } - - public Type GetPropertyType() - { - if (Member is MethodInfo info) - { - return info.ReturnType; - } - - return ((FieldInfo)Member).FieldType; - } - - public void GenerateCode(string propertyName, ILGenerator gen, CodeFlow cf) - { - if (Member is MethodInfo info) - { - GenerateCode(info, gen, cf); - } - else - { - GenerateCode((FieldInfo)Member, gen, cf); - } - } - - private void GenerateCode(MethodInfo method, ILGenerator gen, CodeFlow cf) - { - TypeDescriptor stackDescriptor = cf.LastDescriptor(); - - if (stackDescriptor == null) - { - CodeFlow.LoadTarget(gen); - stackDescriptor = Spring.TypeDescriptor.Object; - } - - if (!method.IsStatic) - { - // Instance - if (method.DeclaringType.IsValueType) - { - if (stackDescriptor != null && stackDescriptor.IsBoxed) - { - gen.Emit(OpCodes.Unbox_Any, method.DeclaringType); - } - - LocalBuilder vtLocal = gen.DeclareLocal(method.DeclaringType); - gen.Emit(OpCodes.Stloc, vtLocal); - gen.Emit(OpCodes.Ldloca, vtLocal); - gen.Emit(OpCodes.Call, method); - } - else - { - if (stackDescriptor == null || method.DeclaringType != stackDescriptor.Value) - { - gen.Emit(OpCodes.Castclass, method.DeclaringType); - } - - gen.Emit(OpCodes.Callvirt, method); - } - } - else - { - // Static - if (stackDescriptor != null) - { - // A static field/method call will not consume what is on the stack, - // it needs to be popped off. - gen.Emit(OpCodes.Pop); - } - - gen.Emit(OpCodes.Call, method); - } - } - - private void GenerateCode(FieldInfo field, ILGenerator gen, CodeFlow cf) - { - TypeDescriptor stackDescriptor = cf.LastDescriptor(); - - if (stackDescriptor == null) - { - CodeFlow.LoadTarget(gen); - stackDescriptor = Spring.TypeDescriptor.Object; - } - - if (!field.IsStatic) - { - // Instance - if (field.DeclaringType.IsValueType) - { - if (stackDescriptor != null && stackDescriptor.IsBoxed) - { - gen.Emit(OpCodes.Unbox_Any, field.DeclaringType); - } - } - else - { - if (stackDescriptor == null || field.DeclaringType != stackDescriptor.Value) - { - gen.Emit(OpCodes.Castclass, field.DeclaringType); - } - } - - gen.Emit(OpCodes.Ldfld, field); - } - else - { - // Static - if (stackDescriptor != null) - { - // A static field/method call will not consume what is on the stack, - // it needs to be popped off. - gen.Emit(OpCodes.Pop); - } - - if (field.IsLiteral) - { - EmitLiteralFieldCode(gen, field); - } - else - { - gen.Emit(OpCodes.Ldsfld, field); - } - } - } - - private void EmitLiteralFieldCode(ILGenerator gen, FieldInfo field) - { - object constant = field.GetRawConstantValue(); - - if (field.FieldType.IsClass && constant == null) - { - gen.Emit(OpCodes.Ldnull); - return; - } - - if (constant == null) - { - return; - } - - switch (field.FieldType) - { - case var type when NumericIntegralTypes.Contains(type): - gen.Emit(OpCodes.Ldc_I4, (int)constant); - return; - case var type when type == typeof(long) || type == typeof(ulong): - gen.Emit(OpCodes.Ldc_I8, (long)constant); - return; - case var type when type == typeof(float): - gen.Emit(OpCodes.Ldc_R4, (float)constant); - return; - case var type when type == typeof(double): - gen.Emit(OpCodes.Ldc_R8, (double)constant); - return; - case var type when type == typeof(string): - gen.Emit(OpCodes.Ldstr, (string)constant); - return; - default: - return; - } - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/SimpleEvaluationContext.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/SimpleEvaluationContext.cs deleted file mode 100644 index 140fb78b98..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/SimpleEvaluationContext.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Converter; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class SimpleEvaluationContext : IEvaluationContext -{ - private static readonly List EmptyConstructorResolver = new(); - private readonly Dictionary _variables = new(); - - public ITypedValue RootObject { get; } - - public List PropertyAccessors { get; } - - public List ConstructorResolvers => EmptyConstructorResolver; - - public List MethodResolvers { get; } - - public IServiceResolver ServiceResolver => null; - - public ITypeLocator TypeLocator { get; } = new TypeNotFoundTypeLocator(); - - public ITypeConverter TypeConverter { get; } - - public ITypeComparator TypeComparator { get; } = new StandardTypeComparator(); - - public IOperatorOverloader OperatorOverloader { get; } = new StandardOperatorOverloader(); - - private SimpleEvaluationContext(List accessors, List resolvers, ITypeConverter converter, ITypedValue rootObject) - { - PropertyAccessors = accessors; - MethodResolvers = resolvers; - TypeConverter = converter ?? new StandardTypeConverter(); - RootObject = rootObject ?? TypedValue.Null; - } - - public static Builder ForPropertyAccessors(params IPropertyAccessor[] accessors) - { - foreach (IPropertyAccessor accessor in accessors) - { - if (accessor.GetType() == typeof(ReflectivePropertyAccessor)) - { - throw new InvalidOperationException("SimpleEvaluationContext is not designed for use with a plain " + - "ReflectivePropertyAccessor. Consider using DataBindingPropertyAccessor or a custom subclass."); - } - } - - return new Builder(accessors); - } - - public static Builder ForReadOnlyDataBinding() - { - return new Builder(DataBindingPropertyAccessor.ForReadOnlyAccess()); - } - - public static Builder ForReadWriteDataBinding() - { - return new Builder(DataBindingPropertyAccessor.ForReadWriteAccess()); - } - - public void SetVariable(string name, object value) - { - _variables[name] = value; - } - - public object LookupVariable(string name) - { - _variables.TryGetValue(name, out object result); - return result; - } - - public T LookupVariable(string name) - { - _variables.TryGetValue(name, out object result); - return (T)result; - } - - public class Builder - { - private readonly List _accessors; - private List _resolvers = new(); - private ITypeConverter _typeConverter; - private ITypedValue _rootObject; - - public Builder(params IPropertyAccessor[] accessors) - { - _accessors = new List(accessors); - } - - public Builder WithMethodResolvers(params IMethodResolver[] resolvers) - { - foreach (IMethodResolver resolver in resolvers) - { - if (resolver.GetType() == typeof(ReflectiveMethodResolver)) - { - throw new InvalidOperationException("SimpleEvaluationContext is not designed for use with a plain " + - "ReflectiveMethodResolver. Consider using DataBindingMethodResolver or a custom subclass."); - } - } - - _resolvers = new List(resolvers); - return this; - } - - public Builder WithInstanceMethods() - { - _resolvers = new List - { - DataBindingMethodResolver.ForInstanceMethodInvocation() - }; - - return this; - } - - public Builder WithConversionService(IConversionService conversionService) - { - _typeConverter = new StandardTypeConverter(conversionService); - return this; - } - - public Builder WithTypeConverter(ITypeConverter converter) - { - _typeConverter = converter; - return this; - } - - public Builder WithRootObject(object rootObject) - { - _rootObject = new TypedValue(rootObject); - return this; - } - - public Builder WithTypedRootObject(object rootObject, Type typeDescriptor) - { - _rootObject = new TypedValue(rootObject, typeDescriptor); - return this; - } - - public SimpleEvaluationContext Build() - { - return new SimpleEvaluationContext(_accessors, _resolvers, _typeConverter, _rootObject); - } - } - - private sealed class TypeNotFoundTypeLocator : ITypeLocator - { - public Type FindType(string typeName) - { - throw new SpelEvaluationException(SpelMessage.TypeNotFound, typeName); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardEvaluationContext.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/StandardEvaluationContext.cs deleted file mode 100644 index aa25d0e374..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardEvaluationContext.cs +++ /dev/null @@ -1,275 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Reflection; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class StandardEvaluationContext : IEvaluationContext -{ - private readonly ConcurrentDictionary _variables = new(); - - private volatile List _propertyAccessors; - - private volatile List _constructorResolvers; - - private volatile List _methodResolvers; - - private volatile ReflectiveMethodResolver _reflectiveMethodResolver; - - private ITypeLocator _typeLocator; - - private ITypeConverter _typeConverter; - - private ITypeComparator _typeComparator = new StandardTypeComparator(); - - private IOperatorOverloader _operatorOverloader = new StandardOperatorOverloader(); - - public ITypedValue RootObject { get; private set; } - - public IServiceResolver ServiceResolver { get; set; } - - public List PropertyAccessors - { - get - { - _propertyAccessors = InitPropertyAccessors(); - return _propertyAccessors; - } - set => _propertyAccessors = value; - } - - public List ConstructorResolvers - { - get - { - _constructorResolvers = InitConstructorResolvers(); - return _constructorResolvers; - } - set => _constructorResolvers = value; - } - - public List MethodResolvers - { - get - { - _methodResolvers = InitMethodResolvers(); - return _methodResolvers; - } - set => _methodResolvers = value; - } - - public ITypeLocator TypeLocator - { - get - { - _typeLocator ??= new StandardTypeLocator(); - return _typeLocator; - } - set - { - ArgumentGuard.NotNull(value); - - _typeLocator = value; - } - } - - public ITypeConverter TypeConverter - { - get - { - _typeConverter ??= new StandardTypeConverter(); - return _typeConverter; - } - set - { - ArgumentGuard.NotNull(value); - - _typeConverter = value; - } - } - - public ITypeComparator TypeComparator - { - get => _typeComparator; - set - { - ArgumentGuard.NotNull(value); - - _typeComparator = value; - } - } - - public IOperatorOverloader OperatorOverloader - { - get => _operatorOverloader; - set - { - ArgumentGuard.NotNull(value); - - _operatorOverloader = value; - } - } - - public StandardEvaluationContext() - { - RootObject = TypedValue.Null; - } - - public StandardEvaluationContext(object rootObject) - { - RootObject = new TypedValue(rootObject); - } - - public void SetVariable(string name, object value) - { - // For backwards compatibility, we ignore null names here... - // And since ConcurrentHashMap cannot store null values, we simply take null - // as a remove from the Map (with the same result from lookupVariable below). - if (name != null) - { - if (value != null) - { - _variables[name] = value; - } - else - { - _variables.TryRemove(name, out _); - } - } - } - - public void SetVariables(Dictionary variables) - { - foreach (KeyValuePair v in variables) - { - SetVariable(v.Key, v.Value); - } - } - - public void RegisterFunction(string name, MethodInfo method) - { - _variables[name] = method; - } - - public object LookupVariable(string name) - { - _variables.TryGetValue(name, out object result); - return result; - } - - public T LookupVariable(string name) - { - _variables.TryGetValue(name, out object result); - return (T)result; - } - - public void SetRootObject(object rootObject, Type typeDescriptor) - { - RootObject = new TypedValue(rootObject, typeDescriptor); - } - - public void SetRootObject(object rootObject) - { - RootObject = rootObject != null ? new TypedValue(rootObject) : TypedValue.Null; - } - - public void AddPropertyAccessor(IPropertyAccessor accessor) - { - AddBeforeDefault(InitPropertyAccessors(), accessor); - } - - public bool RemovePropertyAccessor(IPropertyAccessor accessor) - { - return InitPropertyAccessors().Remove(accessor); - } - - public void AddConstructorResolver(IConstructorResolver accessor) - { - AddBeforeDefault(InitConstructorResolvers(), accessor); - } - - public bool RemoveConstructorResolver(IConstructorResolver accessor) - { - return InitConstructorResolvers().Remove(accessor); - } - - public void AddMethodResolver(IMethodResolver accessor) - { - AddBeforeDefault(InitMethodResolvers(), accessor); - } - - public bool RemoveMethodResolver(IMethodResolver accessor) - { - return InitMethodResolvers().Remove(accessor); - } - - public void RegisterMethodFilter(Type type, IMethodFilter filter) - { - InitMethodResolvers(); - ReflectiveMethodResolver resolver = _reflectiveMethodResolver; - - if (resolver == null) - { - throw new InvalidOperationException("Method filter cannot be set as the reflective method resolver is not in use"); - } - - resolver.RegisterMethodFilter(type, filter); - } - - private static void AddBeforeDefault(List resolvers, T resolver) - { - resolvers.Insert(resolvers.Count - 1, resolver); - } - - private List InitPropertyAccessors() - { - List accessors = _propertyAccessors; - - if (accessors == null) - { - accessors = new List(5) - { - new ReflectivePropertyAccessor() - }; - - _propertyAccessors = accessors; - } - - return accessors; - } - - private List InitConstructorResolvers() - { - List resolvers = _constructorResolvers; - - if (resolvers == null) - { - resolvers = new List(1) - { - new ReflectiveConstructorResolver() - }; - - _constructorResolvers = resolvers; - } - - return resolvers; - } - - private List InitMethodResolvers() - { - List resolvers = _methodResolvers; - - if (resolvers == null) - { - resolvers = new List(1); - _reflectiveMethodResolver = new ReflectiveMethodResolver(); - resolvers.Add(_reflectiveMethodResolver); - _methodResolvers = resolvers; - } - - return resolvers; - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardOperatorOverloader.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/StandardOperatorOverloader.cs deleted file mode 100644 index 485f35f0e4..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardOperatorOverloader.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class StandardOperatorOverloader : IOperatorOverloader -{ - public bool OverridesOperation(Operation operation, object leftOperand, object rightOperand) - { - return false; - } - - public object Operate(Operation operation, object leftOperand, object rightOperand) - { - throw new EvaluationException("No operation overloaded by default"); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardTypeComparator.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/StandardTypeComparator.cs deleted file mode 100644 index 5344e69d5f..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardTypeComparator.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class StandardTypeComparator : ITypeComparator -{ - public bool CanCompare(object firstObject, object secondObject) - { - if (firstObject == null || secondObject == null) - { - return true; - } - - if (firstObject is IComparable) - { - return true; - } - - return false; - } - - public int Compare(object firstObject, object secondObject) - { - // If one is null, check if the other is - if (firstObject == null) - { - return secondObject == null ? 0 : -1; - } - - if (secondObject == null) - { - return 1; // firstObject cannot be null at this point - } - - if (firstObject is decimal || secondObject is decimal) - { - decimal leftNum = Convert.ToDecimal(firstObject, CultureInfo.InvariantCulture); - decimal rightNum = Convert.ToDecimal(secondObject, CultureInfo.InvariantCulture); - return leftNum.CompareTo(rightNum); - } - - if (firstObject is double || secondObject is double) - { - double leftNum = Convert.ToDouble(firstObject, CultureInfo.InvariantCulture); - double rightNum = Convert.ToDouble(secondObject, CultureInfo.InvariantCulture); - return leftNum.CompareTo(rightNum); - } - - if (firstObject is float || secondObject is float) - { - float leftNum = Convert.ToSingle(firstObject, CultureInfo.InvariantCulture); - float rightNum = Convert.ToSingle(secondObject, CultureInfo.InvariantCulture); - return leftNum.CompareTo(rightNum); - } - - if (firstObject is long || secondObject is long) - { - long leftNum = Convert.ToInt64(firstObject, CultureInfo.InvariantCulture); - long rightNum = Convert.ToInt64(secondObject, CultureInfo.InvariantCulture); - return leftNum.CompareTo(rightNum); - } - - try - { - if (firstObject is IComparable comparable) - { - return comparable.CompareTo(secondObject); - } - } - catch (Exception ex) - { - throw new SpelEvaluationException(ex, SpelMessage.NotComparable, firstObject.GetType(), secondObject.GetType()); - } - - throw new SpelEvaluationException(SpelMessage.NotComparable, firstObject.GetType(), secondObject.GetType()); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardTypeConverter.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/StandardTypeConverter.cs deleted file mode 100644 index 3c2d13b55b..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardTypeConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Converter; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class StandardTypeConverter : ITypeConverter -{ - public IConversionService ConversionService { get; set; } - - public StandardTypeConverter() - { - ConversionService = DefaultConversionService.Singleton; - } - - public StandardTypeConverter(IConversionService conversionService) - { - ArgumentGuard.NotNull(conversionService); - - ConversionService = conversionService; - } - - public bool CanConvert(Type sourceType, Type targetType) - { - return ConversionService.CanConvert(sourceType, targetType); - } - - public object ConvertValue(object value, Type sourceType, Type targetType) - { - try - { - return ConversionService.Convert(value, sourceType, targetType); - } - catch (ConversionException ex) - { - string message = "null"; - - if (sourceType != null) - { - message = sourceType.ToString(); - } - else if (value != null) - { - message = value.GetType().FullName; - } - - throw new SpelEvaluationException(ex, SpelMessage.TypeConversionError, message, targetType.ToString()); - } - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardTypeLocator.cs b/src/Common/src/Common.Expression/Internal/Spring/Support/StandardTypeLocator.cs deleted file mode 100644 index 563e8be035..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/Support/StandardTypeLocator.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Expression.Internal.Spring.Support; - -public class StandardTypeLocator : ITypeLocator -{ - private readonly List _knownNamespacePrefixes = new(1); - - public virtual List ImportPrefixes => new(_knownNamespacePrefixes); - - public StandardTypeLocator() - { - _knownNamespacePrefixes.Add("System"); - } - - public virtual void RegisterImport(string prefix) - { - _knownNamespacePrefixes.Add(prefix); - } - - public virtual void RemoveImport(string prefix) - { - _knownNamespacePrefixes.Remove(prefix); - } - - public virtual Type FindType(string typeName) - { - Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); - Type result = null; - typeName = typeName.Replace('$', '+'); // Handle nested type syntax a.b.C$Nested - - foreach (Assembly assembly in loadedAssemblies) - { - try - { - result = assembly.GetType(typeName, false); - - if (result == null) - { - foreach (string prefix in _knownNamespacePrefixes) - { - try - { - string nameToLookup = $"{prefix}.{typeName}"; - result = assembly.GetType(nameToLookup, false); - - if (result != null) - { - break; - } - } - catch (Exception) - { - // might be a different prefix - } - } - } - } - catch (Exception) - { - // Eat exceptions - } - - if (result != null) - { - return result; - } - } - - throw new SpelEvaluationException(SpelMessage.TypeNotFound, typeName); - } -} diff --git a/src/Common/src/Common.Expression/Internal/Spring/TypeDescriptor.cs b/src/Common/src/Common.Expression/Internal/Spring/TypeDescriptor.cs deleted file mode 100644 index 96d4175b94..0000000000 --- a/src/Common/src/Common.Expression/Internal/Spring/TypeDescriptor.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal.Spring; - -public class TypeDescriptor -{ - public static readonly TypeDescriptor V = new(typeof(void)); - public static readonly TypeDescriptor I = new(typeof(int)); - public static readonly TypeDescriptor J = new(typeof(long)); - public static readonly TypeDescriptor F = new(typeof(float)); - public static readonly TypeDescriptor D = new(typeof(double)); - public static readonly TypeDescriptor B = new(typeof(byte)); - public static readonly TypeDescriptor C = new(typeof(char)); - public static readonly TypeDescriptor S = new(typeof(short)); - public static readonly TypeDescriptor Z = new(typeof(bool)); - public static readonly TypeDescriptor A = new(typeof(sbyte)); - public static readonly TypeDescriptor M = new(typeof(ushort)); - public static readonly TypeDescriptor N = new(typeof(uint)); - public static readonly TypeDescriptor O = new(typeof(ulong)); - public static readonly TypeDescriptor P = new(typeof(IntPtr)); - public static readonly TypeDescriptor Q = new(typeof(UIntPtr)); - - public static readonly TypeDescriptor Object = new(typeof(object)); - public static readonly TypeDescriptor String = new(typeof(string)); - public static readonly TypeDescriptor Type = new(typeof(Type)); - - private TypeDescriptor _boxed; - private TypeDescriptor _unBoxed; - - public Type Value { get; } - - public bool IsBoxed { get; } - - public bool IsValueType => Value.IsValueType; // Returns true for typeof(void) - - public bool IsReferenceType => !IsValueType && !IsBoxed; - - public bool IsBoxedValueType => IsValueType && IsBoxed; - - public bool IsPrimitive => !IsBoxed && !IsVoid && Value.IsPrimitive; // IsPrimitive returns false for typeof(void) - - public bool IsBoxedPrimitive => IsBoxed && Value.IsPrimitive; - - public bool IsBoxedNumber => IsBoxedPrimitive && Value != typeof(IntPtr) && Value != typeof(UIntPtr); - - public bool IsVoid => Value == typeof(void); - - public bool IsBoxable => IsValueType && !IsBoxed && !IsVoid; - - public TypeDescriptor(Type type, bool boxed = false) - { - Value = type; - IsBoxed = boxed; - } - - public TypeDescriptor UnBox() - { - if (!IsBoxed) - { - throw new InvalidOperationException("TypeDescriptor is not boxed"); - } - - if (_unBoxed == null) - { - _unBoxed = new TypeDescriptor(Value) - { - _boxed = this - }; - } - - return _unBoxed; - } - - public TypeDescriptor Boxed() - { - if (!IsBoxable) - { - throw new InvalidOperationException("Type not boxable"); - } - - if (_boxed == null) - { - _boxed = new TypeDescriptor(Value, true) - { - _unBoxed = this - }; - } - - return _boxed; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not TypeDescriptor other) - { - return false; - } - - return other.Value == Value && other.IsBoxed == IsBoxed; - } - - public override int GetHashCode() - { - return Value.GetHashCode(); - } - - public override string ToString() - { - return Value.ToString(); - } - -#pragma warning disable S3875 // "operator==" should not be overloaded on reference types - public static bool operator ==(TypeDescriptor lhs, TypeDescriptor rhs) - { - if (lhs is null) - { - if (rhs is null) - { - return true; - } - - return false; - } - - return lhs.Equals(rhs); - } - - public static bool operator !=(TypeDescriptor lhs, TypeDescriptor rhs) - { - return !(lhs == rhs); - } -#pragma warning restore S3875 // "operator==" should not be overloaded on reference types -} diff --git a/src/Common/src/Common.Expression/Internal/TypedValue.cs b/src/Common/src/Common.Expression/Internal/TypedValue.cs deleted file mode 100644 index 3d2248e62c..0000000000 --- a/src/Common/src/Common.Expression/Internal/TypedValue.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Expression.Internal; - -public class TypedValue : ITypedValue -{ - public static readonly TypedValue Null = new(null); - - public object Value { get; } - - public Type TypeDescriptor { get; } - - public TypedValue(object value) - { - Value = value; - TypeDescriptor = value?.GetType(); - } - - public TypedValue(object value, Type typeDescriptor) - { - Value = value; - TypeDescriptor = typeDescriptor; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not TypedValue otherTv) - { - return false; - } - - return ObjectEquality.ObjectOrCollectionEquals(Value, otherTv.Value) && TypeDescriptor == otherTv.TypeDescriptor; - } - - public override int GetHashCode() - { - return ObjectEquality.GetObjectOrCollectionHashCode(Value); - } - - public override string ToString() - { - return $"TypedValue: '{Value}' of [{TypeDescriptor}]"; - } -} diff --git a/src/Common/src/Common.Expression/Internal/ValueExpression.cs b/src/Common/src/Common.Expression/Internal/ValueExpression.cs deleted file mode 100644 index b289388053..0000000000 --- a/src/Common/src/Common.Expression/Internal/ValueExpression.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Internal; - -public class ValueExpression : IExpression -{ - private readonly TValue _value; - private readonly Type _asClass; - - public string ExpressionString => _value.ToString(); - - public ValueExpression(TValue value) - { - _value = value; - _asClass = value.GetType(); - } - - public virtual object GetValue() - { - return _value; - } - - public virtual object GetValue(Type desiredResultType) - { - if (!desiredResultType.IsInstanceOfType(_value)) - { - throw new EvaluationException(_value.ToString(), "value is not of correct type"); - } - - return _value; - } - - public virtual T GetValue() - { - return (T)GetValue(typeof(T)); - } - - public virtual object GetValue(object rootObject) - { - return _value; - } - - public virtual object GetValue(IEvaluationContext context, Type desiredResultType) - { - return GetValue(desiredResultType); - } - - public virtual object GetValue(object rootObject, Type desiredResultType) - { - return GetValue(desiredResultType); - } - - public virtual T GetValue(object rootObject) - { - return (T)GetValue(typeof(T)); - } - - public virtual object GetValue(IEvaluationContext context) - { - return _value; - } - - public virtual object GetValue(IEvaluationContext context, object rootObject) - { - return _value; - } - - public virtual object GetValue(IEvaluationContext context, object rootObject, Type desiredResultType) - { - return GetValue(desiredResultType); - } - - public virtual T GetValue(IEvaluationContext context) - { - return (T)GetValue(typeof(T)); - } - - public virtual T GetValue(IEvaluationContext context, object rootObject) - { - return (T)GetValue(typeof(T)); - } - - public virtual Type GetValueType() - { - return _asClass; - } - - public virtual Type GetValueType(object rootObject) - { - return _asClass; - } - - public virtual Type GetValueType(IEvaluationContext context) - { - return _asClass; - } - - public virtual Type GetValueType(IEvaluationContext context, object rootObject) - { - return _asClass; - } - - public virtual bool IsWritable(object rootObject) - { - return false; - } - - public virtual bool IsWritable(IEvaluationContext context) - { - return false; - } - - public virtual bool IsWritable(IEvaluationContext context, object rootObject) - { - return false; - } - - public virtual void SetValue(object rootObject, object value) - { - throw new EvaluationException(_value.ToString(), "Cannot call SetValue() on a ValueExpression"); - } - - public virtual void SetValue(IEvaluationContext context, object value) - { - throw new EvaluationException(_value.ToString(), "Cannot call SetValue() on a ValueExpression"); - } - - public virtual void SetValue(IEvaluationContext context, object rootObject, object value) - { - throw new EvaluationException(_value.ToString(), "Cannot call SetValue() on a ValueExpression"); - } -} diff --git a/src/Common/src/Common.Expression/Properties/AssemblyInfo.cs b/src/Common/src/Common.Expression/Properties/AssemblyInfo.cs deleted file mode 100644 index b660e246c1..0000000000 --- a/src/Common/src/Common.Expression/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Steeltoe.Common.Expression.Test")] diff --git a/src/Common/src/Common.Expression/README.md b/src/Common/src/Common.Expression/README.md deleted file mode 100644 index 82a89fa2f4..0000000000 --- a/src/Common/src/Common.Expression/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Steeltoe Expressions - -The contents of this project implements the [Spring Expression Language](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions). -This package should be considered for internal use only. \ No newline at end of file diff --git a/src/Common/src/Common.Expression/Steeltoe.Common.Expression.csproj b/src/Common/src/Common.Expression/Steeltoe.Common.Expression.csproj deleted file mode 100644 index b694b21360..0000000000 --- a/src/Common/src/Common.Expression/Steeltoe.Common.Expression.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - net8.0;net6.0 - Steeltoe common expression language library - NET Core;Expression;SPEL - true - - - - - - - - - - - - diff --git a/src/Common/src/Common.RetryPolly/PollyRetryTemplate.cs b/src/Common/src/Common.RetryPolly/PollyRetryTemplate.cs deleted file mode 100644 index 26041368bc..0000000000 --- a/src/Common/src/Common.RetryPolly/PollyRetryTemplate.cs +++ /dev/null @@ -1,273 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Polly; -using Polly.Contrib.WaitAndRetry; -using Polly.Fallback; -using Polly.Retry; -using Steeltoe.Common.Retry; -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.RetryPolly; - -public class PollyRetryTemplate : RetryTemplate -{ - private const string RecoveryCallbackKey = "PollyRetryTemplate.RecoveryCallback"; - private const string RetryContextKey = "PollyRetryTemplate.RetryContext"; - - private const string Recovered = "context.recovered"; - private const string Closed = "context.closed"; - private const string RecoveredResult = "context.recovered.result"; - - private readonly BinaryExceptionClassifier _retryableExceptions; - private readonly int _maxAttempts; - private readonly int _backOffInitialInterval; - private readonly double _backOffMultiplier; - private readonly ILogger _logger; - - public PollyRetryTemplate(int maxAttempts, int backOffInitialInterval, int backOffMaxInterval, double backOffMultiplier, ILogger logger = null) - : this(new Dictionary(), maxAttempts, true, backOffInitialInterval, backOffMaxInterval, backOffMultiplier, logger) - { - } - - public PollyRetryTemplate(Dictionary retryableExceptions, int maxAttempts, bool defaultRetryable, int backOffInitialInterval, - int backOffMaxInterval, double backOffMultiplier, ILogger logger = null) - { - _retryableExceptions = new BinaryExceptionClassifier(retryableExceptions, defaultRetryable); - _maxAttempts = maxAttempts; - _backOffInitialInterval = backOffInitialInterval; - _backOffMultiplier = backOffMultiplier; - _logger = logger; - } - - public override T Execute(Func retryCallback) - { - return Execute(retryCallback, (IRecoveryCallback)null); - } - - public override T Execute(Func retryCallback, Func recoveryCallback) - { - var callback = new FuncRecoveryCallback(recoveryCallback, _logger); - return Execute(retryCallback, callback); - } - - public override void Execute(Action retryCallback, Action recoveryCallback) - { - var callback = new ActionRecoveryCallback(recoveryCallback, _logger); - Execute(retryCallback, callback); - } - - public override T Execute(Func retryCallback, IRecoveryCallback recoveryCallback) - { - Policy policy = BuildPolicy(); - var retryContext = new RetryContext(); - - var context = new Context - { - { RetryContextKey, retryContext } - }; - - RetrySynchronizationManager.Register(retryContext); - - if (recoveryCallback != null) - { - retryContext.SetAttribute(RecoveryCallbackKey, recoveryCallback); - } - - CallListenerOpen(retryContext); - - T result = policy.Execute(_ => - { - T callbackResult = retryCallback(retryContext); - - if (recoveryCallback != null) - { - bool? recovered = (bool?)retryContext.GetAttribute(Recovered); - - if (recovered != null && recovered.Value) - { - callbackResult = (T)retryContext.GetAttribute(RecoveredResult); - } - } - - return callbackResult; - }, context); - - CallListenerClose(retryContext, retryContext.LastException); - RetrySynchronizationManager.Clear(); - return result; - } - - public override void Execute(Action retryCallback) - { - Execute(retryCallback, (IRecoveryCallback)null); - } - - public override void Execute(Action retryCallback, IRecoveryCallback recoveryCallback) - { - Policy policy = BuildPolicy(); - var retryContext = new RetryContext(); - - var context = new Context - { - { RetryContextKey, retryContext } - }; - - RetrySynchronizationManager.Register(retryContext); - - if (recoveryCallback != null) - { - retryContext.SetAttribute(RecoveryCallbackKey, recoveryCallback); - } - - if (!CallListenerOpen(retryContext)) - { - throw new TerminatedRetryException("Retry terminated abnormally by interceptor before first attempt"); - } - - policy.Execute(_ => - { - retryCallback(retryContext); - return null; - }, context); - - CallListenerClose(retryContext, retryContext.LastException); - RetrySynchronizationManager.Clear(); - } - - private Policy BuildPolicy() - { - IEnumerable delay = - Backoff.ExponentialBackoff(TimeSpan.FromMilliseconds(_backOffInitialInterval), _maxAttempts - 1, _backOffMultiplier, true); - - RetryPolicy retryPolicy = Policy.HandleInner(e => _retryableExceptions.Classify(e)).WaitAndRetry(delay, OnRetry); - - FallbackPolicy fallbackPolicy = Policy.Handle().Fallback((delegateResult, context, _) => - { - RetryContext retryContext = GetRetryContext(context); - retryContext.LastException = delegateResult.Exception; - var result = default(T); - - if (retryContext.GetAttribute(RecoveryCallbackKey) is IRecoveryCallback callback) - { - result = (T)callback.Recover(retryContext); - retryContext.SetAttribute(Recovered, true); - retryContext.SetAttribute(RecoveredResult, result); - } - else if (delegateResult.Exception != null) - { - throw delegateResult.Exception; - } - - return result; - }, (ex, context) => - { - _logger?.LogError(ex.Exception, $"Context: {context}"); - - // throw ex.Exception; throwing here doesn't allow the fall back to work. - }); - - return fallbackPolicy.Wrap(retryPolicy); - } - - private RetryContext GetRetryContext(Context context) - { - if (context.TryGetValue(RetryContextKey, out object obj)) - { - return (RetryContext)obj; - } - - var result = new RetryContext(); - RetrySynchronizationManager.Register(result); - return result; - } - - private void OnRetry(DelegateResult delegateResult, TimeSpan time, int retryCount, Context context) - { - RetryContext retryContext = GetRetryContext(context); - Exception ex = delegateResult.Exception; - - retryContext.LastException = ex; - retryContext.RetryCount = retryCount; - - if (ex != null) - { - CallListenerOnError(retryContext, ex); - } - } - - private bool CallListenerOpen(RetryContext context) - { - bool running = true; - - foreach (IRetryListener listener in listeners) - { - running &= listener.Open(context); - } - - return running; - } - - private void CallListenerClose(RetryContext context, Exception ex) - { - context.SetAttribute(Closed, true); - - foreach (IRetryListener listener in listeners) - { - listener.Close(context, ex); - } - } - - private void CallListenerOnError(RetryContext context, Exception ex) - { - foreach (IRetryListener listener in listeners) - { - listener.OnError(context, ex); - } - } - - private sealed class FuncRecoveryCallback : IRecoveryCallback - { - private readonly Func _func; - private readonly ILogger _logger; - - public FuncRecoveryCallback(Func func, ILogger logger) - { - _func = func; - _logger = logger; - } - - public T Recover(IRetryContext context) - { - _logger?.LogTrace($"FuncRecovery Context: {context}"); - return _func(context); - } - - object IRecoveryCallback.Recover(IRetryContext context) - { - _logger?.LogTrace($"FuncRecovery Context: {context}"); - return _func(context); - } - } - - private sealed class ActionRecoveryCallback : IRecoveryCallback - { - private readonly Action _action; - private readonly ILogger _logger; - - public ActionRecoveryCallback(Action action, ILogger logger) - { - _action = action; - _logger = logger; - } - - public object Recover(IRetryContext context) - { - _logger?.LogTrace($"ActionRecovery Context: {context}"); - _action(context); - return null; - } - } -} diff --git a/src/Common/src/Common.RetryPolly/Steeltoe.Common.RetryPolly.csproj b/src/Common/src/Common.RetryPolly/Steeltoe.Common.RetryPolly.csproj deleted file mode 100644 index 5e86af26ca..0000000000 --- a/src/Common/src/Common.RetryPolly/Steeltoe.Common.RetryPolly.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - net8.0;net6.0 - Steeltoe library for handling retries - NET Core;Retry; - true - - - - - - - - - - - - - diff --git a/src/Common/src/Common.Utils/Diagnostics/CommandException.cs b/src/Common/src/Common.Utils/Diagnostics/CommandException.cs deleted file mode 100644 index edb93f7fc4..0000000000 --- a/src/Common/src/Common.Utils/Diagnostics/CommandException.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Utils.Diagnostics; - -/// -/// The exception that is thrown when a system error occurs running a command. -/// -public class CommandException : Exception -{ - /// - public CommandException(string message) - : base(message) - { - } - - /// - public CommandException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common.Utils/Diagnostics/CommandExecutor.cs b/src/Common/src/Common.Utils/Diagnostics/CommandExecutor.cs deleted file mode 100644 index 9320e35b43..0000000000 --- a/src/Common/src/Common.Utils/Diagnostics/CommandExecutor.cs +++ /dev/null @@ -1,152 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Text; -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Common.Utils.Diagnostics; - -/// -public class CommandExecutor : ICommandExecutor -{ - private static int _commandCounter; - - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Injected logger. - /// - public CommandExecutor(ILogger logger = null) - { - _logger = logger; - } - - /// - public async Task ExecuteAsync(string command, string workingDirectory = null, int timeout = -1) - { - int commandId = NextCommandId(); - using var process = new Process(); - - string[] arguments = command.Split(new[] - { - ' ' - }, 2); - - process.StartInfo.FileName = arguments[0]; - - if (arguments.Length > 1) - { - process.StartInfo.Arguments = arguments[1]; - } - - if (workingDirectory != null) - { - process.StartInfo.WorkingDirectory = workingDirectory; - } - - process.StartInfo.RedirectStandardInput = true; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - process.StartInfo.UseShellExecute = false; - process.StartInfo.CreateNoWindow = true; - - var output = new StringBuilder(); - var outputCloseEvent = new TaskCompletionSource(); - - process.OutputDataReceived += (_, e) => - { - if (e.Data is null) - { - outputCloseEvent.SetResult(true); - } - else - { - output.AppendLine(e.Data); - } - }; - - var error = new StringBuilder(); - var errorCloseEvent = new TaskCompletionSource(); - - process.ErrorDataReceived += (_, e) => - { - if (e.Data is null) - { - errorCloseEvent.SetResult(true); - } - else - { - error.AppendLine(e.Data); - } - }; - - _logger?.LogDebug("[{CommandId}] command: {Command}", commandId, command); - - try - { - if (!process.Start()) - { - _logger?.LogDebug("[{CommandId}] failed to start: {Error}", commandId, "no details available"); - throw new CommandException($"'{command}' failed to start; no details available"); - } - } - catch (Exception ex) - { - _logger?.LogDebug(ex, "[{CommandId}] failed to start: {Error}", commandId, ex.Message); - throw new CommandException($"'{command}' failed to start: {ex.Message}", ex); - } - - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - - // ReSharper disable once AccessToDisposedClosure - Task waitForExit = Task.Run(() => process.WaitForExit(timeout)); - Task processTask = Task.WhenAll(waitForExit, outputCloseEvent.Task, errorCloseEvent.Task); - - if (await Task.WhenAny(Task.Delay(timeout), processTask) == processTask && waitForExit.Result) - { - var result = new CommandResult - { - ExitCode = process.ExitCode, - Output = output.ToString(), - Error = error.ToString() - }; - - _logger?.LogDebug("[{CommandId}] exit code: {ExitCode}", commandId, result.ExitCode); - - if (result.Output.Length > 0) - { - _logger?.LogDebug("[{CommandId}] stdout:\n{Output}", commandId, result.Output); - } - - if (result.Error.Length > 0) - { - _logger?.LogDebug("[{CommandId}] stderr:\n{Error}", commandId, result.Error); - } - - return result; - } - - try - { - process.Kill(); - } - catch - { - // ignore - } - - _logger?.LogDebug("[{CommandId}] timed out: {TimeOut}ms", commandId, timeout); - throw new CommandException($"'{process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out"); - } - - private static int NextCommandId() - { - return ++_commandCounter; - } -} diff --git a/src/Common/src/Common.Utils/Diagnostics/CommandResult.cs b/src/Common/src/Common.Utils/Diagnostics/CommandResult.cs deleted file mode 100644 index 6ff45acab5..0000000000 --- a/src/Common/src/Common.Utils/Diagnostics/CommandResult.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Utils.Diagnostics; - -/// -/// An simple representation of a command result. -/// -public record struct CommandResult -{ - /// - /// Gets or sets the command exit code. - /// - public int ExitCode { get; set; } - - /// - /// Gets or sets the command exit STDOUT. - /// - public string Output { get; set; } - - /// - /// Gets or sets the command exit STDERR. - /// - public string Error { get; set; } -} diff --git a/src/Common/src/Common.Utils/Diagnostics/ICommandExecutor.cs b/src/Common/src/Common.Utils/Diagnostics/ICommandExecutor.cs deleted file mode 100644 index 3364f8bb50..0000000000 --- a/src/Common/src/Common.Utils/Diagnostics/ICommandExecutor.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Utils.Diagnostics; - -/// -/// A utility abstraction to simplify the running of commands. -/// -public interface ICommandExecutor -{ - /// - /// Execute the command and return the result. - /// - /// - /// Command to be executed. - /// - /// - /// The directory that contains the command process. - /// - /// - /// The amount of time in milliseconds to wait for command to complete. - /// - /// - /// Command result. - /// - /// - /// If a process can not be started for command. - /// - Task ExecuteAsync(string command, string workingDirectory = null, int timeout = -1); -} diff --git a/src/Common/src/Common/Contexts/AbstractApplicationContext.cs b/src/Common/src/Common/Contexts/AbstractApplicationContext.cs deleted file mode 100644 index 7f76cb90f4..0000000000 --- a/src/Common/src/Common/Contexts/AbstractApplicationContext.cs +++ /dev/null @@ -1,307 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Configuration; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Services; - -namespace Steeltoe.Common.Contexts; - -public abstract class AbstractApplicationContext : IApplicationContext -{ - private readonly ConcurrentDictionary _instances = new(); - - public IConfiguration Configuration { get; private set; } - - public IServiceProvider ServiceProvider { get; private set; } - - public IServiceExpressionResolver ServiceExpressionResolver { get; set; } - - protected AbstractApplicationContext(IServiceProvider serviceProvider, IConfiguration configuration, IEnumerable nameToTypeMappings) - { - ServiceProvider = serviceProvider; - Configuration = configuration; - - if (nameToTypeMappings != null) - { - foreach (NameToTypeMapping seed in nameToTypeMappings) - { - Register(seed.Name, seed.Type); - } - } - } - - public bool ContainsService(string name) - { - _instances.TryGetValue(name, out object instance); - - if (instance is Type type) - { - instance = ResolveNamedService(name, type); - } - - return instance != null; - } - - public bool ContainsService(string name, Type serviceType) - { - if (_instances.TryGetValue(name, out object instance)) - { - if (instance is Type type) - { - instance = ResolveNamedService(name, type); - } - - return serviceType.IsInstanceOfType(instance); - } - - if (!typeof(IServiceNameAware).IsAssignableFrom(serviceType)) - { - return false; - } - - object found = FindNamedService(name, serviceType); - - if (found != null) - { - Register(((IServiceNameAware)found).ServiceName, found); - return true; - } - - return false; - } - - public bool ContainsService(string name) - { - return ContainsService(name, typeof(T)); - } - - public object GetService(string name) - { - _instances.TryGetValue(name, out object instance); - - if (instance is Type type) - { - instance = ResolveNamedService(name, type); - } - - return instance; - } - - public object GetService(string name, Type serviceType) - { - if (_instances.TryGetValue(name, out object instance)) - { - if (instance is Type type) - { - instance = ResolveNamedService(name, type); - } - - if (serviceType.IsInstanceOfType(instance)) - { - return instance; - } - - return null; - } - - if (!typeof(IServiceNameAware).IsAssignableFrom(serviceType)) - { - return null; - } - - object found = FindNamedService(name, serviceType); - - if (found != null) - { - Register(((IServiceNameAware)found).ServiceName, found); - } - - if (found != null) - { - Register(((IServiceNameAware)found).ServiceName, found); - } - - return found; - } - - public T GetService(string name) - { - return (T)GetService(name, typeof(T)); - } - - public T GetService() - { - return (T)GetService(typeof(T)); - } - - public object GetService(Type serviceType) - { - object result = _instances.Values.LastOrDefault(serviceType.IsInstanceOfType); - - if (result != null) - { - return result; - } - - object found = ServiceProvider.GetService(serviceType); - - if (found is IServiceNameAware aware) - { - Register(aware.ServiceName, found); - } - - return found; - } - - public IEnumerable GetServices(Type serviceType) - { - var services = new List(); - IEnumerable found = ServiceProvider.GetServices(serviceType); - - foreach (object service in found) - { - if (service is IServiceNameAware aware) - { - Register(aware.ServiceName, service); - } - else - { - services.Add(service); - } - } - - IEnumerable results = _instances.Values.Where(serviceType.IsInstanceOfType); - - foreach (object result in results) - { - services.Add(result); - } - - return services; - } - - public IEnumerable GetServices() - { - var services = new List(); - IEnumerable found = ServiceProvider.GetServices(); - - foreach (T service in found) - { - if (service is IServiceNameAware aware) - { - Register(aware.ServiceName, service); - } - else - { - services.Add(service); - } - } - - IEnumerable results = _instances.Values.Where(instance => instance is T); - - foreach (object result in results) - { - services.Add((T)result); - } - - return services; - } - - public void Register(string name, object instance) - { - if (!string.IsNullOrEmpty(name)) - { - _ = _instances.AddOrUpdate(name, instance, (_, _) => instance); - } - } - - public object Deregister(string name) - { - if (string.IsNullOrEmpty(name)) - { - return null; - } - - _instances.TryRemove(name, out object instance); - - if (instance is IDisposable disposable) - { - disposable.Dispose(); - } - - return instance; - } - - public string ResolveEmbeddedValue(string value) - { - if (value == null) - { - return null; - } - - string resolved = PropertyPlaceholderHelper.ResolvePlaceholders(value, Configuration); - return resolved.Trim(); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _instances.Clear(); - Configuration = null; - ServiceProvider = null; - ServiceExpressionResolver = null; - } - } - - private object ResolveNamedService(string name, Type serviceType) - { - object instance = FindNamedService(name, serviceType); - - if (instance != null) - { - Register(name, instance); - } - - return instance; - } - - private object FindNamedService(string name, Type serviceType) - { - object found = ServiceProvider.GetServices(serviceType).SingleOrDefault(service => - { - if (service is IServiceNameAware nameAware) - { - return nameAware.ServiceName == name; - } - - return false; - }); - - return found; - } - - public class NameToTypeMapping - { - public string Name { get; } - - public Type Type { get; } - - public NameToTypeMapping(string name, Type type) - { - Name = name; - Type = type; - } - } -} diff --git a/src/Common/src/Common/Contexts/GenericApplicationContext.cs b/src/Common/src/Common/Contexts/GenericApplicationContext.cs deleted file mode 100644 index 504ae6a40a..0000000000 --- a/src/Common/src/Common/Contexts/GenericApplicationContext.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; - -namespace Steeltoe.Common.Contexts; - -public class GenericApplicationContext : AbstractApplicationContext -{ - public GenericApplicationContext(IServiceProvider serviceProvider, IConfiguration configuration, IEnumerable nameToTypeMappings = null) - : base(serviceProvider, configuration, nameToTypeMappings) - { - } -} diff --git a/src/Common/src/Common/Contexts/ServiceCollectionExtensions.cs b/src/Common/src/Common/Contexts/ServiceCollectionExtensions.cs deleted file mode 100644 index 94f3809d96..0000000000 --- a/src/Common/src/Common/Contexts/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using static Steeltoe.Common.Contexts.AbstractApplicationContext; - -namespace Steeltoe.Common.Contexts; - -public static class ServiceCollectionExtensions -{ - public static IServiceCollection AddGenericApplicationContext(this IServiceCollection services, - Action configure) - { - services.TryAddSingleton(p => - { - var configuration = p.GetService(); - var context = new GenericApplicationContext(p, configuration); - - if (configure != null) - { - configure(p, context); - } - - return context; - }); - - return services; - } - - public static IServiceCollection AddGenericApplicationContext(this IServiceCollection services) - { - return services.AddGenericApplicationContext(null); - } - - public static IServiceCollection RegisterService(this IServiceCollection services, string serviceName, Type implementationType) - { - services.AddSingleton(new NameToTypeMapping(serviceName, implementationType)); - return services; - } -} diff --git a/src/Common/src/Common/Converter/AbstractConverter.cs b/src/Common/src/Common/Converter/AbstractConverter.cs deleted file mode 100644 index cad82657bd..0000000000 --- a/src/Common/src/Common/Converter/AbstractConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public abstract class AbstractConverter : AbstractGenericConditionalConverter, IConverter -{ - protected AbstractConverter() - : base(new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(TSource), typeof(TTarget)) - }) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - return typeof(TTarget) == targetType; - } - - public abstract TTarget Convert(TSource source); - - public override object Convert(object source, Type sourceType, Type targetType) - { - return source switch - { - null => null, - not TSource => throw new ArgumentException($"Expected source type '{typeof(TSource).Name}' instead of '{source.GetType().Name}'.", nameof(source)), - _ => Convert((TSource)source) - }; - } -} diff --git a/src/Common/src/Common/Converter/AbstractGenericConditionalConverter.cs b/src/Common/src/Common/Converter/AbstractGenericConditionalConverter.cs deleted file mode 100644 index 844919c308..0000000000 --- a/src/Common/src/Common/Converter/AbstractGenericConditionalConverter.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public abstract class AbstractGenericConditionalConverter : AbstractGenericConverter, IConditionalGenericConverter -{ - protected AbstractGenericConditionalConverter(ISet<(Type SourceType, Type TargetType)> convertableTypes) - : base(convertableTypes) - { - } - - public abstract bool Matches(Type sourceType, Type targetType); -} diff --git a/src/Common/src/Common/Converter/AbstractGenericConverter.cs b/src/Common/src/Common/Converter/AbstractGenericConverter.cs deleted file mode 100644 index 4371c997f2..0000000000 --- a/src/Common/src/Common/Converter/AbstractGenericConverter.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public abstract class AbstractGenericConverter : IGenericConverter -{ - public ISet<(Type SourceType, Type TargetType)> ConvertibleTypes { get; } - - protected AbstractGenericConverter(ISet<(Type SourceType, Type TargetType)> convertableTypes) - { - ConvertibleTypes = convertableTypes; - } - - public abstract object Convert(object source, Type sourceType, Type targetType); -} diff --git a/src/Common/src/Common/Converter/AbstractToCollectionConverter.cs b/src/Common/src/Common/Converter/AbstractToCollectionConverter.cs deleted file mode 100644 index 6072f13101..0000000000 --- a/src/Common/src/Common/Converter/AbstractToCollectionConverter.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public abstract class AbstractToCollectionConverter : AbstractGenericConditionalConverter -{ - protected readonly IConversionService ConversionService; - - protected AbstractToCollectionConverter(IConversionService conversionService) - : base(null) - { - ConversionService = conversionService; - } - - protected AbstractToCollectionConverter(ISet<(Type SourceType, Type TargetType)> convertableTypes, IConversionService conversionService) - : base(convertableTypes) - { - ConversionService = conversionService; - } -} diff --git a/src/Common/src/Common/Converter/AbstractToNumberConverter.cs b/src/Common/src/Common/Converter/AbstractToNumberConverter.cs deleted file mode 100644 index bec7415f67..0000000000 --- a/src/Common/src/Common/Converter/AbstractToNumberConverter.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; - -namespace Steeltoe.Common.Converter; - -public abstract class AbstractToNumberConverter : AbstractGenericConditionalConverter -{ - protected ISet<(Type SourceType, Type TargetType)> convertableTypes; - - protected AbstractToNumberConverter(ISet<(Type SourceType, Type TargetType)> convertableTypes) - : base(null) - { - this.convertableTypes = convertableTypes; - } - - public override bool Matches(Type sourceType, Type targetType) - { - Type targetTypeCheck = ConversionUtils.GetNullableElementType(targetType); - return convertableTypes.Contains((sourceType, targetTypeCheck)); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - targetType = ConversionUtils.GetNullableElementType(targetType); - - if (typeof(int) == targetType) - { - return System.Convert.ToInt32(source, CultureInfo.InvariantCulture); - } - - if (typeof(float) == targetType) - { - return System.Convert.ToSingle(source, CultureInfo.InvariantCulture); - } - - if (typeof(uint) == targetType) - { - return System.Convert.ToUInt32(source, CultureInfo.InvariantCulture); - } - - if (typeof(ulong) == targetType) - { - return System.Convert.ToUInt64(source, CultureInfo.InvariantCulture); - } - - if (typeof(long) == targetType) - { - return System.Convert.ToInt64(source, CultureInfo.InvariantCulture); - } - - if (typeof(double) == targetType) - { - return System.Convert.ToDouble(source, CultureInfo.InvariantCulture); - } - - if (typeof(short) == targetType) - { - return System.Convert.ToInt16(source, CultureInfo.InvariantCulture); - } - - if (typeof(ushort) == targetType) - { - return System.Convert.ToUInt16(source, CultureInfo.InvariantCulture); - } - - if (typeof(decimal) == targetType) - { - return System.Convert.ToDecimal(source, CultureInfo.InvariantCulture); - } - - if (typeof(byte) == targetType) - { - return System.Convert.ToByte(source, CultureInfo.InvariantCulture); - } - - if (typeof(sbyte) == targetType) - { - return System.Convert.ToSByte(source, CultureInfo.InvariantCulture); - } - - throw new ArgumentException($"Target type '{targetType.Name}' is not supported.", nameof(targetType)); - } -} diff --git a/src/Common/src/Common/Converter/ArrayToArrayConverter.cs b/src/Common/src/Common/Converter/ArrayToArrayConverter.cs deleted file mode 100644 index 1ebb404cd4..0000000000 --- a/src/Common/src/Common/Converter/ArrayToArrayConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ArrayToArrayConverter : AbstractGenericConditionalConverter -{ - private readonly IConversionService _conversionService; - - public ArrayToArrayConverter(IConversionService conversionService) - : base(new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(object[]), typeof(object[])) - }) - { - _conversionService = conversionService; - } - - public override bool Matches(Type sourceType, Type targetType) - { - if (!sourceType.IsArray || !targetType.IsArray) - { - return false; - } - - return ConversionUtils.CanConvertElements(ConversionUtils.GetElementType(sourceType), ConversionUtils.GetElementType(targetType), _conversionService); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - Type targetElement = ConversionUtils.GetElementType(targetType); - Type sourceElement = ConversionUtils.GetElementType(sourceType); - - if (targetElement != null && _conversionService.CanBypassConvert(sourceElement, targetElement)) - { - return source; - } - - var sourceArray = source as Array; - var targetArray = Array.CreateInstance(targetElement, sourceArray.GetLength(0)); - int i = 0; - - foreach (object elem in sourceArray) - { - object newElem = _conversionService.Convert(elem, sourceElement, targetElement); - targetArray.SetValue(newElem, i++); - } - - return targetArray; - } -} diff --git a/src/Common/src/Common/Converter/ArrayToCollectionConverter.cs b/src/Common/src/Common/Converter/ArrayToCollectionConverter.cs deleted file mode 100644 index bd82330a74..0000000000 --- a/src/Common/src/Common/Converter/ArrayToCollectionConverter.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Converter; - -public class ArrayToCollectionConverter : AbstractToCollectionConverter -{ - public ArrayToCollectionConverter(IConversionService conversionService) - : base(GetConvertiblePairs(), conversionService) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - // NO OP check Arrays already implement IList, etc. - if (targetType.IsAssignableFrom(sourceType)) - { - return false; - } - - if (sourceType.IsArray && ConversionUtils.CanCreateCompatListFor(targetType)) - { - return ConversionUtils.CanConvertElements(ConversionUtils.GetElementType(sourceType), ConversionUtils.GetElementType(targetType), - ConversionService); - } - - return false; - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source is not Array asArray) - { - return null; - } - - int len = asArray.GetLength(0); - Type arrayElementType = ConversionUtils.GetElementType(asArray.GetType()); - - IList list = ConversionUtils.CreateCompatListFor(targetType); - - if (list == null) - { - throw new InvalidOperationException("Unable to create compatible list"); - } - - if (!targetType.IsGenericType) - { - for (int i = 0; i < len; i++) - { - list.Add(asArray.GetValue(i)); - } - } - else - { - Type targetElementType = ConversionUtils.GetElementType(targetType); - - for (int i = 0; i < len; i++) - { - object sourceElement = asArray.GetValue(i); - object targetElement = ConversionService.Convert(sourceElement, arrayElementType, targetElementType); - list.Add(targetElement); - } - } - - return list; - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(object[]), typeof(ICollection)), - (typeof(object[]), typeof(ICollection<>)), - (typeof(object[]), typeof(IEnumerable)), - (typeof(object[]), typeof(IEnumerable<>)) - }; - } -} diff --git a/src/Common/src/Common/Converter/ArrayToObjectConverter.cs b/src/Common/src/Common/Converter/ArrayToObjectConverter.cs deleted file mode 100644 index c03c61ff1d..0000000000 --- a/src/Common/src/Common/Converter/ArrayToObjectConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ArrayToObjectConverter : AbstractGenericConditionalConverter -{ - private readonly IConversionService _conversionService; - - public ArrayToObjectConverter(IConversionService conversionService) - : base(new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(object[]), typeof(object)) - }) - { - _conversionService = conversionService; - } - - public override bool Matches(Type sourceType, Type targetType) - { - if (!sourceType.IsArray) - { - return false; - } - - return ConversionUtils.CanConvertElements(ConversionUtils.GetElementType(sourceType), targetType, _conversionService); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - if (targetType.IsAssignableFrom(sourceType)) - { - return source; - } - - var sourceArray = source as Array; - - if (sourceArray.GetLength(0) == 0) - { - return null; - } - - object firstElement = sourceArray.GetValue(0); - return _conversionService.Convert(firstElement, firstElement.GetType(), targetType); - } -} diff --git a/src/Common/src/Common/Converter/ArrayToStringConverter.cs b/src/Common/src/Common/Converter/ArrayToStringConverter.cs deleted file mode 100644 index e13577d0fd..0000000000 --- a/src/Common/src/Common/Converter/ArrayToStringConverter.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ArrayToStringConverter : AbstractGenericConditionalConverter -{ - private readonly IConversionService _conversionService; - - public ArrayToStringConverter(IConversionService conversionService) - : base(new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(object[]), typeof(string)) - }) - { - _conversionService = conversionService; - } - - public override bool Matches(Type sourceType, Type targetType) - { - if (!sourceType.IsArray || typeof(string) != targetType) - { - return false; - } - - return ConversionUtils.CanConvertElements(ConversionUtils.GetElementType(sourceType), targetType, _conversionService); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - var sourceArray = source as Array; - - if (sourceArray.GetLength(0) == 0) - { - return string.Empty; - } - - return ConversionUtils.ToString(sourceArray, targetType, _conversionService); - } -} diff --git a/src/Common/src/Common/Converter/BooleanToStringConverter.cs b/src/Common/src/Common/Converter/BooleanToStringConverter.cs deleted file mode 100644 index 282f0f44a2..0000000000 --- a/src/Common/src/Common/Converter/BooleanToStringConverter.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class BooleanToStringConverter : ObjectToStringConverter -{ -} diff --git a/src/Common/src/Common/Converter/CharacterToNumberConverter.cs b/src/Common/src/Common/Converter/CharacterToNumberConverter.cs deleted file mode 100644 index 31655bf859..0000000000 --- a/src/Common/src/Common/Converter/CharacterToNumberConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class CharacterToNumberConverter : AbstractToNumberConverter -{ - public CharacterToNumberConverter() - : base(GetConvertiblePairs()) - { - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(char), typeof(int)), - (typeof(char), typeof(float)), - (typeof(char), typeof(uint)), - (typeof(char), typeof(ulong)), - (typeof(char), typeof(long)), - (typeof(char), typeof(double)), - (typeof(char), typeof(short)), - (typeof(char), typeof(ushort)), - (typeof(char), typeof(decimal)), - (typeof(char), typeof(byte)), - (typeof(char), typeof(sbyte)) - }; - } -} diff --git a/src/Common/src/Common/Converter/CharacterToStringConverter.cs b/src/Common/src/Common/Converter/CharacterToStringConverter.cs deleted file mode 100644 index e8c0afe2c0..0000000000 --- a/src/Common/src/Common/Converter/CharacterToStringConverter.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class CharacterToStringConverter : ObjectToStringConverter -{ -} diff --git a/src/Common/src/Common/Converter/CollectionToArrayConverter.cs b/src/Common/src/Common/Converter/CollectionToArrayConverter.cs deleted file mode 100644 index 71976f2754..0000000000 --- a/src/Common/src/Common/Converter/CollectionToArrayConverter.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Converter; - -public class CollectionToArrayConverter : AbstractGenericConditionalConverter -{ - private readonly IConversionService _conversionService; - - public CollectionToArrayConverter(IConversionService conversionService) - : base(GetConvertiblePairs()) - { - _conversionService = conversionService; - } - - public override bool Matches(Type sourceType, Type targetType) - { - if (!targetType.IsArray || sourceType.IsArray) - { - return false; - } - - return ConversionUtils.CanConvertElements(ConversionUtils.GetElementType(sourceType), ConversionUtils.GetElementType(targetType), _conversionService); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - Type targetElementType = ConversionUtils.GetElementType(targetType); - - if (targetElementType == null) - { - throw new InvalidOperationException("No target element type"); - } - - var enumerable = source as IEnumerable; - int len = ConversionUtils.Count(enumerable); - - var array = Array.CreateInstance(targetElementType, len); - int i = 0; - - foreach (object sourceElement in enumerable) - { - object targetElement = _conversionService.Convert(sourceElement, sourceElement.GetType(), targetElementType); - array.SetValue(targetElement, i++); - } - - return array; - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(ICollection), typeof(object[])), - (typeof(ISet<>), typeof(object[])) - }; - } -} diff --git a/src/Common/src/Common/Converter/CollectionToCollectionConverter.cs b/src/Common/src/Common/Converter/CollectionToCollectionConverter.cs deleted file mode 100644 index afac559b86..0000000000 --- a/src/Common/src/Common/Converter/CollectionToCollectionConverter.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Converter; - -public class CollectionToCollectionConverter : AbstractToCollectionConverter -{ - public CollectionToCollectionConverter(IConversionService conversionService) - : base(GetConvertiblePairs(), conversionService) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - // NO OP check Arrays already implement IList, etc. - if (targetType.IsAssignableFrom(sourceType)) - { - return false; - } - - if (ConversionUtils.CanCreateCompatListFor(targetType)) - { - return ConversionUtils.CanConvertElements(ConversionUtils.GetElementType(sourceType), ConversionUtils.GetElementType(targetType), - ConversionService); - } - - return false; - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source is not IEnumerable sourceCollection) - { - return null; - } - - IList list = ConversionUtils.CreateCompatListFor(targetType); - - if (list == null) - { - throw new InvalidOperationException("Unable to create compatible list"); - } - - if (!targetType.IsGenericType) - { - foreach (object elem in sourceCollection) - { - list.Add(elem); - } - } - else - { - Type targetElementType = ConversionUtils.GetElementType(targetType) ?? typeof(object); - - foreach (object sourceElement in sourceCollection) - { - if (sourceElement != null) - { - object targetElement = ConversionService.Convert(sourceElement, sourceElement.GetType(), targetElementType); - list.Add(targetElement); - } - else - { - list.Add(null); - } - } - } - - return list; - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(ICollection), typeof(ICollection)), - (typeof(ICollection), typeof(ICollection<>)), - (typeof(ICollection), typeof(IEnumerable<>)), - (typeof(ICollection), typeof(IEnumerable)), - (typeof(ISet<>), typeof(ICollection)), - (typeof(ISet<>), typeof(ICollection<>)), - (typeof(ISet<>), typeof(IEnumerable<>)), - (typeof(ISet<>), typeof(IEnumerable)) - }; - } -} diff --git a/src/Common/src/Common/Converter/CollectionToObjectConverter.cs b/src/Common/src/Common/Converter/CollectionToObjectConverter.cs deleted file mode 100644 index 463dbc3603..0000000000 --- a/src/Common/src/Common/Converter/CollectionToObjectConverter.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Converter; - -public class CollectionToObjectConverter : AbstractGenericConditionalConverter -{ - private readonly IConversionService _conversionService; - - public CollectionToObjectConverter(IConversionService conversionService, ISet<(Type SourceType, Type TargetType)> convertableTypes = null) - : base(convertableTypes ?? new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(ICollection), typeof(object)) - }) - { - _conversionService = conversionService; - } - - public override bool Matches(Type sourceType, Type targetType) - { - if (!typeof(IEnumerable).IsAssignableFrom(sourceType)) - { - return false; - } - - return ConversionUtils.CanConvertElements(ConversionUtils.GetElementType(sourceType), targetType, _conversionService); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - if (targetType.IsAssignableFrom(sourceType)) - { - return source; - } - - var sourceCollection = source as ICollection; - - if (sourceCollection.Count == 0) - { - return null; - } - - IEnumerator enumerator = sourceCollection.GetEnumerator(); - enumerator.MoveNext(); - - return _conversionService.Convert(enumerator.Current, enumerator.Current.GetType(), targetType); - } -} diff --git a/src/Common/src/Common/Converter/CollectionToStringConverter.cs b/src/Common/src/Common/Converter/CollectionToStringConverter.cs deleted file mode 100644 index 6f14f5c67d..0000000000 --- a/src/Common/src/Common/Converter/CollectionToStringConverter.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Converter; - -public class CollectionToStringConverter : AbstractGenericConditionalConverter -{ - private readonly IConversionService _conversionService; - - public CollectionToStringConverter(IConversionService conversionService) - : base(new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(ICollection), typeof(string)) - }) - { - _conversionService = conversionService; - } - - public override bool Matches(Type sourceType, Type targetType) - { - return ConversionUtils.CanConvertElements(ConversionUtils.GetElementType(sourceType), targetType, _conversionService); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - var sourceCollection = (ICollection)source; - - if (sourceCollection.Count == 0) - { - return string.Empty; - } - - return ConversionUtils.ToString(sourceCollection, targetType, _conversionService); - } -} diff --git a/src/Common/src/Common/Converter/ConversionException.cs b/src/Common/src/Common/Converter/ConversionException.cs deleted file mode 100644 index 55dbe5004f..0000000000 --- a/src/Common/src/Common/Converter/ConversionException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ConversionException : Exception -{ - public ConversionException(string message) - : base(message) - { - } - - public ConversionException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Converter/ConversionFailedException.cs b/src/Common/src/Common/Converter/ConversionFailedException.cs deleted file mode 100644 index a2f8509207..0000000000 --- a/src/Common/src/Common/Converter/ConversionFailedException.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ConversionFailedException : ConversionException -{ - public Type SourceType { get; } - public Type TargetType { get; } - public object Value { get; } - - public ConversionFailedException(Type sourceType, Type targetType, object value, Exception innerException) - : base($"Failed to convert from type [{sourceType}] to type [{targetType}] for value '{(value == null ? "null" : value.ToString())}'", innerException) - { - SourceType = sourceType; - TargetType = targetType; - Value = value; - } -} diff --git a/src/Common/src/Common/Converter/ConversionUtils.cs b/src/Common/src/Common/Converter/ConversionUtils.cs deleted file mode 100644 index 3d21202c87..0000000000 --- a/src/Common/src/Common/Converter/ConversionUtils.cs +++ /dev/null @@ -1,369 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using System.Text; - -namespace Steeltoe.Common.Converter; - -public static class ConversionUtils -{ - private const string Delimiter = ","; - - public static bool CanConvertElements(Type sourceElementType, Type targetElementType, IConversionService conversionService) - { - if (targetElementType == null) - { - // yes - return true; - } - - if (sourceElementType == null) - { - // maybe - return true; - } - - if (conversionService.CanConvert(sourceElementType, targetElementType)) - { - // yes - return true; - } - - if (sourceElementType.IsAssignableFrom(targetElementType)) - { - // maybe - return true; - } - - // no - return false; - } - - public static Type GetElementType(Type sourceType) - { - if (sourceType.HasElementType) - { - return sourceType.GetElementType(); - } - - if (sourceType.IsConstructedGenericType) - { - return sourceType.GetGenericArguments()[0]; - } - - return null; - } - - public static string ToString(IEnumerable collection, Type targetType, IConversionService conversionService) - { - var sj = new StringBuilder(); - - foreach (object sourceElement in collection) - { - object targetElement = conversionService.Convert(sourceElement, sourceElement.GetType(), targetType); - sj.Append(targetElement); - sj.Append(Delimiter); - } - - return sj.ToString(0, sj.Length - 1); - } - - public static int Count(IEnumerable enumerable) - { - if (enumerable is ICollection collection) - { - return collection.Count; - } - - return enumerable.Cast().Count(); - } - - public static Type GetNullableElementType(Type nullableType) - { - if (nullableType.IsGenericType && typeof(Nullable<>) == nullableType.GetGenericTypeDefinition()) - { - return nullableType.GetGenericArguments()[0]; - } - - return nullableType; - } - - public static bool CanCreateCompatListFor(Type type) - { - if (type == null) - { - return false; - } - - if (type.IsGenericTypeDefinition) - { - return false; - } - - if (type.IsInterface) - { - if (typeof(IList).IsAssignableFrom(type)) - { - return true; - } - - if (typeof(IEnumerable).IsAssignableFrom(type)) - { - return true; - } - - if (type.IsGenericType) - { - Type definition = type.GetGenericTypeDefinition(); - - if (typeof(IList<>) == definition || typeof(IEnumerator<>) == definition || typeof(ICollection<>) == definition) - { - return true; - } - } - } - else - { - return typeof(IList).IsAssignableFrom(type) && ContainsPublicNoArgConstructor(type); - } - - return false; - } - - public static bool CanCreateCompatDictionaryFor(Type type) - { - if (type == null) - { - return false; - } - - if (type.IsGenericTypeDefinition) - { - return false; - } - - if (type.IsInterface) - { - if (typeof(IDictionary).IsAssignableFrom(type)) - { - return true; - } - - if (type.IsGenericType) - { - Type definition = type.GetGenericTypeDefinition(); - - if (typeof(IDictionary<,>) == definition) - { - return true; - } - - if (typeof(IEnumerator<>) == definition) - { - return true; - } - } - } - else - { - return typeof(IDictionary).IsAssignableFrom(type) && ContainsPublicNoArgConstructor(type); - } - - return false; - } - - public static Type GetDictionaryKeyType(Type sourceType) - { - return GetDictionaryKeyOrValueType(sourceType, 0); - } - - public static Type GetDictionaryValueType(Type sourceType) - { - return GetDictionaryKeyOrValueType(sourceType, 1); - } - - public static bool ContainsPublicNoArgConstructor(Type collectionType) - { - if (collectionType == null) - { - return false; - } - - return collectionType.GetConstructor(Type.EmptyTypes) != null; - } - - public static IList CreateCompatListFor(Type type) - { - if (!CanCreateCompatListFor(type)) - { - return null; - } - - if (type.IsInterface) - { - if (type.IsGenericType) - { - Type definition = type.GetGenericTypeDefinition(); - - if (typeof(IList<>) == definition || typeof(IEnumerable<>) == definition || typeof(ICollection<>) == definition) - { - return (IList)Activator.CreateInstance(MakeGenericListType(type)); - } - } - - if (typeof(IList).IsAssignableFrom(type)) - { - return new ArrayList(); - } - - if (typeof(IEnumerable).IsAssignableFrom(type)) - { - return new ArrayList(); - } - - return null; - } - - return (IList)Activator.CreateInstance(type); - } - - public static IDictionary CreateCompatDictionaryFor(Type type) - { - if (!CanCreateCompatDictionaryFor(type)) - { - return null; - } - - if (type.IsInterface) - { - if (type.IsGenericType) - { - Type definition = type.GetGenericTypeDefinition(); - - if (typeof(IDictionary<,>) == definition) - { - return (IDictionary)Activator.CreateInstance(MakeGenericDictionaryType(type)); - } - } - - if (typeof(IDictionary).IsAssignableFrom(type)) - { - return new Hashtable(); - } - - if (typeof(IEnumerable).IsAssignableFrom(type)) - { - return new Hashtable(); - } - - return null; - } - - return (IDictionary)Activator.CreateInstance(type); - } - - public static ConstructorInfo GetConstructorIfAvailable(Type type, params Type[] paramTypes) - { - ArgumentGuard.NotNull(type); - - try - { - return type.GetConstructor(paramTypes); - } - catch (Exception) - { - return null; - } - } - - public static MethodInfo GetStaticMethod(Type type, string methodName, params Type[] types) - { - ArgumentGuard.NotNull(type); - ArgumentGuard.NotNullOrEmpty(methodName); - - try - { - MethodInfo method = type.GetMethod(methodName, types); - - if (method != null) - { - return method.IsStatic ? method : null; - } - - return null; - } - catch (Exception) - { - return null; - } - } - - public static MethodInfo GetMethodIfAvailable(Type type, string methodName, params Type[] paramTypes) - { - ArgumentGuard.NotNull(type); - ArgumentGuard.NotNullOrEmpty(methodName); - - if (paramTypes != null) - { - try - { - return type.GetMethod(methodName, paramTypes); - } - catch (Exception) - { - return null; - } - } - - List candidates = FindMethodCandidatesByName(type, methodName); - - if (candidates.Count == 1) - { - return candidates[0]; - } - - return null; - } - - internal static List FindMethodCandidatesByName(Type type, string methodName) - { - var candidates = new List(); - MethodInfo[] methods = type.GetMethods(); - - foreach (MethodInfo method in methods) - { - if (methodName == method.Name) - { - candidates.Add(method); - } - } - - return candidates; - } - - internal static Type GetDictionaryKeyOrValueType(Type sourceType, int index) - { - if (sourceType.IsGenericType) - { - return sourceType.GetGenericArguments()[index]; - } - - return typeof(object); - } - - internal static Type MakeGenericListType(Type type) - { - Type elemType = type.GetGenericArguments()[0]; - return typeof(List<>).MakeGenericType(elemType); - } - - internal static Type MakeGenericDictionaryType(Type type) - { - Type keyType = type.GetGenericArguments()[0]; - Type valType = type.GetGenericArguments()[1]; - return typeof(Dictionary<,>).MakeGenericType(keyType, valType); - } -} diff --git a/src/Common/src/Common/Converter/ConverterNotFoundException.cs b/src/Common/src/Common/Converter/ConverterNotFoundException.cs deleted file mode 100644 index 8eb9103408..0000000000 --- a/src/Common/src/Common/Converter/ConverterNotFoundException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ConverterNotFoundException : ConversionException -{ - public Type SourceType { get; } - public Type TargetType { get; } - - public ConverterNotFoundException(Type sourceType, Type targetType) - : base($"No converter found capable of converting from type [{sourceType}] to type [{targetType}]") - { - SourceType = sourceType; - TargetType = targetType; - } -} diff --git a/src/Common/src/Common/Converter/DefaultConversionService.cs b/src/Common/src/Common/Converter/DefaultConversionService.cs deleted file mode 100644 index 79c9c44698..0000000000 --- a/src/Common/src/Common/Converter/DefaultConversionService.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class DefaultConversionService : GenericConversionService -{ - private static readonly object Lock = new(); - - private static volatile DefaultConversionService _sharedInstance; - - public static IConversionService Singleton - { - get - { - DefaultConversionService cs = _sharedInstance; - - if (cs == null) - { - lock (Lock) - { - cs = _sharedInstance; - - if (cs == null) - { - cs = new DefaultConversionService(); - _sharedInstance = cs; - } - } - } - - return cs; - } - } - - public DefaultConversionService() - { - AddDefaultConverters(this); - } - - public static void AddDefaultConverters(IConverterRegistry converterRegistry) - { - AddScalarConverters(converterRegistry); - AddCollectionConverters(converterRegistry); - - converterRegistry.AddConverter(new ObjectToObjectConverter()); - - converterRegistry.AddConverter(new FallbackObjectToStringConverter()); - } - - public static void AddCollectionConverters(IConverterRegistry converterRegistry) - { - var conversionService = (IConversionService)converterRegistry; - - converterRegistry.AddConverter(new ListToDictionaryConverter(conversionService)); - - converterRegistry.AddConverter(new ArrayToCollectionConverter(conversionService)); - converterRegistry.AddConverter(new CollectionToArrayConverter(conversionService)); - - converterRegistry.AddConverter(new ArrayToArrayConverter(conversionService)); - - converterRegistry.AddConverter(new CollectionToCollectionConverter(conversionService)); - - converterRegistry.AddConverter(new DictionaryToDictionaryConverter(conversionService)); - converterRegistry.AddConverter(new ArrayToStringConverter(conversionService)); - converterRegistry.AddConverter(new StringToArrayConverter(conversionService)); - - converterRegistry.AddConverter(new ArrayToObjectConverter(conversionService)); - converterRegistry.AddConverter(new ObjectToArrayConverter(conversionService)); - - converterRegistry.AddConverter(new CollectionToStringConverter(conversionService)); - converterRegistry.AddConverter(new StringToCollectionConverter(conversionService)); - - converterRegistry.AddConverter(new CollectionToObjectConverter(conversionService)); - - converterRegistry.AddConverter(new ObjectToCollectionConverter(conversionService)); - } - - private static void AddScalarConverters(IConverterRegistry converterRegistry) - { - converterRegistry.AddConverter(new NumberToNumberConverter()); - converterRegistry.AddConverter(new StringToNumberConverter()); - - converterRegistry.AddConverter(new NumberToStringConverter()); - - converterRegistry.AddConverter(new StringToCharacterConverter()); - converterRegistry.AddConverter(new CharacterToStringConverter()); - - converterRegistry.AddConverter(new NumberToCharacterConverter()); - converterRegistry.AddConverter(new CharacterToNumberConverter()); - - converterRegistry.AddConverter(new StringToBooleanConverter()); - converterRegistry.AddConverter(new BooleanToStringConverter()); - - converterRegistry.AddConverter(new StringToEnumConverter()); - converterRegistry.AddConverter(new EnumToStringConverter()); - - converterRegistry.AddConverter(new StringToEncodingConverter()); - converterRegistry.AddConverter(new EncodingToStringConverter()); - - converterRegistry.AddConverter(new StringToGuidConverter()); - converterRegistry.AddConverter(new GuidToStringConverter()); - - converterRegistry.AddConverter(new ObjectToNumberConverter()); - } -} diff --git a/src/Common/src/Common/Converter/DictionaryToDictionaryConverter.cs b/src/Common/src/Common/Converter/DictionaryToDictionaryConverter.cs deleted file mode 100644 index 1374ee822f..0000000000 --- a/src/Common/src/Common/Converter/DictionaryToDictionaryConverter.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Converter; - -public class DictionaryToDictionaryConverter : AbstractToCollectionConverter -{ - public DictionaryToDictionaryConverter(IConversionService conversionService) - : base(GetConvertiblePairs(), conversionService) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - return ConversionUtils.CanCreateCompatDictionaryFor(targetType) && CanConvertKey(sourceType, targetType) && CanConvertValue(sourceType, targetType); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - var sourceDict = source as IDictionary; - int len = sourceDict.Count; - - // Shortcut if possible... - bool copyRequired = !targetType.IsInstanceOfType(source); - - if (!copyRequired && len == 0) - { - return source; - } - - Type targetKeyType = ConversionUtils.GetDictionaryKeyType(targetType); - Type targetValueType = ConversionUtils.GetDictionaryValueType(targetType); - Type sourceKeyType = ConversionUtils.GetDictionaryKeyType(sourceType); - Type sourceValueType = ConversionUtils.GetDictionaryValueType(sourceType); - - IDictionary dict = ConversionUtils.CreateCompatDictionaryFor(targetType); - - if (dict == null) - { - throw new InvalidOperationException("Unable to create compatible dictionary"); - } - - if (!targetType.IsGenericType) - { - IDictionaryEnumerator enumerator = sourceDict.GetEnumerator(); - - while (enumerator.MoveNext()) - { - dict.Add(enumerator.Key, enumerator.Value); - } - } - else - { - IDictionaryEnumerator enumerator = sourceDict.GetEnumerator(); - - while (enumerator.MoveNext()) - { - object targetKey = ConvertKey(enumerator.Key, sourceKeyType, targetKeyType); - object targetValue = ConvertValue(enumerator.Value, sourceValueType, targetValueType); - dict.Add(targetKey, targetValue); - } - } - - return dict; - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(IDictionary), typeof(IDictionary)), - (typeof(IDictionary), typeof(IDictionary<,>)), - (typeof(IDictionary), typeof(Dictionary<,>)) - }; - } - - private bool CanConvertKey(Type sourceType, Type targetType) - { - return ConversionUtils.CanConvertElements(ConversionUtils.GetDictionaryKeyType(sourceType), ConversionUtils.GetDictionaryKeyType(targetType), - ConversionService); - } - - private bool CanConvertValue(Type sourceType, Type targetType) - { - return ConversionUtils.CanConvertElements(ConversionUtils.GetDictionaryValueType(sourceType), ConversionUtils.GetDictionaryValueType(targetType), - ConversionService); - } - - private object ConvertKey(object sourceKey, Type sourceType, Type targetType) - { - if (targetType == null) - { - return sourceKey; - } - - return ConversionService.Convert(sourceKey, sourceType, targetType); - } - - private object ConvertValue(object sourceValue, Type sourceType, Type targetType) - { - if (targetType == null) - { - return sourceValue; - } - - return ConversionService.Convert(sourceValue, sourceValue != null ? sourceValue.GetType() : sourceType, targetType); - } -} diff --git a/src/Common/src/Common/Converter/EncodingToStringConverter.cs b/src/Common/src/Common/Converter/EncodingToStringConverter.cs deleted file mode 100644 index fb73fc1316..0000000000 --- a/src/Common/src/Common/Converter/EncodingToStringConverter.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Converter; - -public class EncodingToStringConverter : AbstractConverter -{ - public override string Convert(Encoding source) - { - return source.BodyName; - } -} diff --git a/src/Common/src/Common/Converter/EnumToStringConverter.cs b/src/Common/src/Common/Converter/EnumToStringConverter.cs deleted file mode 100644 index 5a4eb7ac66..0000000000 --- a/src/Common/src/Common/Converter/EnumToStringConverter.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class EnumToStringConverter : ObjectToStringConverter -{ -} diff --git a/src/Common/src/Common/Converter/FallbackObjectToStringConverter.cs b/src/Common/src/Common/Converter/FallbackObjectToStringConverter.cs deleted file mode 100644 index bfa2873dc3..0000000000 --- a/src/Common/src/Common/Converter/FallbackObjectToStringConverter.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class FallbackObjectToStringConverter : AbstractConverter -{ - public override bool Matches(Type sourceType, Type targetType) - { - if (sourceType == typeof(string)) - { - return false; - } - - return ObjectToObjectConverter.HasConversionMethodOrConstructor(sourceType, typeof(string)); - } - - public override string Convert(object source) - { - return source?.ToString(); - } -} diff --git a/src/Common/src/Common/Converter/GenericConversionService.cs b/src/Common/src/Common/Converter/GenericConversionService.cs deleted file mode 100644 index b956dc5fe7..0000000000 --- a/src/Common/src/Common/Converter/GenericConversionService.cs +++ /dev/null @@ -1,466 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Text; - -namespace Steeltoe.Common.Converter; - -public class GenericConversionService : IConversionService, IConverterRegistry -{ - private static readonly IGenericConverter NoOpConverterSingleton = new NoOpConverter("NO_OP"); - private static readonly IGenericConverter NoMatchSingleton = new NoOpConverter("NO_MATCH"); - private readonly Converters _converters = new(); - - private readonly ConcurrentDictionary _converterCache = new(); - - public bool CanConvert(Type sourceType, Type targetType) - { - ArgumentGuard.NotNull(targetType); - - if (sourceType == null) - { - return true; - } - - IGenericConverter converter = GetConverter(sourceType, targetType); - return converter != null; - } - - public T Convert(object source) - { - return (T)Convert(source, source.GetType(), typeof(T)); - } - - public object Convert(object source, Type sourceType, Type targetType) - { - ArgumentGuard.NotNull(targetType); - - if (sourceType == null) - { - if (source != null) - { - throw new ArgumentException($"{nameof(source)} must be null if {nameof(sourceType)} is null."); - } - - return HandleResult(null, targetType, null); - } - - if (source != null && !sourceType.IsInstanceOfType(source)) - { - throw new ArgumentException($"Source to convert from must be an instance of [{sourceType.Name}]; instead it was a [{source.GetType().Name}]"); - } - - IGenericConverter converter = GetConverter(sourceType, targetType); - - if (converter != null) - { - try - { - object result = converter.Convert(source, sourceType, targetType); - return HandleResult(sourceType, targetType, result); - } - catch (ConversionFailedException) - { - throw; - } - catch (Exception ex) - { - throw new ConversionFailedException(sourceType, targetType, source, ex); - } - } - - return HandleConverterNotFound(source, sourceType, targetType); - } - - public bool CanBypassConvert(Type sourceType, Type targetType) - { - ArgumentGuard.NotNull(targetType); - - if (sourceType == null) - { - return true; - } - - IGenericConverter converter = GetConverter(sourceType, targetType); - return converter == NoOpConverterSingleton; - } - - public void AddConverter(IGenericConverter converter) - { - _converters.Add(converter); - _converterCache.Clear(); - } - - protected virtual IGenericConverter GetConverter(Type sourceType, Type targetType) - { - var key = new ConverterCacheKey(sourceType, targetType); - - if (_converterCache.TryGetValue(key, out IGenericConverter converter)) - { - return converter != NoMatchSingleton ? converter : null; - } - - converter = _converters.Find(sourceType, targetType) ?? GetDefaultConverter(sourceType, targetType); - - if (converter != null) - { - if (!_converterCache.TryAdd(key, converter)) - { - _converterCache.TryGetValue(key, out converter); - } - - return converter; - } - - _converterCache.TryAdd(key, NoMatchSingleton); - return null; - } - - protected virtual IGenericConverter GetDefaultConverter(Type sourceType, Type targetType) - { - return targetType.IsAssignableFrom(sourceType) ? NoOpConverterSingleton : null; - } - - private object HandleResult(Type sourceType, Type targetType, object result) - { - if (result == null) - { - AssertNotPrimitiveTargetType(sourceType, targetType); - } - - return result; - } - - private object HandleConverterNotFound(object source, Type sourceType, Type targetType) - { - if (source == null) - { - AssertNotPrimitiveTargetType(sourceType, targetType); - return null; - } - - if ((sourceType == null || targetType.IsAssignableFrom(sourceType)) && targetType.IsInstanceOfType(source)) - { - return source; - } - - throw new ConverterNotFoundException(sourceType, targetType); - } - - private void AssertNotPrimitiveTargetType(Type sourceType, Type targetType) - { - if (targetType.IsPrimitive) - { - throw new ConversionFailedException(sourceType, targetType, null, new ArgumentException("A null value cannot be assigned to a primitive type.")); - } - } - - private sealed class NoOpConverter : IGenericConverter - { - private readonly string _name; - - public ISet<(Type SourceType, Type TargetType)> ConvertibleTypes => null; - - public NoOpConverter(string name) - { - _name = name; - } - - public object Convert(object source, Type sourceType, Type targetType) - { - return source; - } - - public override string ToString() - { - return _name; - } - } - -#pragma warning disable S1210 // "Equals" and the comparison operators should be overridden when implementing "IComparable" - private sealed class ConverterCacheKey : IComparable -#pragma warning restore S1210 // "Equals" and the comparison operators should be overridden when implementing "IComparable" - { - private readonly Type _sourceType; - - private readonly Type _targetType; - - public ConverterCacheKey(Type sourceType, Type targetType) - { - _sourceType = sourceType; - _targetType = targetType; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not ConverterCacheKey other) - { - return false; - } - - return _sourceType == other._sourceType && _targetType == other._targetType; - } - - public override int GetHashCode() - { - return HashCode.Combine(_sourceType, _targetType); - } - - public override string ToString() - { - return $"ConverterCacheKey [sourceType = {_sourceType}, targetType = {_targetType}]"; - } - - public int CompareTo(ConverterCacheKey other) - { - int result = string.Compare(_sourceType.ToString(), other._sourceType.ToString(), StringComparison.Ordinal); - - if (result == 0) - { - result = string.Compare(_targetType.ToString(), other._targetType.ToString(), StringComparison.Ordinal); - } - - return result; - } - } - - private sealed class ConvertersForPair - { - private readonly LinkedList _converters = new(); - - public void Add(IGenericConverter converter) - { - _converters.AddFirst(converter); - } - - public IGenericConverter GetConverter(Type sourceType, Type targetType) - { - foreach (IGenericConverter converter in _converters) - { - if (converter is not IConditionalGenericConverter genericConverter || genericConverter.Matches(sourceType, targetType)) - { - return converter; - } - } - - return null; - } - - public override string ToString() - { - return string.Join(",", _converters); - } - } - - private sealed class Converters - { - private readonly ISet _globalConverters = new HashSet(); - - private readonly Dictionary<(Type SourceType, Type TargetType), ConvertersForPair> _converters = new(); - - public void Add(IGenericConverter converter) - { - ISet<(Type SourceType, Type TargetType)> convertibleTypes = converter.ConvertibleTypes; - - if (convertibleTypes == null) - { - if (converter is not IConditionalConverter) - { - throw new InvalidOperationException("Only conditional converters may return null convertible types"); - } - - _globalConverters.Add(converter); - } - else - { - foreach ((Type SourceType, Type TargetType) convertiblePair in convertibleTypes) - { - ConvertersForPair convertersForPair = GetMatchableConverters(convertiblePair); - convertersForPair.Add(converter); - } - } - } - - public IGenericConverter Find(Type sourceType, Type targetType) - { - // Search the full type hierarchy - List sourceCandidates = GetClassHierarchy(sourceType); - List targetCandidates = GetClassHierarchy(targetType); - - foreach (Type sourceCandidate in sourceCandidates) - { - IGenericConverter converter = CheckTargets(sourceType, targetType, sourceCandidate, targetCandidates); - - if (converter != null) - { - return converter; - } - - if (sourceCandidate.IsConstructedGenericType) - { - converter = CheckTargets(sourceType, targetType, sourceCandidate.GetGenericTypeDefinition(), targetCandidates); - - if (converter != null) - { - return converter; - } - } - } - - return null; - } - - public IGenericConverter CheckTargets(Type sourceType, Type targetType, Type sourceCandidate, List targetCandidateTypes) - { - foreach (Type targetCandidateType in targetCandidateTypes) - { - (Type SourceType, Type TargetType) convertiblePair = (SourceType: sourceCandidate, TargetType: targetCandidateType); - IGenericConverter converter = GetRegisteredConverter(sourceType, targetType, convertiblePair); - - if (converter != null) - { - return converter; - } - - if (targetCandidateType.IsConstructedGenericType) - { - convertiblePair.TargetType = targetCandidateType.GetGenericTypeDefinition(); - converter = GetRegisteredConverter(sourceType, targetType, convertiblePair); - - if (converter != null) - { - return converter; - } - } - } - - return null; - } - - public override string ToString() - { - var builder = new StringBuilder(); - builder.Append("ConversionService converters =\n"); - - foreach (string converterString in GetConverterStrings()) - { - builder.Append('\t').Append(converterString).Append('\n'); - } - - return builder.ToString(); - } - - private ConvertersForPair GetMatchableConverters((Type SourceType, Type TargetType) convertiblePair) - { - if (!_converters.TryGetValue(convertiblePair, out ConvertersForPair convertersForPair)) - { - convertersForPair = new ConvertersForPair(); - _converters.Add(convertiblePair, convertersForPair); - } - - return convertersForPair; - } - - private IGenericConverter GetRegisteredConverter(Type sourceType, Type targetType, (Type SourceType, Type TargetType) convertiblePair) - { - // Check specifically registered converters - if (_converters.TryGetValue(convertiblePair, out ConvertersForPair convertersForPair)) - { - IGenericConverter converter = convertersForPair.GetConverter(sourceType, targetType); - - if (converter != null) - { - return converter; - } - } - - // Check ConditionalConverters for a dynamic match - foreach (IGenericConverter globalConverter in _globalConverters) - { - if (((IConditionalConverter)globalConverter).Matches(sourceType, targetType)) - { - return globalConverter; - } - } - - return null; - } - - private List GetClassHierarchy(Type type) - { - var hierarchy = new List(); - ISet visited = new HashSet(); - AddToClassHierarchy(0, type, false, hierarchy, visited); - bool array = type.IsArray; - - int i = 0; - - while (i < hierarchy.Count) - { - Type candidate = hierarchy[i]; - candidate = array ? candidate.GetElementType() : candidate; - Type superclass = candidate.BaseType; - - if (superclass != null && superclass != typeof(object) && superclass != typeof(Enum)) - { - AddToClassHierarchy(i + 1, candidate.BaseType, array, hierarchy, visited); - } - - AddInterfacesToClassHierarchy(candidate, array, hierarchy, visited); - i++; - } - - if (typeof(Enum).IsAssignableFrom(type)) - { - AddToClassHierarchy(hierarchy.Count, typeof(Enum), array, hierarchy, visited); - AddToClassHierarchy(hierarchy.Count, typeof(Enum), false, hierarchy, visited); - AddInterfacesToClassHierarchy(typeof(Enum), array, hierarchy, visited); - } - - AddToClassHierarchy(hierarchy.Count, typeof(object), array, hierarchy, visited); - AddToClassHierarchy(hierarchy.Count, typeof(object), false, hierarchy, visited); - return hierarchy; - } - - private void AddInterfacesToClassHierarchy(Type type, bool asArray, List hierarchy, ISet visited) - { - foreach (Type implementedInterface in type.GetInterfaces()) - { - AddToClassHierarchy(hierarchy.Count, implementedInterface, asArray, hierarchy, visited); - } - } - - private void AddToClassHierarchy(int index, Type type, bool asArray, List hierarchy, ISet visited) - { - if (asArray) - { - type = Array.CreateInstance(type, 0).GetType(); - } - - if (visited.Add(type)) - { - hierarchy.Insert(index, type); - } - } - - private List GetConverterStrings() - { - var converterStrings = new List(); - - foreach (ConvertersForPair convertersForPair in _converters.Values) - { - converterStrings.Add(convertersForPair.ToString()); - } - - converterStrings.Sort(); - return converterStrings; - } - } -} diff --git a/src/Common/src/Common/Converter/GuidToStringConverter.cs b/src/Common/src/Common/Converter/GuidToStringConverter.cs deleted file mode 100644 index b512ff7662..0000000000 --- a/src/Common/src/Common/Converter/GuidToStringConverter.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class GuidToStringConverter : ObjectToStringConverter -{ -} diff --git a/src/Common/src/Common/Converter/ListToDictionaryConverter.cs b/src/Common/src/Common/Converter/ListToDictionaryConverter.cs deleted file mode 100644 index 4b5e2f40a8..0000000000 --- a/src/Common/src/Common/Converter/ListToDictionaryConverter.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ListToDictionaryConverter : CollectionToObjectConverter -{ - public ListToDictionaryConverter(IConversionService conversionService) - : base(conversionService, new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(IList), typeof(IDictionary)) - }) - { - } -} diff --git a/src/Common/src/Common/Converter/NumberToCharacterConverter.cs b/src/Common/src/Common/Converter/NumberToCharacterConverter.cs deleted file mode 100644 index a84e8b4647..0000000000 --- a/src/Common/src/Common/Converter/NumberToCharacterConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; - -namespace Steeltoe.Common.Converter; - -public class NumberToCharacterConverter : AbstractGenericConverter -{ - public NumberToCharacterConverter() - : base(GetConvertiblePairs()) - { - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - return System.Convert.ToChar(source, CultureInfo.InvariantCulture); - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(int), typeof(char)), - (typeof(uint), typeof(char)), - (typeof(ulong), typeof(char)), - (typeof(long), typeof(char)), - (typeof(short), typeof(char)), - (typeof(ushort), typeof(char)) - }; - } -} diff --git a/src/Common/src/Common/Converter/NumberToNumberConverter.cs b/src/Common/src/Common/Converter/NumberToNumberConverter.cs deleted file mode 100644 index ed8fdb0827..0000000000 --- a/src/Common/src/Common/Converter/NumberToNumberConverter.cs +++ /dev/null @@ -1,118 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class NumberToNumberConverter : AbstractToNumberConverter -{ - public NumberToNumberConverter() - : base(GetConvertiblePairs()) - { - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(float), typeof(int)), - (typeof(uint), typeof(int)), - (typeof(ulong), typeof(int)), - (typeof(long), typeof(int)), - (typeof(double), typeof(int)), - (typeof(short), typeof(int)), - (typeof(ushort), typeof(int)), - (typeof(decimal), typeof(int)), - (typeof(byte), typeof(int)), - (typeof(sbyte), typeof(int)), - - (typeof(int), typeof(float)), - (typeof(uint), typeof(float)), - (typeof(ulong), typeof(float)), - (typeof(long), typeof(float)), - (typeof(double), typeof(float)), - (typeof(short), typeof(float)), - (typeof(ushort), typeof(float)), - (typeof(decimal), typeof(float)), - (typeof(byte), typeof(float)), - (typeof(sbyte), typeof(float)), - - (typeof(int), typeof(uint)), - (typeof(float), typeof(uint)), - (typeof(ulong), typeof(uint)), - (typeof(long), typeof(uint)), - (typeof(double), typeof(uint)), - (typeof(short), typeof(uint)), - (typeof(ushort), typeof(uint)), - (typeof(decimal), typeof(uint)), - (typeof(byte), typeof(uint)), - (typeof(sbyte), typeof(uint)), - - (typeof(int), typeof(ulong)), - (typeof(float), typeof(ulong)), - (typeof(uint), typeof(ulong)), - (typeof(long), typeof(ulong)), - (typeof(double), typeof(ulong)), - (typeof(short), typeof(ulong)), - (typeof(ushort), typeof(ulong)), - (typeof(decimal), typeof(ulong)), - (typeof(byte), typeof(ulong)), - (typeof(sbyte), typeof(ulong)), - - (typeof(int), typeof(long)), - (typeof(float), typeof(long)), - (typeof(uint), typeof(long)), - (typeof(ulong), typeof(long)), - (typeof(double), typeof(long)), - (typeof(short), typeof(long)), - (typeof(ushort), typeof(long)), - (typeof(decimal), typeof(long)), - (typeof(byte), typeof(long)), - (typeof(sbyte), typeof(long)), - - (typeof(int), typeof(double)), - (typeof(float), typeof(double)), - (typeof(uint), typeof(double)), - (typeof(ulong), typeof(double)), - (typeof(long), typeof(double)), - (typeof(short), typeof(double)), - (typeof(ushort), typeof(double)), - (typeof(decimal), typeof(double)), - (typeof(byte), typeof(double)), - (typeof(sbyte), typeof(double)), - - (typeof(int), typeof(short)), - (typeof(float), typeof(short)), - (typeof(uint), typeof(short)), - (typeof(ulong), typeof(short)), - (typeof(long), typeof(short)), - (typeof(double), typeof(short)), - (typeof(ushort), typeof(short)), - (typeof(decimal), typeof(short)), - (typeof(byte), typeof(short)), - (typeof(sbyte), typeof(short)), - - (typeof(int), typeof(decimal)), - (typeof(float), typeof(decimal)), - (typeof(uint), typeof(decimal)), - (typeof(ulong), typeof(decimal)), - (typeof(long), typeof(decimal)), - (typeof(double), typeof(decimal)), - (typeof(short), typeof(decimal)), - (typeof(ushort), typeof(decimal)), - (typeof(byte), typeof(decimal)), - (typeof(sbyte), typeof(decimal)), - - (typeof(int), typeof(byte)), - (typeof(float), typeof(byte)), - (typeof(uint), typeof(byte)), - (typeof(ulong), typeof(byte)), - (typeof(long), typeof(byte)), - (typeof(double), typeof(byte)), - (typeof(short), typeof(byte)), - (typeof(ushort), typeof(byte)), - (typeof(decimal), typeof(byte)), - (typeof(sbyte), typeof(byte)) - }; - } -} diff --git a/src/Common/src/Common/Converter/NumberToStringConverter.cs b/src/Common/src/Common/Converter/NumberToStringConverter.cs deleted file mode 100644 index e5f6561b8e..0000000000 --- a/src/Common/src/Common/Converter/NumberToStringConverter.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; - -namespace Steeltoe.Common.Converter; - -public class NumberToStringConverter : AbstractGenericConverter -{ - public NumberToStringConverter() - : base(GetConvertiblePairs()) - { - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - return System.Convert.ToString(source, CultureInfo.InvariantCulture); - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(int), typeof(string)), - (typeof(float), typeof(string)), - (typeof(uint), typeof(string)), - (typeof(ulong), typeof(string)), - (typeof(long), typeof(string)), - (typeof(double), typeof(string)), - (typeof(short), typeof(string)), - (typeof(ushort), typeof(string)), - (typeof(decimal), typeof(string)), - (typeof(byte), typeof(string)), - (typeof(sbyte), typeof(string)) - }; - } -} diff --git a/src/Common/src/Common/Converter/ObjectToArrayConverter.cs b/src/Common/src/Common/Converter/ObjectToArrayConverter.cs deleted file mode 100644 index f829601750..0000000000 --- a/src/Common/src/Common/Converter/ObjectToArrayConverter.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ObjectToArrayConverter : AbstractGenericConditionalConverter -{ - private readonly IConversionService _conversionService; - - public ObjectToArrayConverter(IConversionService conversionService) - : base(new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(object), typeof(object[])) - }) - { - _conversionService = conversionService; - } - - public override bool Matches(Type sourceType, Type targetType) - { - if (!targetType.IsArray) - { - return false; - } - - return ConversionUtils.CanConvertElements(sourceType, ConversionUtils.GetElementType(targetType), _conversionService); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - Type targetElementType = ConversionUtils.GetElementType(targetType); - - if (targetElementType == null) - { - throw new InvalidOperationException("No target element type"); - } - - var target = Array.CreateInstance(targetElementType, 1); - object targetElement = _conversionService.Convert(source, sourceType, targetElementType); - target.SetValue(targetElement, 0); - return target; - } -} diff --git a/src/Common/src/Common/Converter/ObjectToCollectionConverter.cs b/src/Common/src/Common/Converter/ObjectToCollectionConverter.cs deleted file mode 100644 index d2ea179dc3..0000000000 --- a/src/Common/src/Common/Converter/ObjectToCollectionConverter.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Converter; - -public class ObjectToCollectionConverter : AbstractToCollectionConverter -{ - public ObjectToCollectionConverter(IConversionService conversionService) - : base(GetConvertiblePairs(), conversionService) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - if (ConversionUtils.CanCreateCompatListFor(targetType)) - { - return ConversionUtils.CanConvertElements(sourceType, ConversionUtils.GetElementType(targetType), ConversionService); - } - - return false; - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - Type elementDesc = ConversionUtils.GetElementType(targetType); - IList target = ConversionUtils.CreateCompatListFor(targetType); - - if (elementDesc == null) - { - target.Add(source); - } - else - { - object singleElement = ConversionService.Convert(source, sourceType, elementDesc); - target.Add(singleElement); - } - - return target; - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(object), typeof(ICollection)), - (typeof(object), typeof(ICollection<>)), - (typeof(object), typeof(IEnumerable)), - (typeof(object), typeof(IEnumerable<>)) - }; - } -} diff --git a/src/Common/src/Common/Converter/ObjectToNumberConverter.cs b/src/Common/src/Common/Converter/ObjectToNumberConverter.cs deleted file mode 100644 index e76732bc2c..0000000000 --- a/src/Common/src/Common/Converter/ObjectToNumberConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ObjectToNumberConverter : AbstractToNumberConverter -{ - public ObjectToNumberConverter() - : base(GetConvertiblePairs()) - { - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(object), typeof(int)), - (typeof(object), typeof(float)), - (typeof(object), typeof(uint)), - (typeof(object), typeof(ulong)), - (typeof(object), typeof(long)), - (typeof(object), typeof(double)), - (typeof(object), typeof(short)), - (typeof(object), typeof(ushort)), - (typeof(object), typeof(decimal)), - (typeof(object), typeof(byte)), - (typeof(object), typeof(sbyte)) - }; - } -} diff --git a/src/Common/src/Common/Converter/ObjectToObjectConverter.cs b/src/Common/src/Common/Converter/ObjectToObjectConverter.cs deleted file mode 100644 index dac5b105cb..0000000000 --- a/src/Common/src/Common/Converter/ObjectToObjectConverter.cs +++ /dev/null @@ -1,158 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Reflection; - -namespace Steeltoe.Common.Converter; - -public class ObjectToObjectConverter : AbstractGenericConditionalConverter -{ - private static readonly ConcurrentDictionary ConversionMemberCache = new(); - - public ObjectToObjectConverter() - : base(GetConvertiblePairs()) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - return sourceType != targetType && HasConversionMethodOrConstructor(targetType, sourceType); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - Type sourceClass = sourceType; - Type targetClass = targetType; - MemberInfo member = GetValidatedMember(targetClass, sourceClass); - - try - { - if (member is MethodInfo method) - { - return !method.IsStatic - ? method.Invoke(source, Array.Empty()) - : method.Invoke(null, new[] - { - source - }); - } - - if (member is ConstructorInfo ctor) - { - return ctor.Invoke(new[] - { - source - }); - } - } - catch (TargetInvocationException ex) - { - throw new ConversionFailedException(sourceType, targetType, source, ex.InnerException); - } - catch (Exception ex) - { - throw new ConversionFailedException(sourceType, targetType, source, ex); - } - - throw new InvalidOperationException($"No To{targetClass.Name}() method exists on {sourceClass.Name}, " + - $"and no static ValueOf/Of/From({sourceClass.Name}) method or {targetClass.Name}({sourceClass.Name}) constructor exists on {targetClass.Name}."); - } - - internal static bool HasConversionMethodOrConstructor(Type targetType, Type sourceType) - { - return GetValidatedMember(targetType, sourceType) != null; - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(object), typeof(object)) - }; - } - - private static MemberInfo GetValidatedMember(Type targetType, Type sourceType) - { - if (ConversionMemberCache.TryGetValue(targetType, out MemberInfo member) && IsApplicable(member, sourceType)) - { - return member; - } - - member = DetermineToMethod(targetType, sourceType); - - if (member == null) - { - member = DetermineFactoryMethod(targetType, sourceType); - - if (member == null) - { - member = DetermineFactoryConstructor(targetType, sourceType); - - if (member == null) - { - return null; - } - } - } - - ConversionMemberCache.TryAdd(targetType, member); - return member; - } - - private static bool IsApplicable(MemberInfo member, Type sourceType) - { - return member switch - { - MethodInfo method => !method.IsStatic ? method.DeclaringType.IsAssignableFrom(sourceType) : method.GetParameters()[0].ParameterType == sourceType, - ConstructorInfo ctor => ctor.GetParameters()[0].ParameterType == sourceType, - _ => false - }; - } - - private static MethodInfo DetermineToMethod(Type targetType, Type sourceType) - { - if (typeof(string) == targetType || typeof(string) == sourceType) - { - // Do not accept a ToString() method or any to methods on String itself - return null; - } - - MethodInfo method = ConversionUtils.GetMethodIfAvailable(sourceType, $"To{targetType.Name}"); - return method != null && !method.IsStatic && targetType.IsAssignableFrom(method.ReturnType) ? method : null; - } - - private static MethodInfo DetermineFactoryMethod(Type targetType, Type sourceType) - { - if (typeof(string) == targetType) - { - // Do not accept the String.valueOf(Object) method - return null; - } - - MethodInfo method = ConversionUtils.GetStaticMethod(targetType, "ValueOf", sourceType); - - if (method == null) - { - method = ConversionUtils.GetStaticMethod(targetType, "Of", sourceType); - - if (method == null) - { - method = ConversionUtils.GetStaticMethod(targetType, "From", sourceType); - } - } - - return method; - } - - private static ConstructorInfo DetermineFactoryConstructor(Type targetType, Type sourceType) - { - return ConversionUtils.GetConstructorIfAvailable(targetType, sourceType); - } -} diff --git a/src/Common/src/Common/Converter/ObjectToStringConverter.cs b/src/Common/src/Common/Converter/ObjectToStringConverter.cs deleted file mode 100644 index 5da37b66af..0000000000 --- a/src/Common/src/Common/Converter/ObjectToStringConverter.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class ObjectToStringConverter : AbstractConverter -{ - public override string Convert(T source) - { - return source.ToString(); - } -} diff --git a/src/Common/src/Common/Converter/StringToArrayConverter.cs b/src/Common/src/Common/Converter/StringToArrayConverter.cs deleted file mode 100644 index d43244bdcb..0000000000 --- a/src/Common/src/Common/Converter/StringToArrayConverter.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class StringToArrayConverter : AbstractGenericConditionalConverter -{ - private readonly char[] _delimit = - { - ',' - }; - - private readonly IConversionService _conversionService; - - public StringToArrayConverter(IConversionService conversionService) - : base(new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(string), typeof(object[])) - }) - { - _conversionService = conversionService; - } - - public override bool Matches(Type sourceType, Type targetType) - { - if (sourceType != typeof(string) || !targetType.IsArray) - { - return false; - } - - return ConversionUtils.CanConvertElements(sourceType, ConversionUtils.GetElementType(targetType), _conversionService); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - string sourceString = source as string; - string[] fields = sourceString.Split(_delimit, StringSplitOptions.RemoveEmptyEntries); - Type targetElementType = ConversionUtils.GetElementType(targetType); - - if (targetElementType == null) - { - throw new InvalidOperationException("No target element type"); - } - - // Handle string, not delimited, to char[] - if (fields.Length == 1 && sourceString[^1] != _delimit[0] && targetElementType == typeof(char)) - { - return sourceString.ToCharArray(); - } - - var target = Array.CreateInstance(targetElementType, fields.Length); - - for (int i = 0; i < fields.Length; i++) - { - string sourceElement = fields[i]; - object targetElement = _conversionService.Convert(sourceElement.Trim(), sourceType, targetElementType); - target.SetValue(targetElement, i); - } - - return target; - } -} diff --git a/src/Common/src/Common/Converter/StringToBooleanConverter.cs b/src/Common/src/Common/Converter/StringToBooleanConverter.cs deleted file mode 100644 index d3c5416334..0000000000 --- a/src/Common/src/Common/Converter/StringToBooleanConverter.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class StringToBooleanConverter : AbstractGenericConditionalConverter -{ - private static readonly ISet TrueValues = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "true", - "on", - "yes", - "1" - }; - - private static readonly ISet FalseValues = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "false", - "off", - "no", - "0" - }; - - public StringToBooleanConverter() - : base(null) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - Type targetCheck = ConversionUtils.GetNullableElementType(targetType); - return sourceType == typeof(string) && targetCheck == typeof(bool); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - string value = ((string)source).Trim(); - - if (string.IsNullOrEmpty(value)) - { - return null; - } - - if (TrueValues.Contains(value)) - { - return true; - } - - if (FalseValues.Contains(value)) - { - return false; - } - - throw new ArgumentException($"Invalid boolean value '{source}'.", nameof(source)); - } -} diff --git a/src/Common/src/Common/Converter/StringToCharacterConverter.cs b/src/Common/src/Common/Converter/StringToCharacterConverter.cs deleted file mode 100644 index 1ceee162d3..0000000000 --- a/src/Common/src/Common/Converter/StringToCharacterConverter.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class StringToCharacterConverter : AbstractGenericConditionalConverter -{ - public StringToCharacterConverter() - : base(null) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - Type targetCheck = ConversionUtils.GetNullableElementType(targetType); - return sourceType == typeof(string) && targetCheck == typeof(char); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - string asString = source as string; - - if (string.IsNullOrEmpty(asString)) - { - return null; - } - - if (asString.Length > 1) - { - throw new ArgumentException( - $"Can only convert a [String] with length of 1 to a [Character]; string value '{source}' has length of {asString.Length}", nameof(source)); - } - - return asString[0]; - } -} diff --git a/src/Common/src/Common/Converter/StringToCollectionConverter.cs b/src/Common/src/Common/Converter/StringToCollectionConverter.cs deleted file mode 100644 index 8b77aa9fdc..0000000000 --- a/src/Common/src/Common/Converter/StringToCollectionConverter.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Converter; - -public class StringToCollectionConverter : AbstractToCollectionConverter -{ - private readonly char[] _delimit = - { - ',' - }; - - public StringToCollectionConverter(IConversionService conversionService) - : base(GetConvertiblePairs(), conversionService) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - if (sourceType == typeof(string) && ConversionUtils.CanCreateCompatListFor(targetType)) - { - return ConversionUtils.CanConvertElements(sourceType, ConversionUtils.GetElementType(targetType), ConversionService); - } - - return false; - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return null; - } - - string sourceString = source as string; - string[] fields = sourceString.Split(_delimit, StringSplitOptions.RemoveEmptyEntries); - Type targetElementType = ConversionUtils.GetElementType(targetType); - - IList list = ConversionUtils.CreateCompatListFor(targetType); - - if (list == null) - { - throw new InvalidOperationException("Unable to create compatible list"); - } - - foreach (string sourceElement in fields) - { - object targetElement = ConversionService.Convert(sourceElement.Trim(), sourceType, targetElementType); - list.Add(targetElement); - } - - return list; - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(string), typeof(ICollection)), - (typeof(string), typeof(ICollection<>)), - (typeof(string), typeof(IEnumerable)), - (typeof(string), typeof(IEnumerable<>)) - }; - } -} diff --git a/src/Common/src/Common/Converter/StringToEncodingConverter.cs b/src/Common/src/Common/Converter/StringToEncodingConverter.cs deleted file mode 100644 index 3c711e5def..0000000000 --- a/src/Common/src/Common/Converter/StringToEncodingConverter.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Converter; - -public class StringToEncodingConverter : AbstractConverter -{ - public override Encoding Convert(string source) - { - return Encoding.GetEncoding(source); - } -} diff --git a/src/Common/src/Common/Converter/StringToEnumConverter.cs b/src/Common/src/Common/Converter/StringToEnumConverter.cs deleted file mode 100644 index ef461526fc..0000000000 --- a/src/Common/src/Common/Converter/StringToEnumConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class StringToEnumConverter : AbstractGenericConditionalConverter -{ - public StringToEnumConverter() - : base(null) - { - } - - public override bool Matches(Type sourceType, Type targetType) - { - Type targetCheck = ConversionUtils.GetNullableElementType(targetType); - return sourceType == typeof(string) && targetCheck.IsEnum; - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - string asString = source as string; - - if (string.IsNullOrEmpty(asString)) - { - return null; - } - - targetType = ConversionUtils.GetNullableElementType(targetType); - return Enum.Parse(targetType, asString.Trim(), true); - } -} diff --git a/src/Common/src/Common/Converter/StringToGuidConverter.cs b/src/Common/src/Common/Converter/StringToGuidConverter.cs deleted file mode 100644 index d72c2d5daf..0000000000 --- a/src/Common/src/Common/Converter/StringToGuidConverter.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class StringToGuidConverter : AbstractConverter -{ - public override Guid Convert(string source) - { - return Guid.Parse(source.Trim()); - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - return Convert((string)source); - } -} diff --git a/src/Common/src/Common/Converter/StringToNumberConverter.cs b/src/Common/src/Common/Converter/StringToNumberConverter.cs deleted file mode 100644 index 71a2efd47a..0000000000 --- a/src/Common/src/Common/Converter/StringToNumberConverter.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Converter; - -public class StringToNumberConverter : AbstractToNumberConverter -{ - public StringToNumberConverter() - : base(GetConvertiblePairs()) - { - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - string asString = source as string; - - if (string.IsNullOrEmpty(asString)) - { - return null; - } - - return base.Convert(source, sourceType, targetType); - } - - private static ISet<(Type SourceType, Type TargetType)> GetConvertiblePairs() - { - return new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(string), typeof(int)), - (typeof(string), typeof(float)), - (typeof(string), typeof(uint)), - (typeof(string), typeof(ulong)), - (typeof(string), typeof(long)), - (typeof(string), typeof(double)), - (typeof(string), typeof(short)), - (typeof(string), typeof(ushort)), - (typeof(string), typeof(decimal)), - (typeof(string), typeof(byte)), - (typeof(string), typeof(sbyte)) - }; - } -} diff --git a/src/Common/src/Common/Lifecycle/DefaultLifecycleProcessor.cs b/src/Common/src/Common/Lifecycle/DefaultLifecycleProcessor.cs deleted file mode 100644 index f8dfd5119a..0000000000 --- a/src/Common/src/Common/Lifecycle/DefaultLifecycleProcessor.cs +++ /dev/null @@ -1,277 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Common.Lifecycle; - -public class DefaultLifecycleProcessor : ILifecycleProcessor -{ - private readonly ILogger _logger; - private readonly IApplicationContext _context; - private List _lifecycleServices; - - public int TimeoutPerShutdownPhase { get; set; } = 30000; - - public bool IsRunning { get; set; } - - public DefaultLifecycleProcessor(IApplicationContext context, ILogger logger = null) - { - _context = context; - _logger = logger; - } - - public async Task StartAsync() - { - BuildServicesList(); - await StartServicesAsync(false); - IsRunning = true; - } - - public async Task StopAsync() - { - await StopServicesAsync(); - IsRunning = false; - } - - public async Task OnRefreshAsync() - { - BuildServicesList(); - await StartServicesAsync(true); - IsRunning = true; - } - - public async Task OnCloseAsync() - { - await StopAsync(); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - internal static int GetPhase(ILifecycle bean) - { - return bean is IPhased phased ? phased.Phase : 0; - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - OnCloseAsync().GetAwaiter().GetResult(); - } - } - - private async Task StartServicesAsync(bool autoStartupOnly) - { - var phases = new Dictionary(); - - foreach (ILifecycle service in _lifecycleServices) - { - if (!autoStartupOnly || (service is ISmartLifecycle lifecycle && lifecycle.IsAutoStartup)) - { - int phase = GetPhase(service); - phases.TryGetValue(phase, out LifecycleGroup group); - - if (group == null) - { - group = new LifecycleGroup(TimeoutPerShutdownPhase, autoStartupOnly, _logger); - phases.Add(phase, group); - } - - group.Add(service); - } - } - - if (phases.Count > 0) - { - var keys = new List(phases.Keys); - keys.Sort(); - - foreach (int key in keys) - { - await phases[key].StartAsync(); - } - } - } - - private async Task StopServicesAsync() - { - var phases = new Dictionary(); - - foreach (ILifecycle service in _lifecycleServices) - { - int phase = GetPhase(service); - phases.TryGetValue(phase, out LifecycleGroup group); - - if (group == null) - { - group = new LifecycleGroup(TimeoutPerShutdownPhase, false, _logger); - phases.Add(phase, group); - } - - group.Add(service); - } - - if (phases.Count > 0) - { - var keys = new List(phases.Keys); - keys.Sort(); - keys.Reverse(); - - foreach (int key in keys) - { - await phases[key].StopAsync(); - } - } - } - - private void BuildServicesList() - { - if (_lifecycleServices == null) - { - List lifeCycles = _context.GetServices().ToList(); - IEnumerable smartCycles = _context.GetServices(); - - foreach (ISmartLifecycle smart in smartCycles) - { - if (!lifeCycles.Contains(smart)) - { - lifeCycles.Add(smart); - } - } - - _lifecycleServices = lifeCycles; - } - } - -#pragma warning disable S1210 // "Equals" and the comparison operators should be overridden when implementing "IComparable" - private sealed class LifecycleGroupMember : IComparable -#pragma warning restore S1210 // "Equals" and the comparison operators should be overridden when implementing "IComparable" - { - public ILifecycle Bean { get; } - - public LifecycleGroupMember(ILifecycle bean) - { - Bean = bean; - } - - public int CompareTo(LifecycleGroupMember other) - { - int thisPhase = GetPhase(Bean); - int otherPhase = GetPhase(other.Bean); - return thisPhase.CompareTo(otherPhase); - } - } - - private sealed class LifecycleGroup - { - private readonly int _timeout; - - private readonly bool _autoStartupOnly; - - private readonly List _members = new(); - - private readonly ILogger _logger; - - public LifecycleGroup(int timeout, bool autoStartupOnly, ILogger logger = null) - { - _timeout = timeout; - _autoStartupOnly = autoStartupOnly; - _logger = logger; - } - - public void Add(ILifecycle bean) - { - _members.Add(new LifecycleGroupMember(bean)); - } - - public async Task StartAsync() - { - if (_members.Count <= 0) - { - return; - } - - _members.Sort(); - - foreach (LifecycleGroupMember member in _members) - { - await DoStartAsync(member.Bean); - } - } - - public Task StopAsync() - { - if (_members.Count <= 0) - { - return Task.CompletedTask; - } - - _members.Sort(); - _members.Reverse(); - - var tasks = new List(); - - foreach (LifecycleGroupMember member in _members) - { - tasks.Add(DoStopAsync(member.Bean)); - } - - try - { - Task.WaitAll(tasks.ToArray(), _timeout); - } - catch (Exception e) - { - _logger?.LogError(e, "Exception waiting for lifecycle tasks to stop"); - } - - foreach (Task task in tasks) - { - if (!task.IsCompleted) - { - _logger?.LogWarning("Not all lifecycle tasks completed"); - break; - } - } - - return Task.CompletedTask; - } - - private async Task DoStartAsync(ILifecycle bean) - { - if (bean is { IsRunning: false }) - { - bool canStart = !_autoStartupOnly || bean is not ISmartLifecycle lifecycle || lifecycle.IsAutoStartup; - - if (canStart) - { - try - { - await bean.StartAsync(); - } - catch (Exception ex) - { - throw new LifecycleException($"Failed to start bean(service) '{bean}'", ex); - } - } - } - } - - private Task DoStopAsync(ILifecycle bean) - { - if (bean != null && bean.IsRunning) - { - return bean.StopAsync(); - } - - return Task.CompletedTask; - } - } -} diff --git a/src/Common/src/Common/Lifecycle/LifecycleException.cs b/src/Common/src/Common/Lifecycle/LifecycleException.cs deleted file mode 100644 index 36af47a2f9..0000000000 --- a/src/Common/src/Common/Lifecycle/LifecycleException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Lifecycle; - -public class LifecycleException : Exception -{ - public LifecycleException(string message) - : base(message) - { - } - - public LifecycleException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Order/AbstractOrdered.cs b/src/Common/src/Common/Order/AbstractOrdered.cs deleted file mode 100644 index 9c288b4884..0000000000 --- a/src/Common/src/Common/Order/AbstractOrdered.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Order; - -public class AbstractOrdered : IOrdered -{ - public const int HighestPrecedence = int.MinValue; - public const int LowestPrecedence = int.MaxValue; - - public int Order { get; } - - public AbstractOrdered() - { - Order = 0; - } - - public AbstractOrdered(int order) - { - Order = order; - } -} diff --git a/src/Common/src/Common/Order/OrderComparer.cs b/src/Common/src/Common/Order/OrderComparer.cs deleted file mode 100644 index 3193a3ac78..0000000000 --- a/src/Common/src/Common/Order/OrderComparer.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Order; - -public class OrderComparer : IComparer -{ - public static OrderComparer Instance { get; } = new(); - - public int Compare(IOrdered o1, IOrdered o2) - { - bool p1 = o1 is IPriorityOrdered; - bool p2 = o2 is IPriorityOrdered; - - if (p1 && !p2) - { - return -1; - } - - if (p2 && !p1) - { - return 1; - } - - int i1 = GetOrder(o1); - int i2 = GetOrder(o2); - - return GetOrder(i1, i2); - } - - protected int GetOrder(int i1, int i2) - { -#pragma warning disable S3358 // Ternary operators should not be nested - return i1 < i2 ? -1 : i1 == i2 ? 0 : 1; -#pragma warning restore S3358 // Ternary operators should not be nested - } - - protected int GetOrder(IOrdered o1) - { - if (o1 != null) - { - return o1.Order; - } - - return AbstractOrdered.LowestPrecedence; - } -} diff --git a/src/Common/src/Common/Properties/AssemblyInfo.cs b/src/Common/src/Common/Properties/AssemblyInfo.cs index 5a401f3320..06b169e731 100644 --- a/src/Common/src/Common/Properties/AssemblyInfo.cs +++ b/src/Common/src/Common/Properties/AssemblyInfo.cs @@ -4,11 +4,9 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Steeltoe.Common.Expression")] [assembly: InternalsVisibleTo("Steeltoe.Common.Test")] [assembly: InternalsVisibleTo("Steeltoe.Common.Hosting")] [assembly: InternalsVisibleTo("Steeltoe.Common.Hosting.Test")] [assembly: InternalsVisibleTo("Steeltoe.Management.Tracing")] [assembly: InternalsVisibleTo("Steeltoe.Management.Tracing.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Messaging")] [assembly: InternalsVisibleTo("Steeltoe.Management.Endpoint")] diff --git a/src/Common/src/Common/Retry/RetryContext.cs b/src/Common/src/Common/Retry/RetryContext.cs deleted file mode 100644 index abe6f2826f..0000000000 --- a/src/Common/src/Common/Retry/RetryContext.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Common.Retry; - -public class RetryContext : AbstractAttributeAccessor, IRetryContext -{ - private const string LastExceptionName = "RetryContext.LastException"; - private const string RetryCountName = "RetryContext.RetryCount"; - private const string RetryParentName = "RetryContext.RetryParent"; - - public Exception LastException - { - get => (Exception)GetAttribute(LastExceptionName); - set - { - if (value == null && HasAttribute(LastExceptionName)) - { - RemoveAttribute(LastExceptionName); - } - else - { - SetAttribute(LastExceptionName, value); - } - } - } - - public int RetryCount - { - get - { - int? result = (int?)GetAttribute(RetryCountName); - - if (result == null) - { - return 0; - } - - return result.Value; - } - set => SetAttribute(RetryCountName, value); - } - - public IRetryContext Parent - { - get => (IRetryContext)GetAttribute(RetryParentName); - set - { - if (value == null && HasAttribute(RetryParentName)) - { - RemoveAttribute(RetryParentName); - } - else - { - SetAttribute(RetryParentName, value); - } - } - } - - public override string ToString() - { - return $"LastException: {LastException?.Message}, RetryCount: {RetryCount}, RetryParent: {Parent}"; - } -} diff --git a/src/Common/src/Common/Retry/RetryException.cs b/src/Common/src/Common/Retry/RetryException.cs deleted file mode 100644 index 59cce217a3..0000000000 --- a/src/Common/src/Common/Retry/RetryException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Retry; - -public class RetryException : Exception -{ - public RetryException(string message) - : base(message) - { - } - - public RetryException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Retry/RetrySynchronizationManager.cs b/src/Common/src/Common/Retry/RetrySynchronizationManager.cs deleted file mode 100644 index 2801378be1..0000000000 --- a/src/Common/src/Common/Retry/RetrySynchronizationManager.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Retry; - -public static class RetrySynchronizationManager -{ - private static readonly AsyncLocal Context = new(); - - public static IRetryContext GetContext() - { - return Context.Value; - } - - public static IRetryContext Register(IRetryContext context) - { - IRetryContext oldContext = GetContext(); - Context.Value = context; - return oldContext; - } - - public static IRetryContext Clear() - { - IRetryContext value = GetContext(); - Context.Value = value?.Parent; - return value; - } -} diff --git a/src/Common/src/Common/Retry/RetryTemplate.cs b/src/Common/src/Common/Retry/RetryTemplate.cs deleted file mode 100644 index b19f1690b7..0000000000 --- a/src/Common/src/Common/Retry/RetryTemplate.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Retry; - -public abstract class RetryTemplate : IRetryOperation -{ - protected List listeners = new(); - - public void RegisterListener(IRetryListener listener) - { - listeners.Add(listener); - } - - public abstract T Execute(Func retryCallback); - - public abstract T Execute(Func retryCallback, IRecoveryCallback recoveryCallback); - - public abstract void Execute(Action retryCallback); - - public abstract void Execute(Action retryCallback, IRecoveryCallback recoveryCallback); - - public abstract T Execute(Func retryCallback, Func recoveryCallback); - - public abstract void Execute(Action retryCallback, Action recoveryCallback); -} diff --git a/src/Common/src/Common/Retry/TerminatedRetryException.cs b/src/Common/src/Common/Retry/TerminatedRetryException.cs deleted file mode 100644 index 4e4992ee77..0000000000 --- a/src/Common/src/Common/Retry/TerminatedRetryException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Retry; - -public class TerminatedRetryException : RetryException -{ - public TerminatedRetryException(string message) - : base(message) - { - } - - public TerminatedRetryException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Transaction/AbstractPlatformTransactionManager.cs b/src/Common/src/Common/Transaction/AbstractPlatformTransactionManager.cs deleted file mode 100644 index d07e858bc2..0000000000 --- a/src/Common/src/Common/Transaction/AbstractPlatformTransactionManager.cs +++ /dev/null @@ -1,719 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Common.Transaction; - -public abstract class AbstractPlatformTransactionManager : IPlatformTransactionManager -{ - public const int SynchronizationAlways = 0; - public const int SynchronizationOnActualTransaction = 1; - public const int SynchronizationNever = 2; - - protected readonly ILogger Logger; - private int _defaultTimeout; - - protected virtual bool ShouldCommitOnGlobalRollbackOnly => false; - - protected virtual bool UseSavepointForNestedTransaction => true; - - public virtual int TransactionSynchronization { get; set; } - - public virtual int DefaultTimeout - { - get => _defaultTimeout; - set - { - if (value < AbstractTransactionDefinition.TimeoutDefault) - { - throw new ArgumentException("Invalid timeout value.", nameof(value)); - } - - _defaultTimeout = value; - } - } - - public virtual bool NestedTransactionAllowed { get; set; } - - public virtual bool ValidateExistingTransaction { get; set; } - - public virtual bool GlobalRollbackOnParticipationFailure { get; set; } - - public virtual bool FailEarlyOnGlobalRollbackOnly { get; set; } - - public virtual bool RollbackOnCommitFailure { get; set; } - - protected AbstractPlatformTransactionManager(ILogger logger = null) - { - Logger = logger; - } - - public virtual ITransactionStatus GetTransaction(ITransactionDefinition definition) - { - // Use defaults if no transaction definition given. - ITransactionDefinition def = definition ?? AbstractTransactionDefinition.WithDefaults; - - object transaction = DoGetTransaction(); - - if (IsExistingTransaction(transaction)) - { - // Existing transaction found -> check propagation behavior to find out how to behave. - return HandleExistingTransaction(def, transaction); - } - - // Check definition settings for new transaction. - if (def.Timeout < AbstractTransactionDefinition.TimeoutDefault) - { - throw new InvalidTimeoutException("Invalid transaction timeout", def.Timeout); - } - - // No existing transaction found -> check propagation behavior to find out how to proceed. - if (def.PropagationBehavior == AbstractTransactionDefinition.PropagationMandatory) - { - throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'"); - } - - if (def.PropagationBehavior == AbstractTransactionDefinition.PropagationRequired || - def.PropagationBehavior == AbstractTransactionDefinition.PropagationRequiresNew || - def.PropagationBehavior == AbstractTransactionDefinition.PropagationNested) - { - SuspendedResourcesHolder suspendedResources = Suspend(null); - Logger?.LogDebug("Creating new transaction with name [{name}] with {def}", def.Name, def); - - try - { - bool newSynchronization = TransactionSynchronization != SynchronizationNever; - DefaultTransactionStatus status = NewTransactionStatus(def, transaction, true, newSynchronization, suspendedResources); - DoBegin(transaction, def); - PrepareSynchronization(status, def); - return status; - } - catch (Exception) - { - Resume(null, suspendedResources); - throw; - } - } - - // Create "empty" transaction: no actual transaction, but potentially synchronization. - if (def.IsolationLevel != AbstractTransactionDefinition.IsolationDefault) - { - Logger?.LogWarning("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: {def}", def); - } - - bool isSynchronizationAlways = TransactionSynchronization == SynchronizationAlways; - return PrepareTransactionStatus(def, null, true, isSynchronizationAlways, null); - } - - public virtual void Commit(ITransactionStatus status) - { - if (status.IsCompleted) - { - throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction"); - } - - var defStatus = (DefaultTransactionStatus)status; - - if (defStatus.IsLocalRollbackOnly) - { - Logger?.LogDebug("Transactional code has requested rollback"); - ProcessRollback(defStatus, false); - return; - } - - if (!ShouldCommitOnGlobalRollbackOnly && defStatus.IsGlobalRollbackOnly) - { - Logger?.LogDebug("Global transaction is marked as rollback-only but transactional code requested commit"); - ProcessRollback(defStatus, true); - return; - } - - ProcessCommit(defStatus); - } - - public virtual void Rollback(ITransactionStatus status) - { - if (status.IsCompleted) - { - throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction"); - } - - var defStatus = (DefaultTransactionStatus)status; - ProcessRollback(defStatus, false); - } - - protected virtual DefaultTransactionStatus PrepareTransactionStatus(ITransactionDefinition definition, object transaction, bool newTransaction, - bool newSynchronization, object suspendedResources) - { - DefaultTransactionStatus status = NewTransactionStatus(definition, transaction, newTransaction, newSynchronization, suspendedResources); - PrepareSynchronization(status, definition); - return status; - } - - protected virtual DefaultTransactionStatus NewTransactionStatus(ITransactionDefinition definition, object transaction, bool newTransaction, - bool newSynchronization, object suspendedResources) - { - bool actualNewSynchronization = newSynchronization && !TransactionSynchronizationManager.IsSynchronizationActive(); - return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization, definition.IsReadOnly, suspendedResources, Logger); - } - - protected virtual void PrepareSynchronization(DefaultTransactionStatus status, ITransactionDefinition definition) - { - if (status.IsNewSynchronization) - { - TransactionSynchronizationManager.SetActualTransactionActive(status.HasTransaction); - - TransactionSynchronizationManager.SetCurrentTransactionIsolationLevel(definition.IsolationLevel != AbstractTransactionDefinition.IsolationDefault - ? definition.IsolationLevel - : null); - - TransactionSynchronizationManager.SetCurrentTransactionReadOnly(definition.IsReadOnly); - TransactionSynchronizationManager.SetCurrentTransactionName(definition.Name); - TransactionSynchronizationManager.InitSynchronization(); - } - } - - protected virtual int DetermineTimeout(ITransactionDefinition definition) - { - if (definition.Timeout != AbstractTransactionDefinition.TimeoutDefault) - { - return definition.Timeout; - } - - return DefaultTimeout; - } - - protected virtual SuspendedResourcesHolder Suspend(object transaction) - { - if (TransactionSynchronizationManager.IsSynchronizationActive()) - { - List suspendedSynchronizations = DoSuspendSynchronization(); - - try - { - object suspendedResources = null; - - if (transaction != null) - { - suspendedResources = DoSuspend(transaction); - } - - string name = TransactionSynchronizationManager.GetCurrentTransactionName(); - TransactionSynchronizationManager.SetCurrentTransactionName(null); - bool readOnly = TransactionSynchronizationManager.IsCurrentTransactionReadOnly(); - TransactionSynchronizationManager.SetCurrentTransactionReadOnly(false); - int? isolationLevel = TransactionSynchronizationManager.GetCurrentTransactionIsolationLevel(); - TransactionSynchronizationManager.SetCurrentTransactionIsolationLevel(null); - bool wasActive = TransactionSynchronizationManager.IsActualTransactionActive(); - TransactionSynchronizationManager.SetActualTransactionActive(false); - return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive); - } - catch (Exception) - { - // doSuspend failed - original transaction is still active... - DoResumeSynchronization(suspendedSynchronizations); - throw; - } - } - - if (transaction != null) - { - // Transaction active but no synchronization active. - object suspendedResources = DoSuspend(transaction); - return new SuspendedResourcesHolder(suspendedResources); - } - - // Neither transaction nor synchronization active. - return null; - } - - protected virtual void Resume(object transaction, SuspendedResourcesHolder resourcesHolder) - { - if (resourcesHolder != null) - { - object suspendedResources = resourcesHolder.SuspendedResources; - - if (suspendedResources != null) - { - DoResume(transaction, suspendedResources); - } - - List suspendedSynchronizations = resourcesHolder.SuspendedSynchronizations; - - if (suspendedSynchronizations != null) - { - TransactionSynchronizationManager.SetActualTransactionActive(resourcesHolder.WasActive); - TransactionSynchronizationManager.SetCurrentTransactionIsolationLevel(resourcesHolder.IsolationLevel); - TransactionSynchronizationManager.SetCurrentTransactionReadOnly(resourcesHolder.ReadOnly); - TransactionSynchronizationManager.SetCurrentTransactionName(resourcesHolder.Name); - DoResumeSynchronization(suspendedSynchronizations); - } - } - } - - protected virtual void TriggerBeforeCommit(DefaultTransactionStatus status) - { - if (status.IsNewSynchronization) - { - Logger?.LogTrace("Triggering beforeCommit synchronization"); - TransactionSynchronizationUtils.TriggerBeforeCommit(status.IsReadOnly); - } - } - - protected virtual void TriggerBeforeCompletion(DefaultTransactionStatus status) - { - if (status.IsNewSynchronization) - { - Logger?.LogTrace("Triggering beforeCompletion synchronization"); - TransactionSynchronizationUtils.TriggerBeforeCompletion(Logger); - } - } - - protected virtual void InvokeAfterCompletion(List synchronizations, int completionStatus) - { - TransactionSynchronizationUtils.InvokeAfterCompletion(synchronizations, completionStatus); - } - - protected abstract object DoGetTransaction(); - - protected virtual bool IsExistingTransaction(object transaction) - { - return false; - } - - protected abstract void DoBegin(object transaction, ITransactionDefinition definition); - - protected virtual object DoSuspend(object transaction) - { - throw new TransactionSuspensionNotSupportedException($"Transaction manager [{GetType().Name}] does not support transaction suspension"); - } - - protected virtual void DoResume(object transaction, object suspendedResources) - { - throw new TransactionSuspensionNotSupportedException($"Transaction manager [{GetType().Name}] does not support transaction suspension"); - } - - protected virtual void PrepareForCommit(DefaultTransactionStatus status) - { - } - - protected virtual void DoSetRollbackOnly(DefaultTransactionStatus status) - { - throw new IllegalTransactionStateException("Participating in existing transactions is not supported - when 'isExistingTransaction' " + - "returns true, appropriate 'doSetRollbackOnly' behavior must be provided"); - } - - protected virtual void RegisterAfterCompletionWithExistingTransaction(object transaction, List synchronizations) - { - Logger?.LogDebug("Cannot register Spring after-completion synchronization with existing transaction - " + - "processing Spring after-completion callbacks immediately, with outcome status 'unknown'"); - - InvokeAfterCompletion(synchronizations, AbstractTransactionSynchronization.StatusUnknown); - } - - protected virtual void DoCleanupAfterCompletion(object transaction) - { - } - - protected abstract void DoCommit(DefaultTransactionStatus status); - - protected abstract void DoRollback(DefaultTransactionStatus status); - - private void CleanupAfterCompletion(DefaultTransactionStatus status) - { - status.IsCompleted = true; - - if (status.IsNewSynchronization) - { - TransactionSynchronizationManager.Clear(); - } - - if (status.IsNewTransaction) - { - DoCleanupAfterCompletion(status.Transaction); - } - - if (status.SuspendedResources != null) - { - Logger?.LogDebug("Resuming suspended transaction after completion of inner transaction"); - object transaction = status.HasTransaction ? status.Transaction : null; - Resume(transaction, (SuspendedResourcesHolder)status.SuspendedResources); - } - } - - private void TriggerAfterCommit(DefaultTransactionStatus status) - { - if (status.IsNewSynchronization) - { - Logger?.LogTrace("Triggering afterCommit synchronization"); - TransactionSynchronizationUtils.TriggerAfterCommit(); - } - } - - private void TriggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) - { - if (status.IsNewSynchronization) - { - List synchronizations = TransactionSynchronizationManager.GetSynchronizations(); - TransactionSynchronizationManager.ClearSynchronization(); - - if (!status.HasTransaction || status.IsNewTransaction) - { - Logger?.LogTrace("Triggering afterCompletion synchronization"); - - // No transaction or new transaction for the current scope -> - // invoke the afterCompletion callbacks immediately - InvokeAfterCompletion(synchronizations, completionStatus); - } - else if (synchronizations.Count > 0) - { - // Existing transaction that we participate in, controlled outside - // of the scope of this Spring transaction manager -> try to register - // an afterCompletion callback with the existing (JTA) transaction. - RegisterAfterCompletionWithExistingTransaction(status.Transaction, synchronizations); - } - } - } - - private void DoRollbackOnCommitException(DefaultTransactionStatus status, Exception exception) - { - try - { - if (status.IsNewTransaction) - { - Logger?.LogDebug(exception, "Initiating transaction rollback after commit exception"); - DoRollback(status); - } - else if (status.HasTransaction && GlobalRollbackOnParticipationFailure) - { - Logger?.LogDebug(exception, "Marking existing transaction as rollback-only after commit exception"); - DoSetRollbackOnly(status); - } - } - catch (Exception ex) - { - Logger?.LogError(ex, "Commit exception overridden by rollback exception"); - TriggerAfterCompletion(status, AbstractTransactionSynchronization.StatusUnknown); - throw; - } - - TriggerAfterCompletion(status, AbstractTransactionSynchronization.StatusRolledBack); - } - - private void ProcessRollback(DefaultTransactionStatus status, bool unexpected) - { - try - { - bool unexpectedRollback = unexpected; - - try - { - TriggerBeforeCompletion(status); - - if (status.HasSavepoint) - { - Logger?.LogDebug("Rolling back transaction to savepoint"); - status.RollbackToHeldSavepoint(); - } - else if (status.IsNewTransaction) - { - Logger?.LogDebug("Initiating transaction rollback"); - DoRollback(status); - } - else - { - // Participating in larger transaction - if (status.HasTransaction) - { - if (status.IsLocalRollbackOnly || GlobalRollbackOnParticipationFailure) - { - Logger?.LogDebug("Participating transaction failed - marking existing transaction as rollback-only"); - DoSetRollbackOnly(status); - } - else - { - Logger?.LogDebug("Participating transaction failed - letting transaction originator decide on rollback"); - } - } - else - { - Logger?.LogDebug("Should roll back transaction but cannot - no transaction available"); - } - - // Unexpected rollback only matters here if we're asked to fail early - if (!FailEarlyOnGlobalRollbackOnly) - { - unexpectedRollback = false; - } - } - } - catch (Exception) - { - TriggerAfterCompletion(status, AbstractTransactionSynchronization.StatusUnknown); - throw; - } - - TriggerAfterCompletion(status, AbstractTransactionSynchronization.StatusRolledBack); - - // Raise UnexpectedRollbackException if we had a global rollback-only marker - if (unexpectedRollback) - { - throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only"); - } - } - finally - { - CleanupAfterCompletion(status); - } - } - - private void ProcessCommit(DefaultTransactionStatus status) - { - try - { - bool beforeCompletionInvoked = false; - - try - { - bool unexpectedRollback = false; - PrepareForCommit(status); - TriggerBeforeCommit(status); - TriggerBeforeCompletion(status); - beforeCompletionInvoked = true; - - if (status.HasSavepoint) - { - Logger?.LogDebug("Releasing transaction savepoint"); - unexpectedRollback = status.IsGlobalRollbackOnly; - status.ReleaseHeldSavepoint(); - } - else if (status.IsNewTransaction) - { - Logger?.LogDebug("Initiating transaction commit"); - unexpectedRollback = status.IsGlobalRollbackOnly; - DoCommit(status); - } - else if (FailEarlyOnGlobalRollbackOnly) - { - unexpectedRollback = status.IsGlobalRollbackOnly; - } - - // Throw UnexpectedRollbackException if we have a global rollback-only - // marker but still didn't get a corresponding exception from commit. - if (unexpectedRollback) - { - throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only"); - } - } - catch (UnexpectedRollbackException) - { - // can only be caused by doCommit - TriggerAfterCompletion(status, AbstractTransactionSynchronization.StatusRolledBack); - throw; - } - catch (TransactionException ex) - { - // can only be caused by doCommit - if (RollbackOnCommitFailure) - { - DoRollbackOnCommitException(status, ex); - } - else - { - TriggerAfterCompletion(status, AbstractTransactionSynchronization.StatusUnknown); - } - - throw; - } - catch (Exception ex) - { - if (!beforeCompletionInvoked) - { - TriggerBeforeCompletion(status); - } - - DoRollbackOnCommitException(status, ex); - throw; - } - - // Trigger afterCommit callbacks, with an exception thrown there - // propagated to callers but the transaction still considered as committed. - try - { - TriggerAfterCommit(status); - } - finally - { - TriggerAfterCompletion(status, AbstractTransactionSynchronization.StatusCommitted); - } - } - finally - { - CleanupAfterCompletion(status); - } - } - - private void DoResumeSynchronization(List suspendedSynchronizations) - { - TransactionSynchronizationManager.InitSynchronization(); - - foreach (ITransactionSynchronization synchronization in suspendedSynchronizations) - { - synchronization.Resume(); - TransactionSynchronizationManager.RegisterSynchronization(synchronization); - } - } - - private List DoSuspendSynchronization() - { - List suspendedSynchronizations = TransactionSynchronizationManager.GetSynchronizations(); - - foreach (ITransactionSynchronization synchronization in suspendedSynchronizations) - { - synchronization.Suspend(); - } - - TransactionSynchronizationManager.ClearSynchronization(); - return suspendedSynchronizations; - } - - private void ResumeAfterBeginException(object transaction, SuspendedResourcesHolder suspendedResources, Exception beginEx) - { - try - { - Resume(transaction, suspendedResources); - } - catch (Exception) - { - const string exMessage = "Inner transaction begin exception overridden by outer transaction resume exception"; - Logger?.LogError(beginEx, exMessage); - throw; - } - } - - private ITransactionStatus HandleExistingTransaction(ITransactionDefinition definition, object transaction) - { - if (definition.PropagationBehavior == AbstractTransactionDefinition.PropagationNever) - { - throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'"); - } - - if (definition.PropagationBehavior == AbstractTransactionDefinition.PropagationNotSupported) - { - Logger?.LogDebug("Suspending current transaction"); - SuspendedResourcesHolder suspendedResources = Suspend(transaction); - return PrepareTransactionStatus(definition, null, false, TransactionSynchronization == SynchronizationAlways, suspendedResources); - } - - if (definition.PropagationBehavior == AbstractTransactionDefinition.PropagationRequiresNew) - { - Logger?.LogDebug("Suspending current transaction, creating new transaction with name [{name}]", definition.Name); - SuspendedResourcesHolder suspendedResources = Suspend(transaction); - - try - { - DefaultTransactionStatus status = NewTransactionStatus(definition, transaction, true, TransactionSynchronization != SynchronizationNever, - suspendedResources); - - DoBegin(transaction, definition); - PrepareSynchronization(status, definition); - return status; - } - catch (Exception ex) - { - ResumeAfterBeginException(transaction, suspendedResources, ex); - throw; - } - } - - if (definition.PropagationBehavior == AbstractTransactionDefinition.PropagationNested) - { - if (!NestedTransactionAllowed) - { - throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - " + - "specify 'nestedTransactionAllowed' property with value 'true'"); - } - - Logger?.LogDebug("Creating nested transaction with name [{name}]", definition.Name); - - if (UseSavepointForNestedTransaction) - { - // Create savepoint within existing Spring-managed transaction, - // through the SavepointManager API implemented by TransactionStatus. - // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization. - DefaultTransactionStatus status = PrepareTransactionStatus(definition, transaction, false, false, null); - status.CreateAndHoldSavepoint(); - return status; - } - else - { - // Nested transaction through nested begin and commit/rollback calls. - // Usually only for JTA: Spring synchronization might get activated here - // in case of a pre-existing JTA transaction. - DefaultTransactionStatus status = NewTransactionStatus(definition, transaction, true, TransactionSynchronization != SynchronizationNever, null); - DoBegin(transaction, definition); - PrepareSynchronization(status, definition); - return status; - } - } - - // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. - Logger?.LogDebug("Participating in existing transaction"); - - if (ValidateExistingTransaction) - { - if (definition.IsolationLevel != AbstractTransactionDefinition.IsolationDefault) - { - int? currentIsolationLevel = TransactionSynchronizationManager.GetCurrentTransactionIsolationLevel(); - - if (currentIsolationLevel == null || currentIsolationLevel != definition.IsolationLevel) - { - throw new IllegalTransactionStateException( - $"Participating transaction with definition [{definition}] specifies isolation level which is incompatible with existing transaction: "); - } - } - - if (!definition.IsReadOnly && TransactionSynchronizationManager.IsCurrentTransactionReadOnly()) - { - throw new IllegalTransactionStateException( - $"Participating transaction with definition [{definition}] is not marked as read-only but existing transaction is"); - } - } - - bool newSynchronization = TransactionSynchronization != SynchronizationNever; - return PrepareTransactionStatus(definition, transaction, false, newSynchronization, null); - } - - protected class SuspendedResourcesHolder - { - public object SuspendedResources { get; } - - public List SuspendedSynchronizations { get; } - - public string Name { get; } - - public bool ReadOnly { get; } - - public int? IsolationLevel { get; } - - public bool WasActive { get; } - - public SuspendedResourcesHolder(object suspendedResources) - { - SuspendedResources = suspendedResources; - } - - public SuspendedResourcesHolder(object suspendedResources, List suspendedSynchronizations, string name, bool readOnly, - int? isolationLevel, bool wasActive) - { - SuspendedResources = suspendedResources; - SuspendedSynchronizations = suspendedSynchronizations; - Name = name; - ReadOnly = readOnly; - IsolationLevel = isolationLevel; - WasActive = wasActive; - } - } -} diff --git a/src/Common/src/Common/Transaction/AbstractTransactionDefinition.cs b/src/Common/src/Common/Transaction/AbstractTransactionDefinition.cs deleted file mode 100644 index cbba66dd72..0000000000 --- a/src/Common/src/Common/Transaction/AbstractTransactionDefinition.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public abstract class AbstractTransactionDefinition : ITransactionDefinition -{ - public const int PropagationRequired = 0; - public const int PropagationSupports = 1; - public const int PropagationMandatory = 2; - public const int PropagationRequiresNew = 3; - public const int PropagationNotSupported = 4; - public const int PropagationNever = 5; - public const int PropagationNested = 6; - public const int IsolationDefault = -1; - public const int IsolationReadUncommitted = 1; - public const int IsolationReadCommitted = 2; - public const int IsolationRepeatableRead = 4; - public const int IsolationSerializable = 8; - public const int TimeoutDefault = -1; - - public static ITransactionDefinition WithDefaults => StaticTransactionDefinition.Instance; - - public virtual int PropagationBehavior { get; set; } = PropagationRequired; - - public virtual int IsolationLevel { get; set; } = IsolationDefault; - - public virtual int Timeout { get; set; } = TimeoutDefault; - - public virtual bool IsReadOnly { get; set; } - - public virtual string Name { get; set; } -} diff --git a/src/Common/src/Common/Transaction/AbstractTransactionOperations.cs b/src/Common/src/Common/Transaction/AbstractTransactionOperations.cs deleted file mode 100644 index abd9aa4ac4..0000000000 --- a/src/Common/src/Common/Transaction/AbstractTransactionOperations.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public abstract class AbstractTransactionOperations : ITransactionOperations -{ - public static ITransactionOperations WithoutTransaction() - { - return WithoutTransactionOperations.Instance; - } - - public abstract T Execute(Func action); - - public virtual void ExecuteWithoutResult(Action action) - { - Execute(status => - { - action(status); - return null; - }); - } -} diff --git a/src/Common/src/Common/Transaction/AbstractTransactionStatus.cs b/src/Common/src/Common/Transaction/AbstractTransactionStatus.cs deleted file mode 100644 index 76bb611297..0000000000 --- a/src/Common/src/Common/Transaction/AbstractTransactionStatus.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public abstract class AbstractTransactionStatus : ITransactionStatus -{ - public abstract bool IsNewTransaction { get; } - - public virtual bool IsRollbackOnly => IsLocalRollbackOnly || IsGlobalRollbackOnly; - - public virtual bool IsLocalRollbackOnly { get; set; } - - public virtual bool IsGlobalRollbackOnly { get; set; } - - public virtual bool IsCompleted { get; set; } - - public virtual object Savepoint { get; set; } - - public virtual bool HasSavepoint => Savepoint != null; - - public virtual void SetRollbackOnly() - { - IsLocalRollbackOnly = true; - } - - public virtual void CreateAndHoldSavepoint() - { - Savepoint = GetSavepointManager().CreateSavepoint(); - } - - public virtual void RollbackToHeldSavepoint() - { - object savepoint = Savepoint; - - if (savepoint == null) - { - throw new TransactionUsageException("Cannot roll back to savepoint - no savepoint associated with current transaction"); - } - - GetSavepointManager().RollbackToSavepoint(savepoint); - GetSavepointManager().ReleaseSavepoint(savepoint); - Savepoint = null; - } - - public virtual void ReleaseHeldSavepoint() - { - object savepoint = Savepoint; - - if (savepoint == null) - { - throw new TransactionUsageException("Cannot release savepoint - no savepoint associated with current transaction"); - } - - GetSavepointManager().ReleaseSavepoint(savepoint); - Savepoint = null; - } - - public virtual object CreateSavepoint() - { - return GetSavepointManager().CreateSavepoint(); - } - - public virtual void RollbackToSavepoint(object savepoint) - { - GetSavepointManager().RollbackToSavepoint(savepoint); - } - - public virtual void ReleaseSavepoint(object savepoint) - { - GetSavepointManager().ReleaseSavepoint(savepoint); - } - - public virtual void Flush() - { - } - - protected virtual ISavepointManager GetSavepointManager() - { - throw new NestedTransactionNotSupportedException("This transaction does not support savepoints"); - } -} diff --git a/src/Common/src/Common/Transaction/AbstractTransactionSynchronization.cs b/src/Common/src/Common/Transaction/AbstractTransactionSynchronization.cs deleted file mode 100644 index 758920da80..0000000000 --- a/src/Common/src/Common/Transaction/AbstractTransactionSynchronization.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public abstract class AbstractTransactionSynchronization : ITransactionSynchronization -{ - public const int StatusCommitted = 0; - - public const int StatusRolledBack = 1; - - public const int StatusUnknown = 2; - - public abstract void AfterCommit(); - - public abstract void AfterCompletion(int status); - - public abstract void BeforeCommit(bool readOnly); - - public abstract void BeforeCompletion(); - - public abstract void Flush(); - - public abstract void Resume(); - - public abstract void Suspend(); -} diff --git a/src/Common/src/Common/Transaction/CannotCreateTransactionException.cs b/src/Common/src/Common/Transaction/CannotCreateTransactionException.cs deleted file mode 100644 index 7e0cf9da21..0000000000 --- a/src/Common/src/Common/Transaction/CannotCreateTransactionException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class CannotCreateTransactionException : TransactionException -{ - public CannotCreateTransactionException(string message) - : base(message) - { - } - - public CannotCreateTransactionException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Transaction/DefaultTransactionAttribute.cs b/src/Common/src/Common/Transaction/DefaultTransactionAttribute.cs deleted file mode 100644 index 7b26c297d9..0000000000 --- a/src/Common/src/Common/Transaction/DefaultTransactionAttribute.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Transaction; - -public class DefaultTransactionAttribute : DefaultTransactionDefinition, ITransactionAttribute -{ - public string Qualifier { get; } - - public string Descriptor { get; } - - public DefaultTransactionAttribute() - { - } - - public DefaultTransactionAttribute(ITransactionAttribute other) - : base(other) - { - } - - public DefaultTransactionAttribute(int propagationBehavior) - : base(propagationBehavior) - { - } - - public bool RollbackOn(Exception exception) - { - return exception switch - { - OutOfMemoryException => true, - InvalidProgramException => true, - AccessViolationException => true, - BadImageFormatException => true, - _ => false - }; - } - - protected StringBuilder GetAttributeDescription() - { - StringBuilder result = GetDefinitionDescription(); - - if (!string.IsNullOrEmpty(Qualifier)) - { - result.Append("; '").Append(Qualifier).Append('\''); - } - - return result; - } -} diff --git a/src/Common/src/Common/Transaction/DefaultTransactionDefinition.cs b/src/Common/src/Common/Transaction/DefaultTransactionDefinition.cs deleted file mode 100644 index 253b4a6a09..0000000000 --- a/src/Common/src/Common/Transaction/DefaultTransactionDefinition.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Transaction; - -public class DefaultTransactionDefinition : AbstractTransactionDefinition -{ - public const string PrefixPropagation = "PROPAGATION_"; - public const string PrefixIsolation = "ISOLATION_"; - public const string PrefixTimeout = "timeout_"; - public const string ReadOnlyMarker = "readOnly"; - - public override int PropagationBehavior { get; set; } - - public override int IsolationLevel { get; set; } - - public override int Timeout { get; set; } - - public override bool IsReadOnly { get; set; } - - public override string Name { get; set; } - - public DefaultTransactionDefinition() - { - } - - public DefaultTransactionDefinition(ITransactionDefinition other) - { - PropagationBehavior = other.PropagationBehavior; - IsolationLevel = other.IsolationLevel; - Timeout = other.Timeout; - IsReadOnly = other.IsReadOnly; - Name = other.Name; - } - - public DefaultTransactionDefinition(int propagationBehavior) - { - PropagationBehavior = propagationBehavior; - } - - public override bool Equals(object obj) - { - return ReferenceEquals(this, obj) || (obj is ITransactionDefinition && ToString() == obj.ToString()); - } - - public override int GetHashCode() - { - return ToString().GetHashCode(); - } - - public override string ToString() - { - return GetDefinitionDescription().ToString(); - } - - protected StringBuilder GetDefinitionDescription() - { - var result = new StringBuilder(); - result.Append(PropagationBehavior); - result.Append(','); - result.Append(IsolationLevel); - - if (Timeout != TimeoutDefault) - { - result.Append(','); - result.Append(PrefixTimeout).Append(Timeout); - } - - if (IsReadOnly) - { - result.Append(','); - result.Append(ReadOnlyMarker); - } - - return result; - } -} diff --git a/src/Common/src/Common/Transaction/DefaultTransactionStatus.cs b/src/Common/src/Common/Transaction/DefaultTransactionStatus.cs deleted file mode 100644 index 665315edc6..0000000000 --- a/src/Common/src/Common/Transaction/DefaultTransactionStatus.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Common.Transaction; - -public class DefaultTransactionStatus : AbstractTransactionStatus -{ - public object Transaction { get; } - - public bool HasTransaction => Transaction != null; - - public bool NewTransaction { get; } - - public bool IsNewSynchronization { get; } - - public bool IsReadOnly { get; } - - public object SuspendedResources { get; } - - public override bool IsNewTransaction => HasTransaction && NewTransaction; - - public bool IsTransactionSavepointManager => Transaction is ISavepointManager; - - public override bool IsGlobalRollbackOnly - { - get => Transaction is ISmartTransactionObject transactionObject && transactionObject.IsRollbackOnly; - set => base.IsGlobalRollbackOnly = value; - } - - public DefaultTransactionStatus(object transaction, bool newTransaction, bool newSynchronization, bool readOnly, object suspendedResources, ILogger logger) - { - Transaction = transaction; - NewTransaction = newTransaction; - IsNewSynchronization = newSynchronization; - IsReadOnly = readOnly; - SuspendedResources = suspendedResources; - } - - public override void Flush() - { - if (Transaction is ISmartTransactionObject transactionObject) - { - transactionObject.Flush(); - } - } - - protected override ISavepointManager GetSavepointManager() - { - object transaction = Transaction; - - if (transaction is not ISavepointManager savepointManager) - { - throw new NestedTransactionNotSupportedException($"Transaction object [{Transaction}] does not support savepoints"); - } - - return savepointManager; - } -} diff --git a/src/Common/src/Common/Transaction/IllegalTransactionStateException.cs b/src/Common/src/Common/Transaction/IllegalTransactionStateException.cs deleted file mode 100644 index 7ad3019b07..0000000000 --- a/src/Common/src/Common/Transaction/IllegalTransactionStateException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class IllegalTransactionStateException : TransactionUsageException -{ - public IllegalTransactionStateException(string message) - : base(message) - { - } - - public IllegalTransactionStateException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Transaction/InvalidIsolationLevelException.cs b/src/Common/src/Common/Transaction/InvalidIsolationLevelException.cs deleted file mode 100644 index 7f421c241a..0000000000 --- a/src/Common/src/Common/Transaction/InvalidIsolationLevelException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class InvalidIsolationLevelException : TransactionUsageException -{ - public InvalidIsolationLevelException(string message) - : base(message) - { - } -} diff --git a/src/Common/src/Common/Transaction/InvalidTimeoutException.cs b/src/Common/src/Common/Transaction/InvalidTimeoutException.cs deleted file mode 100644 index 64b02d71b2..0000000000 --- a/src/Common/src/Common/Transaction/InvalidTimeoutException.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class InvalidTimeoutException : TransactionUsageException -{ - public int Timeout { get; } - - public InvalidTimeoutException(string message, int timeout) - : base(message) - { - Timeout = timeout; - } -} diff --git a/src/Common/src/Common/Transaction/NestedTransactionNotSupportedException.cs b/src/Common/src/Common/Transaction/NestedTransactionNotSupportedException.cs deleted file mode 100644 index f5e3d00046..0000000000 --- a/src/Common/src/Common/Transaction/NestedTransactionNotSupportedException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class NestedTransactionNotSupportedException : CannotCreateTransactionException -{ - public NestedTransactionNotSupportedException(string message) - : base(message) - { - } - - public NestedTransactionNotSupportedException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Transaction/ResourceHolderSupport.cs b/src/Common/src/Common/Transaction/ResourceHolderSupport.cs deleted file mode 100644 index fa68a2bfa6..0000000000 --- a/src/Common/src/Common/Transaction/ResourceHolderSupport.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public abstract class ResourceHolderSupport : IResourceHolder -{ - private int _referenceCount; - - public bool SynchronizedWithTransaction { get; set; } - - public bool RollbackOnly { get; set; } - - public bool HasTimeout => Deadline.HasValue; - - public DateTime? Deadline { get; private set; } - - public bool IsOpen => _referenceCount > 0; - - public bool IsVoid { get; private set; } - - public int GetTimeToLiveInSeconds() - { - double diff = (double)GetTimeToLiveInMillis() / 1000; - int secs = (int)Math.Ceiling(diff); - CheckTransactionTimeout(secs <= 0); - return secs; - } - - public long GetTimeToLiveInMillis() - { - if (!Deadline.HasValue) - { - throw new InvalidOperationException("No timeout specified for this resource holder"); - } - - long timeToLive = (Deadline.Value.Ticks - DateTime.UtcNow.Ticks) / TimeSpan.TicksPerMillisecond; - CheckTransactionTimeout(timeToLive <= 0); - return timeToLive; - } - - public void SetTimeoutInSeconds(int seconds) - { - Deadline = DateTime.UtcNow + TimeSpan.FromSeconds(seconds); - } - - public void SetTimeoutInMillis(long milliseconds) - { - Deadline = DateTime.UtcNow + TimeSpan.FromMilliseconds(milliseconds); - } - - public void Requested() - { - _referenceCount++; - } - - public void Released() - { - _referenceCount--; - } - - public void Clear() - { - SynchronizedWithTransaction = false; - RollbackOnly = false; - Deadline = null; - } - - public void Reset() - { - Clear(); - _referenceCount = 0; - } - - public void Unbound() - { - IsVoid = true; - } - - private void CheckTransactionTimeout(bool deadlineReached) - { - if (deadlineReached) - { - RollbackOnly = true; - throw new TransactionTimedOutException($"Transaction timed out: deadline was {Deadline}"); - } - } -} diff --git a/src/Common/src/Common/Transaction/ResourceHolderSynchronization.cs b/src/Common/src/Common/Transaction/ResourceHolderSynchronization.cs deleted file mode 100644 index 7982ccc1ff..0000000000 --- a/src/Common/src/Common/Transaction/ResourceHolderSynchronization.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class ResourceHolderSynchronization : ITransactionSynchronization - where THolder : IResourceHolder -{ - private readonly THolder _resourceHolder; - - private readonly TKey _resourceKey; - - private volatile bool _holderActive; - - public ResourceHolderSynchronization(THolder resourceHolder, TKey resourceKey) - { - _resourceHolder = resourceHolder; - _resourceKey = resourceKey; - _holderActive = true; - } - - public virtual void Suspend() - { - if (_holderActive) - { - TransactionSynchronizationManager.UnbindResource(_resourceKey); - } - } - - public virtual void Resume() - { - if (_holderActive) - { - TransactionSynchronizationManager.BindResource(_resourceKey, _resourceHolder); - } - } - - public virtual void Flush() - { - FlushResource(_resourceHolder); - } - - public virtual void BeforeCommit(bool readOnly) - { - // Intentionally left empty. - } - - public virtual void BeforeCompletion() - { - if (ShouldUnbindAtCompletion()) - { - TransactionSynchronizationManager.UnbindResource(_resourceKey); - _holderActive = false; - - if (ShouldReleaseBeforeCompletion()) - { - ReleaseResource(_resourceHolder, _resourceKey); - } - } - } - - public virtual void AfterCommit() - { - if (!ShouldReleaseBeforeCompletion()) - { - ProcessResourceAfterCommit(_resourceHolder); - } - } - - public virtual void AfterCompletion(int status) - { - if (ShouldUnbindAtCompletion()) - { - bool releaseNecessary = false; - - if (_holderActive) - { - // The thread-bound resource holder might not be available anymore, - // since afterCompletion might get called from a different thread. - _holderActive = false; - TransactionSynchronizationManager.UnbindResourceIfPossible(_resourceKey); - _resourceHolder.Unbound(); - releaseNecessary = true; - } - else - { - releaseNecessary = ShouldReleaseAfterCompletion(_resourceHolder); - } - - if (releaseNecessary) - { - ReleaseResource(_resourceHolder, _resourceKey); - } - } - else - { - // Probably a pre-bound resource... - CleanupResource(_resourceHolder, _resourceKey, status == AbstractTransactionSynchronization.StatusCommitted); - } - - _resourceHolder.Reset(); - } - - protected virtual bool ShouldUnbindAtCompletion() - { - return true; - } - - protected virtual bool ShouldReleaseBeforeCompletion() - { - return true; - } - - protected virtual bool ShouldReleaseAfterCompletion(THolder resourceHolder) - { - return !ShouldReleaseBeforeCompletion(); - } - - protected virtual void FlushResource(THolder resourceHolder) - { - } - - protected virtual void ProcessResourceAfterCommit(THolder resourceHolder) - { - } - - protected virtual void ReleaseResource(THolder resourceHolder, TKey resourceKey) - { - } - - protected virtual void CleanupResource(THolder resourceHolder, TKey resourceKey, bool committed) - { - } -} diff --git a/src/Common/src/Common/Transaction/SimpleTransactionStatus.cs b/src/Common/src/Common/Transaction/SimpleTransactionStatus.cs deleted file mode 100644 index 909652b95a..0000000000 --- a/src/Common/src/Common/Transaction/SimpleTransactionStatus.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class SimpleTransactionStatus : AbstractTransactionStatus -{ - public override bool IsNewTransaction { get; } - - public SimpleTransactionStatus() - : this(true) - { - } - - public SimpleTransactionStatus(bool newTransaction) - { - IsNewTransaction = newTransaction; - } -} diff --git a/src/Common/src/Common/Transaction/StaticTransactionDefinition.cs b/src/Common/src/Common/Transaction/StaticTransactionDefinition.cs deleted file mode 100644 index f6fea97982..0000000000 --- a/src/Common/src/Common/Transaction/StaticTransactionDefinition.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -internal sealed class StaticTransactionDefinition : AbstractTransactionDefinition -{ - internal static readonly StaticTransactionDefinition Instance = new(); - - private StaticTransactionDefinition() - { - } -} diff --git a/src/Common/src/Common/Transaction/TransactionException.cs b/src/Common/src/Common/Transaction/TransactionException.cs deleted file mode 100644 index e4542d358f..0000000000 --- a/src/Common/src/Common/Transaction/TransactionException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public abstract class TransactionException : Exception -{ - protected TransactionException(string message) - : base(message) - { - } - - protected TransactionException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Transaction/TransactionSuspensionNotSupportedException.cs b/src/Common/src/Common/Transaction/TransactionSuspensionNotSupportedException.cs deleted file mode 100644 index d3211ac261..0000000000 --- a/src/Common/src/Common/Transaction/TransactionSuspensionNotSupportedException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class TransactionSuspensionNotSupportedException : CannotCreateTransactionException -{ - public TransactionSuspensionNotSupportedException(string message) - : base(message) - { - } - - public TransactionSuspensionNotSupportedException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Transaction/TransactionSynchronizationManager.cs b/src/Common/src/Common/Transaction/TransactionSynchronizationManager.cs deleted file mode 100644 index aaba4f5b9b..0000000000 --- a/src/Common/src/Common/Transaction/TransactionSynchronizationManager.cs +++ /dev/null @@ -1,279 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.ObjectModel; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Order; - -namespace Steeltoe.Common.Transaction; - -public static class TransactionSynchronizationManager -{ - private static readonly AsyncLocal> Resources = new(); - private static readonly AsyncLocal> Synchronizations = new(); - private static readonly AsyncLocal ActualTransactionActive = new(); - private static readonly AsyncLocal CurrentTransactionIsolationLevel = new(); - private static readonly AsyncLocal CurrentTransactionName = new(); - private static readonly AsyncLocal CurrentTransactionReadOnly = new(); - - private static readonly IDictionary EmptyDict = new Dictionary(); - private static readonly List EmptyList = new(); - - public static IDictionary GetResourceMap() - { - Dictionary resources = Resources.Value; - return resources != null ? new ReadOnlyDictionary(resources) : EmptyDict; - } - - public static bool HasResource(object key) - { - object value = DoGetResource(key); - return value != null; - } - - public static object GetResource(object key, ILogger logger = null) - { - object value = DoGetResource(key); - - if (value != null) - { - logger?.LogTrace("Retrieved value [{value}] for key [{key}] bound to thread [{thread}]", value, key, Thread.CurrentThread.ManagedThreadId); - } - - return value; - } - - public static void BindResource(object key, object value, ILogger logger = null) - { - ArgumentGuard.NotNull(value); - - Dictionary map = Resources.Value; - - // set ThreadLocal Map if none found - if (map == null) - { - map = new Dictionary(); - Resources.Value = map; - } - - map.TryGetValue(key, out object oldValue); - map[key] = value; - - // Transparently suppress a ResourceHolder that was marked as void... - if (oldValue is IResourceHolder holder && holder.IsVoid) - { - oldValue = null; - } - - if (oldValue != null) - { - throw new InvalidOperationException($"Already value [{oldValue}] for key [{key}] bound to thread [{Thread.CurrentThread.ManagedThreadId}]"); - } - - logger?.LogTrace("Bound value [{value}] for key [{key}] to thread [{thread}]", value, key, Thread.CurrentThread.ManagedThreadId); - } - - public static object UnbindResource(object key, ILogger logger = null) - { - object value = DoUnbindResource(key, logger); - - if (value == null) - { - throw new InvalidOperationException($"No value for key [{key}] bound to thread [{Thread.CurrentThread.ManagedThreadId}]"); - } - - return value; - } - - public static object UnbindResourceIfPossible(object key, ILogger logger = null) - { - return DoUnbindResource(key, logger); - } - - public static bool IsSynchronizationActive() - { - return Synchronizations.Value != null; - } - - public static void InitSynchronization(ILogger logger = null) - { - if (IsSynchronizationActive()) - { - throw new InvalidOperationException("Cannot activate transaction synchronization - already active"); - } - - logger?.LogTrace("Initializing transaction synchronization"); - Synchronizations.Value = new HashSet(); - } - - public static void RegisterSynchronization(ITransactionSynchronization synchronization) - { - ArgumentGuard.NotNull(synchronization); - - ISet synchs = Synchronizations.Value; - - if (synchs == null) - { - throw new InvalidOperationException("Transaction synchronization is not active"); - } - - synchs.Add(synchronization); - } - - public static List GetSynchronizations() - { - ISet synchs = Synchronizations.Value; - - if (synchs == null) - { - throw new InvalidOperationException("Transaction synchronization is not active"); - } - - // Return unmodifiable snapshot, to avoid ConcurrentModificationExceptions - // while iterating and invoking synchronization callbacks that in turn - // might register further synchronizations. - if (synchs.Count == 0) - { - return EmptyList; - } - - // Sort lazily here, not in registerSynchronization. - var sortedOrdered = new List(); - var unordered = new List(); - - foreach (ITransactionSynchronization s in synchs) - { - if (s is IOrdered) - { - var ordered = s as IOrdered; - sortedOrdered.Add(ordered); - } - else - { - unordered.Add(s); - } - } - - sortedOrdered.Sort(OrderComparer.Instance); - unordered.InsertRange(0, sortedOrdered.Select(o => o as ITransactionSynchronization)); - - return unordered; - } - - public static void ClearSynchronization(ILogger logger = null) - { - if (!IsSynchronizationActive()) - { - throw new InvalidOperationException("Cannot deactivate transaction synchronization - not active"); - } - - logger?.LogTrace("Clearing transaction synchronization"); - Synchronizations.Value = null; - } - - public static bool IsActualTransactionActive() - { - return ActualTransactionActive.Value; - } - - public static void SetActualTransactionActive(bool active) - { - ActualTransactionActive.Value = active; - } - - public static int? GetCurrentTransactionIsolationLevel() - { - return CurrentTransactionIsolationLevel.Value; - } - - public static void SetCurrentTransactionIsolationLevel(int? isolationLevel) - { - CurrentTransactionIsolationLevel.Value = isolationLevel; - } - - public static void SetCurrentTransactionName(string name) - { - CurrentTransactionName.Value = name; - } - - public static string GetCurrentTransactionName() - { - return CurrentTransactionName.Value; - } - - public static void SetCurrentTransactionReadOnly(bool readOnly) - { - CurrentTransactionReadOnly.Value = readOnly; - } - - public static bool IsCurrentTransactionReadOnly() - { - return CurrentTransactionReadOnly.Value; - } - - public static void Clear() - { - Synchronizations.Value = null; - CurrentTransactionName.Value = null; - CurrentTransactionReadOnly.Value = false; - CurrentTransactionIsolationLevel.Value = null; - ActualTransactionActive.Value = false; - } - - private static object DoUnbindResource(object actualKey, ILogger logger = null) - { - Dictionary map = Resources.Value; - - if (map == null) - { - return null; - } - - map.TryGetValue(actualKey, out object value); - map.Remove(actualKey); - - // Remove entire ThreadLocal if empty... - if (map.Count == 0) - { - Resources.Value = null; - } - - // Transparently suppress a ResourceHolder that was marked as void... - if (value is IResourceHolder holder && holder.IsVoid) - { - value = null; - } - - logger?.LogTrace("Removed value [{value}] for key [{key}] from thread [{thread}]", value, actualKey, Thread.CurrentThread.ManagedThreadId); - return value; - } - - private static object DoGetResource(object actualKey) - { - Dictionary map = Resources.Value; - - if (map == null) - { - return null; - } - - map.TryGetValue(actualKey, out object value); - - // Transparently remove ResourceHolder that was marked as void... - if (value is IResourceHolder holder && holder.IsVoid) - { - map.Remove(actualKey); - - // Remove entire ThreadLocal if empty... - if (map.Count == 0) - { - Resources.Value = null; - } - - value = null; - } - - return value; - } -} diff --git a/src/Common/src/Common/Transaction/TransactionSynchronizationUtils.cs b/src/Common/src/Common/Transaction/TransactionSynchronizationUtils.cs deleted file mode 100644 index ddef761003..0000000000 --- a/src/Common/src/Common/Transaction/TransactionSynchronizationUtils.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Common.Transaction; - -public static class TransactionSynchronizationUtils -{ - public static void TriggerBeforeCommit(bool readOnly) - { - foreach (ITransactionSynchronization synchronization in TransactionSynchronizationManager.GetSynchronizations()) - { - synchronization.BeforeCommit(readOnly); - } - } - - public static void TriggerBeforeCompletion(ILogger logger = null) - { - foreach (ITransactionSynchronization synchronization in TransactionSynchronizationManager.GetSynchronizations()) - { - try - { - synchronization.BeforeCompletion(); - } - catch (Exception ex) - { - logger?.LogError(ex, "TransactionSynchronization.beforeCompletion threw exception"); - } - } - } - - public static void TriggerAfterCommit() - { - InvokeAfterCommit(TransactionSynchronizationManager.GetSynchronizations()); - } - - public static void InvokeAfterCommit(List synchronizations) - { - if (synchronizations != null) - { - foreach (ITransactionSynchronization synchronization in synchronizations) - { - synchronization.AfterCommit(); - } - } - } - - public static void TriggerAfterCompletion(int completionStatus) - { - List synchronizations = TransactionSynchronizationManager.GetSynchronizations(); - InvokeAfterCompletion(synchronizations, completionStatus); - } - - public static void InvokeAfterCompletion(List synchronizations, int completionStatus, ILogger logger = null) - { - if (synchronizations != null) - { - foreach (ITransactionSynchronization synchronization in synchronizations) - { - try - { - synchronization.AfterCompletion(completionStatus); - } - catch (Exception ex) - { - logger.LogError(ex, "TransactionSynchronization.afterCompletion threw exception"); - } - } - } - } -} diff --git a/src/Common/src/Common/Transaction/TransactionSystemException.cs b/src/Common/src/Common/Transaction/TransactionSystemException.cs deleted file mode 100644 index 142e36a297..0000000000 --- a/src/Common/src/Common/Transaction/TransactionSystemException.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class TransactionSystemException : TransactionException -{ - public Exception ApplicationException { get; private set; } - public Exception OriginalException => ApplicationException ?? InnerException; - - public TransactionSystemException(string message) - : base(message) - { - } - - public TransactionSystemException(string message, Exception innerException) - : base(message, innerException) - { - } - - public void InitApplicationException(Exception exception) - { - ArgumentGuard.NotNull(exception); - - if (ApplicationException != null) - { - throw new InvalidOperationException($"Already holding an application exception: {ApplicationException}"); - } - - ApplicationException = exception; - } - - public bool Contains(Type exceptionType) - { - return exceptionType != null && exceptionType.IsInstanceOfType(ApplicationException); - } -} diff --git a/src/Common/src/Common/Transaction/TransactionTemplate.cs b/src/Common/src/Common/Transaction/TransactionTemplate.cs deleted file mode 100644 index 4e364f1f16..0000000000 --- a/src/Common/src/Common/Transaction/TransactionTemplate.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Common.Transaction; - -public class TransactionTemplate : DefaultTransactionDefinition -{ - private readonly ILogger _logger; - - public IPlatformTransactionManager TransactionManager { get; set; } - - public TransactionTemplate(ILogger logger = null) - { - _logger = logger; - } - - public TransactionTemplate(IPlatformTransactionManager transactionManager, ILogger logger = null) - { - _logger = logger; - TransactionManager = transactionManager; - } - - public TransactionTemplate(IPlatformTransactionManager transactionManager, ITransactionDefinition transactionDefinition, ILogger logger = null) - : base(transactionDefinition) - { - _logger = logger; - TransactionManager = transactionManager; - } - - public void Execute(Action action) - { - Execute(s => - { - action(s); - return null; - }); - } - - public T Execute(Func action) - { - if (TransactionManager == null) - { - throw new InvalidOperationException("No PlatformTransactionManager set"); - } - - ITransactionStatus status = TransactionManager.GetTransaction(this); - T result; - - try - { - result = action(status); - } - catch (Exception ex) - { - // Transactional code threw application exception -> rollback - RollbackOnException(status, ex); - throw; - } - - TransactionManager.Commit(status); - return result; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - return base.Equals(obj) && (obj is not TransactionTemplate otherTemplate || TransactionManager == otherTemplate.TransactionManager); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - private void RollbackOnException(ITransactionStatus status, Exception ex) - { - if (TransactionManager == null) - { - throw new InvalidOperationException("No PlatformTransactionManager set"); - } - - _logger?.LogDebug(ex, "Initiating transaction rollback on application exception"); - - try - { - TransactionManager.Rollback(status); - } - catch (TransactionSystemException ex2) - { - _logger?.LogError(ex, "Application exception overridden by rollback exception"); - ex2.InitApplicationException(ex); - throw; - } - catch (Exception ex2) - { - _logger?.LogError(ex2, "Application exception overridden by rollback exception: {original}", ex); - throw; - } - } -} diff --git a/src/Common/src/Common/Transaction/TransactionTimedOutException.cs b/src/Common/src/Common/Transaction/TransactionTimedOutException.cs deleted file mode 100644 index eedbe5fac1..0000000000 --- a/src/Common/src/Common/Transaction/TransactionTimedOutException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class TransactionTimedOutException : TransactionException -{ - public TransactionTimedOutException(string message) - : base(message) - { - } - - public TransactionTimedOutException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Transaction/TransactionUsageException.cs b/src/Common/src/Common/Transaction/TransactionUsageException.cs deleted file mode 100644 index 180fd43bbf..0000000000 --- a/src/Common/src/Common/Transaction/TransactionUsageException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class TransactionUsageException : TransactionException -{ - public TransactionUsageException(string message) - : base(message) - { - } - - public TransactionUsageException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Transaction/UnexpectedRollbackException.cs b/src/Common/src/Common/Transaction/UnexpectedRollbackException.cs deleted file mode 100644 index 9ac38204e4..0000000000 --- a/src/Common/src/Common/Transaction/UnexpectedRollbackException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -public class UnexpectedRollbackException : TransactionException -{ - public UnexpectedRollbackException(string message) - : base(message) - { - } - - public UnexpectedRollbackException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Common/src/Common/Transaction/WithoutTransactionOperations.cs b/src/Common/src/Common/Transaction/WithoutTransactionOperations.cs deleted file mode 100644 index ea781039a4..0000000000 --- a/src/Common/src/Common/Transaction/WithoutTransactionOperations.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Transaction; - -internal sealed class WithoutTransactionOperations : ITransactionOperations -{ - public static readonly WithoutTransactionOperations Instance = new(); - - private WithoutTransactionOperations() - { - } - - public T Execute(Func action) - { - return action(new SimpleTransactionStatus(false)); - } - - public void ExecuteWithoutResult(Action action) - { - action(new SimpleTransactionStatus(false)); - } -} diff --git a/src/Common/src/Common/Util/AbstractAttributeAccessor.cs b/src/Common/src/Common/Util/AbstractAttributeAccessor.cs deleted file mode 100644 index 3b7d561333..0000000000 --- a/src/Common/src/Common/Util/AbstractAttributeAccessor.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public abstract class AbstractAttributeAccessor : IAttributeAccessor -{ - private readonly Dictionary _attributes = new(); - - public virtual string[] AttributeNames - { -#pragma warning disable S2365 // Properties should not make collection or array copies - get - { - return _attributes.Keys.ToArray(); - } -#pragma warning restore S2365 // Properties should not make collection or array copies - } - - public virtual void SetAttribute(string name, object value) - { - ArgumentGuard.NotNull(name); - - if (value != null) - { - _attributes[name] = value; - } - else - { - RemoveAttribute(name); - } - } - - public virtual object GetAttribute(string name) - { - ArgumentGuard.NotNull(name); - - _attributes.TryGetValue(name, out object result); - return result; - } - - public virtual object RemoveAttribute(string name) - { - ArgumentGuard.NotNull(name); - - _attributes.TryGetValue(name, out object original); - _attributes.Remove(name); - return original; - } - - public virtual bool HasAttribute(string name) - { - ArgumentGuard.NotNull(name); - - return _attributes.ContainsKey(name); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not AbstractAttributeAccessor accessor) - { - return false; - } - - if (accessor._attributes.Count != _attributes.Count) - { - return false; - } - - foreach (KeyValuePair kvp in _attributes) - { - if (!accessor._attributes.TryGetValue(kvp.Key, out object value2)) - { - return false; - } - - if (!kvp.Value.Equals(value2)) - { - return false; - } - } - - return true; - } - - public override int GetHashCode() - { - return _attributes.GetHashCode(); - } - - protected virtual void CopyAttributesFrom(IAttributeAccessor source) - { - ArgumentGuard.NotNull(source); - - string[] attributeNames = source.AttributeNames; - - foreach (string attributeName in attributeNames) - { - SetAttribute(attributeName, source.GetAttribute(attributeName)); - } - } -} diff --git a/src/Common/src/Common/Util/AntPathMatcher.cs b/src/Common/src/Common/Util/AntPathMatcher.cs deleted file mode 100644 index af1aed21a6..0000000000 --- a/src/Common/src/Common/Util/AntPathMatcher.cs +++ /dev/null @@ -1,880 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Text; -using System.Text.RegularExpressions; - -namespace Steeltoe.Common.Util; - -public class AntPathMatcher : IPathMatcher -{ - private const int CacheTurnoffThreshold = 65536; - public const string DefaultPathSeparator = "/"; - - private static readonly Regex VariablePattern = new("\\{[^/]+?\\}", RegexOptions.Compiled); - - private static readonly char[] WildcardChars = - { - '*', - '?', - '{' - }; - - private readonly ConcurrentDictionary _tokenizedPatternCache = new(); - - private readonly ConcurrentDictionary _stringMatcherCache = new(); - - private string _pathSeparator; - - private PathSeparatorPatternCache _pathSeparatorPatternCache; - - public virtual string PathSeparator - { - get => _pathSeparator; - set - { - _pathSeparator = value ?? DefaultPathSeparator; - _pathSeparatorPatternCache = new PathSeparatorPatternCache(_pathSeparator); - } - } - - public virtual bool CaseSensitive { get; set; } = true; - - public virtual bool TrimTokens { get; set; } - - public virtual bool? CachePatterns { get; set; } - - public AntPathMatcher() - { - _pathSeparator = DefaultPathSeparator; - _pathSeparatorPatternCache = new PathSeparatorPatternCache(DefaultPathSeparator); - } - - public AntPathMatcher(string pathSeparator) - { - ArgumentGuard.NotNullOrEmpty(pathSeparator); - - _pathSeparator = pathSeparator; - _pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator); - } - - public virtual bool IsPattern(string path) - { - bool uriVar = false; - - foreach (char c in path) - { - if (c == '*' || c == '?') - { - return true; - } - - if (c == '{') - { - uriVar = true; - continue; - } - - if (c == '}' && uriVar) - { - return true; - } - } - - return false; - } - - public virtual bool Match(string pattern, string path) - { - return DoMatch(pattern, path, true, null); - } - - public virtual bool MatchStart(string pattern, string path) - { - return DoMatch(pattern, path, false, null); - } - - public virtual string ExtractPathWithinPattern(string pattern, string path) - { - string[] patternParts = TokenizePath(pattern); - string[] pathParts = TokenizePath(path); - - var builder = new StringBuilder(); - bool pathStarted = false; - - for (int segment = 0; segment < patternParts.Length; segment++) - { - string patternPart = patternParts[segment]; - - if (patternPart.IndexOf('*') > -1 || patternPart.IndexOf('?') > -1) - { - for (; segment < pathParts.Length; segment++) - { - if (pathStarted || (segment == 0 && !pattern.StartsWith(_pathSeparator, StringComparison.Ordinal))) - { - builder.Append(_pathSeparator); - } - - builder.Append(pathParts[segment]); - pathStarted = true; - } - } - } - - return builder.ToString(); - } - - public virtual IDictionary ExtractUriTemplateVariables(string pattern, string path) - { - var variables = new Dictionary(); - bool result = DoMatch(pattern, path, true, variables); - - if (!result) - { - throw new InvalidOperationException($"Pattern \"{pattern}\" is not a match for \"{path}\""); - } - - return variables; - } - - public virtual string Combine(string pattern1, string pattern2) - { - if (string.IsNullOrEmpty(pattern1) && string.IsNullOrEmpty(pattern2)) - { - return string.Empty; - } - - if (string.IsNullOrEmpty(pattern1)) - { - return pattern2; - } - - if (string.IsNullOrEmpty(pattern2)) - { - return pattern1; - } - - bool pattern1ContainsUriVar = pattern1.IndexOf('{') != -1; - - if (pattern1 != pattern2 && !pattern1ContainsUriVar && Match(pattern1, pattern2)) - { - // /* + /hotel -> /hotel ; "/*.*" + "/*.html" -> /*.html - // However /user + /user -> /usr/user ; /{foo} + /bar -> /{foo}/bar - return pattern2; - } - - // /hotels/* + /booking -> /hotels/booking - // /hotels/* + booking -> /hotels/booking - if (pattern1.EndsWith(_pathSeparatorPatternCache.EndsOnWildCard, StringComparison.Ordinal)) - { - return Concat(pattern1.Substring(0, pattern1.Length - 2), pattern2); - } - - // /hotels/** + /booking -> /hotels/**/booking - // /hotels/** + booking -> /hotels/**/booking - if (pattern1.EndsWith(_pathSeparatorPatternCache.EndsOnDoubleWildCard, StringComparison.Ordinal)) - { - return Concat(pattern1, pattern2); - } - - int starDotPos1 = pattern1.IndexOf("*.", StringComparison.Ordinal); - - if (pattern1ContainsUriVar || starDotPos1 == -1 || _pathSeparator == ".") - { - // simply concatenate the two patterns - return Concat(pattern1, pattern2); - } - - string ext1 = pattern1.Substring(starDotPos1 + 1); - int dotPos2 = pattern2.IndexOf('.'); - string file2 = dotPos2 == -1 ? pattern2 : pattern2.Substring(0, dotPos2); - string ext2 = dotPos2 == -1 ? string.Empty : pattern2.Substring(dotPos2); - bool ext1All = ext1 == ".*" || ext1 == string.Empty; - bool ext2All = ext2 == ".*" || ext2 == string.Empty; - - if (!ext1All && !ext2All) - { - throw new InvalidOperationException($"Cannot combine patterns: {pattern1} vs {pattern2}"); - } - - string ext = ext1All ? ext2 : ext1; - return file2 + ext; - } - - public virtual IComparer GetPatternComparer(string path) - { - return new AntPatternComparator(path); - } - - protected virtual bool DoMatch(string pattern, string path, bool fullMatch, IDictionary uriTemplateVariables) - { - if (path.StartsWith(_pathSeparator, StringComparison.Ordinal) != pattern.StartsWith(_pathSeparator, StringComparison.Ordinal)) - { - return false; - } - - string[] patternDirs = TokenizePattern(pattern); - - if (fullMatch && CaseSensitive && !IsPotentialMatch(path, patternDirs)) - { - return false; - } - - string[] pathDirs = TokenizePath(path); - - int patternIdxStart = 0; - int patternIdxEnd = patternDirs.Length - 1; - int pathIdxStart = 0; - int pathIdxEnd = pathDirs.Length - 1; - - // Match all elements up to the first ** - while (patternIdxStart <= patternIdxEnd && pathIdxStart <= pathIdxEnd) - { - string patternDir = patternDirs[patternIdxStart]; - - if (patternDir == "**") - { - break; - } - - if (!MatchStrings(patternDir, pathDirs[pathIdxStart], uriTemplateVariables)) - { - return false; - } - - patternIdxStart++; - pathIdxStart++; - } - - if (pathIdxStart > pathIdxEnd) - { - // Path is exhausted, only match if rest of pattern is * or **'s - if (patternIdxStart > patternIdxEnd) - { - return pattern.EndsWith(_pathSeparator, StringComparison.Ordinal) == path.EndsWith(_pathSeparator, StringComparison.Ordinal); - } - - if (!fullMatch) - { - return true; - } - - if (patternIdxStart == patternIdxEnd && patternDirs[patternIdxStart] == "*" && path.EndsWith(_pathSeparator, StringComparison.Ordinal)) - { - return true; - } - - for (int i = patternIdxStart; i <= patternIdxEnd; i++) - { - if (patternDirs[i] != "**") - { - return false; - } - } - - return true; - } - - if (patternIdxStart > patternIdxEnd) - { - // String not exhausted, but pattern is. Failure. - return false; - } - - if (!fullMatch && patternDirs[patternIdxStart] == "**") - { - // Path start definitely matches due to "**" part in pattern. - return true; - } - - // up to last '**' - while (patternIdxStart <= patternIdxEnd && pathIdxStart <= pathIdxEnd) - { - string patternDir = patternDirs[patternIdxEnd]; - - if (patternDir == "**") - { - break; - } - - if (!MatchStrings(patternDir, pathDirs[pathIdxEnd], uriTemplateVariables)) - { - return false; - } - - patternIdxEnd--; - pathIdxEnd--; - } - - if (pathIdxStart > pathIdxEnd) - { - // String is exhausted - for (int i = patternIdxStart; i <= patternIdxEnd; i++) - { - if (patternDirs[i] != "**") - { - return false; - } - } - - return true; - } - - while (patternIdxStart != patternIdxEnd && pathIdxStart <= pathIdxEnd) - { - int patIdxTmp = -1; - - for (int i = patternIdxStart + 1; i <= patternIdxEnd; i++) - { - if (patternDirs[i] == "**") - { - patIdxTmp = i; - break; - } - } - - if (patIdxTmp == patternIdxStart + 1) - { - // '**/**' situation, so skip one - patternIdxStart++; - continue; - } - - // Find the pattern between padIdxStart & padIdxTmp in str between - // strIdxStart & strIdxEnd - int patLength = patIdxTmp - patternIdxStart - 1; - int strLength = pathIdxEnd - pathIdxStart + 1; - int foundIdx = -1; - - for (int i = 0; i <= strLength - patLength; i++) - { - bool failedMatch = false; - - for (int j = 0; j < patLength; j++) - { - string subPat = patternDirs[patternIdxStart + j + 1]; - string subStr = pathDirs[pathIdxStart + i + j]; - - if (!MatchStrings(subPat, subStr, uriTemplateVariables)) - { - failedMatch = true; - break; - } - } - - if (failedMatch) - { - continue; - } - - foundIdx = pathIdxStart + i; - break; - } - - if (foundIdx == -1) - { - return false; - } - - patternIdxStart = patIdxTmp; - pathIdxStart = foundIdx + patLength; - } - - for (int i = patternIdxStart; i <= patternIdxEnd; i++) - { - if (patternDirs[i] != "**") - { - return false; - } - } - - return true; - } - - protected virtual string[] TokenizePattern(string pattern) - { - string[] tokenized = null; - - if (CachePatterns == null || CachePatterns.Value) - { - _tokenizedPatternCache.TryGetValue(pattern, out tokenized); - } - - if (tokenized == null) - { - tokenized = TokenizePath(pattern); - - if (CachePatterns == null && _tokenizedPatternCache.Count >= CacheTurnoffThreshold) - { - // Try to adapt to the runtime situation that we're encountering: - // There are obviously too many different patterns coming in here... - // So let's turn off the cache since the patterns are unlikely to be reoccurring. - DeactivatePatternCache(); - return tokenized; - } - - if (CachePatterns == null || CachePatterns.Value) - { - _tokenizedPatternCache[pattern] = tokenized; - } - } - - return tokenized; - } - - protected virtual string[] TokenizePath(string path) - { - if (path == null) - { - return Array.Empty(); - } - - string[] split = path.Split(new[] - { - _pathSeparator - }, StringSplitOptions.RemoveEmptyEntries); - - if (TrimTokens) - { - for (int i = 0; i < split.Length; i++) - { - split[i] = split[i].Trim(); - } - } - - return split; - } - - protected virtual AntPathStringMatcher GetStringMatcher(string pattern) - { - AntPathStringMatcher matcher = null; - - if (CachePatterns == null || CachePatterns.Value) - { - _stringMatcherCache.TryGetValue(pattern, out matcher); - } - - if (matcher == null) - { - matcher = new AntPathStringMatcher(pattern, CaseSensitive); - - if (CachePatterns == null && _stringMatcherCache.Count >= CacheTurnoffThreshold) - { - // Try to adapt to the runtime situation that we're encountering: - // There are obviously too many different patterns coming in here... - // So let's turn off the cache since the patterns are unlikely to be reoccurring. - DeactivatePatternCache(); - return matcher; - } - - if (CachePatterns == null || CachePatterns.Value) - { - _stringMatcherCache.TryAdd(pattern, matcher); - } - } - - return matcher; - } - - private string Concat(string path1, string path2) - { - bool path1EndsWithSeparator = path1.EndsWith(_pathSeparator, StringComparison.Ordinal); - bool path2StartsWithSeparator = path2.StartsWith(_pathSeparator, StringComparison.Ordinal); - - if (path1EndsWithSeparator && path2StartsWithSeparator) - { - return path1 + path2.Substring(1); - } - - if (path1EndsWithSeparator || path2StartsWithSeparator) - { - return path1 + path2; - } - - return path1 + _pathSeparator + path2; - } - - private bool MatchStrings(string pattern, string str, IDictionary uriTemplateVariables) - { - return GetStringMatcher(pattern).MatchStrings(str, uriTemplateVariables); - } - - private bool IsPotentialMatch(string path, string[] patternDirs) - { - if (!TrimTokens) - { - int pos = 0; - - foreach (string patternDir in patternDirs) - { - int skipped = SkipSeparator(path, pos, _pathSeparator); - pos += skipped; - skipped = SkipSegment(path, pos, patternDir); - - if (skipped < patternDir.Length) - { - return skipped > 0 || (patternDir.Length > 0 && IsWildcardChar(patternDir[0])); - } - - pos += skipped; - } - } - - return true; - } - - private int SkipSegment(string path, int pos, string prefix) - { - int skipped = 0; - - foreach (char c in prefix) - { - if (IsWildcardChar(c)) - { - return skipped; - } - - int currentPos = pos + skipped; - - if (currentPos >= path.Length) - { - return 0; - } - - if (c == path[currentPos]) - { - skipped++; - } - } - - return skipped; - } - - private int SkipSeparator(string path, int pos, string separator) - { - int skipped = 0; - path = path.Substring(pos); - - while (path.StartsWith(separator, StringComparison.Ordinal)) - { - skipped += separator.Length; - path = path.Substring(separator.Length); - } - - return skipped; - } - - private bool IsWildcardChar(char c) - { - foreach (char candidate in WildcardChars) - { - if (c == candidate) - { - return true; - } - } - - return false; - } - - private void DeactivatePatternCache() - { - CachePatterns = false; - _tokenizedPatternCache.Clear(); - _stringMatcherCache.Clear(); - } - - protected class AntPatternComparator : IComparer - { - private readonly string _path; - - public AntPatternComparator(string path) - { - _path = path; - } - - public int Compare(string pattern1, string pattern2) - { - var info1 = new PatternInfo(pattern1); - var info2 = new PatternInfo(pattern2); - - if (info1.IsLeastSpecific && info2.IsLeastSpecific) - { - return 0; - } - - if (info1.IsLeastSpecific) - { - return 1; - } - - if (info2.IsLeastSpecific) - { - return -1; - } - - bool pattern1EqualsPath = pattern1 == _path; - bool pattern2EqualsPath = pattern2 == _path; - - if (pattern1EqualsPath && pattern2EqualsPath) - { - return 0; - } - - if (pattern1EqualsPath) - { - return -1; - } - - if (pattern2EqualsPath) - { - return 1; - } - - if (info1.IsPrefixPattern && info2.DoubleWildcards == 0) - { - return 1; - } - - if (info2.IsPrefixPattern && info1.DoubleWildcards == 0) - { - return -1; - } - - if (info1.TotalCount != info2.TotalCount) - { - return info1.TotalCount - info2.TotalCount; - } - - if (info1.Length != info2.Length) - { - return info2.Length - info1.Length; - } - - if (info1.SingleWildcards < info2.SingleWildcards) - { - return -1; - } - - if (info2.SingleWildcards < info1.SingleWildcards) - { - return 1; - } - - if (info1.UriVars < info2.UriVars) - { - return -1; - } - - if (info2.UriVars < info1.UriVars) - { - return 1; - } - - return 0; - } - - private sealed class PatternInfo - { - private readonly string _pattern; - private readonly bool _catchAllPattern; - private int? _length; - - public int UriVars { get; private set; } - - public int SingleWildcards { get; private set; } - - public int DoubleWildcards { get; private set; } - - public bool IsLeastSpecific => _pattern == null || _catchAllPattern; - - public bool IsPrefixPattern { get; } - - public int TotalCount => UriVars + SingleWildcards + 2 * DoubleWildcards; - - public int Length - { - get - { - _length ??= _pattern != null ? VariablePattern.Replace(_pattern, "#").Length : 0; - - return _length.Value; - } - } - - public PatternInfo(string pattern) - { - _pattern = pattern; - - if (_pattern != null) - { - InitCounters(); - _catchAllPattern = _pattern == "/**"; - IsPrefixPattern = !_catchAllPattern && _pattern.EndsWith("/**", StringComparison.Ordinal); - } - - if (UriVars == 0) - { - _length = _pattern?.Length ?? 0; - } - } - - private void InitCounters() - { - int pos = 0; - - if (_pattern != null) - { - while (pos < _pattern.Length) - { - if (_pattern[pos] == '{') - { - UriVars++; - pos++; - } - else if (_pattern[pos] == '*') - { - if (pos + 1 < _pattern.Length && _pattern[pos + 1] == '*') - { - DoubleWildcards++; - pos += 2; - } - else if (pos > 0 && _pattern.Substring(pos - 1) != ".*") - { - SingleWildcards++; - pos++; - } - else - { - pos++; - } - } - else - { - pos++; - } - } - } - } - } - } - - protected class AntPathStringMatcher - { - private const string DefaultVariablePattern = "(.*)"; - private static readonly Regex GlobPattern = new("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}", RegexOptions.Compiled); - private readonly List _variableNames = new(); - private readonly Regex _pattern; - - public AntPathStringMatcher(string pattern) - : this(pattern, true) - { - } - - public AntPathStringMatcher(string pattern, bool caseSensitive) - { - var patternBuilder = new StringBuilder(); - Match matcher = GlobPattern.Match(pattern); - int end = 0; - - while (matcher.Success) - { - patternBuilder.Append(Quote(pattern, end, matcher.Index)); - string match = matcher.Value; - - if (match == "?") - { - patternBuilder.Append('.'); - } - else if (match == "*") - { - patternBuilder.Append(".*"); - } - else if (match.StartsWith('{') && match.EndsWith('}')) - { - int colonIdx = match.IndexOf(':'); - - if (colonIdx == -1) - { - patternBuilder.Append(DefaultVariablePattern); - Group group = matcher.Groups[1]; - _variableNames.Add(group.Value); - } - else - { - string variablePattern = match.Substring(colonIdx + 1, match.Length - 1 - (colonIdx + 1)); - patternBuilder.Append('('); - patternBuilder.Append(variablePattern); - patternBuilder.Append(')'); - string variableName = match.Substring(1, colonIdx - 1); - _variableNames.Add(variableName); - } - } - - end = matcher.Index + matcher.Length; - matcher = matcher.NextMatch(); - } - - patternBuilder.Append(Quote(pattern, end, pattern.Length)); - - _pattern = caseSensitive - ? new Regex(patternBuilder.ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled) - : new Regex(patternBuilder.ToString(), RegexOptions.Compiled); - } - - public bool MatchStrings(string str, IDictionary uriTemplateVariables) - { - Match matcher = _pattern.Match(str); - - if (matcher.Success && matcher.Length == str.Length) - { - if (uriTemplateVariables != null) - { - // SPR-8455 - if (_variableNames.Count != matcher.Groups.Count - 1) - { - throw new InvalidOperationException( - $"The number of capturing groups in the pattern segment {_pattern} does not match the number of URI template variables it defines, which can occur if capturing groups are used in a URI template regex. Use non-capturing groups instead."); - } - - for (int i = 1; i <= matcher.Groups.Count - 1; i++) - { - string name = _variableNames[i - 1]; - string value = matcher.Groups[i].Value; - uriTemplateVariables[name] = value; - } - } - - return true; - } - - return false; - } - - private string Quote(string str, int start, int end) - { - if (start == end) - { - return string.Empty; - } - - string s = str.Substring(start, end - start); - return Regex.Escape(s); - } - } - - protected class PathSeparatorPatternCache - { - public string EndsOnWildCard { get; } - - public string EndsOnDoubleWildCard { get; } - - public PathSeparatorPatternCache(string pathSeparator) - { - EndsOnWildCard = $"{pathSeparator}*"; - EndsOnDoubleWildCard = $"{pathSeparator}**"; - } - } -} diff --git a/src/Common/src/Common/Util/AttributeUtils.cs b/src/Common/src/Common/Util/AttributeUtils.cs deleted file mode 100644 index 8c34499b24..0000000000 --- a/src/Common/src/Common/Util/AttributeUtils.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Util; - -public static class AttributeUtils -{ - public static object GetValue(Attribute attribute, string propertyName) - { - if (attribute == null || string.IsNullOrEmpty(propertyName)) - { - return null; - } - - PropertyInfo property = attribute.GetType().GetProperty(propertyName); - - if (property != null) - { - return property.GetValue(attribute); - } - - return null; - } - - public static List FindMethodsWithAttribute(Type targetClass, Type attribute, BindingFlags flags) - { - var results = new List(); - MethodInfo[] targetMethods = targetClass.GetMethods(flags); - - foreach (MethodInfo method in targetMethods) - { - Attribute attr = method.GetCustomAttribute(attribute); - - if (attr != null) - { - results.Add(method); - } - } - - return results; - } -} diff --git a/src/Common/src/Common/Util/BinaryExceptionClassifier.cs b/src/Common/src/Common/Util/BinaryExceptionClassifier.cs deleted file mode 100644 index 567f090234..0000000000 --- a/src/Common/src/Common/Util/BinaryExceptionClassifier.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; - -namespace Steeltoe.Common.Util; - -public class BinaryExceptionClassifier : SubclassClassifier -{ - public bool TraverseInnerExceptions { get; set; } = true; - - public BinaryExceptionClassifier(bool defaultValue) - : base(defaultValue) - { - } - - public BinaryExceptionClassifier(IList exceptionClasses, bool defaultValue) - : this(!defaultValue) - { - if (exceptionClasses != null) - { - var map = new ConcurrentDictionary(); - - foreach (Type type in exceptionClasses) - { - map.TryAdd(type, !DefaultValue); - } - - TypeMap = map; - } - } - - public BinaryExceptionClassifier(IList exceptionClasses) - : this(exceptionClasses, true) - { - } - - public BinaryExceptionClassifier(Dictionary typeMap) - : this(typeMap, false) - { - } - - public BinaryExceptionClassifier(Dictionary typeMap, bool defaultValue) - : base(new ConcurrentDictionary(typeMap), defaultValue) - { - } - - public override bool Classify(Exception classifiable) - { - bool classified = base.Classify(classifiable); - - if (!TraverseInnerExceptions) - { - return classified; - } - - if (classified == DefaultValue) - { - Exception cause = classifiable; - - do - { - if (TypeMap.TryGetValue(cause.GetType(), out classified)) - { - return classified; - } - - cause = cause.InnerException; - classified = base.Classify(cause); - } - while (cause != null && classified == DefaultValue); - } - - return classified; - } -} diff --git a/src/Common/src/Common/Util/ClassUtils.cs b/src/Common/src/Common/Util/ClassUtils.cs deleted file mode 100644 index 99631198a4..0000000000 --- a/src/Common/src/Common/Util/ClassUtils.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Util; - -public static class ClassUtils -{ - public static Type GetGenericTypeDefinition(Type type) - { - if (type != null && type.IsGenericType && !type.IsGenericTypeDefinition) - { - return type.GetGenericTypeDefinition(); - } - - return type; - } - - public static bool IsAssignableValue(Type type, object value) - { - ArgumentGuard.NotNull(type); - - return value != null ? IsAssignable(type, value.GetType()) : !type.IsPrimitive; - } - - public static bool IsAssignable(Type lhsType, Type rhsType) - { - ArgumentGuard.NotNull(lhsType); - ArgumentGuard.NotNull(rhsType); - - if (lhsType.IsAssignableFrom(rhsType)) - { - return true; - } - - return false; - } - - public static MethodInfo GetInterfaceMethodIfPossible(MethodInfo method) - { - if (!method.IsPublic || method.IsStatic || method.DeclaringType.IsInterface) - { - return method; - } - - Type current = method.DeclaringType; - - while (current != null && current != typeof(object)) - { - Type[] interfaces = current.GetInterfaces(); - - foreach (Type ifc in interfaces) - { - try - { - MethodInfo found = ifc.GetMethod(method.Name, GetParameterTypes(method)); - - if (found != null) - { - return found; - } - } - catch (Exception) - { - // Ignore - } - } - - current = current.BaseType; - } - - return method; - } - - public static string GetQualifiedMethodName(MethodInfo method) - { - ArgumentGuard.NotNull(method); - - return $"{method.DeclaringType.FullName}.{method.Name}"; - } - - public static Type[] GetParameterTypes(MethodBase method) - { - ArgumentGuard.NotNull(method); - - var results = new Type[method.GetParameters().Length]; - int index = 0; - - foreach (ParameterInfo param in method.GetParameters()) - { - results[index++] = param.ParameterType; - } - - return results; - } - - public static object[][] GetParameterAttributes(MethodInfo method) - { - ParameterInfo[] parameters = method.GetParameters(); - object[][] paramsAttributes = new object[parameters.Length][]; - - for (int i = 0; i < parameters.Length; i++) - { - paramsAttributes[i] = parameters[i].GetCustomAttributes(false); - } - - return paramsAttributes; - } - - public static Type DetermineCommonAncestor(Type type1, Type type2) - { - if (type1 == null) - { - return type2; - } - - if (type2 == null) - { - return type1; - } - - if (type1.IsAssignableFrom(type2)) - { - return type1; - } - - if (type2.IsAssignableFrom(type1)) - { - return type2; - } - - Type ancestor = type1; - - do - { - ancestor = ancestor.BaseType; - - if (ancestor == null || typeof(object) == ancestor) - { - return null; - } - } - while (!ancestor.IsAssignableFrom(type2)); - - return ancestor; - } -} diff --git a/src/Common/src/Common/Util/DefaultIdGenerator.cs b/src/Common/src/Common/Util/DefaultIdGenerator.cs deleted file mode 100644 index ced34c4f51..0000000000 --- a/src/Common/src/Common/Util/DefaultIdGenerator.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public class DefaultIdGenerator : IIdGenerator -{ - public string GenerateId() - { - return Guid.NewGuid().ToString(); - } -} diff --git a/src/Common/src/Common/Util/EncodingUtils.cs b/src/Common/src/Common/Util/EncodingUtils.cs deleted file mode 100644 index cc497e74f5..0000000000 --- a/src/Common/src/Common/Util/EncodingUtils.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Common.Util; - -public static class EncodingUtils -{ - public static readonly Encoding Utf8 = new UTF8Encoding(false); - public static readonly Encoding Utf16 = new UnicodeEncoding(false, false); - public static readonly Encoding Utf16BigEndian = new UnicodeEncoding(true, false); - public static readonly Encoding Utf32 = new UTF32Encoding(false, false); - public static readonly Encoding Utf32BigEndian = new UTF32Encoding(true, false); - - public static Encoding GetDefaultEncoding() - { - return GetEncoding(Encoding.Default.WebName); - } - - public static Encoding GetEncoding(string name) - { - if (name == null) - { - return Utf8; - } - - if (name.Equals("utf-7", StringComparison.OrdinalIgnoreCase)) - { - throw new NotSupportedException("The UTF-7 encoding is insecure and should not be used. Consider using UTF-8 instead."); - } - - if (name.Equals("utf-8", StringComparison.OrdinalIgnoreCase)) - { - return Utf8; - } - - if (name.Equals("utf-16", StringComparison.OrdinalIgnoreCase)) - { - return Utf16; - } - - if (name.Equals("utf-16be", StringComparison.OrdinalIgnoreCase)) - { - return Utf16BigEndian; - } - - if (name.Equals("utf-32", StringComparison.OrdinalIgnoreCase)) - { - return Utf32; - } - - if (name.Equals("utf-32be", StringComparison.OrdinalIgnoreCase)) - { - return Utf32BigEndian; - } - - throw new ArgumentException($"Invalid encoding name '{name}'.", nameof(name)); - } - - public static string GetEncoding(Encoding encoding) - { - if (encoding == null || encoding.Equals(Utf8)) - { - return "utf-8"; - } - - if (encoding.Equals(Utf16)) - { - return "utf-16"; - } - - if (encoding.Equals(Utf16BigEndian)) - { - return "utf-16be"; - } - - if (encoding.Equals(Utf32)) - { - return "utf-32"; - } - - if (encoding.Equals(Utf32BigEndian)) - { - return "utf-32be"; - } - - if (IsUtf7(encoding)) - { - // https://docs.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0001 - throw new NotSupportedException("The UTF-7 encoding is insecure and should not be used. Consider using UTF-8 instead."); - } - - throw new ArgumentException($"Invalid encoding '{encoding.WebName}'.", nameof(encoding)); - } - - private static bool IsUtf7(Encoding encoding) - { - return encoding.CodePage == 65000; - } -} diff --git a/src/Common/src/Common/Util/ExceptionDepthComparator.cs b/src/Common/src/Common/Util/ExceptionDepthComparator.cs deleted file mode 100644 index 396294d3c9..0000000000 --- a/src/Common/src/Common/Util/ExceptionDepthComparator.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public class ExceptionDepthComparator : IComparer -{ - private readonly Type _targetException; - - public ExceptionDepthComparator(Exception exception) - { - ArgumentGuard.NotNull(exception); - - _targetException = exception.GetType(); - } - - public ExceptionDepthComparator(Type exceptionType) - { - ArgumentGuard.NotNull(exceptionType); - - _targetException = exceptionType; - } - - public int Compare(Type o1, Type o2) - { - int depth1 = GetDepth(o1, _targetException, 0); - int depth2 = GetDepth(o2, _targetException, 0); - return depth1 - depth2; - } - - private int GetDepth(Type declaredException, Type exceptionToMatch, int depth) - { - if (exceptionToMatch == declaredException) - { - // Found it! - return depth; - } - - // If we've gone as far as we can go and haven't found it... - if (exceptionToMatch == typeof(Exception)) - { - return int.MaxValue; - } - - return GetDepth(declaredException, exceptionToMatch.BaseType, depth + 1); - } -} diff --git a/src/Common/src/Common/Util/FixedBackOff.cs b/src/Common/src/Common/Util/FixedBackOff.cs deleted file mode 100644 index 2fb24060b0..0000000000 --- a/src/Common/src/Common/Util/FixedBackOff.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; - -namespace Steeltoe.Common.Util; - -public class FixedBackOff : IBackOff -{ - public const int Stop = -1; - public const int DefaultInterval = 5000; - public const int UnlimitedAttempts = int.MaxValue; - - public int Interval { get; set; } = DefaultInterval; - - public int MaxAttempts { get; set; } = UnlimitedAttempts; - - public FixedBackOff() - { - } - - public FixedBackOff(int interval, int maxAttempts) - { - Interval = interval; - MaxAttempts = maxAttempts; - } - - public IBackOffExecution Start() - { - return new FixedBackOffExecution(this); - } - - private sealed class FixedBackOffExecution : IBackOffExecution - { - private readonly FixedBackOff _backOff; - private int _currentAttempts; - - public FixedBackOffExecution(FixedBackOff backOff) - { - _backOff = backOff; - } - - public int NextBackOff() - { - _currentAttempts++; - - if (_currentAttempts <= _backOff.MaxAttempts) - { - return _backOff.Interval; - } - - return Stop; - } - - public override string ToString() - { - string attemptValue = _backOff.MaxAttempts == int.MaxValue ? "unlimited" : _backOff.MaxAttempts.ToString(CultureInfo.InvariantCulture); - return $"FixedBackOff{{interval={_backOff.Interval}, currentAttempts={_currentAttempts}, maxAttempts={attemptValue}}}"; - } - } -} diff --git a/src/Common/src/Common/Util/IListExtensions.cs b/src/Common/src/Common/Util/IListExtensions.cs deleted file mode 100644 index e5dc7090e2..0000000000 --- a/src/Common/src/Common/Util/IListExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -internal static class ListExtensions -{ - public static void AddRange(this IList source, IEnumerable items) - { - ArgumentGuard.NotNull(source); - ArgumentGuard.NotNull(items); - - if (source is List list) - { - list.AddRange(items); - } - else - { - foreach (T item in items) - { - source.Add(item); - } - } - } -} diff --git a/src/Common/src/Common/Util/MethodInvoker.cs b/src/Common/src/Common/Util/MethodInvoker.cs deleted file mode 100644 index 3a686f1d83..0000000000 --- a/src/Common/src/Common/Util/MethodInvoker.cs +++ /dev/null @@ -1,215 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Common.Util; - -public class MethodInvoker -{ - public Type TargetClass { get; set; } - - public object TargetObject { get; private set; } - - public string TargetMethod { get; set; } - - public string StaticMethod { get; set; } - - public object[] Arguments { get; private set; } - - public MethodInfo MethodObject { get; set; } - - public bool IsPrepared => MethodObject != null; - - public static int GetTypeDifferenceWeight(Type[] paramTypes, object[] args) - { - int result = 0; - - for (int i = 0; i < paramTypes.Length; i++) - { - if (!ClassUtils.IsAssignableValue(paramTypes[i], args[i])) - { - return int.MaxValue; - } - - if (args[i] != null) - { - Type paramType = paramTypes[i]; - Type superClass = args[i].GetType().BaseType; - - while (superClass != null) - { - if (paramType == superClass) - { - result += 2; - superClass = null; - } - else if (ClassUtils.IsAssignable(paramType, superClass)) - { - result += 2; - superClass = superClass.BaseType; - } - else - { - superClass = null; - } - } - - if (paramType.IsInterface) - { - result++; - } - } - } - - return result; - } - - public virtual void SetTargetObject(object target) - { - TargetObject = target; - - if (target != null) - { - TargetClass = target.GetType(); - } - } - - public virtual void SetArguments(params object[] arguments) - { - Arguments = arguments; - } - - public virtual void Prepare() - { - if (StaticMethod != null) - { - int lastDotIndex = StaticMethod.LastIndexOf('.'); - - if (lastDotIndex == -1 || lastDotIndex == StaticMethod.Length) - { - throw new InvalidOperationException($"{nameof(StaticMethod)} must be a fully qualified type plus method name: " + - "e.g. 'example.MyExampleClass.myExampleMethod'"); - } - - string className = StaticMethod.Substring(0, lastDotIndex); - string methodName = StaticMethod.Substring(lastDotIndex + 1); - TargetClass = ResolveClassName(className); - TargetMethod = methodName; - } - - Type targetClass = TargetClass; - string targetMethod = TargetMethod; - - if (targetClass == null) - { - throw new InvalidOperationException($"{nameof(TargetClass)} must be set first."); - } - - if (targetMethod == null) - { - throw new InvalidOperationException($"{nameof(TargetMethod)} must be set first."); - } - - object[] arguments = Arguments; - var argTypes = new Type[arguments.Length]; - - for (int i = 0; i < arguments.Length; ++i) - { - argTypes[i] = arguments[i] != null ? arguments[i].GetType() : typeof(object); - } - - try - { - MethodObject = targetClass.GetMethod(targetMethod, argTypes); - } - catch (Exception) - { - // Just rethrow exception if we can't get any match. - MethodObject = FindMatchingMethod(); - - if (MethodObject == null) - { - throw; - } - } - } - - public object Invoke() - { - // In the static case, target will simply be {@code null}. - object targetObject = TargetObject; - MethodInfo preparedMethod = GetPreparedMethod(); - - if (targetObject == null && !preparedMethod.IsStatic) - { - throw new InvalidOperationException("Target method must not be non-static without a target"); - } - - return preparedMethod.Invoke(targetObject, Arguments); - } - - public MethodInfo GetPreparedMethod() - { - if (MethodObject == null) - { - throw new InvalidOperationException("Prepare() must be called prior to invoke() on MethodInvoker"); - } - - return MethodObject; - } - - protected virtual MethodInfo FindMatchingMethod() - { - string targetMethod = TargetMethod; - object[] arguments = Arguments; - int argCount = arguments.Length; - - Type targetClass = TargetClass; - - if (targetClass == null) - { - throw new InvalidOperationException("No target class set"); - } - - MethodInfo[] candidates = targetClass.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); - int minTypeDiffWeight = int.MaxValue; - MethodInfo matchingMethod = null; - - foreach (MethodInfo candidate in candidates) - { - if (candidate.Name == targetMethod && candidate.GetParameters().Length == argCount) - { - Type[] paramTypes = GetParameterTypes(candidate); - int typeDiffWeight = GetTypeDifferenceWeight(paramTypes, arguments); - - if (typeDiffWeight < minTypeDiffWeight) - { - minTypeDiffWeight = typeDiffWeight; - matchingMethod = candidate; - } - } - } - - return matchingMethod; - } - - protected virtual Type ResolveClassName(string className) - { - return Type.GetType(className, false); - } - - private Type[] GetParameterTypes(MethodInfo candidate) - { - ParameterInfo[] parameters = candidate.GetParameters(); - var result = new Type[parameters.Length]; - - for (int i = 0; i < parameters.Length; i++) - { - result[i] = parameters[i].ParameterType; - } - - return result; - } -} diff --git a/src/Common/src/Common/Util/MimeType.cs b/src/Common/src/Common/Util/MimeType.cs deleted file mode 100644 index 3c88da2130..0000000000 --- a/src/Common/src/Common/Util/MimeType.cs +++ /dev/null @@ -1,594 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Text; - -namespace Steeltoe.Common.Util; -#pragma warning disable S1210 // "Equals" and the comparison operators should be overridden when implementing "IComparable" -public class MimeType : IComparable -#pragma warning restore S1210 // "Equals" and the comparison operators should be overridden when implementing "IComparable" -{ - private const string ParamCharset = "charset"; - public const string WildcardType = "*"; - - private static readonly BitArray Token = new(128); - - private volatile string _toStringValue; - - public bool IsWildcardType => Type == WildcardType; - - public bool IsWildcardSubtype => Subtype == WildcardType || Subtype.StartsWith("*+", StringComparison.Ordinal); - - public bool IsConcrete => !IsWildcardType && !IsWildcardSubtype; - - public string Type { get; } - - public string Subtype { get; } - - public Encoding Encoding - { - get - { - string charset = GetParameter(ParamCharset); - return charset != null ? GetEncoding(Unquote(charset)) : null; - } - } - - public IDictionary Parameters { get; } - - static MimeType() - { - // variable names refer to RFC 2616, section 2.2 - var ctl = new BitArray(128); - - for (int i = 0; i <= 31; i++) - { - ctl.Set(i, true); - } - - ctl.Set(127, true); - - var separators = new BitArray(128); - separators.Set('(', true); - separators.Set(')', true); - separators.Set('<', true); - separators.Set('>', true); - separators.Set('@', true); - separators.Set(',', true); - separators.Set(';', true); - separators.Set(':', true); - separators.Set('\\', true); - separators.Set('\"', true); - separators.Set('/', true); - separators.Set('[', true); - separators.Set(']', true); - separators.Set('?', true); - separators.Set('=', true); - separators.Set('{', true); - separators.Set('}', true); - separators.Set(' ', true); - separators.Set('\t', true); - - for (int i = 0; i < 128; i++) - { - Token.Set(i, true); - } - - Token.And(ctl.Not()); - Token.And(separators.Not()); - } - - public MimeType(string type) - : this(type, WildcardType) - { - } - - public MimeType(string type, string subtype) - : this(type, subtype, new Dictionary()) - { - } - - public MimeType(string type, string subtype, Encoding charset) - : this(type, subtype, new Dictionary - { - { ParamCharset, charset.BodyName } - }) - { - } - - public MimeType(MimeType other, Encoding charset) - : this(other.Type, other.Subtype, AddCharsetParameter(charset, other.Parameters)) - { - } - - public MimeType(MimeType other, IDictionary parameters = null) - : this(other.Type, other.Subtype, parameters) - { - } - - public MimeType(string type, string subtype, IDictionary parameters) - { - ArgumentGuard.NotNullOrEmpty(type); - ArgumentGuard.NotNullOrEmpty(subtype); - - CheckToken(type); - CheckToken(subtype); - -#pragma warning disable S4040 // Strings should be normalized to uppercase - Type = type.ToLowerInvariant(); - Subtype = subtype.ToLowerInvariant(); -#pragma warning restore S4040 // Strings should be normalized to uppercase - - if (parameters.Count > 0) - { - var map = new Dictionary(); - - foreach (KeyValuePair p in parameters) - { - CheckParameters(p.Key, p.Value); - map.Add(p.Key, p.Value); - } - - Parameters = map; // Read only - } - else - { - Parameters = new Dictionary(); - } - } - - public static MimeType ToMimeType(string value) - { - return MimeTypeUtils.ParseMimeType(value); - } - - protected void CheckParameters(string attribute, string value) - { - ArgumentGuard.NotNullOrEmpty(attribute); - ArgumentGuard.NotNullOrEmpty(value); - - CheckToken(attribute); - - if (attribute == ParamCharset) - { - value = Unquote(value); - _ = Encoding.GetEncoding(value); - } - else if (!IsQuotedString(value)) - { - CheckToken(value); - } - } - - protected string Unquote(string s) - { - return IsQuotedString(s) ? s.Substring(1, s.Length - 1 - 1) : s; - } - - public string GetParameter(string name) - { - if (Parameters.TryGetValue(name, out string value)) - { - return value; - } - - return null; - } - - public bool Includes(MimeType other) - { - if (other == null) - { - return false; - } - - if (IsWildcardType) - { - // */* includes anything - return true; - } - - if (Type == other.Type) - { - if (Subtype == other.Subtype) - { - return true; - } - - if (IsWildcardSubtype) - { - // Wildcard with suffix, e.g. application/*+xml - int thisPlusIdx = Subtype.LastIndexOf('+'); - - if (thisPlusIdx == -1) - { - return true; - } - - // application/*+xml includes application/soap+xml - int otherPlusIdx = other.Subtype.LastIndexOf('+'); - - if (otherPlusIdx != -1) - { - string thisSubtypeNoSuffix = Subtype.Substring(0, thisPlusIdx); - string thisSubtypeSuffix = Subtype.Substring(thisPlusIdx + 1); - string otherSubtypeSuffix = other.Subtype.Substring(otherPlusIdx + 1); - - if (thisSubtypeSuffix == otherSubtypeSuffix && thisSubtypeNoSuffix == WildcardType) - { - return true; - } - } - } - } - - return false; - } - - public bool IsCompatibleWith(MimeType other) - { - if (other == null) - { - return false; - } - - if (IsWildcardType || other.IsWildcardType) - { - return true; - } - - if (Type == other.Type) - { - if (Subtype == other.Subtype) - { - return true; - } - - // Wildcard with suffix? e.g. application/*+xml - if (IsWildcardSubtype || other.IsWildcardSubtype) - { - int thisPlusIdx = Subtype.LastIndexOf('+'); - int otherPlusIdx = other.Subtype.LastIndexOf('+'); - - if (thisPlusIdx == -1 && otherPlusIdx == -1) - { - return true; - } - - if (thisPlusIdx != -1 && otherPlusIdx != -1) - { - string thisSubtypeNoSuffix = Subtype.Substring(0, thisPlusIdx); - string otherSubtypeNoSuffix = other.Subtype.Substring(0, otherPlusIdx); - string thisSubtypeSuffix = Subtype.Substring(thisPlusIdx + 1); - string otherSubtypeSuffix = other.Subtype.Substring(otherPlusIdx + 1); - - if (thisSubtypeSuffix == otherSubtypeSuffix && (thisSubtypeNoSuffix == WildcardType || otherSubtypeNoSuffix == WildcardType)) - { - return true; - } - } - } - } - - return false; - } - - public bool EqualsTypeAndSubtype(MimeType other) - { - if (other == null) - { - return false; - } - - return Type.Equals(other.Type, StringComparison.OrdinalIgnoreCase) && Subtype.Equals(other.Subtype, StringComparison.OrdinalIgnoreCase); - } - - public bool IsPresentIn(ICollection mimeTypes) - where T : MimeType - { - foreach (T mimeType in mimeTypes) - { - if (mimeType.EqualsTypeAndSubtype(this)) - { - return true; - } - } - - return false; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not MimeType otherType) - { - return false; - } - - return Type.Equals(otherType.Type, StringComparison.OrdinalIgnoreCase) && Subtype.Equals(otherType.Subtype, StringComparison.OrdinalIgnoreCase) && - ParametersAreEqual(otherType); - } - - public override int GetHashCode() - { - return HashCode.Combine(Type, Subtype, Parameters); - } - - public override string ToString() - { - string value = _toStringValue; - - if (value == null) - { - var builder = new StringBuilder(); - AppendTo(builder); - value = builder.ToString(); - _toStringValue = value; - } - - return value; - } - - public int CompareTo(MimeType other) - { - int comp = string.Compare(Type, other.Type, StringComparison.Ordinal); - - if (comp != 0) - { - return comp; - } - - comp = string.Compare(Subtype, other.Subtype, StringComparison.Ordinal); - - if (comp != 0) - { - return comp; - } - - comp = Parameters.Count - other.Parameters.Count; - - if (comp != 0) - { - return comp; - } - - var thisAttributes = new SortedSet(Parameters.Keys, StringComparer.OrdinalIgnoreCase); - var otherAttributes = new SortedSet(other.Parameters.Keys, StringComparer.OrdinalIgnoreCase); - - using SortedSet.Enumerator thisAttributesIterator = thisAttributes.GetEnumerator(); - using SortedSet.Enumerator otherAttributesIterator = otherAttributes.GetEnumerator(); - StringComparer comparer = StringComparer.OrdinalIgnoreCase; - - while (thisAttributesIterator.MoveNext()) - { - otherAttributesIterator.MoveNext(); - - string thisAttribute = thisAttributesIterator.Current; - string otherAttribute = otherAttributesIterator.Current; - - comp = comparer.Compare(thisAttribute, otherAttribute); - - if (comp != 0) - { - return comp; - } - - if (thisAttribute == ParamCharset) - { - Encoding thisCharset = Encoding; - Encoding otherCharset = other.Encoding; - - if (!Equals(thisCharset, otherCharset)) - { - if (thisCharset == null) - { - return -1; - } - - if (otherCharset == null) - { - return 1; - } - - comp = comparer.Compare(thisCharset.BodyName, otherCharset.BodyName); - - if (comp != 0) - { - return comp; - } - } - } - else - { - string thisValue = Parameters[thisAttribute]; - string otherValue = other.Parameters[otherAttribute] ?? string.Empty; - - comp = string.Compare(thisValue, otherValue, StringComparison.Ordinal); - - if (comp != 0) - { - return comp; - } - } - } - - return 0; - } - - internal void AppendTo(StringBuilder builder) - { - builder.Append(Type); - builder.Append('/'); - builder.Append(Subtype); - AppendTo(Parameters, builder); - } - - private static IDictionary AddCharsetParameter(Encoding charset, IDictionary parameters) - { - IDictionary map = new Dictionary(parameters) - { - [ParamCharset] = charset.BodyName - }; - - return map; - } - - private bool IsQuotedString(string s) - { - if (s.Length < 2) - { - return false; - } - - return (s.StartsWith('"') && s.EndsWith('"')) || (s.StartsWith('\'') && s.EndsWith('\'')); - } - - private bool ParametersAreEqual(MimeType other) - { - if (Parameters.Count != other.Parameters.Count) - { - return false; - } - - foreach (KeyValuePair entry in Parameters) - { - string key = entry.Key; - - if (!other.Parameters.ContainsKey(key)) - { - return false; - } - - if (key == ParamCharset) - { - if (!Equals(Encoding, other.Encoding)) - { - return false; - } - } - else if (entry.Value != other.Parameters[key]) - { - return false; - } - } - - return true; - } - - private void CheckToken(string token) - { - foreach (char ch in token) - { - if (!Token.Get(ch)) - { - throw new ArgumentException($"Invalid token character '{ch}' in token \"{token}\"", nameof(token)); - } - } - } - - private Encoding GetEncoding(string name) - { - if (name.Equals("utf-7", StringComparison.OrdinalIgnoreCase)) - { - throw new NotSupportedException("The UTF-7 encoding is insecure and should not be used. Consider using UTF-8 instead."); - } - - if (name.Equals("utf-8", StringComparison.OrdinalIgnoreCase)) - { - return EncodingUtils.Utf8; - } - - if (name.Equals("utf-16", StringComparison.OrdinalIgnoreCase)) - { - return EncodingUtils.Utf16; - } - - if (name.Equals("utf-16be", StringComparison.OrdinalIgnoreCase)) - { - return EncodingUtils.Utf16BigEndian; - } - - if (name.Equals("utf-32", StringComparison.OrdinalIgnoreCase)) - { - return EncodingUtils.Utf32; - } - - if (name.Equals("utf-32BE", StringComparison.OrdinalIgnoreCase)) - { - return EncodingUtils.Utf32BigEndian; - } - - return Encoding.GetEncoding(name); - } - - private void AppendTo(IDictionary map, StringBuilder builder) - { - foreach (KeyValuePair entry in map) - { - builder.Append(';'); - builder.Append(entry.Key); - builder.Append('='); - builder.Append(entry.Value); - } - } - - public class SpecificityComparator : IComparer - where T : MimeType - { - public int Compare(T x, T y) - { - if (x.IsWildcardType && !y.IsWildcardType) - { - // */* < audio/* - return 1; - } - - if (y.IsWildcardType && !x.IsWildcardType) - { - // audio/* > */* - return -1; - } - - if (x.Type != y.Type) - { - // audio/basic == text/html - return 0; - } - - // mediaType1.getType().Equals(mediaType2.getType()) - if (x.IsWildcardSubtype && !y.IsWildcardSubtype) - { - // audio/* < audio/basic - return 1; - } - - if (y.IsWildcardSubtype && !x.IsWildcardSubtype) - { - // audio/basic > audio/* - return -1; - } - - if (x.Subtype != y.Subtype) - { - // audio/basic == audio/wave - return 0; - } - - // mediaType2.Subtype.Equals(mediaType2.Subtype) - return CompareParameters(x, y); - } - - protected int CompareParameters(T mimeType1, T mimeType2) - { - int paramsSize1 = mimeType1.Parameters.Count; - int paramsSize2 = mimeType2.Parameters.Count; - return paramsSize1.CompareTo(paramsSize2); // audio/basic;level=1 < audio/basic - } - } -} diff --git a/src/Common/src/Common/Util/MimeTypeUtils.cs b/src/Common/src/Common/Util/MimeTypeUtils.cs deleted file mode 100644 index 48b4d63548..0000000000 --- a/src/Common/src/Common/Util/MimeTypeUtils.cs +++ /dev/null @@ -1,239 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Text; - -namespace Steeltoe.Common.Util; - -public static class MimeTypeUtils -{ - public const string AllValue = "*/*"; - public const string ApplicationJsonValue = "application/json"; - public const string ApplicationOctetStreamValue = "application/octet-stream"; - public const string ApplicationXmlValue = "application/xml"; - public const string ImageGifValue = "image/gif"; - public const string ImageJpegValue = "image/jpeg"; - public const string ImagePngValue = "image/png"; - public const string TextHtmlValue = "text/html"; - public const string TextPlainValue = "text/plain"; - public const string TextXmlValue = "text/xml"; - - private static readonly ConcurrentDictionary CachedMimeTypes = new(); - private static readonly char[] BoundaryChars = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray(); - public static readonly IComparer SpecificityComparator = new MimeType.SpecificityComparator(); - public static readonly MimeType All = new("*", "*"); - public static readonly MimeType ApplicationJson = new("application", "json"); - public static readonly MimeType ApplicationOctetStream = new("application", "octet-stream"); - public static readonly MimeType ApplicationXml = new("application", "xml"); - public static readonly MimeType ImageGif = new("image", "gif"); - public static readonly MimeType ImageJpeg = new("image", "jpeg"); - public static readonly MimeType ImagePng = new("image", "png"); - public static readonly MimeType TextHtml = new("text", "html"); - public static readonly MimeType TextPlain = new("text", "plain"); - public static readonly MimeType TextXml = new("text", "xml"); - - public static MimeType ParseMimeType(string mimeType) - { - return CachedMimeTypes.GetOrAdd(mimeType, ParseMimeTypeInternal(mimeType)); - } - - public static List ParseMimeTypes(string mimeTypes) - { - if (string.IsNullOrEmpty(mimeTypes)) - { - return new List(); - } - - List tokens = Tokenize(mimeTypes); - var results = new List(); - - foreach (string token in tokens) - { - results.Add(ParseMimeType(token)); - } - - return results; - } - - public static List Tokenize(string mimeTypes) - { - if (string.IsNullOrEmpty(mimeTypes)) - { - return new List(); - } - - var tokens = new List(); - bool inQuotes = false; - int startIndex = 0; - int i = 0; - - while (i < mimeTypes.Length) - { - switch (mimeTypes[i]) - { - case '"': - inQuotes = !inQuotes; - break; - case ',': - if (!inQuotes) - { - tokens.Add(mimeTypes.Substring(startIndex, i - startIndex)); - startIndex = i + 1; - } - - break; - case '\\': - i++; - break; - } - - i++; - } - - tokens.Add(mimeTypes.Substring(startIndex)); - return tokens; - } - - public static string ToString(ICollection mimeTypes) - { - var builder = new StringBuilder(); - - foreach (MimeType mimeType in mimeTypes) - { - mimeType.AppendTo(builder); - builder.Append(", "); - } - - string built = builder.ToString(); - - if (built.EndsWith(", ", StringComparison.Ordinal)) - { - built = built.Substring(0, built.Length - 2); - } - - return built; - } - - public static void SortBySpecificity(List mimeTypes) - { - ArgumentGuard.NotNull(mimeTypes); - - if (mimeTypes.Count > 1) - { - mimeTypes.Sort(SpecificityComparator); - } - } - - public static char[] GenerateMultipartBoundary() - { - int size = Random.Shared.Next(11) + 30; - char[] boundary = new char[size]; - - for (int i = 0; i < boundary.Length; i++) - { - boundary[i] = BoundaryChars[Random.Shared.Next(BoundaryChars.Length)]; - } - - return boundary; - } - - public static string GenerateMultipartBoundaryString() - { - return new string(GenerateMultipartBoundary()); - } - - private static MimeType ParseMimeTypeInternal(string mimeType) - { - ArgumentGuard.NotNullOrEmpty(mimeType); - - int index = mimeType.IndexOf(';'); - string fullType = (index >= 0 ? mimeType.Substring(0, index) : mimeType).Trim(); - - if (string.IsNullOrEmpty(fullType)) - { - throw new ArgumentException("mimeType must contain a value before the ';' separator.", nameof(mimeType)); - } - - if (fullType == MimeType.WildcardType) - { - fullType = "*/*"; - } - - int subIndex = fullType.IndexOf('/'); - - if (subIndex == -1) - { - throw new ArgumentException($"{mimeType} does not contain '/'", nameof(mimeType)); - } - - if (subIndex == fullType.Length - 1) - { - throw new ArgumentException($"{mimeType} does not contain subtype after '/'", nameof(mimeType)); - } - - string type = fullType.Substring(0, subIndex); - string subtype = fullType.Substring(subIndex + 1, fullType.Length - type.Length - 1); - - if (type == MimeType.WildcardType && subtype != MimeType.WildcardType) - { - throw new ArgumentException($"{mimeType} wildcard type is legal only in '*/*' (all mime types)", nameof(mimeType)); - } - - Dictionary parameters = null; - - do - { - int nextIndex = index + 1; - bool quoted = false; - - while (nextIndex < mimeType.Length) - { - char ch = mimeType[nextIndex]; - - if (ch == ';') - { - if (!quoted) - { - break; - } - } - else if (ch == '"') - { - quoted = !quoted; - } - - nextIndex++; - } - - string parameter = mimeType.Substring(index + 1, nextIndex - index - 1).Trim(); - - if (parameter.Length > 0) - { - parameters ??= new Dictionary(4); - - int eqIndex = parameter.IndexOf('='); - - if (eqIndex >= 0) - { - string attribute = parameter.Substring(0, eqIndex).Trim(); - string value = parameter.Substring(eqIndex + 1, parameter.Length - eqIndex - 1).Trim(); - parameters[attribute] = value; - } - } - - index = nextIndex; - } - while (index < mimeType.Length); - - try - { - return new MimeType(type, subtype, parameters); - } - catch (Exception ex) - { - throw new ArgumentException($"The specified value '{mimeType}' is invalid.", ex); - } - } -} diff --git a/src/Common/src/Common/Util/ObjectEquality.cs b/src/Common/src/Common/Util/ObjectEquality.cs deleted file mode 100644 index df6cf8450c..0000000000 --- a/src/Common/src/Common/Util/ObjectEquality.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Common.Util; - -internal static class ObjectEquality -{ - public static bool ObjectOrCollectionEquals(object obj1, object obj2) - { - if (ReferenceEquals(obj1, obj2)) - { - return true; - } - - if (obj1 is null) - { - return false; - } - - if (obj1 is IEnumerable enumerable1 && obj2 is IEnumerable enumerable2) - { - return enumerable1.Cast().SequenceEqual(enumerable2.Cast()); - } - - return obj1.Equals(obj2); - } - - public static int GetObjectOrCollectionHashCode(object obj) - { - if (obj is null) - { - return 0; - } - - if (obj is IEnumerable enumerable) - { - var hashCode = default(HashCode); - - foreach (object item in enumerable) - { - hashCode.Add(item); - } - - return hashCode.ToHashCode(); - } - - return obj.GetHashCode(); - } -} diff --git a/src/Common/src/Common/Util/PatternMatchUtils.cs b/src/Common/src/Common/Util/PatternMatchUtils.cs deleted file mode 100644 index a1be3bc939..0000000000 --- a/src/Common/src/Common/Util/PatternMatchUtils.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public static class PatternMatchUtils -{ - public static bool SimpleMatch(string pattern, string str) - { - if (pattern == null || str == null) - { - return false; - } - - int firstIndex = pattern.IndexOf('*'); - - if (firstIndex == -1) - { - return pattern == str; - } - - if (firstIndex == 0) - { - if (pattern.Length == 1) - { - return true; - } - - int nextIndex = pattern.IndexOf('*', firstIndex + 1); - - if (nextIndex == -1) - { - return str.EndsWith(pattern.Substring(1), StringComparison.Ordinal); - } - - string part = pattern.Substring(1, nextIndex - 1); - - if (string.IsNullOrEmpty(part)) - { - return SimpleMatch(pattern.Substring(nextIndex), str); - } - - int partIndex = str.IndexOf(part, StringComparison.Ordinal); - - while (partIndex != -1) - { - if (SimpleMatch(pattern.Substring(nextIndex), str.Substring(partIndex + part.Length))) - { - return true; - } - - partIndex = str.IndexOf(part, partIndex + 1, StringComparison.Ordinal); - } - - return false; - } - - return str.Length >= firstIndex && pattern.Substring(0, firstIndex) == str.Substring(0, firstIndex) && - SimpleMatch(pattern.Substring(firstIndex), str.Substring(firstIndex)); - } - - public static bool SimpleMatch(string[] patterns, string str) - { - if (patterns != null) - { - foreach (string pattern in patterns) - { - if (SimpleMatch(pattern, str)) - { - return true; - } - } - } - - return false; - } -} diff --git a/src/Common/src/Common/Util/SimpleRouteMatcher.cs b/src/Common/src/Common/Util/SimpleRouteMatcher.cs deleted file mode 100644 index dbe3aecece..0000000000 --- a/src/Common/src/Common/Util/SimpleRouteMatcher.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Util; - -public class SimpleRouteMatcher : IRouteMatcher -{ - public IPathMatcher PathMatcher { get; } - - public SimpleRouteMatcher(IPathMatcher pathMatcher) - { - ArgumentGuard.NotNull(pathMatcher); - - PathMatcher = pathMatcher; - } - - public IRoute ParseRoute(string routeValue) - { - return new DefaultRoute(routeValue); - } - - public bool IsPattern(string routeValue) - { - return PathMatcher.IsPattern(routeValue); - } - - public string Combine(string pattern1, string pattern2) - { - return PathMatcher.Combine(pattern1, pattern2); - } - - public bool Match(string pattern, IRoute route) - { - return PathMatcher.Match(pattern, route.Value); - } - - public IDictionary MatchAndExtract(string pattern, IRoute route) - { - if (!Match(pattern, route)) - { - return null; - } - - return PathMatcher.ExtractUriTemplateVariables(pattern, route.Value); - } - - public IComparer GetPatternComparer(IRoute route) - { - return PathMatcher.GetPatternComparer(route.Value); - } - - private sealed class DefaultRoute : IRoute - { - public string Value { get; } - - public DefaultRoute(string path) - { - Value = path; - } - } -} diff --git a/src/Common/src/Common/Util/SubclassClassifier.cs b/src/Common/src/Common/Util/SubclassClassifier.cs deleted file mode 100644 index 704797a927..0000000000 --- a/src/Common/src/Common/Util/SubclassClassifier.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; - -namespace Steeltoe.Common.Util; - -public class SubclassClassifier : IClassifier - where TSource : class -{ - protected ConcurrentDictionary TypeMap { get; set; } - - public TTarget DefaultValue { get; set; } - - public SubclassClassifier() - : this(default) - { - } - - public SubclassClassifier(TTarget defaultValue) - : this(new ConcurrentDictionary(), defaultValue) - { - } - - public SubclassClassifier(ConcurrentDictionary typeMap, TTarget defaultValue) - { - TypeMap = new ConcurrentDictionary(typeMap); - DefaultValue = defaultValue; - } - - public virtual TTarget Classify(TSource classifiable) - { - if (classifiable == null) - { - return DefaultValue; - } - - Type type = classifiable.GetType(); - - if (TypeMap.TryGetValue(type, out TTarget result)) - { - return result; - } - - // check for subclasses - bool foundValue = false; - var value = default(TTarget); - - for (Type currentType = type; currentType != typeof(object); currentType = currentType.BaseType) - { - if (TypeMap.TryGetValue(currentType, out value)) - { - foundValue = true; - break; - } - } - - if (foundValue) - { - TypeMap.TryAdd(type, value); - return value; - } - - return DefaultValue; - } -} diff --git a/src/Common/test/Common.Expression.Test/Contexts/ApplicationContextExpressionTests.cs b/src/Common/test/Common.Expression.Test/Contexts/ApplicationContextExpressionTests.cs deleted file mode 100644 index 0b07c94f5e..0000000000 --- a/src/Common/test/Common.Expression.Test/Contexts/ApplicationContextExpressionTests.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Services; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Contexts; - -public sealed class ApplicationContextExpressionTests -{ - private readonly IServiceProvider _serviceProvider; - - public ApplicationContextExpressionTests() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary - { - { "code", "123" } - }).Build(); - - var collection = new ServiceCollection(); - collection.AddSingleton(configurationRoot); - - collection.AddSingleton(_ => - { - var tb = new TestService - { - ServiceName = "tb0" - }; - - return tb; - }); - - collection.AddSingleton(_ => - { - var tb = new TestService - { - ServiceName = "tb1" - }; - - return tb; - }); - - collection.AddSingleton(p => - { - var context = new GenericApplicationContext(p, configurationRoot) - { - ServiceExpressionResolver = new StandardServiceExpressionResolver() - }; - - return context; - }); - - _serviceProvider = collection.BuildServiceProvider(true); - } - - [Fact] - public void GenericApplicationContext() - { - var context = _serviceProvider.GetService(); - IEnumerable services = context.GetServices(); - Assert.Equal(2, services.Count()); - Assert.Equal("XXXtb0YYYZZZ", Evaluate("XXX#{tb0.Name}YYYZZZ")); - Assert.Equal("123", Evaluate("${code}")); - Assert.Equal("123", Evaluate("${code?#{null}}")); - Assert.Null(Evaluate("${codeX?#{null}}")); - Assert.Equal("123 tb1", Evaluate("${code} #{tb1.Name}")); - Assert.Equal("foo tb1", Evaluate("${bar?foo} #{tb1.Name}")); - } - - private object Evaluate(string value) - { - var context = _serviceProvider.GetService(); - string result = context.ResolveEmbeddedValue(value); - return context.ServiceExpressionResolver.Evaluate(result, new ServiceExpressionContext(context)); - } - - public sealed class TestService : IServiceNameAware - { - public string Name => ServiceName; - - public string ServiceName { get; set; } = "NotSet"; - } - - public sealed class ValueTestService : IServiceNameAware - { - public string ServiceName { get; set; } = "tb3"; - } -} diff --git a/src/Common/test/Common.Expression.Test/Contexts/ConfigurationAccessorTests.cs b/src/Common/test/Common.Expression.Test/Contexts/ConfigurationAccessorTests.cs deleted file mode 100644 index 926103c108..0000000000 --- a/src/Common/test/Common.Expression.Test/Contexts/ConfigurationAccessorTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Contexts; - -public sealed class ConfigurationAccessorTests -{ - private readonly IServiceProvider _serviceProvider; - - public ConfigurationAccessorTests() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary - { - { "my.name", "myservice" } - }).Build(); - - var collection = new ServiceCollection(); - collection.AddSingleton(configurationRoot); - collection.AddSingleton(p => new GenericApplicationContext(p, configurationRoot)); - - _serviceProvider = collection.BuildServiceProvider(true); - } - - [Fact] - public void TestBraceAccess() - { - var rootObject = new ServiceExpressionContext(_serviceProvider.GetService()); - var context = new StandardEvaluationContext(rootObject); - context.AddPropertyAccessor(new ServiceExpressionContextAccessor()); - context.AddPropertyAccessor(new ConfigurationAccessor()); - var sep = new SpelExpressionParser(); - - // basic - IExpression ex = sep.ParseExpression("configuration['my.name']"); - Assert.Equal("myservice", ex.GetValue(context)); - } -} diff --git a/src/Common/test/Common.Expression.Test/Contexts/DictionaryAccessorTests.cs b/src/Common/test/Common.Expression.Test/Contexts/DictionaryAccessorTests.cs deleted file mode 100644 index 9d5d3c0225..0000000000 --- a/src/Common/test/Common.Expression.Test/Contexts/DictionaryAccessorTests.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Contexts; - -public sealed class DictionaryAccessorTests -{ - [Fact] - public void MapAccessorCompilable() - { - Dictionary testMap = GetSimpleTestDictionary(); - var sec = new StandardEvaluationContext(); - sec.AddPropertyAccessor(new DictionaryAccessor()); - var sep = new SpelExpressionParser(); - - // basic - IExpression ex = sep.ParseExpression("foo"); - Assert.Equal("bar", ex.GetValue(sec, testMap)); - - // compound expression - ex = sep.ParseExpression("foo.ToUpperInvariant()"); - Assert.Equal("BAR", ex.GetValue(sec, testMap)); - - // nested map - Dictionary> nestedMap = GetNestedTestDictionary(); - ex = sep.ParseExpression("aaa.foo.ToUpperInvariant()"); - Assert.Equal("BAR", ex.GetValue(sec, nestedMap)); - - // avoiding inserting checkcast because first part of expression returns a Map - ex = sep.ParseExpression("Map.foo"); - var mapGetter = new MapGetter(); - Assert.Equal("bar", ex.GetValue(sec, mapGetter)); - } - - private Dictionary GetSimpleTestDictionary() - { - var map = new Dictionary - { - { "foo", "bar" } - }; - - return map; - } - - private Dictionary> GetNestedTestDictionary() - { - var map = new Dictionary - { - { "foo", "bar" } - }; - - var map2 = new Dictionary> - { - { "aaa", map } - }; - - return map2; - } - - public sealed class MapGetter - { - private readonly Dictionary _map = new(); - - public IDictionary Map => _map; - - public MapGetter() - { - _map.Add("foo", "bar"); - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Contexts/ServiceFactoryAccessorTests.cs b/src/Common/test/Common.Expression.Test/Contexts/ServiceFactoryAccessorTests.cs deleted file mode 100644 index 91c39797d4..0000000000 --- a/src/Common/test/Common.Expression.Test/Contexts/ServiceFactoryAccessorTests.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Services; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Contexts; - -public sealed class ServiceFactoryAccessorTests -{ - private readonly IServiceProvider _serviceProvider; - - public ServiceFactoryAccessorTests() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - var collection = new ServiceCollection(); - collection.AddSingleton(configurationRoot); - collection.AddSingleton(p => new GenericApplicationContext(p, configurationRoot)); - collection.AddSingleton(typeof(Car)); - collection.AddSingleton(typeof(Boat)); - _serviceProvider = collection.BuildServiceProvider(true); - } - - [Fact] - public void TestServiceAccess() - { - var appContext = _serviceProvider.GetService(); - - var context = new StandardEvaluationContext - { - ServiceResolver = new ServiceFactoryResolver(appContext) - }; - - IExpression expr = new SpelExpressionParser().ParseRaw($"@'T({typeof(ServiceFactoryAccessorTests).FullName}$Car)car'.Color"); - Assert.Equal("red", expr.GetValue(context)); - expr = new SpelExpressionParser().ParseRaw("@car.Color"); - Assert.Equal("red", expr.GetValue(context)); - - expr = new SpelExpressionParser().ParseRaw($"@'T({typeof(ServiceFactoryAccessorTests).FullName}$Boat)boat'.Color"); - Assert.Equal("blue", expr.GetValue(context)); - expr = new SpelExpressionParser().ParseRaw("@boat.Color"); - Assert.Equal("blue", expr.GetValue(context)); - - IExpression noServiceExpr = new SpelExpressionParser().ParseRaw("@truck"); - Assert.Throws(() => noServiceExpr.GetValue(context)); - } - - public sealed class Car : IServiceNameAware - { - public string Color => "red"; - - public string ServiceName { get; set; } = "car"; - } - - public sealed class Boat : IServiceNameAware - { - public string Color => "blue"; - - public string ServiceName { get; set; } = "boat"; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/AbstractExpressionTests.cs b/src/Common/test/Common.Expression.Test/Spring/AbstractExpressionTests.cs deleted file mode 100644 index c80c277e93..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/AbstractExpressionTests.cs +++ /dev/null @@ -1,315 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Globalization; -using System.Text; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public abstract class AbstractExpressionTests -{ - protected const bool ShouldBeWritable = true; - private static readonly bool IsDebug = bool.Parse(bool.FalseString); - protected static readonly bool ShouldNotBeWritable; - protected IExpressionParser Parser { get; } = new SpelExpressionParser(); - protected StandardEvaluationContext Context { get; } = TestScenarioCreator.GetTestEvaluationContext(); - - public virtual void EvaluateAndAskForReturnType(string expression, object expectedValue, Type expectedResultType) - { - IExpression expr = Parser.ParseExpression(expression); - Assert.NotNull(expr); - - if (IsDebug) - { - SpelUtilities.PrintAbstractSyntaxTree(Console.Out, expr); - } - - object value = expr.GetValue(Context, expectedResultType); - - if (value == null) - { - if (expectedValue == null) - { - return; // no point doing other checks - } - - Assert.Fail($"Expression returned null value, but expected '{expectedValue}'"); - } - - Type resultType = value.GetType(); - Assert.Equal(expectedResultType, resultType); - Assert.Equal(expectedValue, value); - } - - public virtual void Evaluate(string expression, object expectedValue, Type expectedResultType) - { - IExpression expr = Parser.ParseExpression(expression); - Assert.NotNull(expr); - - if (IsDebug) - { - SpelUtilities.PrintAbstractSyntaxTree(Console.Out, expr); - } - - object value = expr.GetValue(Context); - - // Check the return value - if (value == null) - { - if (expectedValue == null) - { - return; // no point doing other checks - } - - Assert.Fail($"Expression returned null value, but expected '{expectedValue}'"); - } - - Type resultType = value.GetType(); - Assert.Equal(expectedResultType, resultType); - Assert.Equal(expectedValue, expectedValue is string ? StringValueOf(value) : value); - } - - public virtual void Evaluate(string expression, object expectedValue, Type expectedClassOfResult, bool shouldBeWritable) - { - IExpression expr = Parser.ParseExpression(expression); - Assert.NotNull(expr); - - if (IsDebug) - { - SpelUtilities.PrintAbstractSyntaxTree(Console.Out, expr); - } - - object value = expr.GetValue(Context); - - if (value == null) - { - if (expectedValue == null) - { - return; // no point doing other checks - } - - Assert.Fail($"Expression returned null value, but expected '{expectedValue}'"); - } - - Type resultType = value.GetType(); - Assert.Equal(expectedValue, expectedValue is string ? StringValueOf(value) : value); - - Assert.Equal(expectedClassOfResult, resultType); - Assert.Equal(shouldBeWritable, expr.IsWritable(Context)); - } - - protected static void PrintDimension(StringBuilder sb, Array array, int[] indexes, int dimension) - { - bool isLeaf = dimension == array.Rank - 1; - - if (isLeaf) - { - int len = array.GetLength(dimension); - - for (int i = 0; i < len; i++) - { - indexes[dimension] = i; - sb.Append('(').Append(string.Join(",", indexes)).Append(")="); - object val = array.GetValue(indexes); - sb.Append(val == null ? "null" : val.ToString()); - - sb.Append(','); - } - } - else - { - int dimLen = array.GetLength(dimension); - - for (int i = 0; i < dimLen; i++) - { - indexes[dimension] = i; - PrintDimension(sb, array, indexes, dimension + 1); - } - } - } - - protected static string PrintArray(Array array) - { - int[] indexes = new int[array.Rank]; - var sb = new StringBuilder(); - sb.Append(array.GetType().GetElementType().FullName); - - for (int i = 0; i < array.Rank; i++) - { - sb.Append('[').Append(array.GetLength(i)).Append(']'); - } - - sb.Append('{'); - PrintDimension(sb, array, indexes, 0); - sb.Append('}'); - return sb.ToString(); - } - - protected static string StringValueOf(object value) - { - return StringValueOf(value, false); - } - - protected static string StringValueOf(object value, bool isNested) - { - // do something nice for arrays - if (value == null) - { - return "null"; - } - - Type valueType = value.GetType(); - - if (valueType.IsArray) - { - string result = PrintArray(value as Array); - return result; - } - - if (value is IDictionary dictionary) - { - var sb = new StringBuilder("{"); - - foreach (DictionaryEntry obj in dictionary) - { - sb.Append(StringValueOf(obj.Key)); - sb.Append('='); - sb.Append(StringValueOf(obj.Value)); - sb.Append(','); - } - - if (sb[^1] == ',') - { - return $"{sb.ToString(0, sb.Length - 1)}}}"; - } - - sb.Append('}'); - return sb.ToString(); - } - - if (value is IEnumerable enumerable && value is not string) - { - var sb = new StringBuilder("["); - - foreach (object obj in enumerable) - { - if (obj is IEnumerable && obj is not string) - { - sb.Append(StringValueOf(obj)); - sb.Append(','); - } - else - { - sb.Append(obj); - sb.Append(','); - } - } - - if (sb[^1] == ',') - { - return $"{sb.ToString(0, sb.Length - 1)}]"; - } - - sb.Append(']'); - return sb.ToString(); - } - - if (value is IFormattable formattable) - { - return formattable.ToString(null, CultureInfo.InvariantCulture); - } - - return value.ToString(); - } - - protected virtual void EvaluateAndCheckError(string expression, SpelMessage expectedMessage, params object[] otherProperties) - { - EvaluateAndCheckError(expression, null, expectedMessage, otherProperties); - } - - protected virtual void EvaluateAndCheckError(string expression, Type expectedReturnType, SpelMessage expectedMessage, params object[] otherProperties) - { - var ex = Assert.Throws(() => - { - IExpression expr = Parser.ParseExpression(expression); - Assert.NotNull(expr); - - if (expectedReturnType != null) - { - expr.GetValue(Context, expectedReturnType); - } - else - { - expr.GetValue(Context); - } - }); - - Assert.Equal(expectedMessage, ex.MessageCode); - - if (otherProperties != null && otherProperties.Length != 0) - { - // first one is expected position of the error within the string - int pos = (int)otherProperties[0]; - Assert.Equal(pos, ex.Position); - - if (otherProperties.Length > 1) - { - // Check inserts match - object[] inserts = ex.Inserts; - Assert.True(inserts.Length >= otherProperties.Length - 1); - object[] expectedInserts = new object[inserts.Length]; - Array.Copy(otherProperties, 1, expectedInserts, 0, expectedInserts.Length); - Assert.Equal(expectedInserts.Length, inserts.Length); - - for (int i = 0; i < inserts.Length; i++) - { - Assert.Equal(expectedInserts[i], inserts[i]); - } - } - } - } - - protected virtual void ParseAndCheckError(string expression, SpelMessage expectedMessage, params object[] otherProperties) - { - var ex = Assert.Throws(() => - { - IExpression expr = Parser.ParseExpression(expression); - - if (IsDebug) - { - SpelUtilities.PrintAbstractSyntaxTree(Console.Out, expr); - } - }); - - Assert.Equal(expectedMessage, ex.MessageCode); - - if (otherProperties != null && otherProperties.Length != 0) - { - // first one is expected position of the error within the string - int pos = (int)otherProperties[0]; - Assert.Equal(pos, ex.Position); - - if (otherProperties.Length > 1) - { - // Check inserts match - object[] inserts = ex.Inserts; - Assert.True(inserts.Length >= otherProperties.Length - 1); - object[] expectedInserts = new object[inserts.Length]; - Array.Copy(otherProperties, 1, expectedInserts, 0, expectedInserts.Length); - Assert.Equal(expectedInserts.Length, inserts.Length); - - for (int i = 0; i < inserts.Length; i++) - { - Assert.Equal(expectedInserts[i], inserts[i]); - } - } - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/ArrayConstructorTests.cs b/src/Common/test/Common.Expression.Test/Spring/ArrayConstructorTests.cs deleted file mode 100644 index 295468cad2..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/ArrayConstructorTests.cs +++ /dev/null @@ -1,227 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Text; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class ArrayConstructorTests : AbstractExpressionTests -{ - [Fact] - public void SimpleArrayWithInitializer() - { - EvaluateArrayBuildingExpression("new int[]{1,2,3}", "[1,2,3]"); - EvaluateArrayBuildingExpression("new int[]{}", "[]"); - Evaluate("new int[]{}.Length", "0", typeof(int)); - Evaluate("new int[]{1,2,3}.Length", "3", typeof(int)); - } - - [Fact] - public void Conversion() - { - Evaluate("new String[]{1,2,3}[0]", "1", typeof(string)); - Evaluate("new int[]{'123'}[0]", 123, typeof(int)); - } - - [Fact] - public void MultidimensionalArrays() - { - EvaluateAndCheckError("new int[][]{{1,2},{3,4}}", SpelMessage.MultidimensionalArrayInitializerNotSupported); - EvaluateAndCheckError("new int[3][]", SpelMessage.MissingArrayDimension); - EvaluateAndCheckError("new int[]", SpelMessage.MissingArrayDimension); - EvaluateAndCheckError("new String[]", SpelMessage.MissingArrayDimension); - EvaluateAndCheckError("new int[][1]", SpelMessage.MissingArrayDimension); - } - - [Fact] - public void PrimitiveTypeArrayConstructors() - { - EvaluateArrayBuildingExpression("new int[]{1,2,3,4}", "[1,2,3,4]"); - EvaluateArrayBuildingExpression("new boolean[]{true,false,true}", "[True,False,True]"); - EvaluateArrayBuildingExpression("new char[]{'a','b','c'}", "[a,b,c]"); - EvaluateArrayBuildingExpression("new long[]{1,2,3,4,5}", "[1,2,3,4,5]"); - EvaluateArrayBuildingExpression("new short[]{2,3,4,5,6}", "[2,3,4,5,6]"); - EvaluateArrayBuildingExpression("new double[]{1d,2d,3d,4d}", "[1.0,2.0,3.0,4.0]"); - EvaluateArrayBuildingExpression("new float[]{1f,2f,3f,4f}", "[1.0,2.0,3.0,4.0]"); - EvaluateArrayBuildingExpression("new byte[]{1,2,3,4}", "[1,2,3,4]"); - } - - [Fact] - public void PrimitiveTypeArrayConstructorsElements() - { - Evaluate("new int[]{1,2,3,4}[0]", 1, typeof(int)); - Evaluate("new boolean[]{true,false,true}[0]", true, typeof(bool)); - Evaluate("new char[]{'a','b','c'}[0]", 'a', typeof(char)); - Evaluate("new long[]{1,2,3,4,5}[0]", 1L, typeof(long)); - Evaluate("new short[]{2,3,4,5,6}[0]", (short)2, typeof(short)); - Evaluate("new double[]{1d,2d,3d,4d}[0]", 1D, typeof(double)); - Evaluate("new float[]{1f,2f,3f,4f}[0]", 1F, typeof(float)); - Evaluate("new byte[]{1,2,3,4}[0]", (byte)1, typeof(byte)); - Evaluate("new String(new char[]{'h','e','l','l','o'})", "hello", typeof(string)); - } - - [Fact] - public void ErrorCases() - { - EvaluateAndCheckError("new char[7]{'a','c','d','e'}", SpelMessage.InitializerLengthIncorrect); - EvaluateAndCheckError("new char[3]{'a','c','d','e'}", SpelMessage.InitializerLengthIncorrect); - EvaluateAndCheckError("new char[2]{'hello','world'}", SpelMessage.TypeConversionError); - EvaluateAndCheckError("new String('a','c','d')", SpelMessage.ConstructorInvocationProblem); - } - - [Fact] - public void TypeArrayConstructors() - { - Evaluate("new String[]{'a','b','c','d'}[1]", "b", typeof(string)); - EvaluateAndCheckError("new String[]{'a','b','c','d'}.size()", SpelMessage.MethodNotFound, 30, "size()", "System.String[]"); - Evaluate("new String[]{'a','b','c','d'}.Length", 4, typeof(int)); - } - - [Fact] - public void BasicArray() - { - Evaluate("new String[3]", "System.String[3]{(0)=null,(1)=null,(2)=null,}", typeof(string[])); - } - - [Fact] - public void MultiDimensionalArray() - { - Evaluate("new String[2][2]", "System.String[2][2]{(0,0)=null,(0,1)=null,(1,0)=null,(1,1)=null,}", typeof(string[,])); - - Evaluate("new String[3][2][1]", "System.String[3][2][1]{(0,0,0)=null,(0,1,0)=null,(1,0,0)=null,(1,1,0)=null,(2,0,0)=null,(2,1,0)=null,}", - typeof(string[,,])); - } - - [Fact] - public void ConstructorInvocation03() - { - EvaluateAndCheckError("new String[]", SpelMessage.MissingArrayDimension); - } - - [Fact] - public void ConstructorInvocation04() - { - EvaluateAndCheckError("new int[3]{'3','ghi','5'}", SpelMessage.TypeConversionError, 0); - } - - private void EvaluateArrayBuildingExpression(string expression, string expectedToString) - { - var parser = new SpelExpressionParser(); - IExpression e = parser.ParseExpression(expression); - object o = e.GetValue(); - Assert.NotNull(o); - Assert.True(o.GetType().IsArray); - var s = new StringBuilder(); - s.Append('['); - - if (o is int[] iArray) - { - for (int i = 0; i < iArray.Length; i++) - { - if (i > 0) - { - s.Append(','); - } - - s.Append(iArray[i]); - } - } - else if (o is bool[] bArray) - { - for (int i = 0; i < bArray.Length; i++) - { - if (i > 0) - { - s.Append(','); - } - - s.Append(bArray[i]); - } - } - else if (o is char[] cArray) - { - for (int i = 0; i < cArray.Length; i++) - { - if (i > 0) - { - s.Append(','); - } - - s.Append(cArray[i]); - } - } - else if (o is long[] lArray) - { - for (int i = 0; i < lArray.Length; i++) - { - if (i > 0) - { - s.Append(','); - } - - s.Append(lArray[i]); - } - } - else if (o is short[] sArray) - { - for (int i = 0; i < sArray.Length; i++) - { - if (i > 0) - { - s.Append(','); - } - - s.Append(sArray[i]); - } - } - else if (o is double[] dArray) - { - for (int i = 0; i < dArray.Length; i++) - { - if (i > 0) - { - s.Append(','); - } - - s.Append(dArray[i].ToString("F1", CultureInfo.InvariantCulture)); - } - } - else if (o is float[] fArray) - { - for (int i = 0; i < fArray.Length; i++) - { - if (i > 0) - { - s.Append(','); - } - - s.Append(fArray[i].ToString("F1", CultureInfo.InvariantCulture)); - } - } - else if (o is byte[] byArray) - { - for (int i = 0; i < byArray.Length; i++) - { - if (i > 0) - { - s.Append(','); - } - - s.Append(byArray[i]); - } - } - else - { - throw new InvalidOperationException($"Not supported {o.GetType()}"); - } - - s.Append(']'); - Assert.Equal(expectedToString, s.ToString()); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/Ast/FormatHelperTests.cs b/src/Common/test/Common.Expression.Test/Spring/Ast/FormatHelperTests.cs deleted file mode 100644 index 96906a92c4..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/Ast/FormatHelperTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring.Ast; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring.Ast; - -public sealed class FormatHelperTests -{ - [Fact] - public void FormatMethodWithSingleArgumentForMessage() - { - string message = FormatHelper.FormatMethodForMessage("foo", new List - { - typeof(string) - }); - - Assert.Equal("foo(System.String)", message); - } - - [Fact] - public void FormatMethodWithMultipleArgumentsForMessage() - { - string message = FormatHelper.FormatMethodForMessage("foo", new List - { - typeof(string), - typeof(int) - }); - - Assert.Equal("foo(System.String,System.Int32)", message); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/Ast/OpPlusTests.cs b/src/Common/test/Common.Expression.Test/Spring/Ast/OpPlusTests.cs deleted file mode 100644 index 87522f4d41..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/Ast/OpPlusTests.cs +++ /dev/null @@ -1,176 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Ast; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring.Ast; - -public sealed class OpPlusTests -{ - [Fact] - public void Test_EmptyOperands() - { - Assert.Throws(() => new OpPlus(-1, -1)); - } - - [Fact] - public void Test_UnaryPlusWithStringLiteral() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var str = new StringLiteral("word", -1, -1, "word"); - - var o = new OpPlus(-1, -1, str); - Assert.Throws(() => o.GetValueInternal(expressionState)); - } - - [Fact] - public void Test_UnaryPlusWithRealOperand() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var realLiteral = new RealLiteral("123.00", -1, -1, 123.0); - var o = new OpPlus(-1, -1, realLiteral); - ITypedValue value = o.GetValueInternal(expressionState); - - Assert.Equal(typeof(double), value.TypeDescriptor); - Assert.Equal(typeof(double), value.TypeDescriptor); - Assert.Equal(realLiteral.GetLiteralValue().Value, value.Value); - } - - [Fact] - public void Test_UnaryPlusWithLongOperand() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var longLiteral = new LongLiteral("123", -1, -1, 123L); - var o = new OpPlus(-1, -1, longLiteral); - ITypedValue value = o.GetValueInternal(expressionState); - - Assert.Equal(typeof(long), value.TypeDescriptor); - Assert.Equal(typeof(long), value.TypeDescriptor); - Assert.Equal(longLiteral.GetLiteralValue().Value, value.Value); - } - - [Fact] - public void Test_UnaryPlusWithIntOperand() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var intLiteral = new IntLiteral("123", -1, -1, 123); - var o = new OpPlus(-1, -1, intLiteral); - ITypedValue value = o.GetValueInternal(expressionState); - - Assert.Equal(typeof(int), value.TypeDescriptor); - Assert.Equal(typeof(int), value.TypeDescriptor); - Assert.Equal(intLiteral.GetLiteralValue().Value, value.Value); - } - - [Fact] - public void Test_BinaryPlusWithRealOperands() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var n1 = new RealLiteral("123.00", -1, -1, 123.0); - var n2 = new RealLiteral("456.00", -1, -1, 456.0); - var o = new OpPlus(-1, -1, n1, n2); - ITypedValue value = o.GetValueInternal(expressionState); - - Assert.Equal(typeof(double), value.TypeDescriptor); - Assert.Equal(typeof(double), value.TypeDescriptor); - Assert.Equal(123.0d + 456.0d, value.Value); - } - - [Fact] - public void Test_BinaryPlusWithLongOperands() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var n1 = new LongLiteral("123", -1, -1, 123L); - var n2 = new LongLiteral("456", -1, -1, 456L); - var o = new OpPlus(-1, -1, n1, n2); - ITypedValue value = o.GetValueInternal(expressionState); - Assert.Equal(typeof(long), value.TypeDescriptor); - Assert.Equal(typeof(long), value.TypeDescriptor); - Assert.Equal(123L + 456L, value.Value); - } - - [Fact] - public void Test_BinaryPlusWithIntOperands() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var n1 = new IntLiteral("123", -1, -1, 123); - var n2 = new IntLiteral("456", -1, -1, 456); - var o = new OpPlus(-1, -1, n1, n2); - ITypedValue value = o.GetValueInternal(expressionState); - Assert.Equal(typeof(int), value.TypeDescriptor); - Assert.Equal(typeof(int), value.TypeDescriptor); - Assert.Equal(123 + 456, value.Value); - } - - [Fact] - public void Test_BinaryPlusWithStringOperands() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var n1 = new StringLiteral("\"foo\"", -1, -1, "\"foo\""); - var n2 = new StringLiteral("\"bar\"", -1, -1, "\"bar\""); - var o = new OpPlus(-1, -1, n1, n2); - ITypedValue value = o.GetValueInternal(expressionState); - - Assert.Equal(typeof(string), value.TypeDescriptor); - Assert.Equal(typeof(string), value.TypeDescriptor); - Assert.Equal("foobar", value.Value); - } - - [Fact] - public void Test_BinaryPlusWithLeftStringOperand() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var n1 = new StringLiteral("\"number is \"", -1, -1, "\"number is \""); - var n2 = new LongLiteral("123", -1, -1, 123); - var o = new OpPlus(-1, -1, n1, n2); - ITypedValue value = o.GetValueInternal(expressionState); - Assert.Equal(typeof(string), value.TypeDescriptor); - Assert.Equal(typeof(string), value.TypeDescriptor); - Assert.Equal("number is 123", value.Value); - } - - [Fact] - public void Test_BinaryPlusWithRightStringOperand() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - - var n1 = new LongLiteral("123", -1, -1, 123); - var n2 = new StringLiteral("\" is a number\"", -1, -1, "\" is a number\""); - var o = new OpPlus(-1, -1, n1, n2); - ITypedValue value = o.GetValueInternal(expressionState); - Assert.Equal(typeof(string), value.TypeDescriptor); - Assert.Equal(typeof(string), value.TypeDescriptor); - Assert.Equal("123 is a number", value.Value); - } - - [Fact] - public void Test_BinaryPlusWithTime_ToString() - { - var expressionState = new ExpressionState(new StandardEvaluationContext()); - var time = default(DateTime); - - var var = new VariableReference("timeVar", -1, -1); - var.SetValue(expressionState, time); - - var n2 = new StringLiteral("\" is now\"", -1, -1, "\" is now\""); - var o = new OpPlus(-1, -1, var, n2); - ITypedValue value = o.GetValueInternal(expressionState); - Assert.Equal(typeof(string), value.TypeDescriptor); - Assert.Equal(typeof(string), value.TypeDescriptor); - Assert.Equal($"{time} is now", value.Value); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/BooleanExpressionTests.cs b/src/Common/test/Common.Expression.Test/Spring/BooleanExpressionTests.cs deleted file mode 100644 index 64e0e3f540..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/BooleanExpressionTests.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class BooleanExpressionTests : AbstractExpressionTests -{ - [Fact] - public void TestBooleanTrue() - { - Evaluate("true", true, typeof(bool)); - } - - [Fact] - public void TestBooleanFalse() - { - Evaluate("false", false, typeof(bool)); - } - - [Fact] - public void TestOr() - { - Evaluate("false or false", false, typeof(bool)); - Evaluate("false or true", true, typeof(bool)); - Evaluate("true or false", true, typeof(bool)); - Evaluate("true or true", true, typeof(bool)); - } - - [Fact] - public void TestAnd() - { - Evaluate("false and false", false, typeof(bool)); - Evaluate("false and true", false, typeof(bool)); - Evaluate("true and false", false, typeof(bool)); - Evaluate("true and true", true, typeof(bool)); - } - - [Fact] - public void TestNot() - { - Evaluate("!false", true, typeof(bool)); - Evaluate("!true", false, typeof(bool)); - - Evaluate("not false", true, typeof(bool)); - Evaluate("NoT true", false, typeof(bool)); - } - - [Fact] - public void TestCombinations01() - { - Evaluate("false and false or true", true, typeof(bool)); - Evaluate("true and false or true", true, typeof(bool)); - Evaluate("true and false or false", false, typeof(bool)); - } - - [Fact] - public void TestWritability() - { - Evaluate("true and true", true, typeof(bool), false); - Evaluate("true or true", true, typeof(bool), false); - Evaluate("!false", true, typeof(bool), false); - } - - [Fact] - public void TestBooleanErrors01() - { - EvaluateAndCheckError("1.0 or false", SpelMessage.TypeConversionError, 0); - EvaluateAndCheckError("false or 39.4", SpelMessage.TypeConversionError, 9); - EvaluateAndCheckError("true and 'hello'", SpelMessage.TypeConversionError, 9); - EvaluateAndCheckError(" 'hello' and 'goodbye'", SpelMessage.TypeConversionError, 1); - EvaluateAndCheckError("!35.2", SpelMessage.TypeConversionError, 1); - EvaluateAndCheckError("! 'foob'", SpelMessage.TypeConversionError, 2); - } - - [Fact] - public void TestConvertAndHandleNull() - { - // SPR-9445 - // without null conversion - EvaluateAndCheckError("null or true", SpelMessage.TypeConversionError, 0, "null", "System.Boolean"); - EvaluateAndCheckError("null and true", SpelMessage.TypeConversionError, 0, "null", "System.Boolean"); - EvaluateAndCheckError("!null", SpelMessage.TypeConversionError, 1, "null", "System.Boolean"); - EvaluateAndCheckError("null ? 'foo' : 'bar'", SpelMessage.TypeConversionError, 0, "null", "System.Boolean"); - - Context.TypeConverter = new StandardTypeConverter(new TestGenericConversionService()); - - Evaluate("null or true", true, typeof(bool), false); - Evaluate("null and true", false, typeof(bool), false); - Evaluate("!null", true, typeof(bool), false); - Evaluate("null ? 'foo' : 'bar'", "bar", typeof(string), false); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/CachedMethodExecutorTests.cs b/src/Common/test/Common.Expression.Test/Spring/CachedMethodExecutorTests.cs deleted file mode 100644 index 38b1c81822..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/CachedMethodExecutorTests.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class CachedMethodExecutorTests -{ - private readonly IExpressionParser _parser = new SpelExpressionParser(); - private readonly StandardEvaluationContext _context = new(new RootObject()); - - [Fact] - public void TestCachedExecutionForParameters() - { - IExpression expression = _parser.ParseExpression("Echo(#var)"); - - AssertMethodExecution(expression, 42, "int: 42"); - AssertMethodExecution(expression, 42, "int: 42"); - AssertMethodExecution(expression, "Deep Thought", "String: Deep Thought"); - AssertMethodExecution(expression, 42, "int: 42"); - } - - [Fact] - public void TestCachedExecutionForTarget() - { - IExpression expression = _parser.ParseExpression("#var.Echo(42)"); - - AssertMethodExecution(expression, new RootObject(), "int: 42"); - AssertMethodExecution(expression, new RootObject(), "int: 42"); - AssertMethodExecution(expression, new BaseObject(), "String: 42"); - AssertMethodExecution(expression, new RootObject(), "int: 42"); - } - - private void AssertMethodExecution(IExpression expression, object var, string expected) - { - _context.SetVariable("var", var); - Assert.Equal(expected, expression.GetValue(_context)); - } - - public class BaseObject - { - public string Echo(string value) - { - return $"String: {value}"; - } - } - - public sealed class RootObject : BaseObject - { - public string Echo(int value) - { - return $"int: {value}"; - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/ConstructorInvocationTests.cs b/src/Common/test/Common.Expression.Test/Spring/ConstructorInvocationTests.cs deleted file mode 100644 index 38ae8ad35f..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/ConstructorInvocationTests.cs +++ /dev/null @@ -1,204 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestResources; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class ConstructorInvocationTests : AbstractExpressionTests -{ - [Fact] - public void TestTypeConstructors() - { - Evaluate("new String('hello world')", "hello world", typeof(string)); - } - - [Fact] - public void TestNonExistentType() - { - EvaluateAndCheckError("new FooBar()", SpelMessage.ConstructorInvocationProblem); - } - - [Fact] - public void TestConstructorThrowingException_SPR6760() - { - // Test ctor on inventor: - // On 1 it will throw an IllegalArgumentException - // On 2 it will throw a RuntimeException - // On 3 it will exit normally - // In each case it increments the Tester field 'counter' when invoked - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression($"new {typeof(ConstructorInvocationTests).FullName}$Tester(#bar).I"); - - // Normal exit - StandardEvaluationContext eContext = TestScenarioCreator.GetTestEvaluationContext(); - eContext.SetRootObject(new Tester()); - eContext.SetVariable("bar", 3); - object o = expr.GetValue(eContext); - Assert.Equal(3, o); - Assert.Equal(1, parser.ParseExpression("Counter").GetValue(eContext)); - - // Now the expression has cached that throwException(int) is the right thing to - // call. Let's change 'bar' to be a PlaceOfBirth which indicates the cached - // reference is out of date. - eContext.SetVariable("bar", new PlaceOfBirth("London")); - o = expr.GetValue(eContext); - Assert.Equal(0, o); - - // That confirms the logic to mark the cached reference stale and retry is working - // Now let's cause the method to exit via exception and ensure it doesn't cause - // a retry. - // First, switch back to throwException(int) - eContext.SetVariable("bar", 3); - o = expr.GetValue(eContext); - Assert.Equal(3, o); - Assert.Equal(2, parser.ParseExpression("Counter").GetValue(eContext)); - - // 4 will make it throw a checked exception - this will be wrapped by spel on the - // way out - eContext.SetVariable("bar", 4); - var ex = Assert.Throws(() => expr.GetValue(eContext)); - Assert.Contains("Tester", ex.Message, StringComparison.Ordinal); - - // A problem occurred whilst attempting to construct an object of type - // 'org.springframework.expression.spel.ConstructorInvocationTests$Tester' - // using arguments '(int)' - // If counter is 4 then the method got called twice! - Assert.Equal(3, parser.ParseExpression("Counter").GetValue(eContext)); - - // 2 will make it throw a SystemException - SpEL will let this through - eContext.SetVariable("bar", 2); - var ex2 = Assert.Throws(() => expr.GetValue(eContext)); - Assert.IsNotType(ex2); - - // A problem occurred whilst attempting to construct an object of type - // 'org.springframework.expression.spel.ConstructorInvocationTests$Tester' - // using arguments '(int)' - // If counter is 5 then the method got called twice! - Assert.Equal(4, parser.ParseExpression("Counter").GetValue(eContext)); - } - - [Fact] - public void TestAddingConstructorResolvers() - { - var ctx = new StandardEvaluationContext(); - - // reflective constructor accessor is the only one by default - List constructorResolvers = ctx.ConstructorResolvers; - Assert.Single(constructorResolvers); - - var dummy = new DummyConstructorResolver(); - ctx.AddConstructorResolver(dummy); - Assert.Equal(2, ctx.ConstructorResolvers.Count); - - var copy = new List(ctx.ConstructorResolvers); - Assert.True(ctx.RemoveConstructorResolver(dummy)); - Assert.False(ctx.RemoveConstructorResolver(dummy)); - Assert.Single(ctx.ConstructorResolvers); - - ctx.ConstructorResolvers = copy; - Assert.Equal(2, ctx.ConstructorResolvers.Count); - } - - [Fact] - public void TestVarargsInvocation01() - { - // Calling 'Fruit(String... strings)' - Evaluate($"new {typeof(Fruit).FullName}('a','b','c').StringsCount", 3, typeof(int)); - Evaluate($"new {typeof(Fruit).FullName}('a').StringsCount", 1, typeof(int)); - Evaluate($"new {typeof(Fruit).FullName}().StringsCount", 0, typeof(int)); - - // all need converting to strings - Evaluate($"new {typeof(Fruit).FullName}(1,2,3).StringsCount", 3, typeof(int)); - - // needs string conversion - Evaluate($"new {typeof(Fruit).FullName}(1).StringsCount", 1, typeof(int)); - - // first and last need conversion - Evaluate($"new {typeof(Fruit).FullName}(1,'a',3.0d).StringsCount", 3, typeof(int)); - } - - [Fact] - public void TestVarargsInvocation02() - { - // Calling 'Fruit(int i, String... strings)' - returns int+length_of_strings - Evaluate($"new {typeof(Fruit).FullName}(5,'a','b','c').StringsCount", 8, typeof(int)); - Evaluate($"new {typeof(Fruit).FullName}(2,'a').StringsCount", 3, typeof(int)); - Evaluate($"new {typeof(Fruit).FullName}(4).StringsCount", 4, typeof(int)); - Evaluate($"new {typeof(Fruit).FullName}(8,2,3).StringsCount", 10, typeof(int)); - Evaluate($"new {typeof(Fruit).FullName}(9).StringsCount", 9, typeof(int)); - Evaluate($"new {typeof(Fruit).FullName}(2,'a',3.0d).StringsCount", 4, typeof(int)); - Evaluate($"new {typeof(Fruit).FullName}(8,StringArrayOfThreeItems).StringsCount", 11, typeof(int)); - } - - [Fact] - public void TestWidening01() - { - // widening of int 3 to double 3 is OK - Evaluate($"new {typeof(WidenDouble).FullName}(3).D", 3.0d, typeof(double)); - - // widening of int 3 to long 3 is OK - Evaluate($"new {typeof(WidenLong).FullName}(3).L", 3L, typeof(long)); - } - - [Fact] - public void TestArgumentConversion01() - { - // Closest ctor will be new Company(String) and converter supports Double>String - Evaluate($"new {typeof(Company).FullName}(1.1d).Address", "1.1", typeof(string)); - } - - public sealed class DummyConstructorResolver : IConstructorResolver - { - public IConstructorExecutor Resolve(IEvaluationContext context, string typeName, List argumentTypes) - { - throw new InvalidOperationException("Auto-generated method stub"); - } - } - - public sealed class TestException : Exception - { - } - - public sealed class Tester - { - public static int Counter { get; set; } - public int I { get; } - - public Tester() - { - } - - public Tester(int i) - { - Counter++; - - if (i == 1) - { - throw new ArgumentException("ArgumentException for 1", nameof(i)); - } - - if (i == 2) - { - throw new SystemException("SystemException for 2"); - } - - if (i == 4) - { - throw new TestException(); - } - - I = i; - } - - public Tester(PlaceOfBirth pob) - { - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/DefaultComparatorUnitTests.cs b/src/Common/test/Common.Expression.Test/Spring/DefaultComparatorUnitTests.cs deleted file mode 100644 index a6858e3ea6..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/DefaultComparatorUnitTests.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class DefaultComparatorUnitTests -{ - [Fact] - public void TestPrimitives() - { - var comparator = new StandardTypeComparator(); - - // primitive int - Assert.True(comparator.Compare(1, 2) < 0); - Assert.True(comparator.Compare(1, 1) == 0); - Assert.True(comparator.Compare(2, 1) > 0); - - Assert.True(comparator.Compare(1.0d, 2) < 0); - Assert.True(comparator.Compare(1.0d, 1) == 0); - Assert.True(comparator.Compare(2.0d, 1) > 0); - - Assert.True(comparator.Compare(1.0f, 2) < 0); - Assert.True(comparator.Compare(1.0f, 1) == 0); - Assert.True(comparator.Compare(2.0f, 1) > 0); - - Assert.True(comparator.Compare(1L, 2) < 0); - Assert.True(comparator.Compare(1L, 1) == 0); - Assert.True(comparator.Compare(2L, 1) > 0); - - Assert.True(comparator.Compare(1, 2L) < 0); - Assert.True(comparator.Compare(1, 1L) == 0); - Assert.True(comparator.Compare(2, 1L) > 0); - - Assert.True(comparator.Compare(1L, 2L) < 0); - Assert.True(comparator.Compare(1L, 1L) == 0); - Assert.True(comparator.Compare(2L, 1L) > 0); - } - - [Fact] - public void TestNonPrimitiveNumbers() - { - var comparator = new StandardTypeComparator(); - - const decimal bdOne = 1; - const decimal bdTwo = 2; - - Assert.True(comparator.Compare(bdOne, bdTwo) < 0); - Assert.True(comparator.Compare(bdOne, 1M) == 0); - Assert.True(comparator.Compare(bdTwo, bdOne) > 0); - - Assert.True(comparator.Compare(1, bdTwo) < 0); - Assert.True(comparator.Compare(1, bdOne) == 0); - Assert.True(comparator.Compare(2, bdOne) > 0); - - Assert.True(comparator.Compare(1.0d, bdTwo) < 0); - Assert.True(comparator.Compare(1.0d, bdOne) == 0); - Assert.True(comparator.Compare(2.0d, bdOne) > 0); - - Assert.True(comparator.Compare(1.0f, bdTwo) < 0); - Assert.True(comparator.Compare(1.0f, bdOne) == 0); - Assert.True(comparator.Compare(2.0f, bdOne) > 0); - - Assert.True(comparator.Compare(1L, bdTwo) < 0); - Assert.True(comparator.Compare(1L, bdOne) == 0); - Assert.True(comparator.Compare(2L, bdOne) > 0); - } - - [Fact] - public void TestNulls() - { - var comparator = new StandardTypeComparator(); - Assert.True(comparator.Compare(null, "abc") < 0); - Assert.True(comparator.Compare(null, null) == 0); - Assert.True(comparator.Compare("abc", null) > 0); - } - - [Fact] - public void TestObjects() - { - var comparator = new StandardTypeComparator(); - Assert.True(comparator.Compare("a", "a") == 0); - Assert.True(comparator.Compare("a", "b") < 0); - Assert.True(comparator.Compare("b", "a") > 0); - } - - [Fact] - public void TestCanCompare() - { - var comparator = new StandardTypeComparator(); - Assert.True(comparator.CanCompare(null, 1)); - Assert.True(comparator.CanCompare(1, null)); - - Assert.True(comparator.CanCompare(2, 1)); - Assert.True(comparator.CanCompare("abc", "def")); - Assert.True(comparator.CanCompare("abc", 3)); - Assert.False(comparator.CanCompare(typeof(string), 3)); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/EvaluationTests.cs b/src/Common/test/Common.Expression.Test/Spring/EvaluationTests.cs deleted file mode 100644 index f39ef8f48f..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/EvaluationTests.cs +++ /dev/null @@ -1,1468 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using System.Text.RegularExpressions; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestResources; -using Xunit; - -// ReSharper disable InconsistentNaming -#pragma warning disable S3443 // Type should not be examined on "System.Type" instances -#pragma warning disable S4004 // Collection properties should be readonly - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class EvaluationTests : AbstractExpressionTests -{ - private static readonly bool IsDebug = bool.Parse(bool.FalseString); - - [Fact] - public void TestCreateListsOnAttemptToIndexNull01() - { - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e = parser.ParseExpression("List[0]"); - var testClass = new TestClass(); - - object o = e.GetValue(new StandardEvaluationContext(testClass)); - Assert.Equal(string.Empty, o); - o = parser.ParseExpression("List[3]").GetValue(new StandardEvaluationContext(testClass)); - Assert.Equal(string.Empty, o); - Assert.Equal(4, testClass.List.Count); - Assert.Throws(() => parser.ParseExpression("List2[3]").GetValue(new StandardEvaluationContext(testClass))); - - o = parser.ParseExpression("Foo[3]").GetValue(new StandardEvaluationContext(testClass)); - Assert.Equal(string.Empty, o); - Assert.Equal(4, testClass.Foo.Count); - - o = parser.ParseExpression("FooIList[3]").GetValue(new StandardEvaluationContext(testClass)); - Assert.Equal(string.Empty, o); - Assert.Equal(4, testClass.Foo.Count); - } - - [Fact] - public void TestCreateMapsOnAttemptToIndexNull01() - { - var testClass = new TestClass(); - var ctx = new StandardEvaluationContext(testClass); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - - object o = parser.ParseExpression("Map['a']").GetValue(ctx); - Assert.Null(o); - o = parser.ParseExpression("Map").GetValue(ctx); - Assert.NotNull(o); - - // map2 should be null, there is no setter - Assert.Throws(() => parser.ParseExpression("Map2['a']").GetValue(ctx)); - } - - // wibble2 should be null (cannot be initialized dynamically), there is no setter - [Fact] - public void TestCreateObjectsOnAttemptToReferenceNull() - { - var testClass = new TestClass(); - var ctx = new StandardEvaluationContext(testClass); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - - object o = parser.ParseExpression("Wibble.Bar").GetValue(ctx); - Assert.Equal("hello", o); - o = parser.ParseExpression("Wibble").GetValue(ctx); - Assert.NotNull(o); - - Assert.Throws(() => parser.ParseExpression("Wibble2.Bar").GetValue(ctx)); - } - - [Fact] - public void TestElvis01() - { - Evaluate("'Andy'?:'Dave'", "Andy", typeof(string)); - Evaluate("null?:'Dave'", "Dave", typeof(string)); - } - - [Fact] - public void TestSafeNavigation() - { - Evaluate("null?.null?.null", null, null); - } - - [Fact] - public void TestRelOperatorGT01() - { - Evaluate("3 > 6", "False", typeof(bool)); - } - - [Fact] - public void TestRelOperatorLT01() - { - Evaluate("3 < 6", "True", typeof(bool)); - } - - [Fact] - public void TestRelOperatorLE01() - { - Evaluate("3 <= 6", "True", typeof(bool)); - } - - [Fact] - public void TestRelOperatorGE01() - { - Evaluate("3 >= 6", "False", typeof(bool)); - } - - [Fact] - public void TestRelOperatorGE02() - { - Evaluate("3 >= 3", "True", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsInstanceOf01() - { - Evaluate("'xyz' instanceof T(int)", "False", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsInstanceOf04() - { - Evaluate("null instanceof T(String)", "False", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsInstanceOf05() - { - Evaluate("null instanceof T(System.Int32)", "False", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsInstanceOf06() - { - EvaluateAndCheckError("'A' instanceof null", SpelMessage.InstanceOfOperatorNeedsClassOperand, 15, "null"); - } - - [Fact] - public void TestRelOperatorsMatches01() - { - Evaluate("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "False", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsMatches02() - { - Evaluate("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "True", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsMatches03() - { - EvaluateAndCheckError("null matches '^.*$'", SpelMessage.InvalidFirstOperandForMatchesOperator, 0, null); - } - - [Fact] - public void TestRelOperatorsMatches04() - { - EvaluateAndCheckError("'abc' matches null", SpelMessage.InvalidSecondOperandForMatchesOperator, 14, null); - } - - [Fact] - public void TestRelOperatorsMatches05() - { - Evaluate("27 matches '^.*2.*$'", true, typeof(bool)); // conversion int>string - } - - // SPR-16731 - [Fact] - public void TestMatchesWithPatternAccessThreshold() - { - const string pattern = "^(?=[a-z0-9-]{1,47})([a-z0-9]+[-]{0,1}){1,47}[a-z0-9]{1}$"; - const string expression = $"'abcde-fghijklmn-o42pasdfasdfasdf.qrstuvwxyz10x.xx.yyy.zasdfasfd' matches '{pattern}'"; - IExpression expr = Parser.ParseExpression(expression); - var ex = Assert.Throws(() => expr.GetValue()); - Assert.IsType(ex.InnerException); - Assert.Equal(SpelMessage.FlawedPattern, ex.MessageCode); - } - - // property access - [Fact] - public void TestPropertyField01() - { - Evaluate("Name", "Nikola Tesla", typeof(string), false); - - // not writable because (1) name is private (2) there is no setter, only a getter - EvaluateAndCheckError("madeup", SpelMessage.PropertyOrFieldNotReadable, 0, "madeup", typeof(Inventor).FullName); - } - - [Fact] - public void TestPropertyField02_SPR7100() - { - Evaluate("_name", "Nikola Tesla", typeof(string)); - Evaluate("_name_", "Nikola Tesla", typeof(string)); - } - - [Fact] - public void TestRogueTrailingDotCausesNPE_SPR6866() - { - var ex = Assert.Throws(() => new SpelExpressionParser().ParseExpression("PlaceOfBirth.foo.")); - Assert.Equal(SpelMessage.Ood, ex.MessageCode); - Assert.Equal(16, ex.Position); - } - - // nested properties - [Fact] - public void TestPropertiesNested01() - { - Evaluate("PlaceOfBirth.City", "SmilJan", typeof(string), true); - } - - [Fact] - public void TestPropertiesNested02() - { - Evaluate("PlaceOfBirth.DoubleIt(12)", "24", typeof(int)); - } - - [Fact] - public void TestPropertiesNested03() - { - var ex = Assert.Throws(() => new SpelExpressionParser().ParseRaw("PlaceOfBirth.23")); - Assert.Equal(SpelMessage.UnexpectedDataAfterDot, ex.MessageCode); - Assert.Equal("23", ex.Inserts[0]); - } - - // methods - [Fact] - public void TestMethods01() - { - Evaluate("Echo(12)", "12", typeof(string)); - } - - [Fact] - public void TestMethods02() - { - Evaluate("Echo(Name)", "Nikola Tesla", typeof(string)); - } - - // constructors - [Fact] - public void TestConstructorInvocation01() - { - Evaluate("new String('hello')", "hello", typeof(string)); - } - - [Fact] - public void TestConstructorInvocation05() - { - Evaluate("new System.String('foobar')", "foobar", typeof(string)); - } - - [Fact] - public void TestConstructorInvocation06() - { - // repeated evaluation to drive use of cached executor - var e = (SpelExpression)Parser.ParseExpression("new String('wibble')"); - string newString = e.GetValue(); - Assert.Equal("wibble", newString); - newString = e.GetValue(); - Assert.Equal("wibble", newString); - - // not writable - Assert.False(e.IsWritable(new StandardEvaluationContext())); - - // ast - Assert.Equal("new String('wibble')", e.ToStringAst()); - } - - // unary expressions - [Fact] - public void TestUnaryMinus01() - { - Evaluate("-5", "-5", typeof(int)); - } - - [Fact] - public void TestUnaryPlus01() - { - Evaluate("+5", "5", typeof(int)); - } - - [Fact] - public void TestUnaryNot01() - { - Evaluate("!true", "False", typeof(bool)); - } - - [Fact] - public void TestUnaryNot02() - { - Evaluate("!false", "True", typeof(bool)); - } - - [Fact] - public void TestUnaryNotWithNullValue() - { - Assert.Throws(() => Parser.ParseExpression("!null").GetValue()); - } - - [Fact] - public void TestAndWithNullValueOnLeft() - { - Assert.Throws(() => Parser.ParseExpression("null and true").GetValue()); - } - - [Fact] - public void TestAndWithNullValueOnRight() - { - Assert.Throws(() => Parser.ParseExpression("true and null").GetValue()); - } - - [Fact] - public void TestOrWithNullValueOnLeft() - { - Assert.Throws(() => Parser.ParseExpression("null or false").GetValue()); - } - - [Fact] - public void TestOrWithNullValueOnRight() - { - Assert.Throws(() => Parser.ParseExpression("false or null").GetValue()); - } - - // assignment - [Fact] - public void TestAssignmentToVariables01() - { - Evaluate("#var1='value1'", "value1", typeof(string)); - } - - [Fact] - public void TestTernaryOperator01() - { - Evaluate("2>4?1:2", 2, typeof(int)); - } - - [Fact] - public void TestTernaryOperator02() - { - Evaluate("'abc'=='abc'?1:2", 1, typeof(int)); - } - - [Fact] - public void TestTernaryOperator03() - { - // cannot convert string to boolean - EvaluateAndCheckError("'hello'?1:2", SpelMessage.TypeConversionError); - } - - [Fact] - public void TestTernaryOperator04() - { - IExpression e = Parser.ParseExpression("1>2?3:4"); - Assert.False(e.IsWritable(Context)); - } - - [Fact] - public void TestTernaryOperator05() - { - Evaluate("1>2?#var=4:#var=5", 5, typeof(int)); - Evaluate("3?:#var=5", 3, typeof(int)); - Evaluate("null?:#var=5", 5, typeof(int)); - Evaluate("2>4?(3>2?true:false):(5<3?true:false)", false, typeof(bool)); - } - - [Fact] - public void TestTernaryOperatorWithNullValue() - { - Assert.Throws(() => Parser.ParseExpression("null ? 0 : 1").GetValue()); - } - - [Fact] - public void MethodCallWithRootReferenceThroughParameter() - { - Evaluate("PlaceOfBirth.DoubleIt(Inventions.Length)", 18, typeof(int)); - } - - [Fact] - public void CtorCallWithRootReferenceThroughParameter() - { - Evaluate($"new {typeof(PlaceOfBirth).FullName}(Inventions[0].ToString()).City", "Telephone repeater", typeof(string)); - } - - [Fact] - public void FnCallWithRootReferenceThroughParameter() - { - Evaluate("#ReverseInt(Inventions.Length, Inventions.Length, Inventions.Length)", "System.Int32[3]{(0)=9,(1)=9,(2)=9,}", typeof(int[])); - } - - [Fact] - public void MethodCallWithRootReferenceThroughParameterThatIsAFunctionCall() - { - Evaluate("PlaceOfBirth.DoubleIt(#ReverseInt(Inventions.Length,2,3)[2])", 18, typeof(int)); - } - - [Fact] - public void TestIndexer03() - { - Evaluate("'christian'[8]", "n", typeof(string)); - } - - [Fact] - public void TestIndexerError() - { - EvaluateAndCheckError($"new {typeof(Inventor).FullName}().Inventions[1]", SpelMessage.CannotIndexIntoNullValue); - } - - [Fact] - public void TestStaticRef02() - { - Evaluate($"T({typeof(Color).FullName}).Green.Rgb!=0", "True", typeof(bool)); - } - - // variables and functions - [Fact] - public void TestVariableAccess01() - { - Evaluate("#answer", "42", typeof(int), true); - } - - [Fact] - public void TestFunctionAccess01() - { - Evaluate("#ReverseInt(1,2,3)", "System.Int32[3]{(0)=3,(1)=2,(2)=1,}", typeof(int[])); - } - - [Fact] - public void TestFunctionAccess02() - { - Evaluate("#ReverseString('hello')", "olleh", typeof(string)); - } - - // type references - [Fact] - public void TestTypeReferences01() - { - Type t = typeof(string); - Evaluate("T(System.String)", "System.String", t.GetType()); - } - - [Fact] - public void TestTypeReferencesAndQualifiedIdentifierCaching() - { - var e = (SpelExpression)Parser.ParseExpression("T(System.String)"); - Assert.False(e.IsWritable(new StandardEvaluationContext())); - Assert.Equal("T(System.String)", e.ToStringAst()); - Assert.Equal(typeof(string), e.GetValue(typeof(Type))); - - // use cached QualifiedIdentifier: - Assert.Equal("T(System.String)", e.ToStringAst()); - Assert.Equal(typeof(string), e.GetValue(typeof(Type))); - } - - [Fact] - public void OperatorVariants() - { - var e = (SpelExpression)Parser.ParseExpression("#a < #b"); - var ctx = new StandardEvaluationContext(); - ctx.SetVariable("a", (short)3); - ctx.SetVariable("b", (short)6); - Assert.True(e.GetValue(ctx)); - ctx.SetVariable("b", (byte)6); - Assert.True(e.GetValue(ctx)); - ctx.SetVariable("a", (byte)9); - ctx.SetVariable("b", (byte)6); - Assert.False(e.GetValue(ctx)); - ctx.SetVariable("a", 10L); - ctx.SetVariable("b", (short)30); - Assert.True(e.GetValue(ctx)); - ctx.SetVariable("a", (byte)3); - ctx.SetVariable("b", (short)30); - Assert.True(e.GetValue(ctx)); - ctx.SetVariable("a", (byte)3); - ctx.SetVariable("b", 30L); - Assert.True(e.GetValue(ctx)); - ctx.SetVariable("a", (byte)3); - ctx.SetVariable("b", 30f); - Assert.True(e.GetValue(ctx)); - ctx.SetVariable("a", 10M); - ctx.SetVariable("b", 20M); - Assert.True(e.GetValue(ctx, typeof(bool))); - } - - [Fact] - public void TestTypeReferencesPrimitive() - { - Evaluate("T(int)", "System.Int32", typeof(int).GetType()); - Evaluate("T(byte)", "System.Byte", typeof(byte).GetType()); - Evaluate("T(char)", "System.Char", typeof(char).GetType()); - Evaluate("T(boolean)", "System.Boolean", typeof(bool).GetType()); - Evaluate("T(long)", "System.Int64", typeof(long).GetType()); - Evaluate("T(short)", "System.Int16", typeof(short).GetType()); - Evaluate("T(double)", "System.Double", typeof(double).GetType()); - Evaluate("T(float)", "System.Single", typeof(float).GetType()); - } - - [Fact] - public void TestTypeReferences02() - { - Type t = typeof(string); - Evaluate("T(String)", "System.String", t.GetType()); - } - - [Fact] - public void TestStringType() - { - EvaluateAndAskForReturnType("PlaceOfBirth.City", "SmilJan", typeof(string)); - } - - [Fact] - public void TestNumbers01() - { - EvaluateAndAskForReturnType("3*4+5", 17, typeof(int)); - EvaluateAndAskForReturnType("3*4+5", 17L, typeof(long)); - EvaluateAndAskForReturnType("65", 'A', typeof(char)); - EvaluateAndAskForReturnType("3*4+5", (short)17, typeof(short)); - EvaluateAndAskForReturnType("3*4+5", "17", typeof(string)); - } - - [Fact] - public void TestAdvancedNumerics() - { - object twentyFour = Parser.ParseExpression("2.0 * 3e0 * 4").GetValue(typeof(int)); - Assert.Equal(24, twentyFour); - double one = Parser.ParseExpression("8.0 / 5e0 % 2").GetValue(); - Assert.InRange((float)one, 1.6f, 1.6f); - int o = Parser.ParseExpression("8.0 / 5e0 % 2").GetValue(); - Assert.Equal(2, o); - int sixteen = Parser.ParseExpression("-2 ^ 4").GetValue(); - Assert.Equal(16, sixteen); - int minusFortyFive = Parser.ParseExpression("1+2-3*8^2/2/2").GetValue(); - Assert.Equal(-45, minusFortyFive); - } - - [Fact] - public void TestComparison() - { - StandardEvaluationContext context = TestScenarioCreator.GetTestEvaluationContext(); - bool trueValue = Parser.ParseExpression("T(DateTime) == BirthDate.GetType()").GetValue(context); - Assert.True(trueValue); - } - - [Fact] - public void TestResolvingList() - { - StandardEvaluationContext context = TestScenarioCreator.GetTestEvaluationContext(); - Assert.Throws(() => Parser.ParseExpression("T(List)!=null").GetValue(context)); - ((StandardTypeLocator)context.TypeLocator).RegisterImport("System.Collections"); - Assert.True(Parser.ParseExpression("T(ArrayList)!=null").GetValue(context)); - } - - [Fact] - public void TestResolvingString() - { - var stringClass = Parser.ParseExpression("T(String)").GetValue(); - Assert.Equal(typeof(string), stringClass); - } - - [Fact] - public void InitializingCollectionElementsOnWrite() - { - var person = new TestPerson(); - var context = new StandardEvaluationContext(person); - var configuration = new SpelParserOptions(true, true); - var parser = new SpelExpressionParser(configuration); - IExpression e = parser.ParseExpression("Name"); - e.SetValue(context, "Oleg"); - Assert.Equal("Oleg", person.Name); - - e = parser.ParseExpression("Address.Street"); - e.SetValue(context, "123 High St"); - Assert.Equal("123 High St", person.Address.Street); - - e = parser.ParseExpression("Address.CrossStreets[0]"); - e.SetValue(context, "Blah"); - Assert.Equal("Blah", person.Address.CrossStreets[0]); - - e = parser.ParseExpression("Address.CrossStreets[3]"); - e.SetValue(context, "Wibble"); - Assert.Equal("Blah", person.Address.CrossStreets[0]); - Assert.Equal("Wibble", person.Address.CrossStreets[3]); - } - - [Fact] - public void CaseInsensitiveNullLiterals() - { - var parser = new SpelExpressionParser(); - - IExpression e = parser.ParseExpression("null"); - Assert.Null(e.GetValue()); - - e = parser.ParseExpression("NULL"); - Assert.Null(e.GetValue()); - - e = parser.ParseExpression("NuLl"); - Assert.Null(e.GetValue()); - } - - [Fact] - public void TestCustomMethodFilter() - { - var context = new StandardEvaluationContext(); - - var customResolvers = new List - { - new CustomMethodResolver() - }; - - context.MethodResolvers = customResolvers; - - var filter = new CustomMethodFilter(); - var ex = Assert.Throws(() => context.RegisterMethodFilter(typeof(string), filter)); - - Assert.Contains(ex.Message, "Method filter cannot be set as the reflective method resolver is not in use", StringComparison.Ordinal); - } - - [Fact] - public void CollectionGrowingViaIndexer() - { - var instance = new Spr9751(); - - // Add a new element to the list - var ctx = new StandardEvaluationContext(instance); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e = parser.ParseExpression("ListOfStrings[++Index3]='def'"); - e.GetValue(ctx); - Assert.Equal(2, instance.ListOfStrings.Count); - Assert.Equal("def", instance.ListOfStrings[1]); - - // Check reference beyond end of collection - ctx = new StandardEvaluationContext(instance); - parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - e = parser.ParseExpression("ListOfStrings[0]"); - string value = e.GetValue(ctx); - Assert.Equal("abc", value); - e = parser.ParseExpression("ListOfStrings[1]"); - value = e.GetValue(ctx); - Assert.Equal("def", value); - e = parser.ParseExpression("ListOfStrings[2]"); - value = e.GetValue(ctx); - Assert.Equal(string.Empty, value); - - // Now turn off growing and reference off the end - var failCtx = new StandardEvaluationContext(instance); - parser = new SpelExpressionParser(new SpelParserOptions(false, false)); - IExpression failExp = parser.ParseExpression("ListOfStrings[3]"); - var ex = Assert.Throws(() => failExp.GetValue(failCtx)); - Assert.Equal(SpelMessage.CollectionIndexOutOfBounds, ex.MessageCode); - } - - [Fact] - public void LimitCollectionGrowing() - { - var instance = new TestClass(); - var ctx = new StandardEvaluationContext(instance); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true, 3)); - IExpression e = parser.ParseExpression("Foo[2]"); - e.SetValue(ctx, "2"); - Assert.Equal(3, instance.Foo.Count); - e = parser.ParseExpression("Foo[3]"); - - try - { - e.SetValue(ctx, "3"); - } - catch (SpelEvaluationException see) - { - Assert.Equal(SpelMessage.UnableToGrowCollection, see.MessageCode); - Assert.Equal(3, instance.Foo.Count); - } - } - - // For now I am making #this not assignable - [Fact] - public void Increment01Root() - { - const int i = 42; - var ctx = new StandardEvaluationContext(i); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e = parser.ParseExpression("#this++"); - Assert.Equal(42, i); - var ex = Assert.Throws(() => e.GetValue(ctx)); - Assert.Equal(SpelMessage.NotAssignable, ex.MessageCode); - } - - [Fact] - public void Increment02Postfix() - { - var helper = new Spr9751(); - var ctx = new StandardEvaluationContext(helper); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e; - - // decimal - e = parser.ParseExpression("Bd++"); - Assert.Equal(2M, helper.Bd); - decimal return_bd = e.GetValue(ctx); - Assert.Equal(2M, return_bd); - Assert.Equal(3M, helper.Bd); - - // double - e = parser.ParseExpression("Ddd++"); - Assert.InRange((float)helper.Ddd, 2.0f, 2.0f); - double return_ddd = e.GetValue(ctx); - Assert.InRange((float)return_ddd, 2.0f, 2.0f); - Assert.InRange((float)helper.Ddd, 3.0f, 3.0f); - - // float - e = parser.ParseExpression("Fff++"); - Assert.InRange(helper.Fff, 3.0f, 3.0f); - float return_fff = e.GetValue(ctx); - Assert.InRange(return_fff, 3.0f, 3.0f); - Assert.InRange(helper.Fff, 4.0f, 4.0f); - - // long - e = parser.ParseExpression("Lll++"); - Assert.Equal(66666L, helper.Lll); - long return_lll = e.GetValue(ctx); - Assert.Equal(66666L, return_lll); - Assert.Equal(66667L, helper.Lll); - - // int - e = parser.ParseExpression("Iii++"); - Assert.Equal(42, helper.Iii); - int return_iii = e.GetValue(ctx); - Assert.Equal(42, return_iii); - Assert.Equal(43, helper.Iii); - return_iii = e.GetValue(ctx); - Assert.Equal(43, return_iii); - Assert.Equal(44, helper.Iii); - - // short - e = parser.ParseExpression("Sss++"); - Assert.Equal((short)15, helper.Sss); - short return_sss = e.GetValue(ctx); - Assert.Equal((short)15, return_sss); - Assert.Equal((short)16, helper.Sss); - } - - [Fact] - public void Increment02Prefix() - { - var helper = new Spr9751(); - var ctx = new StandardEvaluationContext(helper); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e; - - // decimal - e = parser.ParseExpression("++Bd"); - Assert.Equal(2M, helper.Bd); - decimal return_bd = e.GetValue(ctx); - Assert.Equal(3M, return_bd); - Assert.Equal(3M, helper.Bd); - - // double - e = parser.ParseExpression("++Ddd"); - Assert.InRange(helper.Ddd, 2.0d, 2.0d); - double return_ddd = e.GetValue(ctx); - Assert.InRange(return_ddd, 3.0d, 3.0d); - Assert.InRange(helper.Ddd, 3.0d, 3.0d); - - // float - e = parser.ParseExpression("++Fff"); - Assert.InRange(helper.Fff, 3.0f, 3.0f); - float return_fff = e.GetValue(ctx); - Assert.InRange(return_fff, 4.0f, 4.0f); - Assert.InRange(helper.Fff, 4.0f, 4.0f); - - // long - e = parser.ParseExpression("++Lll"); - Assert.Equal(66666L, helper.Lll); - long return_lll = e.GetValue(ctx); - Assert.Equal(66667L, return_lll); - Assert.Equal(66667L, helper.Lll); - - // int - e = parser.ParseExpression("++Iii"); - Assert.Equal(42, helper.Iii); - int return_iii = e.GetValue(ctx); - Assert.Equal(43, return_iii); - Assert.Equal(43, helper.Iii); - return_iii = e.GetValue(ctx); - Assert.Equal(44, return_iii); - Assert.Equal(44, helper.Iii); - - // short - e = parser.ParseExpression("++Sss"); - Assert.Equal((short)15, helper.Sss); - int return_sss = e.GetValue(ctx); - Assert.Equal(16, return_sss); - Assert.Equal((short)16, helper.Sss); - } - - [Fact] - public void Increment03() - { - var helper = new Spr9751(); - var ctx = new StandardEvaluationContext(helper); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - - IExpression e1 = parser.ParseExpression("M()++"); - var ex = Assert.Throws(() => e1.GetValue(ctx)); - Assert.Equal(SpelMessage.OperandNotIncrementable, ex.MessageCode); - - IExpression e2 = parser.ParseExpression("++M()"); - ex = Assert.Throws(() => e2.GetValue(ctx)); - Assert.Equal(SpelMessage.OperandNotIncrementable, ex.MessageCode); - } - - [Fact] - public void Increment04() - { - const int i = 42; - var ctx = new StandardEvaluationContext(i); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e1 = parser.ParseExpression("++1"); - var ex = Assert.Throws(() => e1.GetValue(ctx)); - Assert.Equal(SpelMessage.NotAssignable, ex.MessageCode); - IExpression e2 = parser.ParseExpression("1++"); - ex = Assert.Throws(() => e2.GetValue(ctx)); - Assert.Equal(SpelMessage.NotAssignable, ex.MessageCode); - } - - [Fact] - public void Decrement01Root() - { - const int i = 42; - var ctx = new StandardEvaluationContext(i); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e = parser.ParseExpression("#this--"); - Assert.Equal(42, i); - var ex = Assert.Throws(() => e.GetValue(ctx)); - Assert.Equal(SpelMessage.NotAssignable, ex.MessageCode); - } - - [Fact] - public void Decrement02Postfix() - { - var helper = new Spr9751(); - var ctx = new StandardEvaluationContext(helper); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e; - - // BigDecimal - e = parser.ParseExpression("Bd--"); - Assert.Equal(2M, helper.Bd); - decimal return_bd = e.GetValue(ctx); - Assert.Equal(2M, return_bd); - Assert.Equal(1M, helper.Bd); - - // double - e = parser.ParseExpression("Ddd--"); - Assert.InRange((float)helper.Ddd, 2.0d, 2.0d); - double return_ddd = e.GetValue(ctx); - Assert.InRange(return_ddd, 2.0d, 2.0d); - Assert.InRange(helper.Ddd, 1.0d, 1.0d); - - // float - e = parser.ParseExpression("Fff--"); - Assert.InRange(helper.Fff, 3.0f, 3.0f); - float return_fff = e.GetValue(ctx); - Assert.InRange(return_fff, 3.0f, 3.0f); - Assert.InRange(helper.Fff, 2.0f, 2.0f); - - // long - e = parser.ParseExpression("Lll--"); - Assert.Equal(66666L, helper.Lll); - long return_lll = e.GetValue(ctx); - Assert.Equal(66666L, return_lll); - Assert.Equal(66665L, helper.Lll); - - // int - e = parser.ParseExpression("Iii--"); - Assert.Equal(42, helper.Iii); - int return_iii = e.GetValue(ctx); - Assert.Equal(42, return_iii); - Assert.Equal(41, helper.Iii); - return_iii = e.GetValue(ctx); - Assert.Equal(41, return_iii); - Assert.Equal(40, helper.Iii); - - // short - e = parser.ParseExpression("Sss--"); - Assert.Equal((short)15, helper.Sss); - short return_sss = e.GetValue(ctx); - Assert.Equal((short)15, return_sss); - Assert.Equal((short)14, helper.Sss); - } - - [Fact] - public void Decrement02Prefix() - { - var helper = new Spr9751(); - var ctx = new StandardEvaluationContext(helper); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e; - - // BigDecimal - e = parser.ParseExpression("--Bd"); - Assert.Equal(2M, helper.Bd); - decimal return_bd = e.GetValue(ctx); - Assert.Equal(1M, return_bd); - Assert.Equal(1M, helper.Bd); - - // double - e = parser.ParseExpression("--Ddd"); - Assert.InRange((float)helper.Ddd, 2.0d, 2.0d); - double return_ddd = e.GetValue(ctx); - Assert.InRange(return_ddd, 1.0d, 1.0d); - Assert.InRange(helper.Ddd, 1.0d, 1.0d); - - // float - e = parser.ParseExpression("--Fff"); - Assert.InRange(helper.Fff, 3.0f, 3.0f); - float return_fff = e.GetValue(ctx); - Assert.InRange(return_fff, 2.0f, 2.0f); - Assert.InRange(helper.Fff, 2.0f, 2.0f); - - // long - e = parser.ParseExpression("--Lll"); - Assert.Equal(66666L, helper.Lll); - long return_lll = e.GetValue(ctx); - Assert.Equal(66665L, return_lll); - Assert.Equal(66665L, helper.Lll); - - // int - e = parser.ParseExpression("--Iii"); - Assert.Equal(42, helper.Iii); - int return_iii = e.GetValue(ctx); - Assert.Equal(41, return_iii); - Assert.Equal(41, helper.Iii); - return_iii = e.GetValue(ctx); - Assert.Equal(40, return_iii); - Assert.Equal(40, helper.Iii); - - // short - e = parser.ParseExpression("--Sss"); - Assert.Equal((short)15, helper.Sss); - int return_sss = e.GetValue(ctx); - Assert.Equal(14, return_sss); - Assert.Equal(14, helper.Sss); - } - - [Fact] - public void Decrement03() - { - var helper = new Spr9751(); - var ctx = new StandardEvaluationContext(helper); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - - IExpression e1 = parser.ParseExpression("M()--"); - var ex = Assert.Throws(() => e1.GetValue(ctx)); - Assert.Equal(SpelMessage.OperandNotDecrementable, ex.MessageCode); - - IExpression e2 = parser.ParseExpression("--M()"); - ex = Assert.Throws(() => e2.GetValue(ctx)); - Assert.Equal(SpelMessage.OperandNotDecrementable, ex.MessageCode); - } - - [Fact] - public void Decrement04() - { - const int i = 42; - var ctx = new StandardEvaluationContext(i); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e1 = parser.ParseExpression("--1"); - var ex = Assert.Throws(() => e1.GetValue(ctx)); - Assert.Equal(SpelMessage.NotAssignable, ex.MessageCode); - - e1 = parser.ParseExpression("1--"); - ex = Assert.Throws(() => e1.GetValue(ctx)); - Assert.Equal(SpelMessage.NotAssignable, ex.MessageCode); - } - - [Fact] - public void IncDecTogether() - { - var helper = new Spr9751(); - var ctx = new StandardEvaluationContext(helper); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e; - -#pragma warning disable S125 // Sections of code should not be commented out - // index1 is 2 at the start - the 'intArray[#root.index1++]' should not be evaluated twice! - // intArray[2] is 3 -#pragma warning restore S125 // Sections of code should not be commented out - e = parser.ParseExpression("IntArray[#root.Index1++]++"); - e.GetValue(ctx); - Assert.Equal(3, helper.Index1); - Assert.Equal(4, helper.IntArray[2]); - - // index1 is 3 intArray[3] is 4 - e = parser.ParseExpression("IntArray[#root.Index1++]--"); - Assert.Equal(4, e.GetValue(ctx)); - Assert.Equal(4, helper.Index1); - Assert.Equal(3, helper.IntArray[3]); - - // index1 is 4, intArray[3] is 3 - e = parser.ParseExpression("IntArray[--#root.Index1]++"); - Assert.Equal(3, e.GetValue(ctx)); - Assert.Equal(3, helper.Index1); - Assert.Equal(4, helper.IntArray[3]); - } - - [Fact] - public void IncrementAllNodeTypes() - { - var helper = new Spr9751(); - var ctx = new StandardEvaluationContext(helper); - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression e; - - // BooleanLiteral - ExpectFailNotAssignable(parser, ctx, "true++"); - ExpectFailNotAssignable(parser, ctx, "--false"); - ExpectFailSetValueNotSupported(parser, ctx, "true=false"); - - // IntLiteral - ExpectFailNotAssignable(parser, ctx, "12++"); - ExpectFailNotAssignable(parser, ctx, "--1222"); - ExpectFailSetValueNotSupported(parser, ctx, "12=16"); - - // LongLiteral - ExpectFailNotAssignable(parser, ctx, "1.0d++"); - ExpectFailNotAssignable(parser, ctx, "--3.4d"); - ExpectFailSetValueNotSupported(parser, ctx, "1.0d=3.2d"); - - // NullLiteral - ExpectFailNotAssignable(parser, ctx, "null++"); - ExpectFailNotAssignable(parser, ctx, "--null"); - ExpectFailSetValueNotSupported(parser, ctx, "null=null"); - ExpectFailSetValueNotSupported(parser, ctx, "null=123"); - - // OpAnd - ExpectFailNotAssignable(parser, ctx, "(true && false)++"); - ExpectFailNotAssignable(parser, ctx, "--(false AND true)"); - ExpectFailSetValueNotSupported(parser, ctx, "(true && false)=(false && true)"); - - // OpDivide - ExpectFailNotAssignable(parser, ctx, "(3/4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2/5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1/2)=(3/4)"); - - // OpEq - ExpectFailNotAssignable(parser, ctx, "(3==4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2==5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1==2)=(3==4)"); - - // OpGE - ExpectFailNotAssignable(parser, ctx, "(3>=4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2>=5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1>=2)=(3>=4)"); - - // OpGT - ExpectFailNotAssignable(parser, ctx, "(3>4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2>5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1>2)=(3>4)"); - - // OpLE - ExpectFailNotAssignable(parser, ctx, "(3<=4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2<=5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1<=2)=(3<=4)"); - - // OpLT - ExpectFailNotAssignable(parser, ctx, "(3<4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2<5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1<2)=(3<4)"); - - // OpMinus - ExpectFailNotAssignable(parser, ctx, "(3-4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2-5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1-2)=(3-4)"); - - // OpModulus - ExpectFailNotAssignable(parser, ctx, "(3%4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2%5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1%2)=(3%4)"); - - // OpMultiply - ExpectFailNotAssignable(parser, ctx, "(3*4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2*5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1*2)=(3*4)"); - - // OpNE - ExpectFailNotAssignable(parser, ctx, "(3!=4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2!=5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1!=2)=(3!=4)"); - - // OpOr - ExpectFailNotAssignable(parser, ctx, "(true || false)++"); - ExpectFailNotAssignable(parser, ctx, "--(false OR true)"); - ExpectFailSetValueNotSupported(parser, ctx, "(true || false)=(false OR true)"); - - // OpPlus - ExpectFailNotAssignable(parser, ctx, "(3+4)++"); - ExpectFailNotAssignable(parser, ctx, "--(2+5)"); - ExpectFailSetValueNotSupported(parser, ctx, "(1+2)=(3+4)"); - - // RealLiteral - ExpectFailNotAssignable(parser, ctx, "1.0d++"); - ExpectFailNotAssignable(parser, ctx, "--2.0d"); - ExpectFailSetValueNotSupported(parser, ctx, "(1.0d)=(3.0d)"); - ExpectFailNotAssignable(parser, ctx, "1.0f++"); - ExpectFailNotAssignable(parser, ctx, "--2.0f"); - ExpectFailSetValueNotSupported(parser, ctx, "(1.0f)=(3.0f)"); - - // stringLiteral - ExpectFailNotAssignable(parser, ctx, "'abc'++"); - ExpectFailNotAssignable(parser, ctx, "--'def'"); - ExpectFailSetValueNotSupported(parser, ctx, "'abc'='def'"); - - // Ternary - ExpectFailNotAssignable(parser, ctx, "(true?true:false)++"); - ExpectFailNotAssignable(parser, ctx, "--(true?true:false)"); - ExpectFailSetValueNotSupported(parser, ctx, "(true?true:false)=(true?true:false)"); - - // TypeReference - ExpectFailNotAssignable(parser, ctx, "T(String)++"); - ExpectFailNotAssignable(parser, ctx, "--T(Int32)"); - ExpectFailSetValueNotSupported(parser, ctx, "T(String)=T(Int32)"); - - // OperatorBetween - ExpectFailNotAssignable(parser, ctx, "(3 between {1,5})++"); - ExpectFailNotAssignable(parser, ctx, "--(3 between {1,5})"); - ExpectFailSetValueNotSupported(parser, ctx, "(3 between {1,5})=(3 between {1,5})"); - - // OperatorInstanceOf - ExpectFailNotAssignable(parser, ctx, "(Type instanceof T(String))++"); - ExpectFailNotAssignable(parser, ctx, "--(Type instanceof T(String))"); - ExpectFailSetValueNotSupported(parser, ctx, "(Type instanceof T(String))=(Type instanceof T(String))"); - - // Elvis - ExpectFailNotAssignable(parser, ctx, "(true?:false)++"); - ExpectFailNotAssignable(parser, ctx, "--(true?:false)"); - ExpectFailSetValueNotSupported(parser, ctx, "(true?:false)=(true?:false)"); - - // OpInc - ExpectFailNotAssignable(parser, ctx, "(Iii++)++"); - ExpectFailNotAssignable(parser, ctx, "--(++Iii)"); - ExpectFailSetValueNotSupported(parser, ctx, "(Iii++)=(++Iii)"); - - // OpDec - ExpectFailNotAssignable(parser, ctx, "(Iii--)++"); - ExpectFailNotAssignable(parser, ctx, "--(--Iii)"); - ExpectFailSetValueNotSupported(parser, ctx, "(Iii--)=(--Iii)"); - - // OperatorNot - ExpectFailNotAssignable(parser, ctx, "(!true)++"); - ExpectFailNotAssignable(parser, ctx, "--(!false)"); - ExpectFailSetValueNotSupported(parser, ctx, "(!true)=(!false)"); - - // OperatorPower - ExpectFailNotAssignable(parser, ctx, "(Iii^2)++"); - ExpectFailNotAssignable(parser, ctx, "--(Iii^2)"); - ExpectFailSetValueNotSupported(parser, ctx, "(Iii^2)=(Iii^3)"); - - // Assign - // iii=42 - e = parser.ParseExpression("Iii=Iii++"); - Assert.Equal(42, helper.Iii); - int return_iii = e.GetValue(ctx); - Assert.Equal(42, helper.Iii); - Assert.Equal(42, return_iii); - - // Identifier - e = parser.ParseExpression("Iii++"); - Assert.Equal(42, helper.Iii); - return_iii = e.GetValue(ctx); - Assert.Equal(42, return_iii); - Assert.Equal(43, helper.Iii); - - e = parser.ParseExpression("--Iii"); - Assert.Equal(43, helper.Iii); - return_iii = e.GetValue(ctx); - Assert.Equal(42, return_iii); - Assert.Equal(42, helper.Iii); - - e = parser.ParseExpression("Iii=99"); - Assert.Equal(42, helper.Iii); - return_iii = e.GetValue(ctx); - Assert.Equal(99, return_iii); - Assert.Equal(99, helper.Iii); - - // CompoundExpression - // foo.iii == 99 - e = parser.ParseExpression("Foo.Iii++"); - Assert.Equal(99, helper.Foo.Iii); - int return_foo_iii = e.GetValue(ctx); - Assert.Equal(99, return_foo_iii); - Assert.Equal(100, helper.Foo.Iii); - - e = parser.ParseExpression("--Foo.Iii"); - Assert.Equal(100, helper.Foo.Iii); - return_foo_iii = e.GetValue(ctx); - Assert.Equal(99, return_foo_iii); - Assert.Equal(99, helper.Foo.Iii); - - e = parser.ParseExpression("Foo.Iii=999"); - Assert.Equal(99, helper.Foo.Iii); - return_foo_iii = e.GetValue(ctx); - Assert.Equal(999, return_foo_iii); - Assert.Equal(999, helper.Foo.Iii); - - // ConstructorReference - ExpectFailNotAssignable(parser, ctx, "(new String('abc'))++"); - ExpectFailNotAssignable(parser, ctx, "--(new String('abc'))"); - ExpectFailSetValueNotSupported(parser, ctx, "(new String('abc'))=(new String('abc'))"); - - // MethodReference - ExpectFailNotIncrementable(parser, ctx, "M()++"); - ExpectFailNotDecrementable(parser, ctx, "--M()"); - ExpectFailSetValueNotSupported(parser, ctx, "M()=M()"); - - // OperatorMatches - ExpectFailNotAssignable(parser, ctx, "('abc' matches '^a..')++"); - ExpectFailNotAssignable(parser, ctx, "--('abc' matches '^a..')"); - ExpectFailSetValueNotSupported(parser, ctx, "('abc' matches '^a..')=('abc' matches '^a..')"); - - // Selection - ctx.RegisterFunction("IsEven", typeof(Spr9751).GetMethod(nameof(Spr9751.IsEven), new[] - { - typeof(int) - })); - - ExpectFailNotIncrementable(parser, ctx, "({1,2,3}.?[#IsEven(#this)])++"); - ExpectFailNotDecrementable(parser, ctx, "--({1,2,3}.?[#IsEven(#this)])"); - ExpectFailNotAssignable(parser, ctx, "({1,2,3}.?[#IsEven(#this)])=({1,2,3}.?[#IsEven(#this)])"); - - // slightly diff here because return value isn't a list, it is a single entity - ExpectFailNotAssignable(parser, ctx, "({1,2,3}.^[#IsEven(#this)])++"); - ExpectFailNotAssignable(parser, ctx, "--({1,2,3}.^[#IsEven(#this)])"); - ExpectFailNotAssignable(parser, ctx, "({1,2,3}.^[#IsEven(#this)])=({1,2,3}.^[#IsEven(#this)])"); - - ExpectFailNotAssignable(parser, ctx, "({1,2,3}.$[#IsEven(#this)])++"); - ExpectFailNotAssignable(parser, ctx, "--({1,2,3}.$[#IsEven(#this)])"); - ExpectFailNotAssignable(parser, ctx, "({1,2,3}.$[#IsEven(#this)])=({1,2,3}.$[#IsEven(#this)])"); - - // FunctionReference - ExpectFailNotAssignable(parser, ctx, "#IsEven(3)++"); - ExpectFailNotAssignable(parser, ctx, "--#IsEven(4)"); - ExpectFailSetValueNotSupported(parser, ctx, "#IsEven(3)=#IsEven(5)"); - - // VariableReference - ctx.SetVariable("wibble", "hello world"); - ExpectFailNotIncrementable(parser, ctx, "#wibble++"); - ExpectFailNotDecrementable(parser, ctx, "--#wibble"); - e = parser.ParseExpression("#wibble=#wibble+#wibble"); - string s = e.GetValue(ctx); - Assert.Equal("hello worldhello world", s); - Assert.Equal("hello worldhello world", ctx.LookupVariable("wibble")); - - ctx.SetVariable("wobble", 3); - e = parser.ParseExpression("#wobble++"); - Assert.Equal(3, ctx.LookupVariable("wobble")); - int r = e.GetValue(ctx); - Assert.Equal(3, r); - Assert.Equal(4, ctx.LookupVariable("wobble")); - - e = parser.ParseExpression("--#wobble"); - Assert.Equal(4, ctx.LookupVariable("wobble")); - r = e.GetValue(ctx); - Assert.Equal(3, r); - Assert.Equal(3, ctx.LookupVariable("wobble")); - - e = parser.ParseExpression("#wobble=34"); - Assert.Equal(3, ctx.LookupVariable("wobble")); - r = e.GetValue(ctx); - Assert.Equal(34, r); - Assert.Equal(34, ctx.LookupVariable("wobble")); - - // Projection - ExpectFailNotIncrementable(parser, ctx, "({1,2,3}.![#IsEven(#this)])++"); // projection would be {false,true,false} - ExpectFailNotDecrementable(parser, ctx, "--({1,2,3}.![#IsEven(#this)])"); // projection would be {false,true,false} - ExpectFailNotAssignable(parser, ctx, "({1,2,3}.![#IsEven(#this)])=({1,2,3}.![#IsEven(#this)])"); - - // InlineList - ExpectFailNotAssignable(parser, ctx, "({1,2,3})++"); - ExpectFailNotAssignable(parser, ctx, "--({1,2,3})"); - ExpectFailSetValueNotSupported(parser, ctx, "({1,2,3})=({1,2,3})"); - - // InlineMap - ExpectFailNotAssignable(parser, ctx, "({'a':1,'b':2,'c':3})++"); - ExpectFailNotAssignable(parser, ctx, "--({'a':1,'b':2,'c':3})"); - ExpectFailSetValueNotSupported(parser, ctx, "({'a':1,'b':2,'c':3})=({'a':1,'b':2,'c':3})"); - - // ServiceReference - ctx.ServiceResolver = new MyServiceResolver(); - ExpectFailNotAssignable(parser, ctx, "@foo++"); - ExpectFailNotAssignable(parser, ctx, "--@foo"); - ExpectFailSetValueNotSupported(parser, ctx, "@foo=@bar"); - - // PropertyOrFieldReference - helper.Iii = 42; - e = parser.ParseExpression("Iii++"); - Assert.Equal(42, helper.Iii); - r = e.GetValue(ctx); - Assert.Equal(42, r); - Assert.Equal(43, helper.Iii); - - e = parser.ParseExpression("--Iii"); - Assert.Equal(43, helper.Iii); - r = e.GetValue(ctx); - Assert.Equal(42, r); - Assert.Equal(42, helper.Iii); - - e = parser.ParseExpression("Iii=100"); - Assert.Equal(42, helper.Iii); - r = e.GetValue(ctx); - Assert.Equal(100, r); - Assert.Equal(100, helper.Iii); - } - - private void ExpectFailNotAssignable(IExpressionParser parser, IEvaluationContext eContext, string expressionString) - { - ExpectFail(parser, eContext, expressionString, SpelMessage.NotAssignable); - } - - private void ExpectFailSetValueNotSupported(IExpressionParser parser, IEvaluationContext eContext, string expressionString) - { - ExpectFail(parser, eContext, expressionString, SpelMessage.SetValueNotSupported); - } - - private void ExpectFailNotIncrementable(IExpressionParser parser, IEvaluationContext eContext, string expressionString) - { - ExpectFail(parser, eContext, expressionString, SpelMessage.OperandNotIncrementable); - } - - private void ExpectFailNotDecrementable(IExpressionParser parser, IEvaluationContext eContext, string expressionString) - { - ExpectFail(parser, eContext, expressionString, SpelMessage.OperandNotDecrementable); - } - - private void ExpectFail(IExpressionParser parser, IEvaluationContext eContext, string expressionString, SpelMessage messageCode) - { - var ex = Assert.Throws(() => - { - IExpression e = parser.ParseExpression(expressionString); - - if (IsDebug) - { - SpelUtilities.PrintAbstractSyntaxTree(Console.Out, e); - } - - e.GetValue(eContext); - }); - - Assert.Equal(messageCode, ex.MessageCode); - } - - public sealed class MyServiceResolver : IServiceResolver - { - public object Resolve(IEvaluationContext context, string serviceName) - { - if (serviceName == "foo" || serviceName == "bar") - { - return new Spr9751_2(); - } - - throw new AccessException($"not heard of {serviceName}"); - } - } - - public sealed class Spr9751 - { - public string Type { get; set; } = "hello"; - public decimal Bd { get; set; } = 2M; - public double Ddd { get; set; } = 2.0d; - public float Fff { get; set; } = 3.0f; - public long Lll { get; set; } = 66666L; - public int Iii { get; set; } = 42; - public short Sss { get; set; } = 15; - public Spr9751_2 Foo { get; set; } = new(); - - public int[] IntArray { get; } = - { - 1, - 2, - 3, - 4, - 5 - }; - - public int Index1 { get; set; } = 2; - - public int[] IntegerArray { get; } - public int Index2 { get; } = 2; - - public List ListOfStrings { get; } - public int Index3 { get; set; } - - public Spr9751() - { - IntegerArray = new int[5]; - IntegerArray[0] = 1; - IntegerArray[1] = 2; - IntegerArray[2] = 3; - IntegerArray[3] = 4; - IntegerArray[4] = 5; - - ListOfStrings = new List - { - "abc" - }; - } - - public static bool IsEven(int i) - { - return i % 2 == 0; - } - - public void M() - { - } - } - - public sealed class Spr9751_2 - { - public int Iii { get; set; } = 99; - } - - public sealed class CustomMethodResolver : IMethodResolver - { - public IMethodExecutor Resolve(IEvaluationContext context, object targetObject, string name, List argumentTypes) - { - return null; - } - } - - public sealed class CustomMethodFilter : IMethodFilter - { - public List Filter(List methods) - { - return null; - } - } - - public sealed class Foo - { - public string Bar { get; set; } = "hello"; - } - - public sealed class TestClass - { - public Foo Wibble { get; set; } - - public IDictionary Map { get; set; } - public Dictionary MapStringToInteger { get; } - public List List { get; set; } - public IList List2 { get; } - - public List Foo { get; set; } - - public IList FooIList { get; set; } - - public IDictionary Map2 { get; } - - public Foo Wibble2 { get; } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/ExpressionLanguageScenarioTests.cs b/src/Common/test/Common.Expression.Test/Spring/ExpressionLanguageScenarioTests.cs deleted file mode 100644 index a14e3954c5..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/ExpressionLanguageScenarioTests.cs +++ /dev/null @@ -1,286 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestResources; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class ExpressionLanguageScenarioTests : AbstractExpressionTests -{ - [Fact] - public void TestScenario_UsingStandardInfrastructure() - { - try - { - // Create a parser - var parser = new SpelExpressionParser(); - - // Parse an expression - IExpression expr = parser.ParseRaw("new String('hello world')"); - - // Evaluate it using a 'standard' context - object value1 = expr.GetValue(); - - // They are reusable - object value2 = expr.GetValue(); - - Assert.Equal(value1, value2); - Assert.Equal("hello world", value2); - Assert.IsType(value1); - } - catch (SpelEvaluationException ex) - { - throw new SystemException(ex.Message, ex); - } - catch (ParseException ex) - { - throw new SystemException(ex.Message, ex); - } - } - - [Fact] - public void TestScenario_DefiningVariablesThatWillBeAccessibleInExpressions() - { - // Create a parser - var parser = new SpelExpressionParser(); - - // Use the standard evaluation context - var ctx = new StandardEvaluationContext(); - ctx.SetVariable("favouriteColour", "blue"); - - var primes = new List - { - 2, - 3, - 5, - 7, - 11, - 13, - 17 - }; - - ctx.SetVariable("primes", primes); - - IExpression expr = parser.ParseRaw("#favouriteColour"); - object value = expr.GetValue(ctx); - Assert.Equal("blue", value); - - expr = parser.ParseRaw("#primes[1]"); - value = expr.GetValue(ctx); - Assert.Equal(3, value); - - // all prime numbers > 10 from the list (using selection ?{...}) - expr = parser.ParseRaw("#primes.?[#this>10]"); - value = expr.GetValue(ctx); - var asList = value as IList; - Assert.Equal(3, asList.Count); - Assert.Equal(11, asList[0]); - Assert.Equal(13, asList[1]); - Assert.Equal(17, asList[2]); - } - - [Fact] - public void TestScenario_UsingADifferentRootContextObject() - { - // Create a parser - var parser = new SpelExpressionParser(); - - // Use the standard evaluation context - var ctx = new StandardEvaluationContext(); - - var tc = new TestClass - { - Property = 42, - Str = "wibble" - }; - - ctx.SetRootObject(tc); - - // read it, set it, read it again - IExpression expr = parser.ParseRaw("Str"); - object value = expr.GetValue(ctx); - Assert.Equal("wibble", value); - expr = parser.ParseRaw("Str"); - expr.SetValue(ctx, "wobble"); - expr = parser.ParseRaw("Str"); - value = expr.GetValue(ctx); - Assert.Equal("wobble", value); - - // or using assignment within the expression - expr = parser.ParseRaw("Str='wabble'"); - value = expr.GetValue(ctx); - Assert.Equal("wabble", value); - expr = parser.ParseRaw("Str"); - value = expr.GetValue(ctx); - Assert.Equal("wabble", value); - - // private property will be accessed through getter() - expr = parser.ParseRaw("Property"); - value = expr.GetValue(ctx); - Assert.Equal(42, value); - - // ... and set through setter - expr = parser.ParseRaw("Property=4"); - value = expr.GetValue(ctx); - Assert.Equal(4, value); - expr = parser.ParseRaw("Property"); - value = expr.GetValue(ctx); - Assert.Equal(4, value); - } - - [Fact] - public void TestScenario_RegisteringJavaMethodsAsFunctionsAndCallingThem() - { - try - { - // Create a parser - var parser = new SpelExpressionParser(); - - // Use the standard evaluation context - var ctx = new StandardEvaluationContext(); - - ctx.RegisterFunction("Repeat", typeof(ExpressionLanguageScenarioTests).GetMethod(nameof(Repeat), new[] - { - typeof(string) - })); - - IExpression expr = parser.ParseRaw("#Repeat('hello')"); - object value = expr.GetValue(ctx); - Assert.Equal("hellohello", value); - } - catch (EvaluationException ex) - { - throw new SystemException(ex.Message, ex); - } - catch (ParseException ex) - { - throw new SystemException(ex.Message, ex); - } - } - - [Fact] - public void TestScenario_AddingYourOwnPropertyResolvers_1() - { - // Create a parser - var parser = new SpelExpressionParser(); - - // Use the standard evaluation context - var ctx = new StandardEvaluationContext(); - - ctx.AddPropertyAccessor(new FruitColorAccessor()); - IExpression expr = parser.ParseRaw("Orange"); - object value = expr.GetValue(ctx); - Assert.Equal(Color.Orange, value); - var ex = Assert.Throws(() => expr.SetValue(ctx, Color.Blue)); - Assert.Equal(SpelMessage.PropertyOrFieldNotWritableOnNull, ex.MessageCode); - } - - [Fact] - public void TestScenario_AddingYourOwnPropertyResolvers_2() - { - // Create a parser - var parser = new SpelExpressionParser(); - - // Use the standard evaluation context - var ctx = new StandardEvaluationContext(); - - ctx.AddPropertyAccessor(new VegetableColorAccessor()); - IExpression expr = parser.ParseRaw("Pea"); - object value = expr.GetValue(ctx); - Assert.Equal(Color.Green, value); - var ex = Assert.Throws(() => expr.SetValue(ctx, Color.Blue)); - Assert.Equal(SpelMessage.PropertyOrFieldNotWritableOnNull, ex.MessageCode); - } - - public static string Repeat(string s) - { - return s + s; - } - - public sealed class FruitColorAccessor : IPropertyAccessor - { - private static readonly Dictionary PropertyMap = new(); - - static FruitColorAccessor() - { - PropertyMap.Add("Banana", Color.Yellow); - PropertyMap.Add("Apple", Color.Red); - PropertyMap.Add("Orange", Color.Orange); - } - - public IList GetSpecificTargetClasses() - { - return null; - } - - public bool CanRead(IEvaluationContext context, object target, string name) - { - return PropertyMap.ContainsKey(name); - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - PropertyMap.TryGetValue(name, out Color value); - return new TypedValue(value); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return false; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - } - } - - public sealed class VegetableColorAccessor : IPropertyAccessor - { - private static readonly Dictionary PropertyMap = new(); - - static VegetableColorAccessor() - { - PropertyMap.Add("Pea", Color.Green); - PropertyMap.Add("Carrot", Color.Orange); - } - - public IList GetSpecificTargetClasses() - { - return null; - } - - public bool CanRead(IEvaluationContext context, object target, string name) - { - return PropertyMap.ContainsKey(name); - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - PropertyMap.TryGetValue(name, out Color value); - return new TypedValue(value); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return false; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - } - } - - public sealed class TestClass - { - public string Str { get; set; } - - public int Property { get; set; } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/ExpressionStateTests.cs b/src/Common/test/Common.Expression.Test/Spring/ExpressionStateTests.cs deleted file mode 100644 index bf5dd3c263..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/ExpressionStateTests.cs +++ /dev/null @@ -1,256 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestResources; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class ExpressionStateTests : AbstractExpressionTests -{ - [Fact] - public void TestConstruction() - { - StandardEvaluationContext context = TestScenarioCreator.GetTestEvaluationContext(); - var state = new ExpressionState(context); - Assert.Equal(context, state.EvaluationContext); - } - - [Fact] - public void TestLocalVariables() - { - ExpressionState state = GetState(); - - object value = state.LookupLocalVariable("foo"); - Assert.Null(value); - - state.SetLocalVariable("foo", 34); - value = state.LookupLocalVariable("foo"); - Assert.Equal(34, value); - - state.SetLocalVariable("foo", null); - value = state.LookupLocalVariable("foo"); - Assert.Null(value); - } - - [Fact] - public void TestVariables() - { - ExpressionState state = GetState(); - ITypedValue typedValue = state.LookupVariable("foo"); - Assert.Equal(TypedValue.Null, typedValue); - - state.SetVariable("foo", 34); - typedValue = state.LookupVariable("foo"); - Assert.Equal(34, typedValue.Value); - Assert.Equal(typeof(int), typedValue.TypeDescriptor); - - state.SetVariable("foo", "abc"); - typedValue = state.LookupVariable("foo"); - Assert.Equal("abc", typedValue.Value); - Assert.Equal(typeof(string), typedValue.TypeDescriptor); - } - - [Fact] - public void TestNoVariableInterference() - { - ExpressionState state = GetState(); - ITypedValue typedValue = state.LookupVariable("foo"); - Assert.Equal(TypedValue.Null, typedValue); - - state.SetLocalVariable("foo", 34); - typedValue = state.LookupVariable("foo"); - Assert.Equal(TypedValue.Null, typedValue); - - state.SetVariable("goo", "hello"); - Assert.Null(state.LookupLocalVariable("goo")); - } - - [Fact] - public void TestLocalVariableNestedScopes() - { - ExpressionState state = GetState(); - Assert.Null(state.LookupLocalVariable("foo")); - - state.SetLocalVariable("foo", 12); - Assert.Equal(12, state.LookupLocalVariable("foo")); - - state.EnterScope(null); - - // found in upper scope - Assert.Equal(12, state.LookupLocalVariable("foo")); - - state.SetLocalVariable("foo", "abc"); - - // found in nested scope - Assert.Equal("abc", state.LookupLocalVariable("foo")); - - state.ExitScope(); - - // found in nested scope - Assert.Equal(12, state.LookupLocalVariable("foo")); - } - - [Fact] - public void TestRootContextObject() - { - ExpressionState state = GetState(); - Assert.IsType(state.RootContextObject.Value); - - // although the root object is being set on the evaluation context, the value in the 'state' remains what it was when constructed - ((StandardEvaluationContext)state.EvaluationContext).SetRootObject(null); - Assert.IsType(state.RootContextObject.Value); - - state = new ExpressionState(new StandardEvaluationContext()); - Assert.Equal(TypedValue.Null, state.RootContextObject); - - ((StandardEvaluationContext)state.EvaluationContext).SetRootObject(null); - Assert.Null(state.RootContextObject.Value); - } - - [Fact] - public void TestActiveContextObject() - { - ExpressionState state = GetState(); - Assert.Equal(state.RootContextObject.Value, state.GetActiveContextObject().Value); - - Assert.Throws(() => state.PopActiveContextObject()); - - state.PushActiveContextObject(new TypedValue(34)); - Assert.Equal(34, state.GetActiveContextObject().Value); - - state.PushActiveContextObject(new TypedValue("hello")); - Assert.Equal("hello", state.GetActiveContextObject().Value); - - state.PopActiveContextObject(); - Assert.Equal(34, state.GetActiveContextObject().Value); - - state.PopActiveContextObject(); - Assert.Equal(state.RootContextObject.Value, state.GetActiveContextObject().Value); - - state = new ExpressionState(new StandardEvaluationContext()); - Assert.Equal(TypedValue.Null, state.GetActiveContextObject()); - } - - [Fact] - public void TestPopulatedNestedScopes() - { - ExpressionState state = GetState(); - Assert.Null(state.LookupLocalVariable("foo")); - - state.EnterScope("foo", 34); - Assert.Equal(34, state.LookupLocalVariable("foo")); - - state.EnterScope(null); - state.SetLocalVariable("foo", 12); - Assert.Equal(12, state.LookupLocalVariable("foo")); - - state.ExitScope(); - Assert.Equal(34, state.LookupLocalVariable("foo")); - - state.ExitScope(); - Assert.Null(state.LookupLocalVariable("goo")); - } - - [Fact] - public void TestRootObjectConstructor() - { - IEvaluationContext ctx = GetContext(); - - // supplied should override root on context - var state = new ExpressionState(ctx, new TypedValue("i am a string")); - ITypedValue stateRoot = state.RootContextObject; - Assert.Equal(typeof(string), stateRoot.TypeDescriptor); - Assert.Equal("i am a string", stateRoot.Value); - } - - [Fact] - public void TestPopulatedNestedScopesMap() - { - ExpressionState state = GetState(); - Assert.Null(state.LookupLocalVariable("foo")); - Assert.Null(state.LookupLocalVariable("goo")); - - var m = new Dictionary - { - { "foo", 34 }, - { "goo", "abc" } - }; - - state.EnterScope(m); - Assert.Equal(34, state.LookupLocalVariable("foo")); - Assert.Equal("abc", state.LookupLocalVariable("goo")); - - state.EnterScope(null); - state.SetLocalVariable("foo", 12); - Assert.Equal(12, state.LookupLocalVariable("foo")); - Assert.Equal("abc", state.LookupLocalVariable("goo")); - - state.ExitScope(); - state.ExitScope(); - Assert.Null(state.LookupLocalVariable("foo")); - Assert.Null(state.LookupLocalVariable("goo")); - } - - [Fact] - public void TestOperators() - { - ExpressionState state = GetState(); - var ex = Assert.Throws(() => state.Operate(Operation.Add, 1, 2)); - Assert.Equal(SpelMessage.OperatorNotSupportedBetweenTypes, ex.MessageCode); - - ex = Assert.Throws(() => state.Operate(Operation.Add, null, null)); - Assert.Equal(SpelMessage.OperatorNotSupportedBetweenTypes, ex.MessageCode); - } - - [Fact] - public void TestComparator() - { - ExpressionState state = GetState(); - Assert.Equal(state.EvaluationContext.TypeComparator, state.TypeComparator); - } - - [Fact] - public void TestTypeLocator() - { - ExpressionState state = GetState(); - Assert.NotNull(state.EvaluationContext.TypeLocator); - Assert.Equal(typeof(int), state.FindType("System.Int32")); - var ex = Assert.Throws(() => state.FindType("someMadeUpName")); - Assert.Equal(SpelMessage.TypeNotFound, ex.MessageCode); - } - - [Fact] - public void TestTypeConversion() - { - ExpressionState state = GetState(); - string s = (string)state.ConvertValue(34, typeof(string)); - Assert.Equal("34", s); - - s = (string)state.ConvertValue(new TypedValue(34), typeof(string)); - Assert.Equal("34", s); - } - - [Fact] - public void TestPropertyAccessors() - { - ExpressionState state = GetState(); - Assert.Equal(state.EvaluationContext.PropertyAccessors, state.PropertyAccessors); - } - - private ExpressionState GetState() - { - StandardEvaluationContext context = TestScenarioCreator.GetTestEvaluationContext(); - var state = new ExpressionState(context); - return state; - } - - private IEvaluationContext GetContext() - { - return TestScenarioCreator.GetTestEvaluationContext(); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/ExpressionWithConversionTests.cs b/src/Common/test/Common.Expression.Test/Spring/ExpressionWithConversionTests.cs deleted file mode 100644 index dc68d2b507..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/ExpressionWithConversionTests.cs +++ /dev/null @@ -1,193 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class ExpressionWithConversionTests : AbstractExpressionTests -{ - private static readonly List ListOfString = new(); - private static readonly List ListOfInteger = new(); - private static Type _typeDescriptorForListOfInteger; - private static Type _typeDescriptorForListOfString; - - static ExpressionWithConversionTests() - { - ListOfString.Add("1"); - ListOfString.Add("2"); - ListOfString.Add("3"); - ListOfInteger.Add(4); - ListOfInteger.Add(5); - ListOfInteger.Add(6); - } - - public ExpressionWithConversionTests() - { - _typeDescriptorForListOfString = typeof(ExpressionWithConversionTests).GetField("ListOfString", BindingFlags.NonPublic | BindingFlags.Static).FieldType; - - _typeDescriptorForListOfInteger = - typeof(ExpressionWithConversionTests).GetField("ListOfInteger", BindingFlags.NonPublic | BindingFlags.Static).FieldType; - } - - [Fact] - public void TestConversionsAvailable() - { - var tcs = new TypeConvertorUsingConversionService(); - - // ArrayList containing List to List - Type type = _typeDescriptorForListOfString.GetGenericArguments()[0]; - Assert.Equal(typeof(string), type); - var l = tcs.ConvertValue(ListOfInteger, ListOfInteger.GetType(), _typeDescriptorForListOfString) as List; - Assert.NotNull(l); - - // ArrayList containing List to List - type = _typeDescriptorForListOfInteger.GetGenericArguments()[0]; - Assert.Equal(typeof(int), type); - - var l2 = tcs.ConvertValue(ListOfString, ListOfString.GetType(), _typeDescriptorForListOfString) as List; - Assert.NotNull(l2); - } - - [Fact] - public void TestSetParameterizedList() - { - StandardEvaluationContext context = TestScenarioCreator.GetTestEvaluationContext(); - IExpression e = Parser.ParseExpression("ListOfInteger.Count"); - Assert.Equal(0, e.GetValue(context, typeof(int))); - context.TypeConverter = new TypeConvertorUsingConversionService(); - - // Assign a List to the List field - the component elements should be converted - Parser.ParseExpression("ListOfInteger").SetValue(context, ListOfString); - - // size now 3 - Assert.Equal(3, e.GetValue(context, typeof(int))); - - // element type correctly Integer - object objectType = Parser.ParseExpression("ListOfInteger[1].GetType()").GetValue(context, typeof(Type)); - Assert.Equal(typeof(int), objectType); - } - - [Fact] - public void TestCoercionToCollectionOfPrimitive() - { - var evaluationContext = new StandardEvaluationContext(); - - Type collectionType = typeof(TestTarget).GetMethod(nameof(TestTarget.Sum)).GetParameters()[0].ParameterType; - - // The type conversion is possible - Assert.True(evaluationContext.TypeConverter.CanConvert(typeof(string), collectionType)); - - // ... and it can be done successfully - object result = evaluationContext.TypeConverter.ConvertValue("1,2,3,4", typeof(string), collectionType); - var asList = result as ICollection; - Assert.NotNull(asList); - Assert.Equal(4, asList.Count); - - evaluationContext.SetVariable("target", new TestTarget()); - - // OK up to here, so the evaluation should be fine... - // ... but this fails - int result2 = (int)Parser.ParseExpression("#target.Sum(#root)").GetValue(evaluationContext, "1,2,3,4"); - Assert.Equal(10, result2); - } - - [Fact] - public void TestConvert() - { - var root = new Foo("bar"); - - ICollection foos = new List - { - "baz" - }; - - var context = new StandardEvaluationContext(root); - - // property access - IExpression expression = Parser.ParseExpression("Foos"); - expression.SetValue(context, foos); - Foo baz = root.Foos.Single(); - Assert.Equal("baz", baz.Value); - - // method call - expression = Parser.ParseExpression("Foos=#foos"); - context.SetVariable("foos", foos); - expression.GetValue(context); - baz = root.Foos.Single(); - Assert.Equal("baz", baz.Value); - - // method call with result from method call - expression = Parser.ParseExpression("Foos=FoosAsStrings"); - expression.GetValue(context); - baz = root.Foos.Single(); - Assert.Equal("baz", baz.Value); - - // method call with result from method call - expression = Parser.ParseExpression("Foos=FoosAsObjects"); - expression.GetValue(context); - baz = root.Foos.Single(); - Assert.Equal("baz", baz.Value); - } - - public sealed class TestTarget - { - public int Sum(ICollection numbers) - { - int total = 0; - - foreach (int i in numbers) - { - total += i; - } - - return total; - } - } - - public sealed class TypeConvertorUsingConversionService : ITypeConverter - { - public IConversionService ConversionService { get; set; } = new DefaultConversionService(); - - public bool CanConvert(Type sourceType, Type targetType) - { - return ConversionService.CanConvert(sourceType, targetType); - } - - public object ConvertValue(object value, Type sourceType, Type targetType) - { - return ConversionService.Convert(value, sourceType, targetType); - } - } - - public sealed class Foo - { - public string Value { get; } - -#pragma warning disable S4004 // Collection properties should be readonly - public ICollection Foos { get; set; } -#pragma warning restore S4004 // Collection properties should be readonly - - public ICollection FoosAsStrings => - new List - { - "baz" - }; - - public ICollection FoosAsObjects => - new List - { - "baz" - }; - - public Foo(string value) - { - Value = value; - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/IndexingTests.cs b/src/Common/test/Common.Expression.Test/Spring/IndexingTests.cs deleted file mode 100644 index 8c1605678f..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/IndexingTests.cs +++ /dev/null @@ -1,432 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -#pragma warning disable S4004 // Collection properties should be readonly - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class IndexingTests -{ - [field: FieldAnnotation] - public object Property { get; set; } - - public IList ListOfMapsNotGeneric { get; set; } - public Dictionary ParameterizedMap { get; set; } - public List ParameterizedList { get; set; } - public List> ParameterizedListOfList { get; set; } - public IList Property2 { get; set; } - - [field: FieldAnnotation] - public IList ListNotGeneric { get; set; } - - [field: FieldAnnotation] - public IDictionary MapNotGeneric { get; set; } - - public IList ListOfScalarNotGeneric { get; set; } - - [Fact] - public void IndexIntoGenericPropertyContainingMap() - { - var property = new Dictionary - { - { "foo", "bar" } - }; - - Property = property; - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("Property"); - Assert.Equal(property.GetType(), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - Assert.Equal(property, expression.GetValue(this, typeof(IDictionary))); - expression = parser.ParseExpression("Property['foo']"); - Assert.Equal("bar", expression.GetValue(this)); - } - - [Fact] - public void IndexIntoGenericPropertyContainingMapObject() - { - var property = new Dictionary>(); - - var map = new Dictionary - { - { "foo", "bar" } - }; - - property.Add("property", map); - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - context.AddPropertyAccessor(new MapAccessor()); - context.SetRootObject(property); - IExpression expression = parser.ParseExpression("property"); - Assert.Equal(typeof(Dictionary), expression.GetValueType(context)); - Assert.Equal(map, expression.GetValue(context)); - Assert.Equal(map, expression.GetValue(context, typeof(IDictionary))); - expression = parser.ParseExpression("property['foo']"); - Assert.Equal("bar", expression.GetValue(context)); - } - - [Fact] - public void SetGenericPropertyContainingMap() - { - var property = new Dictionary - { - { "foo", "bar" } - }; - - Property = property; - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("Property"); - Assert.Equal(typeof(Dictionary), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - expression = parser.ParseExpression("Property['foo']"); - Assert.Equal("bar", expression.GetValue(this)); - expression.SetValue(this, "baz"); - Assert.Equal("baz", expression.GetValue(this)); - } - - [Fact] - public void SetPropertyContainingMap() - { - var property = new Dictionary - { - { 9, 3 } - }; - - ParameterizedMap = property; - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("ParameterizedMap"); - Assert.Equal(typeof(Dictionary), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - expression = parser.ParseExpression("ParameterizedMap['9']"); - Assert.Equal(3, expression.GetValue(this)); - expression.SetValue(this, "37"); - Assert.Equal(37, expression.GetValue(this)); - } - - [Fact] - public void SetPropertyContainingMapAutoGrow() - { - var parser = new SpelExpressionParser(new SpelParserOptions(true, false)); - IExpression expression = parser.ParseExpression("ParameterizedMap"); - Assert.Equal(typeof(Dictionary), expression.GetValueType(this)); - Assert.Equal(Property, expression.GetValue(this)); - expression = parser.ParseExpression("ParameterizedMap['9']"); - Assert.Null(expression.GetValue(this)); - expression.SetValue(this, "37"); - Assert.Equal(37, expression.GetValue(this)); - } - - [Fact] - public void IndexIntoGenericPropertyContainingList() - { - var property = new List - { - "bar" - }; - - Property = property; - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("Property"); - Assert.Equal(typeof(List), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - expression = parser.ParseExpression("Property[0]"); - Assert.Equal("bar", expression.GetValue(this)); - } - - [Fact] - public void SetGenericPropertyContainingList() - { - var property = new List - { - 3 - }; - - Property = property; - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("Property"); - Assert.Equal(typeof(List), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - expression = parser.ParseExpression("Property[0]"); - Assert.Equal(3, expression.GetValue(this)); - expression.SetValue(this, "4"); - Assert.Equal(4, expression.GetValue(this)); - } - - [Fact] - public void SetGenericPropertyContainingListAutoGrow() - { - var property = new List(); - Property = property; - var parser = new SpelExpressionParser(new SpelParserOptions(true, true)); - IExpression expression = parser.ParseExpression("Property"); - Assert.Equal(typeof(List), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - expression = parser.ParseExpression("Property[0]"); - - try - { - expression.SetValue(this, "4"); - } - catch (EvaluationException ex) - { - Assert.StartsWith("EL1053E", ex.Message, StringComparison.Ordinal); - } - } - - [Fact] - public void IndexIntoPropertyContainingList() - { - var property = new List - { - 3 - }; - - ParameterizedList = property; - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("ParameterizedList"); - Assert.Equal(typeof(List), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - expression = parser.ParseExpression("ParameterizedList[0]"); - Assert.Equal(3, expression.GetValue(this)); - } - - [Fact] - public void IndexIntoPropertyContainingListOfList() - { - var property = new List> - { - new() - { - 3 - } - }; - - ParameterizedListOfList = property; - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("ParameterizedListOfList[0]"); - Assert.Equal(typeof(List), expression.GetValueType(this)); - Assert.Equal(property[0], expression.GetValue(this)); - expression = parser.ParseExpression("ParameterizedListOfList[0][0]"); - Assert.Equal(3, expression.GetValue(this)); - } - - [Fact] - public void SetPropertyContainingList() - { - var property = new List - { - 3 - }; - - ParameterizedList = property; - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("ParameterizedList"); - Assert.Equal(typeof(List), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - expression = parser.ParseExpression("ParameterizedList[0]"); - Assert.Equal(3, expression.GetValue(this)); - expression.SetValue(this, "4"); - Assert.Equal(4, expression.GetValue(this)); - } - - [Fact] - public void IndexIntoGenericPropertyContainingNullList() - { - var configuration = new SpelParserOptions(true, true); - var parser = new SpelExpressionParser(configuration); - IExpression expression = parser.ParseExpression("Property"); - Assert.Equal(typeof(object), expression.GetValueType(this)); - Assert.Equal(Property, expression.GetValue(this)); - expression = parser.ParseExpression("Property[0]"); - - try - { - Assert.Equal("bar", expression.GetValue(this)); - } - catch (EvaluationException ex) - { - Assert.StartsWith("EL1027E", ex.Message, StringComparison.Ordinal); - } - } - - [Fact] - public void IndexIntoGenericPropertyContainingGrowingList() - { - var property = new ArrayList(); - Property = property; - var configuration = new SpelParserOptions(true, true); - var parser = new SpelExpressionParser(configuration); - IExpression expression = parser.ParseExpression("Property"); - Assert.Equal(typeof(ArrayList), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - expression = parser.ParseExpression("Property[0]"); - - try - { - Assert.Equal("bar", expression.GetValue(this)); - } - catch (EvaluationException ex) - { - Assert.StartsWith("EL1053E", ex.Message, StringComparison.Ordinal); - } - } - - [Fact] - public void IndexIntoGenericPropertyContainingGrowingList2() - { - var property2 = new ArrayList(); - Property2 = property2; - var configuration = new SpelParserOptions(true, true); - var parser = new SpelExpressionParser(configuration); - IExpression expression = parser.ParseExpression("Property2"); - Assert.Equal(typeof(ArrayList), expression.GetValueType(this)); - Assert.Equal(property2, expression.GetValue(this)); - expression = parser.ParseExpression("Property2[0]"); - - try - { - Assert.Equal("bar", expression.GetValue(this)); - } - catch (EvaluationException ex) - { - Assert.StartsWith("EL1053E", ex.Message, StringComparison.Ordinal); - } - } - - [Fact] - public void IndexIntoGenericPropertyContainingArray() - { - string[] property = - { - "bar" - }; - - Property = property; - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("Property"); - Assert.Equal(typeof(string[]), expression.GetValueType(this)); - Assert.Equal(property, expression.GetValue(this)); - expression = parser.ParseExpression("Property[0]"); - Assert.Equal("bar", expression.GetValue(this)); - } - - [Fact] - public void EmptyList() - { - ListOfScalarNotGeneric = new ArrayList(); - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("ListOfScalarNotGeneric"); - Assert.Equal(typeof(ArrayList), expression.GetValueType(this)); - Assert.Equal(string.Empty, expression.GetValue(this, typeof(string))); - } - - [Fact] - public void ResolveCollectionElementType() - { - ListNotGeneric = new ArrayList(2) - { - 5, - 6 - }; - - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("ListNotGeneric"); - Assert.Equal(typeof(ArrayList), expression.GetValueType(this)); - Assert.Equal("5,6", expression.GetValue(this, typeof(string))); - } - - [Fact] - public void ResolveCollectionElementTypeNull() - { - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("ListNotGeneric"); - Assert.Equal(typeof(IList), expression.GetValueType(this)); - } - - [Fact] - public void ResolveMapKeyValueTypes() - { - MapNotGeneric = new Hashtable - { - { "baseAmount", 3.11 }, - { "bonusAmount", 7.17 } - }; - - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("MapNotGeneric"); - Assert.Equal(typeof(Hashtable), expression.GetValueType(this)); - } - - [Fact] - public void TestListOfScalar() - { - ListOfScalarNotGeneric = new ArrayList(1) - { - "5" - }; - - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("ListOfScalarNotGeneric[0]"); - Assert.Equal(5, expression.GetValue(this, typeof(int))); - } - - [Fact] - public void TestListsOfMap() - { - ListOfMapsNotGeneric = new ArrayList(); - - var map = new Hashtable - { - { "fruit", "apple" } - }; - - ListOfMapsNotGeneric.Add(map); - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("ListOfMapsNotGeneric[0]['fruit']"); - Assert.Equal("apple", expression.GetValue(this, typeof(string))); - } - - public sealed class MapAccessor : IPropertyAccessor - { - public bool CanRead(IEvaluationContext context, object target, string name) - { - return ((IDictionary)target).Contains(name); - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - return new TypedValue(((IDictionary)target)[name]); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return true; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - ((IDictionary)target).Add(name, newValue); - } - - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(IDictionary) - }; - } - } - - [AttributeUsage(AttributeTargets.Field)] - public sealed class FieldAnnotationAttribute : Attribute - { - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/ListTests.cs b/src/Common/test/Common.Expression.Test/Spring/ListTests.cs deleted file mode 100644 index 04fccc3bcc..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/ListTests.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.ObjectModel; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Ast; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class ListTests : AbstractExpressionTests -{ - private readonly Type _unmodifiableClass = typeof(ReadOnlyCollection); - - [Fact] - public void TestInlineListCreation01() - { - Evaluate("{1, 2, 3, 4, 5}", "[1,2,3,4,5]", _unmodifiableClass); - } - - [Fact] - public void TestInlineListCreation02() - { - Evaluate("{'abc', 'xyz'}", "[abc,xyz]", _unmodifiableClass); - } - - [Fact] - public void TestInlineListCreation03() - { - Evaluate("{}", "[]", _unmodifiableClass); - } - - [Fact] - public void TestInlineListCreation04() - { - Evaluate("{'abc'=='xyz'}", "[False]", typeof(List)); - } - - [Fact] - public void TestInlineListAndNesting() - { - Evaluate("{{1,2,3},{4,5,6}}", "[[1,2,3],[4,5,6]]", _unmodifiableClass); - Evaluate("{{1,'2',3},{4,{'a','b'},5,6}}", "[[1,2,3],[4,[a,b],5,6]]", _unmodifiableClass); - } - - [Fact] - public void TestInlineListError() - { - ParseAndCheckError("{'abc'", SpelMessage.Ood); - } - - [Fact] - public void TestRelOperatorsIs02() - { - Evaluate("{1, 2, 3, 4, 5} instanceof T(System.Collections.IList)", "True", typeof(bool)); - } - - [Fact] - public void TestInlineListCreation05() - { - Evaluate("3 between {1,5}", "True", typeof(bool)); - } - - [Fact] - public void TestInlineListCreation06() - { - Evaluate("8 between {1,5}", "False", typeof(bool)); - } - - [Fact] - public void TestInlineListAndProjectionSelection() - { - Evaluate("{1,2,3,4,5,6}.![#this>3]", "[False,False,False,True,True,True]", typeof(List)); - Evaluate("{1,2,3,4,5,6}.?[#this>3]", "[4,5,6]", typeof(List)); - Evaluate("{1,2,3,4,5,6,7,8,9,10}.?[#IsEven(#this) == 'y']", "[2,4,6,8,10]", typeof(List)); - } - - [Fact] - public void TestRelOperatorsBetween01() - { - Evaluate("32 between {32, 42}", "True", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsBetween02() - { - Evaluate("'efg' between {'abc', 'xyz'}", "True", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsBetween03() - { - Evaluate("42 between {32, 42}", "True", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsBetween04() - { - Evaluate("new Decimal(1) between {new Decimal(1),new Decimal(5)}", "True", typeof(bool)); - Evaluate("new Decimal(3) between {new Decimal(1),new Decimal(5)}", "True", typeof(bool)); - Evaluate("new Decimal(5) between {new Decimal(1),new Decimal(5)}", "True", typeof(bool)); - Evaluate("new Decimal(8) between {new Decimal(1),new Decimal(5)}", "False", typeof(bool)); - } - - [Fact] - public void TestRelOperatorsBetweenErrors02() - { - EvaluateAndCheckError("'abc' between {5,7}", SpelMessage.NotComparable, 6); - } - - [Fact] - public void TestConstantRepresentation1() - { - CheckConstantList("{1,2,3,4,5}", true); - CheckConstantList("{'abc'}", true); - CheckConstantList("{}", true); - CheckConstantList("{#a,2,3}", false); - CheckConstantList("{1,2,Integer.valueOf(4)}", false); - CheckConstantList("{1,2,{#a}}", false); - } - - [Fact] - public void TestInlineListWriting() - { - // list should be unmodifiable - Assert.Throws(() => Evaluate("{1, 2, 3, 4, 5}[0]=6", "[1, 2, 3, 4, 5]", _unmodifiableClass)); - } - - private void CheckConstantList(string expressionText, bool expectedToBeConstant) - { - var parser = new SpelExpressionParser(); - var expression = (SpelExpression)parser.ParseExpression(expressionText); - ISpelNode node = expression.Ast; - bool condition = node is InlineList; - Assert.True(condition); - var inlineList = (InlineList)node; - - if (expectedToBeConstant) - { - Assert.True(inlineList.IsConstant); - } - else - { - Assert.False(inlineList.IsConstant); - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/LiteralExpressionTests.cs b/src/Common/test/Common.Expression.Test/Spring/LiteralExpressionTests.cs deleted file mode 100644 index 8f183f8760..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/LiteralExpressionTests.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Common; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class LiteralExpressionTests -{ - [Fact] - public void TestGetValue() - { - var lEx = new LiteralExpression("somevalue"); - Assert.Equal("somevalue", lEx.GetValue()); - Assert.Equal("somevalue", lEx.GetValue(typeof(string))); - var ctx = new StandardEvaluationContext(); - Assert.Equal("somevalue", lEx.GetValue(ctx)); - Assert.Equal("somevalue", lEx.GetValue(ctx, typeof(string))); - Assert.Equal("somevalue", lEx.GetValue(new Rooty())); - Assert.Equal("somevalue", lEx.GetValue(new Rooty(), typeof(string))); - Assert.Equal("somevalue", lEx.GetValue(ctx, new Rooty())); - Assert.Equal("somevalue", lEx.GetValue(ctx, new Rooty(), typeof(string))); - Assert.Equal("somevalue", lEx.ExpressionString); - Assert.False(lEx.IsWritable(new StandardEvaluationContext())); - Assert.False(lEx.IsWritable(new Rooty())); - Assert.False(lEx.IsWritable(new StandardEvaluationContext(), new Rooty())); - } - - [Fact] - public void TestSetValue() - { - var ex = Assert.Throws(() => new LiteralExpression("somevalue").SetValue(new StandardEvaluationContext(), "flibble")); - Assert.Equal("somevalue", ex.ExpressionString); - ex = Assert.Throws(() => new LiteralExpression("somevalue").SetValue(new Rooty(), "flibble")); - Assert.Equal("somevalue", ex.ExpressionString); - ex = Assert.Throws(() => new LiteralExpression("somevalue").SetValue(new StandardEvaluationContext(), new Rooty(), "flibble")); - Assert.Equal("somevalue", ex.ExpressionString); - } - - [Fact] - public void TestGetValueType() - { - var lEx = new LiteralExpression("somevalue"); - Assert.Equal(typeof(string), lEx.GetValueType()); - Assert.Equal(typeof(string), lEx.GetValueType(new StandardEvaluationContext())); - Assert.Equal(typeof(string), lEx.GetValueType(new Rooty())); - Assert.Equal(typeof(string), lEx.GetValueType(new StandardEvaluationContext(), new Rooty())); - } - - public sealed class Rooty - { - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/LiteralTests.cs b/src/Common/test/Common.Expression.Test/Spring/LiteralTests.cs deleted file mode 100644 index 3adfc6d7ec..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/LiteralTests.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class LiteralTests : AbstractExpressionTests -{ - [Fact] - public void TestLiteralBoolean01() - { - Evaluate("false", "False", typeof(bool)); - } - - [Fact] - public void TestLiteralBoolean02() - { - Evaluate("true", "True", typeof(bool)); - } - - [Fact] - public void TestLiteralInteger01() - { - Evaluate("1", "1", typeof(int)); - } - - [Fact] - public void TestLiteralInteger02() - { - Evaluate("1415", "1415", typeof(int)); - } - - [Fact] - public void TestLiteralString01() - { - Evaluate("'Hello World'", "Hello World", typeof(string)); - } - - [Fact] - public void TestLiteralString02() - { - Evaluate("'joe bloggs'", "joe bloggs", typeof(string)); - } - - [Fact] - public void TestLiteralString03() - { - Evaluate("'hello'", "hello", typeof(string)); - } - - [Fact] - public void TestLiteralString04() - { - Evaluate("'Tony''s Pizza'", "Tony's Pizza", typeof(string)); - Evaluate("'Tony\\r''s Pizza'", "Tony\\r's Pizza", typeof(string)); - } - - [Fact] - public void TestLiteralString05() - { - Evaluate("\"Hello World\"", "Hello World", typeof(string)); - } - - [Fact] - public void TestLiteralString06() - { - Evaluate("\"Hello ' World\"", "Hello ' World", typeof(string)); - } - - [Fact] - public void TestHexIntLiteral01() - { - Evaluate("0x7FFFF", "524287", typeof(int)); - Evaluate("0x7FFFFL", 524_287L, typeof(long)); - Evaluate("0X7FFFF", "524287", typeof(int)); - Evaluate("0X7FFFFl", 524_287L, typeof(long)); - } - - [Fact] - public void TestLongIntLiteral01() - { - Evaluate("0xCAFEBABEL", 3_405_691_582L, typeof(long)); - } - - [Fact] - public void TestLongIntInteractions01() - { - Evaluate("0x20 * 2L", 64L, typeof(long)); - - // ask for the result to be made into an Integer - EvaluateAndAskForReturnType("0x20 * 2L", 64, typeof(int)); - - // ask for the result to be made into an Integer knowing that it will not fit - EvaluateAndCheckError("0x1220 * 0xffffffffL", typeof(int), SpelMessage.TypeConversionError, 0); - } - - [Fact] - public void TestSignedIntLiterals() - { - Evaluate("-1", -1, typeof(int)); - Evaluate("-0xa", -10, typeof(int)); - Evaluate("-1L", -1L, typeof(long)); - Evaluate("-0x20l", -32L, typeof(long)); - } - - [Fact] - public void TestLiteralReal01_CreatingDoubles() - { - Evaluate("1.25", 1.25d, typeof(double)); - Evaluate("2.99", 2.99d, typeof(double)); - Evaluate("-3.141", -3.141d, typeof(double)); - Evaluate("1.25d", 1.25d, typeof(double)); - Evaluate("2.99d", 2.99d, typeof(double)); - Evaluate("-3.141d", -3.141d, typeof(double)); - Evaluate("1.25D", 1.25d, typeof(double)); - Evaluate("2.99D", 2.99d, typeof(double)); - Evaluate("-3.141D", -3.141d, typeof(double)); - } - - [Fact] - public void TestLiteralReal02_CreatingFloats() - { - // For now, everything becomes a double... - Evaluate("1.25f", 1.25f, typeof(float)); - Evaluate("2.5f", 2.5f, typeof(float)); - Evaluate("-3.5f", -3.5f, typeof(float)); - Evaluate("1.25F", 1.25f, typeof(float)); - Evaluate("2.5F", 2.5f, typeof(float)); - Evaluate("-3.5F", -3.5f, typeof(float)); - } - - [Fact] - public void TestLiteralReal03_UsingExponents() - { - Evaluate("6.0221415E+23", "6.0221415E+23", typeof(double)); - Evaluate("6.0221415e+23", "6.0221415E+23", typeof(double)); - Evaluate("6.0221415E+23d", "6.0221415E+23", typeof(double)); - Evaluate("6.0221415e+23D", "6.0221415E+23", typeof(double)); - Evaluate("6E2f", 6E2f, typeof(float)); - } - - [Fact] - public void TestLiteralReal04_BadExpressions() - { - ParseAndCheckError("6.1e23e22", SpelMessage.MoreInput, 6, "e22"); - ParseAndCheckError("6.1f23e22", SpelMessage.MoreInput, 4, "23e22"); - } - - [Fact] - public void TestLiteralNull01() - { - Evaluate("null", null, null); - } - - [Fact] - public void TestConversions() - { - // getting the expression type to be what we want - either: - Evaluate("T(Convert).ToByte(new Int32(37L))", (byte)37, typeof(byte)); // calling byteValue() on typeof(int) - EvaluateAndAskForReturnType("new Int32(37)", (byte)37, typeof(byte)); // relying on registered type converters - } - - [Fact] - public void TestNotWritable() - { - var expr = (SpelExpression)Parser.ParseExpression("37"); - Assert.False(expr.IsWritable(new StandardEvaluationContext())); - expr = (SpelExpression)Parser.ParseExpression("37L"); - Assert.False(expr.IsWritable(new StandardEvaluationContext())); - expr = (SpelExpression)Parser.ParseExpression("true"); - Assert.False(expr.IsWritable(new StandardEvaluationContext())); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/MapAccessTests.cs b/src/Common/test/Common.Expression.Test/Spring/MapAccessTests.cs deleted file mode 100644 index 2a203834d4..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/MapAccessTests.cs +++ /dev/null @@ -1,153 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Diagnostics; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class MapAccessTests : AbstractExpressionTests -{ - [Fact] - public void TestMapAccessThroughIndexer() - { - Evaluate("TestDictionary['monday']", "montag", typeof(string)); - } - - [Fact] - public void TestCustomMapAccessor() - { - var parser = new SpelExpressionParser(); - StandardEvaluationContext ctx = TestScenarioCreator.GetTestEvaluationContext(); - ctx.AddPropertyAccessor(new MapAccessor()); - - IExpression expr = parser.ParseExpression("TestDictionary.monday"); - object value = expr.GetValue(ctx, typeof(string)); - Assert.Equal("montag", value); - } - - [Fact] - public void TestVariableMapAccess() - { - var parser = new SpelExpressionParser(); - StandardEvaluationContext ctx = TestScenarioCreator.GetTestEvaluationContext(); - ctx.SetVariable("day", "saturday"); - - IExpression expr = parser.ParseExpression("TestDictionary[#day]"); - object value = expr.GetValue(ctx, typeof(string)); - Assert.Equal("samstag", value); - } - - [Fact] - public void TestGetValue() - { - var props1 = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" }, - { "key3", "value3" } - }; - - object bean = new TestBean("name1", new TestBean("name2", null, "Description 2", 15, props1), "description 1", 6, props1); - - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("TestService.Properties['key2']"); - Assert.Equal("value2", expr.GetValue(bean)); - } - - [Fact] - public void TestGetValueFromRootMap() - { - var map = new Dictionary - { - { "key", "value" } - }; - - var spelExpressionParser = new SpelExpressionParser(); - IExpression expr = spelExpressionParser.ParseExpression("#root['key']"); - Assert.Equal("value", expr.GetValue(map)); - } - - [Fact] - public void TestGetValuePerformance() - { - var map = new Dictionary - { - { "key", "value" } - }; - - var context = new StandardEvaluationContext(map); - - var spelExpressionParser = new SpelExpressionParser(); - IExpression expr = spelExpressionParser.ParseExpression("#root['key']"); - - var s = new Stopwatch(); - s.Start(); - - for (int i = 0; i < 10000; i++) - { - expr.GetValue(context); - } - - s.Stop(); - Assert.True(s.ElapsedMilliseconds < 500L); - } - - public sealed class TestBean - { - public string Name { get; set; } - - public TestBean TestService { get; set; } - - public string Description { get; set; } - - public int Priority { get; set; } - - public Dictionary Properties { get; } - - public TestBean(string name, TestBean testBean, string description, int priority, Dictionary props) - { - Name = name; - TestService = testBean; - Description = description; - Priority = priority; - Properties = props; - } - } - - public sealed class MapAccessor : IPropertyAccessor - { - public bool CanRead(IEvaluationContext context, object target, string name) - { - return ((IDictionary)target).Contains(name); - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - return new TypedValue(((IDictionary)target)[name]); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return true; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - ((IDictionary)target).Add(name, newValue); - } - - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(IDictionary) - }; - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/MapTests.cs b/src/Common/test/Common.Expression.Test/Spring/MapTests.cs deleted file mode 100644 index 3ae978df9e..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/MapTests.cs +++ /dev/null @@ -1,195 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Collections.ObjectModel; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Ast; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class MapTests : AbstractExpressionTests -{ - // if the list is full of literals then it will be of the type unmodifiableClass - // rather than HashMap (or similar) - private readonly Type _unmodifiableClass = typeof(ReadOnlyDictionary); - - [Fact] - public void TestInlineMapCreation01() - { - Evaluate("{'a':1, 'b':2, 'c':3, 'd':4, 'e':5}", "{a=1,b=2,c=3,d=4,e=5}", _unmodifiableClass); - Evaluate("{'a':1}", "{a=1}", _unmodifiableClass); - } - - [Fact] - public void TestInlineMapCreation02() - { - Evaluate("{'abc':'def', 'uvw':'xyz'}", "{abc=def,uvw=xyz}", _unmodifiableClass); - } - - [Fact] - public void TestInlineMapCreation03() - { - Evaluate("{:}", "{}", _unmodifiableClass); - } - - [Fact] - public void TestInlineMapCreation04() - { - Evaluate("{'key':'abc'=='xyz'}", "{key=False}", typeof(Dictionary)); - Evaluate("{key:'abc'=='xyz'}", "{key=False}", typeof(Dictionary)); - Evaluate("{key:'abc'=='xyz',key2:true}[key]", "False", typeof(bool)); - Evaluate("{key:'abc'=='xyz',key2:true}['key2']", "True", typeof(bool)); - } - - [Fact] - public void TestInlineMapAndNesting() - { - Evaluate("{a:{a:1,b:2,c:3},b:{d:4,e:5,f:6}}", "{a={a=1,b=2,c=3},b={d=4,e=5,f=6}}", _unmodifiableClass); - Evaluate("{a:{x:1,y:'2',z:3},b:{u:4,v:{'a','b'},w:5,x:6}}", "{a={x=1,y=2,z=3},b={u=4,v=[a,b],w=5,x=6}}", _unmodifiableClass); - Evaluate("{a:{1,2,3},b:{4,5,6}}", "{a=[1,2,3],b=[4,5,6]}", _unmodifiableClass); - } - - [Fact] - public void TestInlineMapWithFunkyKeys() - { - Evaluate("{#root.Name:true}", "{Nikola Tesla=True}", typeof(Dictionary)); - } - - [Fact] - public void TestInlineMapError() - { - ParseAndCheckError("{key:'abc'", SpelMessage.Ood); - } - - [Fact] - public void TestRelOperatorsIs02() - { - Evaluate("{a:1, b:2, c:3, d:4, e:5} instanceof T(System.Collections.IDictionary)", "True", typeof(bool)); - } - - [Fact] - public void TestInlineMapAndProjectionSelection() - { - Evaluate("{a:1,b:2,c:3,d:4,e:5,f:6}.![Value>3]", "[False,False,False,True,True,True]", typeof(List)); - Evaluate("{a:1,b:2,c:3,d:4,e:5,f:6}.?[Value>3]", "{d=4,e=5,f=6}", typeof(Dictionary)); - Evaluate("{a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8,i:9,j:10}.?[Value%2==0]", "{b=2,d=4,f=6,h=8,j=10}", typeof(Dictionary)); - } - - [Fact] - public void TestSetConstruction01() - { - var expected = new Hashtable - { - { "a", "a" }, - { "b", "b" }, - { "c", "c" } - }; - - Evaluate("new System.Collections.Hashtable({a:'a',b:'b',c:'c'})", expected, typeof(Hashtable)); - } - - [Fact] - public void TestConstantRepresentation1() - { - CheckConstantMap("{f:{'a','b','c'}}", true); - CheckConstantMap("{'a':1,'b':2,'c':3,'d':4,'e':5}", true); - CheckConstantMap("{aaa:'abc'}", true); - CheckConstantMap("{:}", true); - CheckConstantMap("{a:#a,b:2,c:3}", false); - CheckConstantMap("{a:1,b:2,c:Integer.valueOf(4)}", false); - CheckConstantMap("{a:1,b:2,c:{#a}}", false); - CheckConstantMap("{#root.name:true}", false); - CheckConstantMap("{a:1,b:2,c:{d:true,e:false}}", true); - CheckConstantMap("{a:1,b:2,c:{d:{1,2,3},e:{4,5,6},f:{'a','b','c'}}}", true); - } - - [Fact] - public void TestInlineMapWriting() - { - // list should be unmodifiable - Assert.Throws(() => Evaluate("{a:1, b:2, c:3, d:4, e:5}[a]=6", "[a:1,b: 2,c: 3,d: 4,e: 5]", _unmodifiableClass)); - } - - [Fact] - public void TestMapKeysThatAreAlsoSpelKeywords() - { - var parser = new SpelExpressionParser(); - SpelExpression expression = null; - object o = null; - - expression = (SpelExpression)parser.ParseExpression("Foo[T]"); - o = expression.GetValue(new MapHolder()); - Assert.Equal("TV", o); - - expression = (SpelExpression)parser.ParseExpression("Foo[t]"); - o = expression.GetValue(new MapHolder()); - Assert.Equal("tv", o); - - expression = (SpelExpression)parser.ParseExpression("Foo[NEW]"); - o = expression.GetValue(new MapHolder()); - Assert.Equal("VALUE", o); - - expression = (SpelExpression)parser.ParseExpression("Foo[new]"); - o = expression.GetValue(new MapHolder()); - Assert.Equal("value", o); - - expression = (SpelExpression)parser.ParseExpression("Foo['abc.def']"); - o = expression.GetValue(new MapHolder()); - Assert.Equal("value", o); - - expression = (SpelExpression)parser.ParseExpression("Foo[Foo[NEW]]"); - o = expression.GetValue(new MapHolder()); - Assert.Equal("37", o); - - expression = (SpelExpression)parser.ParseExpression("Foo[Foo[new]]"); - o = expression.GetValue(new MapHolder()); - Assert.Equal("38", o); - - expression = (SpelExpression)parser.ParseExpression("Foo[Foo[Foo[T]]]"); - o = expression.GetValue(new MapHolder()); - Assert.Equal("value", o); - } - - private void CheckConstantMap(string expressionText, bool expectedToBeConstant) - { - var parser = new SpelExpressionParser(); - var expression = (SpelExpression)parser.ParseExpression(expressionText); - ISpelNode node = expression.Ast; - bool condition = node is InlineMap; - Assert.True(condition); - var inlineMap = (InlineMap)node; - - if (expectedToBeConstant) - { - Assert.True(inlineMap.IsConstant); - } - else - { - Assert.False(inlineMap.IsConstant); - } - } - - public sealed class MapHolder - { - public IDictionary Foo { get; } - - public MapHolder() - { - Foo = new Dictionary - { - { "NEW", "VALUE" }, - { "new", "value" }, - { "T", "TV" }, - { "t", "tv" }, - { "abc.def", "value" }, - { "VALUE", "37" }, - { "value", "38" }, - { "TV", "new" } - }; - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/MethodInvocationTests.cs b/src/Common/test/Common.Expression.Test/Spring/MethodInvocationTests.cs deleted file mode 100644 index 87eebd31f8..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/MethodInvocationTests.cs +++ /dev/null @@ -1,328 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestResources; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class MethodInvocationTests : AbstractExpressionTests -{ - [Fact] - public void TestSimpleAccess01() - { - Evaluate("PlaceOfBirth.City", "SmilJan", typeof(string)); - } - - [Fact] - public void TestStringClass() - { - Evaluate("new String('hello')[2]", "l", typeof(string)); - Evaluate("new String('hello')[2].Equals('l'[0])", true, typeof(bool)); - Evaluate("'HELLO'.ToLowerInvariant()", "hello", typeof(string)); - Evaluate("' abcba '.Trim()", "abcba", typeof(string)); - } - - [Fact] - public void TestNonExistentMethods() - { - // name is ok but madeup() does not exist - EvaluateAndCheckError("Name.MadeUp()", SpelMessage.MethodNotFound, 5); - } - - [Fact] - public void TestWidening01() - { - // widening of int 8 to double 8 is OK - Evaluate("PrintDouble(8)", "8.00", typeof(string)); - } - - [Fact] - public void TestArgumentConversion01() - { - // Rely on Double>String conversion for calling startsWith() - Evaluate("new String('hello 2.0 to you').StartsWith(7.0d)", false, typeof(bool)); - Evaluate("new String('7.0 foobar').StartsWith(7.0d)", true, typeof(bool)); - } - - [Fact] - public void TestMethodThrowingException_SPR6760() - { - // Test method on inventor: throwException() - // On 1 it will throw an IllegalArgumentException - // On 2 it will throw a RuntimeException - // On 3 it will exit normally - // In each case it increments the Inventor field 'counter' when invoked - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("ThrowException(#bar)"); - - // Normal exit - StandardEvaluationContext eContext = TestScenarioCreator.GetTestEvaluationContext(); - eContext.SetVariable("bar", 3); - object o = expr.GetValue(eContext); - Assert.Equal(3, o); - Assert.Equal(1, parser.ParseExpression("Counter").GetValue(eContext)); - - // Now the expression has cached that throwException(int) is the right thing to call - // Let's change 'bar' to be a PlaceOfBirth which indicates the cached reference is - // out of date. - eContext.SetVariable("bar", new PlaceOfBirth("London")); - o = expr.GetValue(eContext); - Assert.Equal("London", o); - - // That confirms the logic to mark the cached reference stale and retry is working - // Now let's cause the method to exit via exception and ensure it doesn't cause a retry. - // First, switch back to throwException(int) - eContext.SetVariable("bar", 3); - o = expr.GetValue(eContext); - Assert.Equal(3, o); - Assert.Equal(2, parser.ParseExpression("Counter").GetValue(eContext)); - - // Now cause it to throw an exception: - eContext.SetVariable("bar", 1); - var ex = Assert.Throws(() => expr.GetValue(eContext)); - Assert.IsNotType(ex); - - // If counter is 4 then the method got called twice! - Assert.Equal(3, parser.ParseExpression("Counter").GetValue(eContext)); - - eContext.SetVariable("bar", 4); - Assert.Throws(() => expr.GetValue(eContext)); - - // If counter is 5 then the method got called twice! - Assert.Equal(4, parser.ParseExpression("Counter").GetValue(eContext)); - } - - // Check on first usage (when the cachedExecutor in MethodReference is null) that the exception is not wrapped. - [Fact] - public void TestMethodThrowingException_SPR6941() - { - // Test method on inventor: throwException() - // On 1 it will throw an IllegalArgumentException - // On 2 it will throw a RuntimeException - // On 3 it will exit normally - // In each case it increments the Inventor field 'counter' when invoked - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("ThrowException(#bar)"); - - Context.SetVariable("bar", 2); - var ex = Assert.Throws(() => expr.GetValue(Context)); - Assert.IsNotType(ex); - } - - [Fact] - public void TestMethodThrowingException_SPR6941_2() - { - // Test method on inventor: throwException() - // On 1 it will throw an IllegalArgumentException - // On 2 it will throw a RuntimeException - // On 3 it will exit normally - // In each case it increments the Inventor field 'counter' when invoked - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("ThrowException(#bar)"); - - Context.SetVariable("bar", 4); - var ex = Assert.Throws(() => expr.GetValue(Context)); - Assert.Contains("TestException", ex.InnerException.GetType().Name, StringComparison.Ordinal); - } - - [Fact] - public void TestMethodFiltering_SPR6764() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - context.SetRootObject(new TestObject()); - var filter = new LocalFilter(); - context.RegisterMethodFilter(typeof(TestObject), filter); - - // Filter will be called but not do anything, so first doit() will be invoked - var expr = (SpelExpression)parser.ParseExpression("DoIt(1)"); - string result = expr.GetValue(context); - Assert.Equal("1", result); - Assert.True(filter.FilterCalled); - - // Filter will now remove non @Anno annotated methods - filter.RemoveIfNotAnnotated = true; - filter.FilterCalled = false; - expr = (SpelExpression)parser.ParseExpression("DoIt(1)"); - result = expr.GetValue(context); - Assert.Equal("double 1.0", result); - Assert.True(filter.FilterCalled); - - // check not called for other types - filter.FilterCalled = false; - context.SetRootObject("abc".Clone()); - expr = (SpelExpression)parser.ParseExpression("[0]"); - result = expr.GetValue(context); - Assert.Equal("a", result); - Assert.False(filter.FilterCalled); - - // check de-registration works - filter.FilterCalled = false; - context.RegisterMethodFilter(typeof(TestObject), null); // clear filter - context.SetRootObject(new TestObject()); - expr = (SpelExpression)parser.ParseExpression("DoIt(1)"); - result = expr.GetValue(context); - Assert.Equal("1", result); - Assert.False(filter.FilterCalled); - } - - [Fact] - public void TestAddingMethodResolvers() - { - var ctx = new StandardEvaluationContext(); - - // reflective method accessor is the only one by default - List methodResolvers = ctx.MethodResolvers; - Assert.Single(methodResolvers); - - var dummy = new DummyMethodResolver(); - ctx.AddMethodResolver(dummy); - Assert.Equal(2, ctx.MethodResolvers.Count); - - var copy = new List(ctx.MethodResolvers); - Assert.True(ctx.RemoveMethodResolver(dummy)); - Assert.False(ctx.RemoveMethodResolver(dummy)); - Assert.Single(ctx.MethodResolvers); - - ctx.MethodResolvers = copy; - Assert.Equal(2, ctx.MethodResolvers.Count); - } - - [Fact] - public void TestVarargsInvocation01() - { - // Calling 'public int aVarargsMethod(String... strings)' - Evaluate("AVarargsMethod()", 0, typeof(int)); - Evaluate("AVarargsMethod(1,2,3)", 3, typeof(int)); // all need converting to strings - Evaluate("AVarargsMethod(1)", 1, typeof(int)); // needs string conversion - Evaluate("AVarargsMethod(1,'a',3.0d)", 3, typeof(int)); // first and last need conversion - } - - [Fact] - public void TestVarargsInvocation02() - { - // Calling 'public int aVarargsMethod2(int i, String... strings)' - returns int+length_of_strings - Evaluate("AVarargsMethod2(5,'a','b','c')", 8, typeof(int)); - Evaluate("AVarargsMethod2(2,'a')", 3, typeof(int)); - Evaluate("AVarargsMethod2(4)", 4, typeof(int)); - Evaluate("AVarargsMethod2(8,2,3)", 10, typeof(int)); - Evaluate("AVarargsMethod2(9)", 9, typeof(int)); - Evaluate("AVarargsMethod2(2,'a',3.0d)", 4, typeof(int)); - } - - [Fact] - public void TestInvocationOnNullContextObject() - { - EvaluateAndCheckError("null.ToString()", SpelMessage.MethodCallOnNullObjectNotAllowed); - } - - [Fact] - public void TestMethodOfClass() - { - IExpression expression = Parser.ParseExpression("FullName"); - object value = expression.GetValue(new StandardEvaluationContext(typeof(string))); - Assert.Equal("System.String", value); - } - - [Fact] - public void InvokeMethodWithoutConversion() - { - byte[] bytes = new byte[100]; - - var context = new StandardEvaluationContext(bytes) - { - ServiceResolver = new TestServiceResolver() - }; - - IExpression expression = Parser.ParseExpression("@service.HandleBytes(#root)"); - byte[] outBytes = expression.GetValue(context); - Assert.Same(bytes, outBytes); - } - - public sealed class TestServiceResolver : IServiceResolver - { - public BytesService Service => new(); - - public object Resolve(IEvaluationContext context, string serviceName) - { - return serviceName == "service" ? Service : null; - } - } - - // Simple filter - public sealed class LocalFilter : IMethodFilter - { - public bool RemoveIfNotAnnotated { get; set; } - public bool FilterCalled { get; set; } - - public List Filter(List methods) - { - FilterCalled = true; - var forRemoval = new List(); - - foreach (MethodInfo method in methods) - { - if (RemoveIfNotAnnotated && !IsAnnotated(method)) - { - forRemoval.Add(method); - } - } - - foreach (MethodInfo method in forRemoval) - { - methods.Remove(method); - } - - return methods; - } - - private bool IsAnnotated(MethodInfo method) - { - var attribute = method.GetCustomAttribute(); - return attribute != null; - } - } - - [AttributeUsage(AttributeTargets.Method)] - public sealed class AnnotationAttribute : Attribute - { - } - - public sealed class TestObject - { - public int DoIt(int i) - { - return i; - } - - [Annotation] - public string DoIt(double d) - { - return string.Create(CultureInfo.InvariantCulture, $"double {d:F1}"); - } - } - - public sealed class DummyMethodResolver : IMethodResolver - { - public IMethodExecutor Resolve(IEvaluationContext context, object targetObject, string name, List argumentTypes) - { - throw new InvalidOperationException(); - } - } - - public sealed class BytesService - { - public byte[] HandleBytes(byte[] bytes) - { - return bytes; - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/OperatorOverloaderTests.cs b/src/Common/test/Common.Expression.Test/Spring/OperatorOverloaderTests.cs deleted file mode 100644 index a1cf493a79..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/OperatorOverloaderTests.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class OperatorOverloaderTests : AbstractExpressionTests -{ - [Fact] - public void TestSimpleOperations() - { - // no built in support for this: - EvaluateAndCheckError("'abc'-true", SpelMessage.OperatorNotSupportedBetweenTypes); - - StandardEvaluationContext eContext = TestScenarioCreator.GetTestEvaluationContext(); - eContext.OperatorOverloader = new StringAndBooleanAddition(); - - var expr = (SpelExpression)Parser.ParseExpression("'abc'+true"); - Assert.Equal("abcTrue", expr.GetValue(eContext)); - - expr = (SpelExpression)Parser.ParseExpression("'abc'-true"); - Assert.Equal("abc", expr.GetValue(eContext)); - - expr = (SpelExpression)Parser.ParseExpression("'abc'+null"); - Assert.Equal("abcnull", expr.GetValue(eContext)); - } - - public sealed class StringAndBooleanAddition : IOperatorOverloader - { - public object Operate(Operation operation, object leftOperand, object rightOperand) - { - if (operation == Operation.Add) - { - return (string)leftOperand + (bool)rightOperand; - } - - return leftOperand; - } - - public bool OverridesOperation(Operation operation, object leftOperand, object rightOperand) - { - if (leftOperand is string && rightOperand is bool) - { - return true; - } - - return false; - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/OperatorTests.cs b/src/Common/test/Common.Expression.Test/Spring/OperatorTests.cs deleted file mode 100644 index d8f273c6f8..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/OperatorTests.cs +++ /dev/null @@ -1,636 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Ast; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class OperatorTests : AbstractExpressionTests -{ - [Fact] - public void TestEqual() - { - Evaluate("3 == 5", false, typeof(bool)); - Evaluate("5 == 3", false, typeof(bool)); - Evaluate("6 == 6", true, typeof(bool)); - Evaluate("3.0f == 5.0f", false, typeof(bool)); - Evaluate("3.0f == 3.0f", true, typeof(bool)); - Evaluate("new System.Decimal('5') == new System.Decimal('5')", true, typeof(bool)); - Evaluate("new System.Decimal('3') == new System.Decimal('5')", false, typeof(bool)); - Evaluate("new System.Decimal('5') == new System.Decimal('3')", false, typeof(bool)); - Evaluate("3 == new System.Decimal('5')", false, typeof(bool)); - Evaluate("new System.Decimal('3') == 5", false, typeof(bool)); - Evaluate("3L == new System.Decimal('5')", false, typeof(bool)); - Evaluate("3.0d == new System.Decimal('5')", false, typeof(bool)); - Evaluate("3L == new System.Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d == new System.Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d == new System.Decimal('3.0')", true, typeof(bool)); - Evaluate("3.0f == 3.0d", true, typeof(bool)); - Evaluate("10 == '10'", false, typeof(bool)); - Evaluate("'abc' == 'abc'", true, typeof(bool)); - Evaluate("'abc' == new System.Text.StringBuilder('abc').ToString()", true, typeof(bool)); - Evaluate("'abc' == 'def'", false, typeof(bool)); - Evaluate("'abc' == null", false, typeof(bool)); - Evaluate($"new {typeof(OperatorTests).FullName}$SubComparable() == new {typeof(OperatorTests).FullName}$OtherSubComparable()", true, typeof(bool)); - Evaluate("3 eq 5", false, typeof(bool)); - Evaluate("5 eQ 3", false, typeof(bool)); - Evaluate("6 Eq 6", true, typeof(bool)); - Evaluate("3.0f eq 5.0f", false, typeof(bool)); - Evaluate("3.0f EQ 3.0f", true, typeof(bool)); - Evaluate("new System.Decimal('5') eq new System.Decimal('5')", true, typeof(bool)); - Evaluate("new System.Decimal('3') eq new System.Decimal('5')", false, typeof(bool)); - Evaluate("new System.Decimal('5') eq new System.Decimal('3')", false, typeof(bool)); - Evaluate("3 eq new System.Decimal('5')", false, typeof(bool)); - Evaluate("new System.Decimal('3') eq 5", false, typeof(bool)); - Evaluate("3L eq new System.Decimal('5')", false, typeof(bool)); - Evaluate("3.0d eq new System.Decimal('5')", false, typeof(bool)); - Evaluate("3L eq new System.Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d eq new System.Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d eq new System.Decimal('3.0')", true, typeof(bool)); - Evaluate("3.0f eq 3.0d", true, typeof(bool)); - Evaluate("10 eq '10'", false, typeof(bool)); - Evaluate("'abc' eq 'abc'", true, typeof(bool)); - Evaluate("'abc' eq new System.Text.StringBuilder('abc').ToString()", true, typeof(bool)); - Evaluate("'abc' eq 'def'", false, typeof(bool)); - Evaluate("'abc' eq null", false, typeof(bool)); - Evaluate($"new {typeof(OperatorTests).FullName}$SubComparable() eq new {typeof(OperatorTests).FullName}$OtherSubComparable()", true, typeof(bool)); - } - - [Fact] - public void TestNotEqual() - { - Evaluate("3 != 5", true, typeof(bool)); - Evaluate("5 != 3", true, typeof(bool)); - Evaluate("6 != 6", false, typeof(bool)); - Evaluate("3.0f != 5.0f", true, typeof(bool)); - Evaluate("3.0f != 3.0f", false, typeof(bool)); - Evaluate("new Decimal('5') != new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('3') != new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('5') != new Decimal('3')", true, typeof(bool)); - Evaluate("3 != new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') != 5", true, typeof(bool)); - Evaluate("3L != new Decimal('5')", true, typeof(bool)); - Evaluate("3.0d != new Decimal('5')", true, typeof(bool)); - Evaluate("3L != new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d != new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d != new Decimal('3.0')", false, typeof(bool)); - Evaluate("3.0f != 3.0d", false, typeof(bool)); - Evaluate("10 != '10'", true, typeof(bool)); - Evaluate("'abc' != 'abc'", false, typeof(bool)); - Evaluate("'abc' != new System.Text.StringBuilder('abc').ToString()", false, typeof(bool)); - Evaluate("'abc' != 'def'", true, typeof(bool)); - Evaluate("'abc' != null", true, typeof(bool)); - Evaluate($"new {typeof(OperatorTests).FullName}$SubComparable() != new {typeof(OperatorTests).FullName}$OtherSubComparable()", false, typeof(bool)); - Evaluate("3 ne 5", true, typeof(bool)); - Evaluate("5 nE 3", true, typeof(bool)); - Evaluate("6 Ne 6", false, typeof(bool)); - Evaluate("3.0f NE 5.0f", true, typeof(bool)); - Evaluate("3.0f ne 3.0f", false, typeof(bool)); - Evaluate("new Decimal('5') ne new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('3') ne new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('5') ne new Decimal('3')", true, typeof(bool)); - Evaluate("3 ne new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') ne 5", true, typeof(bool)); - Evaluate("3L ne new Decimal('5')", true, typeof(bool)); - Evaluate("3.0d ne new Decimal('5')", true, typeof(bool)); - Evaluate("3L ne new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d ne new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d ne new Decimal('3.0')", false, typeof(bool)); - Evaluate("3.0f ne 3.0d", false, typeof(bool)); - Evaluate("10 ne '10'", true, typeof(bool)); - Evaluate("'abc' ne 'abc'", false, typeof(bool)); - Evaluate("'abc' ne new System.Text.StringBuilder('abc').ToString()", false, typeof(bool)); - Evaluate("'abc' ne 'def'", true, typeof(bool)); - Evaluate("'abc' ne null", true, typeof(bool)); - Evaluate($"new {typeof(OperatorTests).FullName}$SubComparable() ne new {typeof(OperatorTests).FullName}$OtherSubComparable()", false, typeof(bool)); - } - - [Fact] - public void TestLessThan() - { - Evaluate("5 < 5", false, typeof(bool)); - Evaluate("3 < 5", true, typeof(bool)); - Evaluate("5 < 3", false, typeof(bool)); - Evaluate("3L < 5L", true, typeof(bool)); - Evaluate("5L < 3L", false, typeof(bool)); - Evaluate("3.0d < 5.0d", true, typeof(bool)); - Evaluate("5.0d < 3.0d", false, typeof(bool)); - Evaluate("new Decimal('3') < new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('5') < new Decimal('3')", false, typeof(bool)); - Evaluate("3 < new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') < 5", true, typeof(bool)); - Evaluate("3L < new Decimal('5')", true, typeof(bool)); - Evaluate("3.0d < new Decimal('5')", true, typeof(bool)); - Evaluate("3L < new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d < new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d < new Decimal('3.0')", false, typeof(bool)); - Evaluate("'abc' < 'def'", true, typeof(bool)); - Evaluate("'abc' < new System.Text.StringBuilder('def').ToString()", true, typeof(bool)); - Evaluate("'def' < 'abc'", false, typeof(bool)); - - Evaluate("3 lt 5", true, typeof(bool)); - Evaluate("5 lt 3", false, typeof(bool)); - Evaluate("3L lt 5L", true, typeof(bool)); - Evaluate("5L lt 3L", false, typeof(bool)); - Evaluate("3.0d lT 5.0d", true, typeof(bool)); - Evaluate("5.0d Lt 3.0d", false, typeof(bool)); - Evaluate("new Decimal('3') lt new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('5') lt new Decimal('3')", false, typeof(bool)); - Evaluate("3 lt new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') lt 5", true, typeof(bool)); - Evaluate("3L lt new Decimal('5')", true, typeof(bool)); - Evaluate("3.0d lt new Decimal('5')", true, typeof(bool)); - Evaluate("3L lt new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d lt new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d lt new Decimal('3.0')", false, typeof(bool)); - Evaluate("'abc' LT 'def'", true, typeof(bool)); - Evaluate("'abc' lt new System.Text.StringBuilder('def').ToString()", true, typeof(bool)); - Evaluate("'def' lt 'abc'", false, typeof(bool)); - } - - [Fact] - public void TestLessThanOrEqual() - { - Evaluate("3 <= 5", true, typeof(bool)); - Evaluate("5 <= 3", false, typeof(bool)); - Evaluate("6 <= 6", true, typeof(bool)); - Evaluate("3L <= 5L", true, typeof(bool)); - Evaluate("5L <= 3L", false, typeof(bool)); - Evaluate("5L <= 5L", true, typeof(bool)); - Evaluate("3.0d <= 5.0d", true, typeof(bool)); - Evaluate("5.0d <= 3.0d", false, typeof(bool)); - Evaluate("5.0d <= 5.0d", true, typeof(bool)); - Evaluate("new Decimal('5') <= new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') <= new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('5') <= new Decimal('3')", false, typeof(bool)); - Evaluate("3 <= new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') <= 5", true, typeof(bool)); - Evaluate("3L <= new Decimal('5')", true, typeof(bool)); - Evaluate("3.0d <= new Decimal('5')", true, typeof(bool)); - Evaluate("3L <= new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d <= new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d <= new Decimal('3.0')", true, typeof(bool)); - Evaluate("'abc' <= 'def'", true, typeof(bool)); - Evaluate("'def' <= 'abc'", false, typeof(bool)); - Evaluate("'abc' <= 'abc'", true, typeof(bool)); - - Evaluate("3 le 5", true, typeof(bool)); - Evaluate("5 le 3", false, typeof(bool)); - Evaluate("6 Le 6", true, typeof(bool)); - Evaluate("3L lE 5L", true, typeof(bool)); - Evaluate("5L LE 3L", false, typeof(bool)); - Evaluate("5L le 5L", true, typeof(bool)); - Evaluate("3.0d LE 5.0d", true, typeof(bool)); - Evaluate("5.0d lE 3.0d", false, typeof(bool)); - Evaluate("5.0d Le 5.0d", true, typeof(bool)); - Evaluate("new Decimal('5') le new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') le new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('5') le new Decimal('3')", false, typeof(bool)); - Evaluate("3 le new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') le 5", true, typeof(bool)); - Evaluate("3L le new Decimal('5')", true, typeof(bool)); - Evaluate("3.0d le new Decimal('5')", true, typeof(bool)); - Evaluate("3L le new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d le new Decimal('3.1')", true, typeof(bool)); - Evaluate("3.0d le new Decimal('3.0')", true, typeof(bool)); - Evaluate("'abc' Le 'def'", true, typeof(bool)); - Evaluate("'def' LE 'abc'", false, typeof(bool)); - Evaluate("'abc' le 'abc'", true, typeof(bool)); - } - - [Fact] - public void TestGreaterThan() - { - Evaluate("3 > 5", false, typeof(bool)); - Evaluate("5 > 3", true, typeof(bool)); - Evaluate("3L > 5L", false, typeof(bool)); - Evaluate("5L > 3L", true, typeof(bool)); - Evaluate("3.0d > 5.0d", false, typeof(bool)); - Evaluate("5.0d > 3.0d", true, typeof(bool)); - Evaluate("new Decimal('3') > new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('5') > new Decimal('3')", true, typeof(bool)); - Evaluate("3 > new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('3') > 5", false, typeof(bool)); - Evaluate("3L > new Decimal('5')", false, typeof(bool)); - Evaluate("3.0d > new Decimal('5')", false, typeof(bool)); - Evaluate("3L > new Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d > new Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d > new Decimal('3.0')", false, typeof(bool)); - Evaluate("'abc' > 'def'", false, typeof(bool)); - Evaluate("'abc' > new System.Text.StringBuilder('def').ToString()", false, typeof(bool)); - Evaluate("'def' > 'abc'", true, typeof(bool)); - - Evaluate("3 gt 5", false, typeof(bool)); - Evaluate("5 gt 3", true, typeof(bool)); - Evaluate("3L gt 5L", false, typeof(bool)); - Evaluate("5L gt 3L", true, typeof(bool)); - Evaluate("3.0d gt 5.0d", false, typeof(bool)); - Evaluate("5.0d gT 3.0d", true, typeof(bool)); - Evaluate("new Decimal('3') gt new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('5') gt new Decimal('3')", true, typeof(bool)); - Evaluate("3 gt new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('3') gt 5", false, typeof(bool)); - Evaluate("3L gt new Decimal('5')", false, typeof(bool)); - Evaluate("3.0d gt new Decimal('5')", false, typeof(bool)); - Evaluate("3L gt new Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d gt new Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d gt new Decimal('3.0')", false, typeof(bool)); - Evaluate("'abc' Gt 'def'", false, typeof(bool)); - Evaluate("'abc' gt new System.Text.StringBuilder('def').ToString()", false, typeof(bool)); - Evaluate("'def' GT 'abc'", true, typeof(bool)); - } - - [Fact] - public void TestGreaterThanOrEqual() - { - Evaluate("3 >= 5", false, typeof(bool)); - Evaluate("5 >= 3", true, typeof(bool)); - Evaluate("6 >= 6", true, typeof(bool)); - Evaluate("3L >= 5L", false, typeof(bool)); - Evaluate("5L >= 3L", true, typeof(bool)); - Evaluate("5L >= 5L", true, typeof(bool)); - Evaluate("3.0d >= 5.0d", false, typeof(bool)); - Evaluate("5.0d >= 3.0d", true, typeof(bool)); - Evaluate("5.0d >= 5.0d", true, typeof(bool)); - Evaluate("new Decimal('5') >= new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') >= new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('5') >= new Decimal('3')", true, typeof(bool)); - Evaluate("3 >= new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('3') >= 5", false, typeof(bool)); - Evaluate("3L >= new Decimal('5')", false, typeof(bool)); - Evaluate("3.0d >= new Decimal('5')", false, typeof(bool)); - Evaluate("3L >= new Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d >= new Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d >= new Decimal('3.0')", true, typeof(bool)); - Evaluate("'abc' >= 'def'", false, typeof(bool)); - Evaluate("'def' >= 'abc'", true, typeof(bool)); - Evaluate("'abc' >= 'abc'", true, typeof(bool)); - - Evaluate("3 GE 5", false, typeof(bool)); - Evaluate("5 gE 3", true, typeof(bool)); - Evaluate("6 Ge 6", true, typeof(bool)); - Evaluate("3L ge 5L", false, typeof(bool)); - Evaluate("5L ge 3L", true, typeof(bool)); - Evaluate("5L ge 5L", true, typeof(bool)); - Evaluate("3.0d ge 5.0d", false, typeof(bool)); - Evaluate("5.0d ge 3.0d", true, typeof(bool)); - Evaluate("5.0d ge 5.0d", true, typeof(bool)); - Evaluate("new Decimal('5') ge new Decimal('5')", true, typeof(bool)); - Evaluate("new Decimal('3') ge new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('5') ge new Decimal('3')", true, typeof(bool)); - Evaluate("3 ge new Decimal('5')", false, typeof(bool)); - Evaluate("new Decimal('3') ge 5", false, typeof(bool)); - Evaluate("3L ge new Decimal('5')", false, typeof(bool)); - Evaluate("3.0d ge new Decimal('5')", false, typeof(bool)); - Evaluate("3L ge new Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d ge new Decimal('3.1')", false, typeof(bool)); - Evaluate("3.0d ge new Decimal('3.0')", true, typeof(bool)); - Evaluate("'abc' ge 'def'", false, typeof(bool)); - Evaluate("'def' ge 'abc'", true, typeof(bool)); - Evaluate("'abc' ge 'abc'", true, typeof(bool)); - } - - [Fact] - public void TestIntegerLiteral() - { - Evaluate("3", 3, typeof(int)); - } - - [Fact] - public void TestRealLiteral() - { - Evaluate("3.5", 3.5d, typeof(double)); - } - - [Fact] - public void TestMultiplyStringInt() - { - Evaluate("'a' * 5", "aaaaa", typeof(string)); - } - - [Fact] - public void TestMultiplyDoubleDoubleGivesDouble() - { - Evaluate("3.0d * 5.0d", 15.0d, typeof(double)); - } - - [Fact] - public void TestMixedOperandsBigDecimal() - { - Evaluate("3 * new Decimal('5')", 15M, typeof(decimal)); - Evaluate("3L * new Decimal('5')", 15M, typeof(decimal)); - Evaluate("3.0d * new Decimal('5')", 15.0M, typeof(decimal)); - - Evaluate("3 + new Decimal('5')", 8M, typeof(decimal)); - Evaluate("3L + new Decimal('5')", 8M, typeof(decimal)); - Evaluate("3.0d + new Decimal('5')", 8.0M, typeof(decimal)); - - Evaluate("3 - new Decimal('5')", -2M, typeof(decimal)); - Evaluate("3L - new Decimal('5')", -2M, typeof(decimal)); - Evaluate("3.0d - new Decimal('5')", -2.0M, typeof(decimal)); - - Evaluate("3 / new Decimal('5')", 0.6M, typeof(decimal)); - Evaluate("3 / new Decimal('5.0')", 0.6M, typeof(decimal)); - Evaluate("3 / new Decimal('5.00')", 0.60M, typeof(decimal)); - Evaluate("3L / new Decimal('5.0')", 0.6M, typeof(decimal)); - Evaluate("3.0d / new Decimal('5.0')", 0.6M, typeof(decimal)); - - Evaluate("5 % new Decimal('3')", 2M, typeof(decimal)); - Evaluate("3 % new Decimal('5')", 3M, typeof(decimal)); - Evaluate("3L % new Decimal('5')", 3M, typeof(decimal)); - Evaluate("3.0d % new Decimal('5')", 3.0M, typeof(decimal)); - } - - [Fact] - public void TestMathOperatorAdd02() - { - Evaluate("'hello' + ' ' + 'world'", "hello world", typeof(string)); - } - - [Fact] - public void TestMathOperatorsInChains() - { - Evaluate("1+2+3", 6, typeof(int)); - Evaluate("2*3*4", 24, typeof(int)); - Evaluate("12-1-2", 9, typeof(int)); - } - - [Fact] - public void TestIntegerArithmetic() - { - Evaluate("2 + 4", "6", typeof(int)); - Evaluate("5 - 4", "1", typeof(int)); - Evaluate("3 * 5", 15, typeof(int)); - Evaluate("3.2d * 5", 16.0d, typeof(double)); - Evaluate("3 * 5f", 15f, typeof(float)); - Evaluate("3 / 1", 3, typeof(int)); - Evaluate("3 % 2", 1, typeof(int)); - Evaluate("3 mod 2", 1, typeof(int)); - Evaluate("3 mOd 2", 1, typeof(int)); - Evaluate("3 Mod 2", 1, typeof(int)); - Evaluate("3 MOD 2", 1, typeof(int)); - } - - [Fact] - public void TestPlus() - { - Evaluate("7 + 2", "9", typeof(int)); - Evaluate("3.0f + 5.0f", 8.0f, typeof(float)); - Evaluate("3.0d + 5.0d", 8.0d, typeof(double)); - Evaluate("3 + new Decimal('5')", 8M, typeof(decimal)); - - Evaluate("'ab' + 2", "ab2", typeof(string)); - Evaluate("2 + 'a'", "2a", typeof(string)); - Evaluate("'ab' + null", "abnull", typeof(string)); - Evaluate("null + 'ab'", "nullab", typeof(string)); - - // AST: - var expr = (SpelExpression)Parser.ParseExpression("+3"); - Assert.Equal("+3", expr.ToStringAst()); - expr = (SpelExpression)Parser.ParseExpression("2+3"); - Assert.Equal("(2 + 3)", expr.ToStringAst()); - - // use as a unary operator - Evaluate("+5d", 5d, typeof(double)); - Evaluate("+5L", 5L, typeof(long)); - Evaluate("+5", 5, typeof(int)); - Evaluate("+new Decimal('5')", 5M, typeof(decimal)); - EvaluateAndCheckError("+'abc'", SpelMessage.OperatorNotSupportedBetweenTypes); - - // string concatenation - Evaluate("'abc'+'def'", "abcdef", typeof(string)); - - Evaluate("5 + new Int32('37')", 42, typeof(int)); - } - - [Fact] - public void TestMinus() - { - Evaluate("'c' - 2", "a", typeof(string)); - Evaluate("3.0f - 5.0f", -2.0f, typeof(float)); - EvaluateAndCheckError("'ab' - 2", SpelMessage.OperatorNotSupportedBetweenTypes); - EvaluateAndCheckError("2-'ab'", SpelMessage.OperatorNotSupportedBetweenTypes); - var expr = (SpelExpression)Parser.ParseExpression("-3"); - Assert.Equal("-3", expr.ToStringAst()); - expr = (SpelExpression)Parser.ParseExpression("2-3"); - Assert.Equal("(2 - 3)", expr.ToStringAst()); - - Evaluate("-5d", -5d, typeof(double)); - Evaluate("-5L", -5L, typeof(long)); - Evaluate("-5", -5, typeof(int)); - Evaluate("-new Decimal('5')", -5M, typeof(decimal)); - EvaluateAndCheckError("-'abc'", SpelMessage.OperatorNotSupportedBetweenTypes); - } - - [Fact] - public void TestModulus() - { - Evaluate("3%2", 1, typeof(int)); - Evaluate("3L%2L", 1L, typeof(long)); - Evaluate("3.0f%2.0f", 1f, typeof(float)); - Evaluate("5.0d % 3.1d", 1.9d, typeof(double)); - Evaluate("new Decimal('5') % new Decimal('3')", 2M, typeof(decimal)); - Evaluate("new Decimal('5') % 3", 2M, typeof(decimal)); - EvaluateAndCheckError("'abc'%'def'", SpelMessage.OperatorNotSupportedBetweenTypes); - } - - [Fact] - public void TestDivide() - { - Evaluate("3.0f / 5.0f", 0.6f, typeof(float)); - Evaluate("4L/2L", 2L, typeof(long)); - Evaluate("3.0f div 5.0f", 0.6f, typeof(float)); - Evaluate("4L DIV 2L", 2L, typeof(long)); - Evaluate("new Decimal('3') / 5", .6M, typeof(decimal)); - Evaluate("new Decimal('3.0') / 5", 0.6M, typeof(decimal)); - Evaluate("new Decimal('3.00') / 5", 0.60M, typeof(decimal)); - Evaluate("new Decimal('3.00') / new Decimal('5.0000')", 0.6000M, typeof(decimal)); - EvaluateAndCheckError("'abc'/'def'", SpelMessage.OperatorNotSupportedBetweenTypes); - } - - [Fact] - public void TestMathOperatorDivide_ConvertToDouble() - { - EvaluateAndAskForReturnType("8/4", 2.0d, typeof(double)); - } - - [Fact] - public void TestMathOperatorDivide04_ConvertToFloat() - { - EvaluateAndAskForReturnType("8/4", 2.0f, typeof(float)); - } - - [Fact] - public void TestDoubles() - { - Evaluate("3.0d == 5.0d", false, typeof(bool)); - Evaluate("3.0d == 3.0d", true, typeof(bool)); - Evaluate("3.0d != 5.0d", true, typeof(bool)); - Evaluate("3.0d != 3.0d", false, typeof(bool)); - Evaluate("3.0d + 5.0d", 8.0d, typeof(double)); - Evaluate("3.0d - 5.0d", -2.0d, typeof(double)); - Evaluate("3.0d * 5.0d", 15.0d, typeof(double)); - Evaluate("3.0d / 5.0d", 0.6d, typeof(double)); - Evaluate("6.0d % 3.5d", 2.5d, typeof(double)); - } - - [Fact] - public void TestBigDecimals() - { - Evaluate("3 + new Decimal('5')", 8M, typeof(decimal)); - Evaluate("3 - new Decimal('5')", -2M, typeof(decimal)); - Evaluate("3 * new Decimal('5')", 15M, typeof(decimal)); - Evaluate("3 / new Decimal('5')", .6M, typeof(decimal)); - Evaluate("5 % new Decimal('3')", 2M, typeof(decimal)); - Evaluate("new Decimal('5') % 3", 2M, typeof(decimal)); - Evaluate("new Decimal('5') ^ 3", 125M, typeof(decimal)); - } - - [Fact] - public void TestOperatorNames() - { - Operator node = GetOperatorNode((SpelExpression)Parser.ParseExpression("1==3")); - Assert.Equal("==", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("1!=3")); - Assert.Equal("!=", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3/3")); - Assert.Equal("/", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3+3")); - Assert.Equal("+", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3-3")); - Assert.Equal("-", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3<4")); - Assert.Equal("<", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3<=4")); - Assert.Equal("<=", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3*4")); - Assert.Equal("*", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3%4")); - Assert.Equal("%", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3>=4")); - Assert.Equal(">=", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3 between 4")); - Assert.Equal("between", node.OperatorName); - - node = GetOperatorNode((SpelExpression)Parser.ParseExpression("3 ^ 4")); - Assert.Equal("^", node.OperatorName); - } - - [Fact] - public void TestOperatorOverloading() - { - EvaluateAndCheckError("'a' * '2'", SpelMessage.OperatorNotSupportedBetweenTypes); - EvaluateAndCheckError("'a' ^ '2'", SpelMessage.OperatorNotSupportedBetweenTypes); - } - - [Fact] - public void TestPower() - { - Evaluate("3^2", 9, typeof(int)); - Evaluate("3.0d^2.0d", 9.0d, typeof(double)); - Evaluate("3L^2L", 9L, typeof(long)); - Evaluate("(2^32)^2", -9_223_372_036_854_775_808L, typeof(long)); - Evaluate("new Decimal('5') ^ 3", 125M, typeof(decimal)); - } - - [Fact] - public void TestMixedOperands_FloatsAndDoubles() - { - Evaluate("3.0d + 5.0f", 8.0d, typeof(double)); - Evaluate("3.0D - 5.0f", -2.0d, typeof(double)); - Evaluate("3.0f * 5.0d", 15.0d, typeof(double)); - Evaluate("3.0f / 5.0D", 0.6d, typeof(double)); - Evaluate("5.0D % 3f", 2.0d, typeof(double)); - } - - [Fact] - public void TestMixedOperands_DoublesAndIntegers() - { - Evaluate("3.0d + 5", 8.0d, typeof(double)); - Evaluate("3.0D - 5", -2.0d, typeof(double)); - Evaluate("3.0f * 5", 15.0f, typeof(float)); - Evaluate("6.0f / 2", 3.0f, typeof(float)); - Evaluate("6.0f / 4", 1.5f, typeof(float)); - Evaluate("5.0D % 3", 2.0d, typeof(double)); - Evaluate("5.5D % 3", 2.5, typeof(double)); - } - - [Fact] - public void TestStrings() - { - Evaluate("'abc' == 'abc'", true, typeof(bool)); - Evaluate("'abc' == 'def'", false, typeof(bool)); - Evaluate("'abc' != 'abc'", false, typeof(bool)); - Evaluate("'abc' != 'def'", true, typeof(bool)); - } - - [Fact] - public void TestLongs() - { - Evaluate("3L == 4L", false, typeof(bool)); - Evaluate("3L == 3L", true, typeof(bool)); - Evaluate("3L != 4L", true, typeof(bool)); - Evaluate("3L != 3L", false, typeof(bool)); - Evaluate("3L * 50L", 150L, typeof(long)); - Evaluate("3L + 50L", 53L, typeof(long)); - Evaluate("3L - 50L", -47L, typeof(long)); - } - - private Operator GetOperatorNode(SpelExpression expr) - { - ISpelNode node = expr.Ast; - return FindOperator(node); - } - - private Operator FindOperator(ISpelNode node) - { - if (node is Operator operatorNode) - { - return operatorNode; - } - - int childCount = node.ChildCount; - - for (int i = 0; i < childCount; i++) - { - Operator possible = FindOperator(node.GetChild(i)); - - if (possible != null) - { - return possible; - } - } - - return null; - } - - public class BaseComparable : IComparable - { - public int CompareTo(object obj) - { - return 0; - } - } - - public sealed class SubComparable : BaseComparable - { - } - - public sealed class OtherSubComparable : BaseComparable - { - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/ParserErrorMessagesTests.cs b/src/Common/test/Common.Expression.Test/Spring/ParserErrorMessagesTests.cs deleted file mode 100644 index 14c04f73d6..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/ParserErrorMessagesTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class ParserErrorMessagesTests : AbstractExpressionTests -{ - [Fact] - public void TestBrokenExpression01() - { - // will not fit into an int, needs L suffix - ParseAndCheckError("0xCAFEBABE", SpelMessage.NotAnInteger); - Evaluate("0xCAFEBABEL", 0xCAFEBABEL, typeof(long)); - ParseAndCheckError("0xCAFEBABECAFEBABEL", SpelMessage.NotALong); - } - - [Fact] - public void TestBrokenExpression02() - { - // rogue 'G' on the end - ParseAndCheckError("0xB0BG", SpelMessage.MoreInput, 5, "G"); - } - - [Fact] - public void TestBrokenExpression04() - { - // missing right operand - ParseAndCheckError("true or ", SpelMessage.RightOperandProblem, 5); - } - - [Fact] - public void TestBrokenExpression05() - { - // missing right operand - ParseAndCheckError("1 + ", SpelMessage.RightOperandProblem, 2); - } - - [Fact] - public void TestBrokenExpression07() - { - // T() can only take an identifier (possibly qualified), not a literal - // message ought to say identifier rather than ID - ParseAndCheckError("null instanceof T('a')", SpelMessage.NotExpectedToken, 18, "qualified ID", "literal_string"); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/ParsingTests.cs b/src/Common/test/Common.Expression.Test/Spring/ParsingTests.cs deleted file mode 100644 index d1f7b3314f..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/ParsingTests.cs +++ /dev/null @@ -1,412 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class ParsingTests -{ - private readonly SpelExpressionParser _parser = new(); - - // literals - [Fact] - public void TestLiteralBoolean01() - { - ParseCheck("False"); - ParseCheck("false"); - ParseCheck("FALSE"); - ParseCheck("FaLsE"); - } - - [Fact] - public void TestLiteralLong01() - { - ParseCheck("37L", "37"); - } - - [Fact] - public void TestLiteralBoolean02() - { - ParseCheck("True"); - ParseCheck("true"); - ParseCheck("TRUE"); - } - - [Fact] - public void TestLiteralBoolean03() - { - ParseCheck("!true"); - } - - [Fact] - public void TestLiteralInteger01() - { - ParseCheck("1"); - } - - [Fact] - public void TestLiteralInteger02() - { - ParseCheck("1415"); - } - - [Fact] - public void TestLiteralString01() - { - ParseCheck("'hello'"); - } - - [Fact] - public void TestLiteralString02() - { - ParseCheck("'joe bloggs'"); - } - - [Fact] - public void TestLiteralString03() - { - ParseCheck("'Tony''s Pizza'", "'Tony's Pizza'"); - } - - [Fact] - public void TestLiteralReal01() - { - ParseCheck("6.0221415E+23"); - } - - [Fact] - public void TestLiteralHex01() - { - ParseCheck("0x7FFFFFFF", "2147483647"); - } - - [Fact] - public void TestLiteralDate01() - { - ParseCheck("date('1974/08/24')"); - } - - [Fact] - public void TestLiteralDate02() - { - ParseCheck("date('19740824T131030','yyyyMMddTHHmmss')"); - } - - [Fact] - public void TestLiteralNull01() - { - ParseCheck("null"); - } - - // boolean operators - [Fact] - public void TestBooleanOperatorsOr01() - { - ParseCheck("false or false", "(false or false)"); - } - - [Fact] - public void TestBooleanOperatorsOr02() - { - ParseCheck("false or true", "(false or true)"); - } - - [Fact] - public void TestBooleanOperatorsOr03() - { - ParseCheck("true or false", "(true or false)"); - } - - [Fact] - public void TestBooleanOperatorsOr04() - { - ParseCheck("true or false", "(true or false)"); - } - - [Fact] - public void TestBooleanOperatorsMix01() - { - ParseCheck("false or true and false", "(false or (true and false))"); - } - - // relational operators - [Fact] - public void TestRelOperatorsGt01() - { - ParseCheck("3>6", "(3 > 6)"); - } - - [Fact] - public void TestRelOperatorsLt01() - { - ParseCheck("3<6", "(3 < 6)"); - } - - [Fact] - public void TestRelOperatorsLe01() - { - ParseCheck("3<=6", "(3 <= 6)"); - } - - [Fact] - public void TestRelOperatorsGe01() - { - ParseCheck("3>=6", "(3 >= 6)"); - } - - [Fact] - public void TestRelOperatorsGe02() - { - ParseCheck("3>=3", "(3 >= 3)"); - } - - [Fact] - public void TestElvis() - { - ParseCheck("3?:1", "3 ?: 1"); - } - - [Fact] - public void TestRelOperatorsBetween01() - { - ParseCheck("1 between {1, 5}", "(1 between {1,5})"); - } - - [Fact] - public void TestRelOperatorsBetween02() - { - ParseCheck("'efg' between {'abc', 'xyz'}", "('efg' between {'abc','xyz'})"); - } // true - - [Fact] - public void TestRelOperatorsIs01() - { - ParseCheck("'xyz' instanceof int", "('xyz' instanceof int)"); - } // false - - [Fact] - public void TestRelOperatorsIs02() - { - ParseCheck("{1, 2, 3, 4, 5} instanceof List", "({1,2,3,4,5} instanceof List)"); - } // true - - [Fact] - public void TestRelOperatorsMatches01() - { - ParseCheck("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'", "('5.0067' matches '^-?\\d+(\\.\\d{2})?$')"); - } // false - - [Fact] - public void TestRelOperatorsMatches02() - { - ParseCheck("'5.00' matches '^-?\\d+(\\.\\d{2})?$'", "('5.00' matches '^-?\\d+(\\.\\d{2})?$')"); - } // true - - // mathematical operators - [Fact] - public void TestMathOperatorsAdd01() - { - ParseCheck("2+4", "(2 + 4)"); - } - - [Fact] - public void TestMathOperatorsAdd02() - { - ParseCheck("'a'+'b'", "('a' + 'b')"); - } - - [Fact] - public void TestMathOperatorsAdd03() - { - ParseCheck("'hello'+' '+'world'", "(('hello' + ' ') + 'world')"); - } - - [Fact] - public void TestMathOperatorsSubtract01() - { - ParseCheck("5-4", "(5 - 4)"); - } - - [Fact] - public void TestMathOperatorsMultiply01() - { - ParseCheck("7*4", "(7 * 4)"); - } - - [Fact] - public void TestMathOperatorsDivide01() - { - ParseCheck("8/4", "(8 / 4)"); - } - - [Fact] - public void TestMathOperatorModulus01() - { - ParseCheck("7 % 4", "(7 % 4)"); - } - - // mixed operators - [Fact] - public void TestMixedOperators01() - { - ParseCheck("true and 5>3", "(true and (5 > 3))"); - } - - // references - [Fact] - public void TestReferences01() - { - ParseCheck("@foo"); - ParseCheck("@'foo.bar'"); - ParseCheck("@\"foo.bar.goo\"", "@'foo.bar.goo'"); - } - - [Fact] - public void TestReferences03() - { - ParseCheck("@$$foo"); - } - - // properties - [Fact] - public void TestProperties01() - { - ParseCheck("name"); - } - - [Fact] - public void TestProperties02() - { - ParseCheck("placeofbirth.CitY"); - } - - [Fact] - public void TestProperties03() - { - ParseCheck("a.b.c.d.e"); - } - - // inline list creation - [Fact] - public void TestInlineListCreation01() - { - ParseCheck("{1, 2, 3, 4, 5}", "{1,2,3,4,5}"); - } - - [Fact] - public void TestInlineListCreation02() - { - ParseCheck("{'abc','xyz'}", "{'abc','xyz'}"); - } - - // inline map creation - [Fact] - public void TestInlineMapCreation01() - { - ParseCheck("{'key1':'Value 1','today':DateTime.Today}"); - } - - [Fact] - public void TestInlineMapCreation02() - { - ParseCheck("{1:'January',2:'February',3:'March'}"); - } - - // methods - [Fact] - public void TestMethods01() - { - ParseCheck("echo(12)"); - } - - [Fact] - public void TestMethods02() - { - ParseCheck("echo(name)"); - } - - [Fact] - public void TestMethods03() - { - ParseCheck("age.doubleItAndAdd(12)"); - } - - // constructors - [Fact] - public void TestConstructors01() - { - ParseCheck("new String('hello')"); - } - - // variables and functions - [Fact] - public void TestVariables01() - { - ParseCheck("#foo"); - } - - [Fact] - public void TestFunctions01() - { - ParseCheck("#fn(1,2,3)"); - } - - [Fact] - public void TestFunctions02() - { - ParseCheck("#fn('hello')"); - } - - // assignment - [Fact] - public void TestAssignmentToVariables01() - { - ParseCheck("#var1='value1'"); - } - - // ternary operator - [Fact] - public void TestTernaryOperator01() - { - ParseCheck("1>2?3:4", "(1 > 2) ? 3 : 4"); - } - - [Fact] - public void TestTernaryOperator02() - { - ParseCheck("{1}.#isEven(#this) == 'y'?'it is even':'it is odd'", "({1}.#isEven(#this) == 'y') ? 'it is even' : 'it is odd'"); - } - - [Fact] - public void TestTypeReferences01() - { - ParseCheck("T(System.String)"); - } - - [Fact] - public void TestTypeReferences02() - { - ParseCheck("T(String)"); - } - - [Fact] - public void TestInlineList1() - { - ParseCheck("{1,2,3,4}"); - } - - private void ParseCheck(string expression) - { - ParseCheck(expression, expression); - } - - private void ParseCheck(string expression, string expectedStringFormOfAst) - { - var e = _parser.ParseRaw(expression) as SpelExpression; - Assert.NotNull(e); - Assert.Equal(expectedStringFormOfAst, e.ToStringAst(), true); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/PerformanceTests.cs b/src/Common/test/Common.Expression.Test/Spring/PerformanceTests.cs deleted file mode 100644 index 183a3871f9..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/PerformanceTests.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Xunit; -using Xunit.Abstractions; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class PerformanceTests -{ - private const int Iterations = 10000; - private static readonly bool IsDebug = bool.Parse(bool.FalseString); - private static readonly IExpressionParser Parser = new SpelExpressionParser(); - private static readonly IEvaluationContext EContext = TestScenarioCreator.GetTestEvaluationContext(); - private readonly ITestOutputHelper _output; - - public PerformanceTests(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - public void TestPerformanceOfPropertyAccess() - { - long startTime = 0; - long endTime = 0; - IExpression expr; - - // warmup - for (int i = 0; i < Iterations; i++) - { - expr = Parser.ParseExpression("PlaceOfBirth.City"); - Assert.NotNull(expr); - expr.GetValue(EContext); - } - - startTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - for (int i = 0; i < Iterations; i++) - { - expr = Parser.ParseExpression("PlaceOfBirth.City"); - Assert.NotNull(expr); - expr.GetValue(EContext); - } - - endTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - long freshParseTime = endTime - startTime; - - if (IsDebug) - { - _output.WriteLine("PropertyAccess: Time for parsing and evaluation x 10000: " + freshParseTime + "ms"); - } - - expr = Parser.ParseExpression("PlaceOfBirth.City"); - Assert.NotNull(expr); - startTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - for (int i = 0; i < Iterations; i++) - { - expr.GetValue(EContext); - } - - endTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - long reuseTime = endTime - startTime; - - if (IsDebug) - { - _output.WriteLine("PropertyAccess: Time for just evaluation x 10000: " + reuseTime + "ms"); - } - - if (reuseTime > freshParseTime) - { - _output.WriteLine("Fresh parse every time, ITERATIONS iterations = " + freshParseTime + "ms"); - _output.WriteLine("Reuse SpelExpression, ITERATIONS iterations = " + reuseTime + "ms"); - throw new Exception("Should have been quicker to reuse!"); - } - } - - [Fact] - public void TestPerformanceOfMethodAccess() - { - long startTime = 0; - long endTime = 0; - IExpression expr; - - // warmup - for (int i = 0; i < Iterations; i++) - { - expr = Parser.ParseExpression("get_PlaceOfBirth().get_City()"); - Assert.NotNull(expr); - expr.GetValue(EContext); - } - - startTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - for (int i = 0; i < Iterations; i++) - { - expr = Parser.ParseExpression("get_PlaceOfBirth().get_City()"); - Assert.NotNull(expr); - expr.GetValue(EContext); - } - - endTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - long freshParseTime = endTime - startTime; - - if (IsDebug) - { - _output.WriteLine("MethodExpression: Time for parsing and evaluation x 10000: " + freshParseTime + "ms"); - } - - expr = Parser.ParseExpression("get_PlaceOfBirth().get_City()"); - Assert.NotNull(expr); - startTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - for (int i = 0; i < Iterations; i++) - { - expr.GetValue(EContext); - } - - endTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - long reuseTime = endTime - startTime; - - if (IsDebug) - { - _output.WriteLine("MethodExpression: Time for just evaluation x 10000: " + reuseTime + "ms"); - } - - if (reuseTime > freshParseTime) - { - _output.WriteLine("Fresh parse every time, ITERATIONS iterations = " + freshParseTime + "ms"); - _output.WriteLine("Reuse SpelExpression, ITERATIONS iterations = " + reuseTime + "ms"); - throw new Exception("Should have been quicker to reuse!"); - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/PropertyAccessTests.cs b/src/Common/test/Common.Expression.Test/Spring/PropertyAccessTests.cs deleted file mode 100644 index f1da6ad2e6..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/PropertyAccessTests.cs +++ /dev/null @@ -1,343 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestResources; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class PropertyAccessTests : AbstractExpressionTests -{ - [Fact] - public void TestSimpleAccess01() - { - Evaluate("Name", "Nikola Tesla", typeof(string)); - } - - [Fact] - public void TestSimpleAccess02() - { - Evaluate("PlaceOfBirth.City", "SmilJan", typeof(string)); - } - - [Fact] - public void TestSimpleAccess03() - { - Evaluate("StringArrayOfThreeItems.Length", "3", typeof(int)); - } - - [Fact] - public void TestNonExistentPropertiesAndMethods() - { - // madeup does not exist as a property - EvaluateAndCheckError("madeup", SpelMessage.PropertyOrFieldNotReadable, 0); - - // name is ok but foobar does not exist: - EvaluateAndCheckError("Name.foobar", SpelMessage.PropertyOrFieldNotReadable, 5); - } - - /* - * The standard reflection resolver cannot find properties on null objects but some - * supplied resolver might be able to - so null shouldn't crash the reflection resolver. - */ - [Fact] - public void TestAccessingOnNullObject() - { - var expr = (SpelExpression)Parser.ParseExpression("madeup"); - var context = new StandardEvaluationContext(null); - var ex = Assert.Throws(() => expr.GetValue(context)); - Assert.Equal(SpelMessage.PropertyOrFieldNotReadableOnNull, ex.MessageCode); - Assert.False(expr.IsWritable(context)); - ex = Assert.Throws(() => expr.SetValue(context, "abc")); - Assert.Equal(SpelMessage.PropertyOrFieldNotWritableOnNull, ex.MessageCode); - } - - // Adding a new property accessor just for a particular type - [Fact] - public void TestAddingSpecificPropertyAccessor() - { - var parser = new SpelExpressionParser(); - var ctx = new StandardEvaluationContext(); - - // Even though this property accessor is added after the reflection one, it specifically - // names the String class as the type it is interested in so is chosen in preference to - // any 'default' ones - ctx.AddPropertyAccessor(new StringyPropertyAccessor()); - IExpression expr = parser.ParseRaw("new String('hello').flibbles"); - object i = expr.GetValue(ctx, typeof(int)); - Assert.Equal(7, (int)i); - - // The reflection one will be used for other properties... - expr = parser.ParseRaw("new String('hello').Length"); - object o = expr.GetValue(ctx); - Assert.NotNull(o); - - IExpression expression = parser.ParseRaw("new String('hello').flibbles"); - expression.SetValue(ctx, 99); - i = expression.GetValue(ctx, typeof(int)); - Assert.Equal(99, (int)i); - - // Cannot set it to a string value - Assert.Throws(() => expression.SetValue(ctx, "not allowed")); - } - - [Fact] - public void TestAddingRemovingAccessors() - { - var ctx = new StandardEvaluationContext(); - - // reflective property accessor is the only one by default - List propertyAccessors = ctx.PropertyAccessors; - Assert.Single(propertyAccessors); - - var spa = new StringyPropertyAccessor(); - ctx.AddPropertyAccessor(spa); - Assert.Equal(2, ctx.PropertyAccessors.Count); - - var copy = new List(ctx.PropertyAccessors); - Assert.True(ctx.RemovePropertyAccessor(spa)); - Assert.False(ctx.RemovePropertyAccessor(spa)); - Assert.Single(ctx.PropertyAccessors); - - ctx.PropertyAccessors = copy; - Assert.Equal(2, ctx.PropertyAccessors.Count); - } - - [Fact] - public void TestAccessingPropertyOfClass() - { - IExpression expression = Parser.ParseExpression("FullName"); - object value = expression.GetValue(new StandardEvaluationContext(typeof(string))); - Assert.Equal("System.String", value); - } - - [Fact] - public void ShouldAlwaysUsePropertyAccessorFromEvaluationContext() - { - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("name"); - - var context = new StandardEvaluationContext(); - - context.AddPropertyAccessor(new ConfigurablePropertyAccessor(new Dictionary - { - { "name", "Ollie" } - })); - - Assert.Equal("Ollie", expression.GetValue(context)); - - context = new StandardEvaluationContext(); - - context.AddPropertyAccessor(new ConfigurablePropertyAccessor(new Dictionary - { - { "name", "Jens" } - })); - - Assert.Equal("Jens", expression.GetValue(context)); - } - - [Fact] - public void StandardGetClassAccess() - { - Assert.Equal(typeof(string).FullName, Parser.ParseExpression("'a'.GetType().FullName").GetValue()); - } - - [Fact] - public void NoGetClassAccess() - { - SimpleEvaluationContext context = SimpleEvaluationContext.ForReadOnlyDataBinding().Build(); - Assert.Throws(() => Parser.ParseExpression("'a'.GetType().Name").GetValue(context)); - } - - [Fact] - public void PropertyReadOnly() - { - SimpleEvaluationContext context = SimpleEvaluationContext.ForReadOnlyDataBinding().Build(); - - IExpression expr = Parser.ParseExpression("Name"); - var target = new Person("p1"); - Assert.Equal("p1", expr.GetValue(context, target)); - target.Name = "p2"; - Assert.Equal("p2", expr.GetValue(context, target)); - - Assert.Throws(() => Parser.ParseExpression("Name='p3'").GetValue(context, target)); - } - - [Fact] - public void PropertyReadWrite() - { - SimpleEvaluationContext context = SimpleEvaluationContext.ForReadWriteDataBinding().Build(); - - IExpression expr = Parser.ParseExpression("Name"); - var target = new Person("p1"); - Assert.Equal("p1", expr.GetValue(context, target)); - target.Name = "p2"; - Assert.Equal("p2", expr.GetValue(context, target)); - - Parser.ParseExpression("Name='p3'").GetValue(context, target); - Assert.Equal("p3", target.Name); - Assert.Equal("p3", expr.GetValue(context, target)); - - expr.SetValue(context, target, "p4"); - Assert.Equal("p4", target.Name); - Assert.Equal("p4", expr.GetValue(context, target)); - } - - [Fact] - public void PropertyReadWriteWithRootObject() - { - var target = new Person("p1"); - SimpleEvaluationContext context = SimpleEvaluationContext.ForReadWriteDataBinding().WithRootObject(target).Build(); - Assert.Same(target, context.RootObject.Value); - - IExpression expr = Parser.ParseExpression("Name"); - Assert.Equal("p1", expr.GetValue(context, target)); - target.Name = "p2"; - Assert.Equal("p2", expr.GetValue(context, target)); - - Parser.ParseExpression("Name='p3'").GetValue(context, target); - Assert.Equal("p3", target.Name); - Assert.Equal("p3", expr.GetValue(context, target)); - - expr.SetValue(context, target, "p4"); - Assert.Equal("p4", target.Name); - Assert.Equal("p4", expr.GetValue(context, target)); - } - - [Fact] - public void PropertyAccessWithoutMethodResolver() - { - SimpleEvaluationContext context = SimpleEvaluationContext.ForReadOnlyDataBinding().Build(); - var target = new Person("p1"); - Assert.Throws(() => Parser.ParseExpression("Name.Substring(1)").GetValue(context, target)); - } - - [Fact] - public void PropertyAccessWithInstanceMethodResolver() - { - SimpleEvaluationContext context = SimpleEvaluationContext.ForReadOnlyDataBinding().WithInstanceMethods().Build(); - var target = new Person("p1"); - Assert.Equal("1", Parser.ParseExpression("Name.Substring(1)").GetValue(context, target)); - } - - [Fact] - public void PropertyAccessWithInstanceMethodResolverAndTypedRootObject() - { - var target = new Person("p1"); - - SimpleEvaluationContext context = SimpleEvaluationContext.ForReadOnlyDataBinding().WithInstanceMethods().WithTypedRootObject(target, typeof(object)) - .Build(); - - Assert.Equal("1", Parser.ParseExpression("Name.Substring(1)").GetValue(context, target)); - Assert.Same(target, context.RootObject.Value); - Assert.Equal(typeof(object), context.RootObject.TypeDescriptor); - } - - [Fact] - public void PropertyAccessWithArrayIndexOutOfBounds() - { - SimpleEvaluationContext context = SimpleEvaluationContext.ForReadOnlyDataBinding().Build(); - IExpression expression = Parser.ParseExpression("StringArrayOfThreeItems[3]"); - var ex = Assert.Throws(() => expression.GetValue(context, new Inventor())); - Assert.Equal(SpelMessage.ArrayIndexOutOfBounds, ex.MessageCode); - } - - private sealed class StringyPropertyAccessor : IPropertyAccessor - { - private int _flibbles = 7; - - public IList GetSpecificTargetClasses() - { - return new[] - { - typeof(string) - }; - } - - public bool CanRead(IEvaluationContext context, object target, string name) - { - return CanReadOrWrite(target, name); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return CanReadOrWrite(target, name); - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - if (name != "flibbles") - { - throw new SystemException("Assertion Failed! name should be flibbles"); - } - - return new TypedValue(_flibbles); - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - if (name != "flibbles") - { - throw new SystemException("Assertion Failed! name should be flibbles"); - } - - try - { - _flibbles = (int)context.TypeConverter.ConvertValue(newValue, newValue?.GetType(), typeof(int)); - } - catch (EvaluationException) - { - throw new AccessException($"Cannot set flibbles to an object of type '{newValue?.GetType()}'"); - } - } - - private static bool CanReadOrWrite(object target, string name) - { - if (target is not string) - { - throw new SystemException("Assertion Failed! target should be string"); - } - - return name == "flibbles"; - } - } - - private sealed class ConfigurablePropertyAccessor : IPropertyAccessor - { - private readonly Dictionary _values; - - public ConfigurablePropertyAccessor(Dictionary values) - { - _values = values; - } - - public IList GetSpecificTargetClasses() - { - return null; - } - - public bool CanRead(IEvaluationContext context, object target, string name) - { - return true; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - return new TypedValue(_values[name]); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return false; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SPR10210/A.cs b/src/Common/test/Common.Expression.Test/Spring/SPR10210/A.cs deleted file mode 100644 index 1bcc09c280..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SPR10210/A.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Test.Spring.SPR10210.Comp; -using Steeltoe.Common.Expression.Test.Spring.SPR10210.Infra; - -namespace Steeltoe.Common.Expression.Test.Spring.SPR10210; - -public abstract class A : B -{ - public void BridgeMethod() - { - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SPR10210/Comp/B.cs b/src/Common/test/Common.Expression.Test/Spring/SPR10210/Comp/B.cs deleted file mode 100644 index 63c3dfc4f0..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SPR10210/Comp/B.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Test.Spring.SPR10210.Infra; - -namespace Steeltoe.Common.Expression.Test.Spring.SPR10210.Comp; - -#pragma warning disable S2326 // Unused type parameters should be removed -public class B -#pragma warning restore S2326 // Unused type parameters should be removed - where T : IC -{ -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SPR10210/D.cs b/src/Common/test/Common.Expression.Test/Spring/SPR10210/D.cs deleted file mode 100644 index 076ffc00c4..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SPR10210/D.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.SPR10210; - -public sealed class D : A -{ -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SPR10210/Infra/IC.cs b/src/Common/test/Common.Expression.Test/Spring/SPR10210/Infra/IC.cs deleted file mode 100644 index 1b847d2639..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SPR10210/Infra/IC.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.SPR10210.Infra; - -public interface IC -{ -} diff --git a/src/Common/test/Common.Expression.Test/Spring/ScenariosForSpringSecurityExpressionTests.cs b/src/Common/test/Common.Expression.Test/Spring/ScenariosForSpringSecurityExpressionTests.cs deleted file mode 100644 index 0fdcae3824..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/ScenariosForSpringSecurityExpressionTests.cs +++ /dev/null @@ -1,320 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Util; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class ScenariosForSpringSecurityExpressionTests : AbstractExpressionTests -{ - [Fact] - public void TestScenario01_Roles() - { - var parser = new SpelExpressionParser(); - var ctx = new StandardEvaluationContext(); - IExpression expr = parser.ParseRaw("HasAnyRole('MANAGER','TELLER')"); - - ctx.SetRootObject(new Person("Ben")); - object value = expr.GetValue(ctx, typeof(bool)); - Assert.False((bool)value); - - ctx.SetRootObject(new Manager("Luke")); - value = expr.GetValue(ctx, typeof(bool)); - Assert.True((bool)value); - } - - [Fact] - public void TestScenario02_ComparingNames() - { - var parser = new SpelExpressionParser(); - var ctx = new StandardEvaluationContext(); - - ctx.AddPropertyAccessor(new SecurityPrincipalAccessor()); - - // Multiple options for supporting this expression: "p.name == principal.name" - // (1) If the right person is the root context object then "name==principal.name" is good enough - IExpression expr = parser.ParseRaw("Name == Principal.Name"); - - ctx.SetRootObject(new Person("Andy")); - object value = expr.GetValue(ctx, typeof(bool)); - Assert.True((bool)value); - - ctx.SetRootObject(new Person("Christian")); - value = expr.GetValue(ctx, typeof(bool)); - Assert.False((bool)value); - - // (2) Or register an accessor that can understand 'p' and return the right person - expr = parser.ParseRaw("P.Name == Principal.Name"); - - var pAccessor = new PersonAccessor(); - ctx.AddPropertyAccessor(pAccessor); - ctx.SetRootObject(null); - - pAccessor.ActivePerson = new Person("Andy"); - value = expr.GetValue(ctx, typeof(bool)); - Assert.True((bool)value); - - pAccessor.ActivePerson = new Person("Christian"); - value = expr.GetValue(ctx, typeof(bool)); - Assert.False((bool)value); - } - - [Fact] - public void TestScenario03_Arithmetic() - { - var parser = new SpelExpressionParser(); - var ctx = new StandardEvaluationContext(); - - // Might be better with a as a variable although it would work as a property too... - // Variable references using a '#' - IExpression expr = parser.ParseRaw("(HasRole('SUPERVISOR') or (#a < 1.042)) and HasIPAddress('10.10.0.0/16')"); - - bool value; - - ctx.SetVariable("a", 1.0d); // referenced as #a in the expression - ctx.SetRootObject(new Supervisor("Ben")); // so non-qualified references 'hasRole()' 'hasIPAddress()' are invoked against it - value = expr.GetValue(ctx); - Assert.True(value); - - ctx.SetRootObject(new Manager("Luke")); - ctx.SetVariable("a", 1.043d); - value = expr.GetValue(ctx); - Assert.False(value); - } - - // Here i'm going to change which hasRole() executes and make it one of my own Java methods - [Fact] - public void TestScenario04_ControllingWhichMethodsRun() - { - var parser = new SpelExpressionParser(); - var ctx = new StandardEvaluationContext(); - - ctx.SetRootObject(new Supervisor("Ben")); - -#pragma warning disable S125 // Sections of code should not be commented out - // NEEDS TO OVERRIDE THE REFLECTION ONE - SHOW REORDERING MECHANISM - // Might be better as a variable although it would work as a property too... - // Variable references using a '#' - // SpelExpression expr = parser.parseExpression("(hasRole('SUPERVISOR') or (#a < 1.042)) and hasIPAddress('10.10.0.0/16')"); -#pragma warning restore S125 // Sections of code should not be commented out - ctx.AddMethodResolver(new MyMethodResolver()); - - IExpression expr = parser.ParseRaw("(HasRole(3) or (#a < 1.042)) and HasIPAddress('10.10.0.0/16')"); - - ctx.SetVariable("a", 1.0d); // referenced as #a in the expression - bool value = expr.GetValue(ctx); - Assert.True(value); - } - - public class Person - { - public virtual string[] Roles => - new[] - { - "NONE" - }; - - public virtual string Name { get; } - - public Person(string n) - { - Name = n; - } - - public virtual bool HasAnyRole(params string[] roles) - { - if (roles == null) - { - return true; - } - - string[] myRoles = Roles; - - foreach (string myRole in myRoles) - { - if (Array.Exists(roles, role => myRole == role)) - { - return true; - } - } - - return false; - } - - public virtual bool HasRole(string role) - { - return HasAnyRole(role); - } - - public virtual bool HasIPAddress(string ipAddress) - { - return true; - } - } - - public sealed class Manager : Person - { - public override string[] Roles => - new[] - { - "MANAGER" - }; - - public Manager(string n) - : base(n) - { - } - } - - public sealed class Teller : Person - { - public override string[] Roles => - new[] - { - "TELLER" - }; - - public Teller(string n) - : base(n) - { - } - } - - public sealed class Supervisor : Person - { - public override string[] Roles => - new[] - { - "SUPERVISOR" - }; - - public Supervisor(string n) - : base(n) - { - } - } - - public sealed class SecurityPrincipalAccessor : IPropertyAccessor - { - public bool CanRead(IEvaluationContext context, object target, string name) - { - return name == "Principal"; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - return new TypedValue(new Principal()); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return false; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - } - - public IList GetSpecificTargetClasses() - { - return null; - } - - public sealed class Principal - { - public string Name { get; } = "Andy"; - } - } - - public sealed class PersonAccessor : IPropertyAccessor - { - public Person ActivePerson { get; set; } - - public bool CanRead(IEvaluationContext context, object target, string name) - { - return name == "P"; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - return new TypedValue(ActivePerson); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return false; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - } - - public IList GetSpecificTargetClasses() - { - return null; - } - } - - public sealed class MyMethodResolver : IMethodResolver - { - public IMethodExecutor Resolve(IEvaluationContext context, object targetObject, string name, List argumentTypes) - { - if (name == "HasRole") - { - return new HasRoleExecutor(context.TypeConverter); - } - - return null; - } - - public sealed class HasRoleExecutor : IMethodExecutor - { - private readonly ITypeConverter _tc; - - public HasRoleExecutor(ITypeConverter typeConverter) - { - _tc = typeConverter; - } - - public static bool HasRole(params string[] strings) - { - return true; - } - - public ITypedValue Execute(IEvaluationContext context, object target, params object[] arguments) - { - try - { - MethodInfo m = typeof(HasRoleExecutor).GetMethod(nameof(HasRole), new[] - { - typeof(string[]) - }); - - object[] args = arguments; - - if (args != null) - { - ReflectionHelper.ConvertAllArguments(_tc, args, m); - } - - if (m.IsVarArgs()) - { - args = ReflectionHelper.SetupArgumentsForVarargsInvocation(ClassUtils.GetParameterTypes(m), args); - } - - return new TypedValue(m.Invoke(null, args), m.ReturnType); - } - catch (Exception ex) - { - throw new AccessException("Problem invoking hasRole", ex); - } - } - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SelectionAndProjectionTests.cs b/src/Common/test/Common.Expression.Test/Spring/SelectionAndProjectionTests.cs deleted file mode 100644 index 7a4cea5980..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SelectionAndProjectionTests.cs +++ /dev/null @@ -1,412 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class SelectionAndProjectionTests -{ - [Fact] - public void SelectionWithList() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.?[#this<5]"); - var context = new StandardEvaluationContext(new ListTestBean()); - object value = expression.GetValue(context); - bool condition = value is List; - Assert.True(condition); - var list = (List)value; - Assert.Equal(5, list.Count); - Assert.Equal(0, list[0]); - Assert.Equal(1, list[1]); - Assert.Equal(2, list[2]); - Assert.Equal(3, list[3]); - Assert.Equal(4, list[4]); - } - - [Fact] - public void SelectFirstItemInList() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.^[#this<5]"); - var context = new StandardEvaluationContext(new ListTestBean()); - object value = expression.GetValue(context); - bool condition = value is int; - Assert.True(condition); - Assert.Equal(0, value); - } - - [Fact] - public void SelectLastItemInList() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.$[#this<5]"); - var context = new StandardEvaluationContext(new ListTestBean()); - object value = expression.GetValue(context); - bool condition = value is int; - Assert.True(condition); - Assert.Equal(4, value); - } - - [Fact] - public void SelectionWithSet() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.?[#this<5]"); - var context = new StandardEvaluationContext(new SetTestBean()); - object value = expression.GetValue(context); - bool condition = value is List; - Assert.True(condition); - var list = (List)value; - Assert.Equal(5, list.Count); - Assert.Equal(0, list[0]); - Assert.Equal(1, list[1]); - Assert.Equal(2, list[2]); - Assert.Equal(3, list[3]); - Assert.Equal(4, list[4]); - } - - [Fact] - public void SelectFirstItemInSet() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.^[#this<5]"); - var context = new StandardEvaluationContext(new SetTestBean()); - object value = expression.GetValue(context); - bool condition = value is int; - Assert.True(condition); - Assert.Equal(0, value); - } - - [Fact] - public void SelectLastItemInSet() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.$[#this<5]"); - var context = new StandardEvaluationContext(new SetTestBean()); - object value = expression.GetValue(context); - bool condition = value is int; - Assert.True(condition); - Assert.Equal(4, value); - } - - [Fact] - public void SelectionWithIEnumerable() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.?[#this<5]"); - var context = new StandardEvaluationContext(new EnumerableTestBean()); - object value = expression.GetValue(context); - bool condition = value is List; - Assert.True(condition); - var list = (List)value; - Assert.Equal(5, list.Count); - Assert.Equal(0, list[0]); - Assert.Equal(1, list[1]); - Assert.Equal(2, list[2]); - Assert.Equal(3, list[3]); - Assert.Equal(4, list[4]); - } - - [Fact] - public void SelectionWithArray() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.?[#this<5]"); - var context = new StandardEvaluationContext(new ArrayTestBean()); - object value = expression.GetValue(context); - Assert.True(value.GetType().IsArray); - var typedValue = new TypedValue(value); - Assert.Equal(typeof(int), typedValue.TypeDescriptor.GetElementType()); - int[] array = (int[])value; - Assert.Equal(5, array.Length); - Assert.Equal(0, array[0]); - Assert.Equal(1, array[1]); - Assert.Equal(2, array[2]); - Assert.Equal(3, array[3]); - Assert.Equal(4, array[4]); - } - - [Fact] - public void SelectFirstItemInArray() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.^[#this<5]"); - var context = new StandardEvaluationContext(new ArrayTestBean()); - object value = expression.GetValue(context); - bool condition = value is int; - Assert.True(condition); - Assert.Equal(0, value); - } - - [Fact] - public void SelectLastItemInArray() - { - IExpression expression = new SpelExpressionParser().ParseRaw("Integers.$[#this<5]"); - var context = new StandardEvaluationContext(new ArrayTestBean()); - object value = expression.GetValue(context); - bool condition = value is int; - Assert.True(condition); - Assert.Equal(4, value); - } - - [Fact] - public void SelectionWithMap() - { - var context = new StandardEvaluationContext(new MapTestBean()); - var parser = new SpelExpressionParser(); - IExpression exp = parser.ParseExpression("Colors.?[Key.StartsWith('b')]"); - - var colorsMap = (Dictionary)exp.GetValue(context); - Assert.Equal(3, colorsMap.Count); - Assert.True(colorsMap.ContainsKey("beige")); - Assert.True(colorsMap.ContainsKey("blue")); - Assert.True(colorsMap.ContainsKey("brown")); - } - - [Fact] - public void SelectFirstItemInMap() - { - var context = new StandardEvaluationContext(new MapTestBean()); - var parser = new SpelExpressionParser(); - - IExpression exp = parser.ParseExpression("Colors.^[Key.StartsWith('b')]"); - var colorsMap = (Dictionary)exp.GetValue(context); - Assert.Single(colorsMap); - Assert.True(colorsMap.ContainsKey("brown")); - } - - [Fact] - public void SelectLastItemInMap() - { - var context = new StandardEvaluationContext(new MapTestBean()); - var parser = new SpelExpressionParser(); - - IExpression exp = parser.ParseExpression("Colors.$[Key.StartsWith('b')]"); - var colorsMap = (Dictionary)exp.GetValue(context); - Assert.Single(colorsMap); - Assert.True(colorsMap.ContainsKey("beige")); - } - - [Fact] - public void ProjectionWithList() - { - IExpression expression = new SpelExpressionParser().ParseRaw("#testList.![Wrapper.Value]"); - var context = new StandardEvaluationContext(); - context.SetVariable("testList", IntegerTestBean.CreateList()); - object value = expression.GetValue(context); - bool condition = value is List; - Assert.True(condition); - var list = (List)value; - Assert.Equal(3, list.Count); - Assert.Equal(5, list[0]); - Assert.Equal(6, list[1]); - Assert.Equal(7, list[2]); - } - - [Fact] - public void ProjectionWithSet() - { - IExpression expression = new SpelExpressionParser().ParseRaw("#testList.![Wrapper.Value]"); - var context = new StandardEvaluationContext(); - context.SetVariable("testList", IntegerTestBean.CreateSet()); - object value = expression.GetValue(context); - bool condition = value is List; - Assert.True(condition); - var list = (List)value; - Assert.Equal(3, list.Count); - Assert.Equal(5, list[0]); - Assert.Equal(6, list[1]); - Assert.Equal(7, list[2]); - } - - [Fact] - public void ProjectionWithIEnumerable() - { - IExpression expression = new SpelExpressionParser().ParseRaw("#testList.![Wrapper.Value]"); - var context = new StandardEvaluationContext(); - context.SetVariable("testList", IntegerTestBean.CreateIterable()); - object value = expression.GetValue(context); - bool condition = value is List; - Assert.True(condition); - var list = (List)value; - Assert.Equal(3, list.Count); - Assert.Equal(5, list[0]); - Assert.Equal(6, list[1]); - Assert.Equal(7, list[2]); - } - - [Fact] - public void ProjectionWithArray() - { - IExpression expression = new SpelExpressionParser().ParseRaw("#testArray.![Wrapper.Value]"); - var context = new StandardEvaluationContext(); - context.SetVariable("testArray", IntegerTestBean.CreateArray()); - object value = expression.GetValue(context); - Assert.True(value.GetType().IsArray); - var typedValue = new TypedValue(value); - Assert.Equal(typeof(ValueType), typedValue.TypeDescriptor.GetElementType()); - var array = (ValueType[])value; - Assert.Equal(3, array.Length); - Assert.Equal(5, array[0]); - Assert.Equal(5.9f, array[1]); - Assert.Equal(7, array[2]); - } - - public sealed class ListTestBean - { - public List Integers { get; } = new(); - - public ListTestBean() - { - for (int i = 0; i < 10; i++) - { - Integers.Add(i); - } - } - } - - public sealed class SetTestBean - { - public ISet Integers { get; } = new HashSet(); - - public SetTestBean() - { - for (int i = 0; i < 10; i++) - { - Integers.Add(i); - } - } - } - - public sealed class EnumerableTestBean - { - private readonly ISet _integers = new HashSet(); - - public IEnumerable Integers => _integers; - - public EnumerableTestBean() - { - for (int i = 0; i < 10; i++) - { - _integers.Add(i); - } - } - } - - public sealed class ArrayTestBean - { - public int[] Integers { get; } = new int[10]; - - public ArrayTestBean() - { - for (int i = 0; i < 10; i++) - { - Integers[i] = i; - } - } - } - - public sealed class MapTestBean - { - public Dictionary Colors { get; } = new(); - - public MapTestBean() - { - Colors.Add("red", "rot"); - Colors.Add("brown", "braun"); - Colors.Add("blue", "blau"); - Colors.Add("yellow", "gelb"); - Colors.Add("beige", "beige"); - } - } - - public sealed class IntegerTestBean - { - public IntegerWrapper Wrapper { get; } - - public IntegerTestBean(float value) - { - Wrapper = new IntegerWrapper(value); - } - - public IntegerTestBean(int value) - { - Wrapper = new IntegerWrapper(value); - } - - public static List CreateList() - { - var list = new List(); - - for (int i = 0; i < 3; i++) - { - list.Add(new IntegerTestBean(i + 5)); - } - - return list; - } - - public static ISet CreateSet() - { - var set = new HashSet(); - - for (int i = 0; i < 3; i++) - { - set.Add(new IntegerTestBean(i + 5)); - } - - return set; - } - - public static IEnumerable CreateIterable() - { - ISet set = CreateSet(); - return set; - } - - public static IntegerTestBean[] CreateArray() - { - var array = new IntegerTestBean[3]; - - for (int i = 0; i < 3; i++) - { - if (i == 1) - { - array[i] = new IntegerTestBean(5.9f); - } - else - { - array[i] = new IntegerTestBean(i + 5); - } - } - - return array; - } - } - - public sealed class IntegerWrapper - { - private readonly int? _int; - private readonly float? _float; - - public object Value - { - get - { - if (_int.HasValue) - { - return _int.Value; - } - - return _float.Value; - } - } - - public IntegerWrapper(float value) - { - _float = value; - } - - public IntegerWrapper(int value) - { - _int = value; - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SetValueTests.cs b/src/Common/test/Common.Expression.Test/Spring/SetValueTests.cs deleted file mode 100644 index 6996c81034..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SetValueTests.cs +++ /dev/null @@ -1,283 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestResources; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class SetValueTests : AbstractExpressionTests -{ - private static readonly bool IsDebug = bool.Parse(bool.FalseString); - - [Fact] - public void TestSetProperty() - { - SetValue("WonNobelPrize", true); - } - - [Fact] - public void TestSetNestedProperty() - { - SetValue("PlaceOfBirth.City", "Wien"); - } - - [Fact] - public void TestSetArrayElementValue() - { - SetValue("Inventions[0]", "Just the telephone"); - } - - [Fact] - public void TestErrorCase() - { - SetValueExpectError("3=4", null); - } - - [Fact] - public void TestSetElementOfNull() - { - SetValueExpectError($"new {typeof(Inventor).FullName}().Inventions[1]", SpelMessage.CannotIndexIntoNullValue); - } - - [Fact] - public void TestSetArrayElementValueAllPrimitiveTypes() - { - SetValue("ArrayContainer.Integers[1]", 3); - SetValue("ArrayContainer.Floats[1]", 3.0f); - SetValue("ArrayContainer.Booleans[1]", false); - SetValue("ArrayContainer.Doubles[1]", 3.4d); - SetValue("ArrayContainer.Shorts[1]", (short)3); - SetValue("ArrayContainer.Longs[1]", 3L); - SetValue("ArrayContainer.Bytes[1]", (byte)3); - SetValue("ArrayContainer.Chars[1]", (char)3); - } - - [Fact] - public void TestIsWritableForInvalidExpressions_SPR10610() - { - StandardEvaluationContext lContext = TestScenarioCreator.GetTestEvaluationContext(); - - // PROPERTYORFIELDREFERENCE - // Non existent field (or property): - IExpression e1 = Parser.ParseExpression("ArrayContainer.wibble"); - Assert.False(e1.IsWritable(lContext)); - - IExpression e2 = Parser.ParseExpression("ArrayContainer.wibble.foo"); - Assert.Throws(() => e2.IsWritable(lContext)); - - // org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 15): Property or field 'wibble' cannot be found on object of type 'org.springframework.expression.spel.Testresources.ArrayContainer' - maybe not public? - // at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:225) - // VARIABLE - // the variable does not exist (but that is OK, we should be writable) - IExpression e3 = Parser.ParseExpression("#madeup1"); - Assert.True(e3.IsWritable(lContext)); - - IExpression e4 = Parser.ParseExpression("#madeup2.bar"); // compound expression - Assert.False(e4.IsWritable(lContext)); - - // INDEXER - // non existent indexer (wibble made up) - IExpression e5 = Parser.ParseExpression("ArrayContainer.wibble[99]"); - Assert.Throws(() => e5.IsWritable(lContext)); - - // non existent indexer (index via a string) - IExpression e6 = Parser.ParseExpression("ArrayContainer.ints['abc']"); - Assert.Throws(() => e6.IsWritable(lContext)); - } - - [Fact] - public void TestSetArrayElementValueAllPrimitiveTypesErrors() - { - // none of these sets are possible due to (expected) conversion problems - SetValueExpectError("ArrayContainer.Integers[1]", "wibble"); - SetValueExpectError("ArrayContainer.Floats[1]", "dribble"); - SetValueExpectError("ArrayContainer.Booleans[1]", "nein"); - SetValueExpectError("ArrayContainer.Bytes[1]", "NaB"); - SetValueExpectError("ArrayContainer.Chars[1]", "NaC"); - } - - [Fact] - public void TestSetArrayElementNestedValue() - { - SetValue("PlacesLived[0].City", "Wien"); - } - - [Fact] - public void TestSetListElementValue() - { - SetValue("PlacesLivedList[0]", new PlaceOfBirth("Wien")); - } - - [Fact] - public void TestSetGenericListElementValueTypeCoersion() - { - SetValue("PlacesLivedList[0]", "Wien"); - } - - [Fact] - public void TestSetGenericListElementValueTypeCoersionOk() - { - SetValue("BoolList[0]", "true", true); - } - - [Fact] - public void TestSetListElementNestedValue() - { - SetValue("PlacesLived[0].City", "Wien"); - } - - [Fact] - public void TestSetArrayElementInvalidIndex() - { - SetValueExpectError("PlacesLived[23]", "Wien"); - SetValueExpectError("PlacesLivedList[23]", "Wien"); - } - - [Fact] - public void TestSetMapElements() - { - SetValue("TestDictionary['montag']", "lundi"); - } - - [Fact] - public void TestIndexingIntoUnsupportedType() - { - SetValueExpectError("'hello'[3]", 'p'); - } - - [Fact] - public void TestSetPropertyTypeCoersion() - { - SetValue("PublicBoolean", "true", true); - } - - [Fact] - public void TestSetPropertyTypeCoersionThroughSetter() - { - SetValue("SomeProperty", "true", true); - } - - [Fact] - public void TestAssign() - { - StandardEvaluationContext eContext = TestScenarioCreator.GetTestEvaluationContext(); - IExpression e = Parse("PublicName='Andy'"); - Assert.False(e.IsWritable(eContext)); - Assert.Equal("Andy", e.GetValue(eContext)); - } - - /* - * Testing the coercion of both the keys and the values to the correct type - */ - [Fact] - public void TestSetGenericMapElementRequiresCoercion() - { - StandardEvaluationContext eContext = TestScenarioCreator.GetTestEvaluationContext(); - IExpression e = Parse("MapOfStringToBoolean[42]"); - Assert.Null(e.GetValue(eContext)); - - // Key should be coerced to string representation of 42 - e.SetValue(eContext, "true"); - - // All keys should be strings - var ks = Parse("MapOfStringToBoolean.Keys").GetValue(eContext); - - foreach (object key in ks) - { - Assert.IsType(key); - } - - // All values should be booleans - var vs = Parse("MapOfStringToBoolean.Values").GetValue(eContext); - - foreach (object val in vs) - { - Assert.IsType(val); - } - - // One final Test check coercion on the key for a map lookup - bool o = e.GetValue(eContext); - Assert.True(o); - } - - private void SetValueExpectError(string expression, object value) - { - IExpression e = Parser.ParseExpression(expression); - Assert.NotNull(e); - - if (IsDebug) - { - SpelUtilities.PrintAbstractSyntaxTree(Console.Out, e); - } - - StandardEvaluationContext lContext = TestScenarioCreator.GetTestEvaluationContext(); - Assert.Throws(() => e.SetValue(lContext, value)); - } - - private void SetValue(string expression, object value) - { - try - { - IExpression e = Parser.ParseExpression(expression); - Assert.NotNull(e); - - if (IsDebug) - { - SpelUtilities.PrintAbstractSyntaxTree(Console.Out, e); - } - - StandardEvaluationContext lContext = TestScenarioCreator.GetTestEvaluationContext(); - Assert.True(e.IsWritable(lContext)); - e.SetValue(lContext, value); - Assert.Equal(value, e.GetValue(lContext, value.GetType())); - } - catch (EvaluationException ex) - { - throw new Exception($"Unexpected Exception: {ex.Message}", ex); - } - catch (ParseException ex) - { - throw new Exception($"Unexpected Exception: {ex.Message}", ex); - } - } - - private void SetValue(string expression, object value, object expectedValue) - { - try - { - IExpression e = Parser.ParseExpression(expression); - Assert.NotNull(e); - - if (IsDebug) - { - SpelUtilities.PrintAbstractSyntaxTree(Console.Out, e); - } - - StandardEvaluationContext lContext = TestScenarioCreator.GetTestEvaluationContext(); - Assert.True(e.IsWritable(lContext)); - e.SetValue(lContext, value); - object a = expectedValue; - object b = e.GetValue(lContext); - Assert.Equal(b, a); - } - catch (EvaluationException ex) - { - throw new Exception($"Unexpected Exception: {ex.Message}", ex); - } - catch (ParseException ex) - { - throw new Exception($"Unexpected Exception: {ex.Message}", ex); - } - } - - private IExpression Parse(string expressionString) - { - return Parser.ParseExpression(expressionString); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SpelCompilationCoverageTests.cs b/src/Common/test/Common.Expression.Test/Spring/SpelCompilationCoverageTests.cs deleted file mode 100644 index 043a705511..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SpelCompilationCoverageTests.cs +++ /dev/null @@ -1,7065 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Globalization; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Ast; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestData; -using Xunit; - -// ReSharper disable InconsistentNaming - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class SpelCompilationCoverageTests : AbstractExpressionTests -{ - private const string InvariantCulturePropertyAccess = "T(System.Globalization.CultureInfo).InvariantCulture"; - private IExpression _expression; - - [Fact] - public void TypeReference() - { - _expression = Parse("T(String)"); - Assert.Equal(typeof(string), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(string), _expression.GetValue()); - - _expression = Parse("T(System.IO.IOException)"); - Assert.Equal(typeof(IOException), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(IOException), _expression.GetValue()); - - _expression = Parse("T(System.IO.IOException[])"); - Assert.Equal(typeof(IOException[]), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(IOException[]), _expression.GetValue()); - - _expression = Parse("T(int[][])"); - Assert.Equal(typeof(int[][]), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(int[][]), _expression.GetValue()); - - _expression = Parse("T(int)"); - Assert.Equal(typeof(int), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(int), _expression.GetValue()); - - _expression = Parse("T(byte)"); - Assert.Equal(typeof(byte), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(byte), _expression.GetValue()); - - _expression = Parse("T(char)"); - Assert.Equal(typeof(char), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(char), _expression.GetValue()); - - _expression = Parse("T(short)"); - Assert.Equal(typeof(short), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(short), _expression.GetValue()); - - _expression = Parse("T(long)"); - Assert.Equal(typeof(long), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(long), _expression.GetValue()); - - _expression = Parse("T(float)"); - Assert.Equal(typeof(float), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(float), _expression.GetValue()); - - _expression = Parse("T(double)"); - Assert.Equal(typeof(double), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(double), _expression.GetValue()); - - _expression = Parse("T(boolean)"); - Assert.Equal(typeof(bool), _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(typeof(bool), _expression.GetValue()); - - _expression = Parse("T(Missing)"); - AssertGetValueFail(_expression); - AssertCantCompile(_expression); - } - - [Fact] - public void OperatorInstanceOf() - { - _expression = Parse("'xyz' instanceof T(String)"); - Assert.True((bool)_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True((bool)_expression.GetValue()); - - _expression = Parse("'xyz' instanceof T(int)"); - Assert.False((bool)_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False((bool)_expression.GetValue()); - - var list = new ArrayList(); - _expression = Parse("#root instanceof T(System.Collections.IList)"); - Assert.True((bool)_expression.GetValue(list)); - AssertCanCompile(_expression); - Assert.True((bool)_expression.GetValue(list)); - - var arrayOfLists = new IList[] - { - new ArrayList() - }; - - _expression = Parse("#root instanceof T(System.Collections.IList[])"); - Assert.True((bool)_expression.GetValue(arrayOfLists)); - AssertCanCompile(_expression); - Assert.True((bool)_expression.GetValue(arrayOfLists)); - - int[] intArray = - { - 1, - 2, - 3 - }; - - _expression = Parse("#root instanceof T(int[])"); - Assert.True((bool)_expression.GetValue(intArray)); - AssertCanCompile(_expression); - Assert.True((bool)_expression.GetValue(intArray)); - - const string root1 = null; - _expression = Parse("#root instanceof T(System.Int32)"); - Assert.False((bool)_expression.GetValue(root1)); - AssertCanCompile(_expression); - Assert.False((bool)_expression.GetValue(root1)); - - // root still null - _expression = Parse("#root instanceof T(System.Object)"); - Assert.False((bool)_expression.GetValue(root1)); - AssertCanCompile(_expression); - Assert.False((bool)_expression.GetValue(root1)); - - const string root2 = "howdy"; - _expression = Parse("#root instanceof T(System.Object)"); - Assert.True((bool)_expression.GetValue(root2)); - AssertCanCompile(_expression); - Assert.True((bool)_expression.GetValue(root2)); - } - - [Fact] - public void OperatorInstanceOf_SPR14250() - { - // primitive left operand - should get boxed, return true - _expression = Parse("3 instanceof T(System.Int32)"); - Assert.True((bool)_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True((bool)_expression.GetValue()); - - // primitive left operand - should get boxed, return false - _expression = Parse("3 instanceof T(String)"); - Assert.False((bool)_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False((bool)_expression.GetValue()); - - // primitive left operand - should get boxed, return false - _expression = Parse("3.0d instanceof T(System.Int32)"); - Assert.False((bool)_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False((bool)_expression.GetValue()); - - // primitive left operand - should get boxed, return false - _expression = Parse("3.0d instanceof T(System.Double)"); - Assert.True((bool)_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True((bool)_expression.GetValue()); - - // Only when the right hand operand is a direct type reference - // will it be compilable. - var ctx = new StandardEvaluationContext(); - ctx.SetVariable("foo", typeof(string)); - _expression = Parse("3 instanceof #foo"); - Assert.False((bool)_expression.GetValue(ctx)); - AssertCantCompile(_expression); - - // use of primitive as type for instanceof check - compilable - // but always false - _expression = Parse("3 instanceof T(int)"); - Assert.True((bool)_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True((bool)_expression.GetValue()); - - _expression = Parse("3 instanceof T(long)"); - Assert.False((bool)_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False((bool)_expression.GetValue()); - } - - [Fact] - public void StringLiteral() - { - _expression = Parser.ParseExpression("'abcde'"); - Assert.Equal("abcde", _expression.GetValue(new TestClass1())); - AssertCanCompile(_expression); - string resultC = _expression.GetValue(new TestClass1()); - Assert.Equal("abcde", resultC); - Assert.Equal("abcde", _expression.GetValue()); - Assert.Equal("abcde", _expression.GetValue()); - Assert.Equal("abcde", _expression.GetValue(new StandardEvaluationContext())); - _expression = Parser.ParseExpression("\"abcde\""); - AssertCanCompile(_expression); - Assert.Equal("abcde", _expression.GetValue()); - } - - [Fact] - public void NullLiteral() - { - _expression = Parser.ParseExpression("null"); - object resultI = _expression.GetValue(new TestClass1()); - AssertCanCompile(_expression); - object resultC = _expression.GetValue(new TestClass1()); - Assert.Null(resultI); - Assert.Null(resultC); - } - - [Fact] - public void RealLiteral() - { - _expression = Parser.ParseExpression("3.4d"); - double resultI = _expression.GetValue(new TestClass1()); - AssertCanCompile(_expression); - double resultC = _expression.GetValue(new TestClass1()); - Assert.Equal(3.4d, resultI); - Assert.Equal(3.4d, resultC); - } - - [Fact] - public void InlineList() - { - _expression = Parser.ParseExpression("'abcde'.Substring({1,3,4}[0])"); - object o = _expression.GetValue(); - Assert.Equal("bcde", o); - AssertCanCompile(_expression); - o = _expression.GetValue(); - Assert.Equal("bcde", o); - - _expression = Parser.ParseExpression("{'abc','def'}"); - var l = _expression.GetValue(); - Assert.Equal(2, l.Count); - Assert.Equal("abc", l[0]); - Assert.Equal("def", l[1]); - AssertCanCompile(_expression); - l = _expression.GetValue(); - Assert.Equal(2, l.Count); - Assert.Equal("abc", l[0]); - Assert.Equal("def", l[1]); - - _expression = Parser.ParseExpression("{'abc','def'}[0]"); - o = _expression.GetValue(); - Assert.Equal("abc", o); - AssertCanCompile(_expression); - o = _expression.GetValue(); - Assert.Equal("abc", o); - - _expression = Parser.ParseExpression("{'abcde','ijklm'}[0].Substring({1,3,4}[0])"); - o = _expression.GetValue(); - Assert.Equal("bcde", o); - AssertCanCompile(_expression); - o = _expression.GetValue(); - Assert.Equal("bcde", o); - - _expression = Parser.ParseExpression("{'abcde','ijklm'}[0].Substring({1,3,4}[0],{1,3,4}[1])"); - o = _expression.GetValue(); - Assert.Equal("bcd", o); - AssertCanCompile(_expression); - o = _expression.GetValue(); - Assert.Equal("bcd", o); - } - - [Fact] - public void NestedInlineLists() - { - _expression = Parser.ParseExpression("{{1,2,3},{4,5,6},{7,8,9}}"); - var o = _expression.GetValue(); - Assert.Equal(3, o.Count); - - var o1 = o[0] as IList; - Assert.Equal(3, o1.Count); - Assert.Equal(3, o1[2]); - - var o2 = o[1] as IList; - Assert.Equal(3, o2.Count); - Assert.Equal(6, o2[2]); - - var o3 = o[2] as IList; - Assert.Equal(3, o3.Count); - Assert.Equal(9, o3[2]); - - AssertCanCompile(_expression); - o = _expression.GetValue(); - Assert.Equal(3, o.Count); - - o1 = o[0] as IList; - Assert.Equal(3, o1.Count); - Assert.Equal(3, o1[2]); - - o2 = o[1] as IList; - Assert.Equal(3, o2.Count); - Assert.Equal(6, o2[2]); - - o3 = o[2] as IList; - Assert.Equal(3, o3.Count); - Assert.Equal(9, o3[2]); - - _expression = Parser.ParseExpression("{{1,2,3},{4,5,6},{7,8,9}}.Count"); - int c = _expression.GetValue(); - Assert.Equal(3, c); - AssertCanCompile(_expression); - Assert.Equal(3, c); - - _expression = Parser.ParseExpression("{{1,2,3},{4,5,6},{7,8,9}}[1][0]"); - int n = _expression.GetValue(); - Assert.Equal(4, n); - AssertCanCompile(_expression); - Assert.Equal(4, n); - - _expression = Parser.ParseExpression("{{1,2,3},'abc',{7,8,9}}[1]"); - object obj = _expression.GetValue(); - Assert.Equal("abc", obj); - AssertCanCompile(_expression); - Assert.Equal("abc", obj); - - _expression = Parser.ParseExpression("'abcde'.Substring({{1,3},1,3,4}[0][1])"); - obj = _expression.GetValue(); - Assert.Equal("de", obj); - AssertCanCompile(_expression); - Assert.Equal("de", obj); - - _expression = Parser.ParseExpression("'abcde'.Substring({{1,3},1,3,4}[1])"); - obj = _expression.GetValue(); - Assert.Equal("bcde", obj); - AssertCanCompile(_expression); - Assert.Equal("bcde", obj); - - _expression = Parser.ParseExpression("{'abc',{'def','ghi'}}"); - var l = _expression.GetValue(); - Assert.Equal(2, l.Count); - AssertCanCompile(_expression); - l = _expression.GetValue(); - Assert.Equal(2, l.Count); - - _expression = Parser.ParseExpression("{'abcde',{'ijklm','nopqr'}}[0].Substring({1,3,4}[0])"); - obj = _expression.GetValue(); - Assert.Equal("bcde", obj); - AssertCanCompile(_expression); - Assert.Equal("bcde", obj); - - _expression = Parser.ParseExpression("{'abcde',{'ijklm','nopqr'}}[1][0].Substring({1,3,4}[0])"); - obj = _expression.GetValue(); - Assert.Equal("jklm", obj); - AssertCanCompile(_expression); - Assert.Equal("jklm", obj); - - _expression = Parser.ParseExpression("{'abcde',{'ijklm','nopqr'}}[1][1].Substring({1,3,4}[0],{1,3,4}[1])"); - obj = _expression.GetValue(); - Assert.Equal("opq", obj); - AssertCanCompile(_expression); - Assert.Equal("opq", obj); - } - - [Fact] - public void IntLiteral() - { - _expression = Parser.ParseExpression("42"); - int resultI = _expression.GetValue(new TestClass1()); - AssertCanCompile(_expression); - int resultC = _expression.GetValue(new TestClass1()); - Assert.Equal(42, resultI); - Assert.Equal(42, resultC); - - _expression = Parser.ParseExpression("0"); - AssertCanCompile(_expression); - Assert.Equal(0, _expression.GetValue()); - _expression = Parser.ParseExpression("2"); - AssertCanCompile(_expression); - Assert.Equal(2, _expression.GetValue()); - _expression = Parser.ParseExpression("7"); - AssertCanCompile(_expression); - Assert.Equal(7, _expression.GetValue()); - } - - [Fact] - public void LongLiteral() - { - _expression = Parser.ParseExpression("99L"); - long resultI = _expression.GetValue(new TestClass1()); - AssertCanCompile(_expression); - long resultC = _expression.GetValue(new TestClass1()); - Assert.Equal(99L, resultI); - Assert.Equal(99L, resultC); - } - - [Fact] - public void BooleanLiteral() - { - _expression = Parser.ParseExpression("True"); - bool resultI = _expression.GetValue(new TestClass1()); - Assert.True(resultI); - AssertCanCompile(_expression); - bool resultC = _expression.GetValue(new TestClass1()); - Assert.True(resultC); - - _expression = Parser.ParseExpression("False"); - resultI = _expression.GetValue(new TestClass1()); - Assert.False(resultI); - AssertCanCompile(_expression); - resultC = _expression.GetValue(new TestClass1()); - Assert.False(resultC); - } - - [Fact] - public void FloatLiteral() - { - _expression = Parser.ParseExpression("3.4f"); - double resultI = _expression.GetValue(new TestClass1()); - AssertCanCompile(_expression); - double resultC = _expression.GetValue(new TestClass1()); - Assert.Equal(3.4f, resultI); - Assert.Equal(3.4f, resultC); - } - - [Fact] - public void OpOr() - { - IExpression expression = Parser.ParseExpression("False or False"); - bool resultI = expression.GetValue(1); - SpelCompiler.Compile(expression); - bool resultC = expression.GetValue(1); - Assert.False(resultI); - Assert.False(resultC); - - expression = Parser.ParseExpression("False or True"); - resultI = expression.GetValue(1); - SpelCompiler.Compile(expression); - resultC = expression.GetValue(1); - Assert.True(resultI); - Assert.True(resultC); - - expression = Parser.ParseExpression("True or False"); - resultI = expression.GetValue(1); - SpelCompiler.Compile(expression); - resultC = expression.GetValue(1); - Assert.True(resultI); - Assert.True(resultC); - - expression = Parser.ParseExpression("True or True"); - resultI = expression.GetValue(1); - SpelCompiler.Compile(expression); - resultC = expression.GetValue(1); - Assert.True(resultI); - Assert.True(resultC); - - var tc = new TestClass4(); - expression = Parser.ParseExpression("GetFalse() or GetTrue()"); - resultI = expression.GetValue(tc); - SpelCompiler.Compile(expression); - resultC = expression.GetValue(tc); - Assert.True(resultI); - Assert.True(resultC); - - // Can't compile this as we aren't going down the getfalse() branch in our evaluation - expression = Parser.ParseExpression("GetTrue() or GetFalse()"); - resultI = expression.GetValue(tc); - Assert.True(resultI); - AssertCantCompile(expression); - - expression = Parser.ParseExpression("A or B"); - tc.A = true; - tc.B = true; - resultI = expression.GetValue(tc); - Assert.True(resultI); - AssertCantCompile(expression); // Haven't yet been into second branch - tc.A = false; - tc.B = true; - resultI = expression.GetValue(tc); - AssertCanCompile(expression); // Now been down both - Assert.True(resultI); - - const bool b = false; - expression = Parse("#root or #root"); - bool resultI2 = expression.GetValue(b); - AssertCanCompile(expression); - Assert.False(resultI2); - Assert.False(expression.GetValue(b)); - } - - [Fact] - public void OpAnd() - { - IExpression expression = Parser.ParseExpression("False and False"); - bool resultI = expression.GetValue(1); - SpelCompiler.Compile(expression); - bool resultC = expression.GetValue(1); - Assert.False(resultI); - Assert.False(resultC); - - expression = Parser.ParseExpression("False and True"); - resultI = expression.GetValue(1); - SpelCompiler.Compile(expression); - resultC = expression.GetValue(1); - Assert.False(resultI); - Assert.False(resultC); - - expression = Parser.ParseExpression("True and False"); - resultI = expression.GetValue(1); - SpelCompiler.Compile(expression); - resultC = expression.GetValue(1); - Assert.False(resultI); - Assert.False(resultC); - - expression = Parser.ParseExpression("True and True"); - resultI = expression.GetValue(1); - SpelCompiler.Compile(expression); - resultC = expression.GetValue(1); - Assert.True(resultI); - Assert.True(resultC); - - var tc = new TestClass4(); - - // Can't compile this as we aren't going down the gettrue() branch in our evaluation - expression = Parser.ParseExpression("GetFalse() and GetTrue()"); - resultI = expression.GetValue(tc); - AssertCantCompile(expression); - Assert.False(resultI); - - expression = Parser.ParseExpression("A and B"); - tc.A = false; - tc.B = false; - resultI = expression.GetValue(tc); - Assert.False(resultI); - AssertCantCompile(expression); // Haven't yet been into second branch - tc.A = true; - tc.B = false; - resultI = expression.GetValue(tc); - AssertCanCompile(expression); // Now been down both - Assert.False(resultI); - tc.A = true; - tc.B = true; - resultI = expression.GetValue(tc); - Assert.True(resultI); - - const bool b = true; - expression = Parse("#root and #root"); - bool resultI2 = expression.GetValue(b); - AssertCanCompile(expression); - Assert.True(resultI2); - Assert.True(expression.GetValue(b)); - } - - [Fact] - public void OperatorNot() - { - _expression = Parser.ParseExpression("!True"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parser.ParseExpression("!False"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parser.ParseExpression("!#root"); - Assert.False(_expression.GetValue(true)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(true)); - - _expression = Parser.ParseExpression("!#root"); - Assert.True(_expression.GetValue(false)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(false)); - } - - [Fact] - public void Ternary() - { - IExpression expression = Parser.ParseExpression("True?'a':'b'"); - string resultI = expression.GetValue(); - AssertCanCompile(expression); - string resultC = expression.GetValue(); - Assert.Equal("a", resultI); - Assert.Equal("a", resultC); - - expression = Parser.ParseExpression("False?'a':'b'"); - resultI = expression.GetValue(); - AssertCanCompile(expression); - resultC = expression.GetValue(); - Assert.Equal("b", resultI); - Assert.Equal("b", resultC); - - expression = Parser.ParseExpression("False?1:'b'"); - AssertCanCompile(expression); - Assert.Equal("b", expression.GetValue()); - - expression = Parser.ParseExpression("(#root and True)?T(int).Parse('1'):T(long).Parse('3')"); - Assert.Equal(1, expression.GetValue(true)); - AssertCantCompile(expression); // Have not gone down false branch - Assert.Equal(3L, expression.GetValue(false)); - AssertCanCompile(expression); - Assert.Equal(3L, expression.GetValue(false)); - Assert.Equal(1, expression.GetValue(true)); - } - - [Fact] - public void TernaryWithBooleanReturn_SPR12271() - { - IExpression expression = Parser.ParseExpression("T(Boolean).Parse('True')?'abc':'def'"); - Assert.Equal("abc", expression.GetValue()); - AssertCanCompile(expression); - Assert.Equal("abc", expression.GetValue()); - - expression = Parser.ParseExpression("T(Boolean).Parse('False')?'abc':'def'"); - Assert.Equal("def", expression.GetValue()); - AssertCanCompile(expression); - Assert.Equal("def", expression.GetValue()); - } - - [Fact] - public void NullSafeFieldPropertyDereferencing_SPR16489() - { - var foh = new FooObjectHolder(); - var context = new StandardEvaluationContext(foh); - - var expression = (SpelExpression)Parser.ParseExpression("Foo?.TheObject"); - Assert.Equal("hello", expression.GetValue(context)); - foh.Foo = null; - Assert.Null(expression.GetValue(context)); - - foh.Foo = new FooObject(); - Assert.Equal("hello", expression.GetValue(context)); - AssertCanCompile(expression); - Assert.Equal("hello", expression.GetValue(context)); - foh.Foo = null; - Assert.Null(expression.GetValue(context)); - - // Static references - expression = (SpelExpression)Parser.ParseExpression("#var?.PropertyA"); - context.SetVariable("var", typeof(StaticsHelper)); - Assert.Equal("sh", expression.GetValue(context).ToString()); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", typeof(StaticsHelper)); - Assert.Equal("sh", expression.GetValue(context).ToString()); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - - // Single size primitive (boolean) - expression = (SpelExpression)Parser.ParseExpression("#var?.A"); - context.SetVariable("var", new TestClass4()); - Assert.False(expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", new TestClass4()); - Assert.False(expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - - // Double slot primitives - expression = (SpelExpression)Parser.ParseExpression("#var?.Four"); - context.SetVariable("var", new Three()); - Assert.Equal(0.04d, expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", new Three()); - Assert.Equal(0.04d, expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - - expression = (SpelExpression)Parser.ParseExpression("#var?.Day"); - context.SetVariable("var", DateTime.UtcNow); - Assert.InRange(expression.GetValue(context), 1, 31); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", DateTime.UtcNow); - Assert.InRange(expression.GetValue(context), 1, 31); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - } - - [Fact] - public void NullSafeMethodChaining_SPR16489() - { - var foh = new FooObjectHolder(); - var context = new StandardEvaluationContext(foh); - - // First non compiled: - var expression = (SpelExpression)Parser.ParseExpression("Foo?.GetObject()"); - Assert.Equal("hello", expression.GetValue(context)); - foh.Foo = null; - Assert.Null(expression.GetValue(context)); - AssertCanCompile(expression); - foh.Foo = new FooObject(); - Assert.Equal("hello", expression.GetValue(context)); - foh.Foo = null; - Assert.Null(expression.GetValue(context)); - - // Static method references - expression = (SpelExpression)Parser.ParseExpression("#var?.MethodA()"); - context.SetVariable("var", typeof(StaticsHelper)); - Assert.Equal("sh", expression.GetValue(context).ToString()); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", typeof(StaticsHelper)); - Assert.Equal("sh", expression.GetValue(context).ToString()); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression)Parser.ParseExpression("#var?.ToString()"); - context.SetVariable("var", 4); - Assert.Equal("4", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", 4); - Assert.Equal("4", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression)Parser.ParseExpression("#var?.ToString()"); - context.SetVariable("var", false); - Assert.Equal("False", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", false); - Assert.Equal("False", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression)Parser.ParseExpression("#var?.ToString()"); - context.SetVariable("var", 5L); - Assert.Equal("5", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", 5L); - Assert.Equal("5", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression)Parser.ParseExpression("#var?.ToString()"); - context.SetVariable("var", (short)10); - Assert.Equal("10", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", (short)10); - Assert.Equal("10", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression)Parser.ParseExpression("#var?.ToString()"); - context.SetVariable("var", 10.0f); - Assert.Equal("10", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", 10.0f); - Assert.Equal("10", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - - // Nullsafe guard on expression element evaluating to primitive/null - expression = (SpelExpression)Parser.ParseExpression("#var?.ToString()"); - context.SetVariable("var", 10.0d); - Assert.Equal("10", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - AssertCanCompile(expression); - context.SetVariable("var", 10.0d); - Assert.Equal("10", expression.GetValue(context)); - context.SetVariable("var", null); - Assert.Null(expression.GetValue()); - } - - [Fact] - public void Elvis() - { - IExpression expression = Parser.ParseExpression("'a'?:'b'"); - string resultI = expression.GetValue(); - AssertCanCompile(expression); - string resultC = expression.GetValue(); - Assert.Equal("a", resultI); - Assert.Equal("a", resultC); - - expression = Parser.ParseExpression("null?:'a'"); - resultI = expression.GetValue(); - AssertCanCompile(expression); - resultC = expression.GetValue(); - Assert.Equal("a", resultI); - Assert.Equal("a", resultC); - - const string s = "abc"; - expression = Parser.ParseExpression("#root?:'b'"); - AssertCantCompile(expression); - resultI = expression.GetValue(s); - Assert.Equal("abc", resultI); - AssertCanCompile(expression); - resultC = expression.GetValue(s); - Assert.Equal("abc", resultC); - } - - [Fact] - public void VariableReference_Root() - { - const string s = "hello"; - IExpression expression = Parser.ParseExpression("#root"); - string resultI = expression.GetValue(s); - AssertCanCompile(expression); - string resultC = expression.GetValue(s); - Assert.Equal("hello", resultI); - Assert.Equal("hello", resultC); - - expression = Parser.ParseExpression("#root"); - int i = expression.GetValue(42); - Assert.Equal(42, i); - AssertCanCompile(expression); - i = expression.GetValue(42); - Assert.Equal(42, i); - } - - [Fact] - public void CompiledExpressionShouldWorkWhenUsingCustomFunctionWithVarargs() - { - StandardEvaluationContext context = null; - - // Here the target method takes Object... and we are passing a string - _expression = Parser.ParseExpression("#DoFormat('hey {0}', 'there')"); - context = new StandardEvaluationContext(); - - context.RegisterFunction("DoFormat", typeof(DelegatingStringFormat).GetMethod(nameof(DelegatingStringFormat.Format), new[] - { - typeof(string), - typeof(object[]) - })); - - ((SpelExpression)_expression).EvaluationContext = context; - - Assert.Equal("hey there", _expression.GetValue()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("hey there", _expression.GetValue()); - - _expression = Parser.ParseExpression("#DoFormat([0], 'there')"); - - context = new StandardEvaluationContext(new object[] - { - "hey {0}" - }); - - context.RegisterFunction("DoFormat", typeof(DelegatingStringFormat).GetMethod(nameof(DelegatingStringFormat.Format), new[] - { - typeof(string), - typeof(object[]) - })); - - ((SpelExpression)_expression).EvaluationContext = context; - - Assert.Equal("hey there", _expression.GetValue()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("hey there", _expression.GetValue()); - - _expression = Parser.ParseExpression("#DoFormat([0], #arg)"); - - context = new StandardEvaluationContext(new object[] - { - "hey {0}" - }); - - context.RegisterFunction("DoFormat", typeof(DelegatingStringFormat).GetMethod(nameof(DelegatingStringFormat.Format), new[] - { - typeof(string), - typeof(object[]) - })); - - context.SetVariable("arg", "there"); - ((SpelExpression)_expression).EvaluationContext = context; - - Assert.Equal("hey there", _expression.GetValue()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("hey there", _expression.GetValue()); - } - - [Fact] - public void FunctionReference() - { - var ctx = new StandardEvaluationContext(); - - MethodInfo m = GetType().GetMethod(nameof(Concat), new[] - { - typeof(string), - typeof(string) - }); - - ctx.SetVariable("Concat", m); - - _expression = Parser.ParseExpression("#Concat('a','b')"); - Assert.Equal("ab", _expression.GetValue(ctx)); - AssertCanCompile(_expression); - Assert.Equal("ab", _expression.GetValue(ctx)); - - _expression = Parser.ParseExpression("#Concat(#Concat('a','b'),'c').get_Chars(1)"); - Assert.Equal('b', _expression.GetValue(ctx)); - AssertCanCompile(_expression); - Assert.Equal('b', _expression.GetValue(ctx)); - - _expression = Parser.ParseExpression("#Concat(#a,#b)"); - ctx.SetVariable("a", "foo"); - ctx.SetVariable("b", "bar"); - Assert.Equal("foobar", _expression.GetValue(ctx)); - AssertCanCompile(_expression); - Assert.Equal("foobar", _expression.GetValue(ctx)); - ctx.SetVariable("b", "boo"); - Assert.Equal("fooboo", _expression.GetValue(ctx)); - - m = typeof(Math).GetMethod(nameof(Math.Pow), new[] - { - typeof(double), - typeof(double) - }); - - ctx.SetVariable("kapow", m); - _expression = Parser.ParseExpression("#kapow(2.0d,2.0d)"); - Assert.Equal(4.0d, _expression.GetValue(ctx)); - AssertCanCompile(_expression); - Assert.Equal(4.0d, _expression.GetValue(ctx)); - } - - [Fact] - public void FunctionReferenceVisibility_SPR12359() - { - // Confirms visibility of what is being called. - var context = new StandardEvaluationContext(new object[] - { - "1" - }); - - MethodInfo m = typeof(SomeCompareMethod).GetMethod("PrivateCompare", BindingFlags.Static | BindingFlags.NonPublic, null, new[] - { - typeof(object), - typeof(object) - }, null); - - context.RegisterFunction("doCompare", m); - context.SetVariable("arg", "2"); - - // type nor method are public - _expression = Parser.ParseExpression("#doCompare([0],#arg)"); - Assert.Equal("-1", _expression.GetValue(context).ToString()); - AssertCantCompile(_expression); - - // type not public but method is - context = new StandardEvaluationContext(new object[] - { - "1" - }); - - m = typeof(SomeCompareMethod).GetMethod(nameof(SomeCompareMethod.PublicCompare), BindingFlags.Static | BindingFlags.Public, null, new[] - { - typeof(object), - typeof(object) - }, null); - - context.RegisterFunction("doCompare", m); - context.SetVariable("arg", "2"); - _expression = Parser.ParseExpression("#doCompare([0],#arg)"); - Assert.Equal("-1", _expression.GetValue(context).ToString()); - AssertCantCompile(_expression); - } - - [Fact] - public void FunctionReferenceNonCompilableArguments_SPR12359() - { - var context = new StandardEvaluationContext(new object[] - { - "1" - }); - - MethodInfo m = typeof(SomeCompareMethod2).GetMethod(nameof(SomeCompareMethod2.Negate), BindingFlags.Static | BindingFlags.Public, null, new[] - { - typeof(int) - }, null); - - context.RegisterFunction("negate", m); - context.SetVariable("arg", "2"); - - int[] integers = - { - 1, - 2, - 3 - }; - - context.SetVariable("integers", integers); - - _expression = Parser.ParseExpression("#negate(#integers.?[#this<2][0])"); - Assert.Equal("-1", _expression.GetValue(context).ToString()); - - // Selection isn't compilable. - Assert.False(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - } - - [Fact] - public void FunctionReferenceVarargs_SPR12359() - { - var context = new StandardEvaluationContext(); - - context.RegisterFunction("append", typeof(SomeCompareMethod2).GetMethod(nameof(SomeCompareMethod2.Append), new[] - { - typeof(string[]) - })); - - context.RegisterFunction("append2", typeof(SomeCompareMethod2).GetMethod(nameof(SomeCompareMethod2.Append2), new[] - { - typeof(object[]) - })); - - context.RegisterFunction("append3", typeof(SomeCompareMethod2).GetMethod(nameof(SomeCompareMethod2.Append3), new[] - { - typeof(string[]) - })); - - context.RegisterFunction("append4", typeof(SomeCompareMethod2).GetMethod(nameof(SomeCompareMethod2.Append4), new[] - { - typeof(string), - typeof(string[]) - })); - - context.RegisterFunction("appendChar", typeof(SomeCompareMethod2).GetMethod(nameof(SomeCompareMethod2.AppendChar), new[] - { - typeof(char[]) - })); - - context.RegisterFunction("sum", typeof(SomeCompareMethod2).GetMethod(nameof(SomeCompareMethod2.Sum), new[] - { - typeof(int[]) - })); - - context.RegisterFunction("sumDouble", typeof(SomeCompareMethod2).GetMethod(nameof(SomeCompareMethod2.SumDouble), new[] - { - typeof(double[]) - })); - - context.RegisterFunction("sumFloat", typeof(SomeCompareMethod2).GetMethod(nameof(SomeCompareMethod2.SumFloat), new[] - { - typeof(float[]) - })); - - context.SetVariable("stringArray", new[] - { - "x", - "y", - "z" - }); - - context.SetVariable("intArray", new[] - { - 5, - 6, - 9 - }); - - context.SetVariable("doubleArray", new[] - { - 5.0d, - 6.0d, - 9.0d - }); - - context.SetVariable("floatArray", new[] - { - 5.0f, - 6.0f, - 9.0f - }); - - _expression = Parser.ParseExpression("#append('a','b','c')"); - Assert.Equal("abc", _expression.GetValue(context).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("abc", _expression.GetValue(context).ToString()); - - _expression = Parser.ParseExpression("#append('a')"); - Assert.Equal("a", _expression.GetValue(context).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("a", _expression.GetValue(context).ToString()); - - _expression = Parser.ParseExpression("#append()"); - Assert.Equal(string.Empty, _expression.GetValue(context).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(string.Empty, _expression.GetValue(context).ToString()); - - _expression = Parser.ParseExpression("#append(#stringArray)"); - Assert.Equal("xyz", _expression.GetValue(context).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("xyz", _expression.GetValue(context).ToString()); - - // This is a methodreference invocation, to compare with functionreference - _expression = Parser.ParseExpression("Append(#stringArray)"); - Assert.Equal("xyz", _expression.GetValue(context, new SomeCompareMethod2()).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("xyz", _expression.GetValue(context, new SomeCompareMethod2()).ToString()); - - _expression = Parser.ParseExpression("#append2('a','b','c')"); - Assert.Equal("abc", _expression.GetValue(context).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("abc", _expression.GetValue(context).ToString()); - - _expression = Parser.ParseExpression("#append2('a','b','c')"); - Assert.Equal("abc", _expression.GetValue(context).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("abc", _expression.GetValue(context).ToString()); - - _expression = Parser.ParseExpression("Append2('a','b')"); - Assert.Equal("ab", _expression.GetValue(context, new SomeCompareMethod2()).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("ab", _expression.GetValue(context, new SomeCompareMethod2()).ToString()); - - _expression = Parser.ParseExpression("#append2('a','b')"); - Assert.Equal("ab", _expression.GetValue(context).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("ab", _expression.GetValue(context).ToString()); - - _expression = Parser.ParseExpression("#append2()"); - Assert.Equal(string.Empty, _expression.GetValue(context).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(string.Empty, _expression.GetValue(context).ToString()); - - _expression = Parser.ParseExpression("#append3(#stringArray)"); - Assert.Equal("xyz", _expression.GetValue(context, new SomeCompareMethod2()).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("xyz", _expression.GetValue(context, new SomeCompareMethod2()).ToString()); - - _expression = Parser.ParseExpression("#sum(1,2,3)"); - Assert.Equal(6, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(6, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sum(2)"); - Assert.Equal(2, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(2, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sum()"); - Assert.Equal(0, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(0, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sum(#intArray)"); - Assert.Equal(20, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(20, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sumDouble(1.0d,2.0d,3.0d)"); - Assert.Equal(6, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(6, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sumDouble(2.0d)"); - Assert.Equal(2, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(2, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sumDouble()"); - Assert.Equal(0, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(0, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sumDouble(#doubleArray)"); - Assert.Equal(20, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(20, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sumFloat(1.0f,2.0f,3.0f)"); - Assert.Equal(6, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(6, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sumFloat(2.0f)"); - Assert.Equal(2, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(2, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sumFloat()"); - Assert.Equal(0, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(0, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#sumFloat(#floatArray)"); - Assert.Equal(20, _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal(20, _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#appendChar('abc'.get_Chars(0),'abc'.get_Chars(1))"); - Assert.Equal("ab", _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("ab", _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#append4('a','b','c')"); - Assert.Equal("a::bc", _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("a::bc", _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#append4('a','b')"); - Assert.Equal("a::b", _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("a::b", _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#append4('a')"); - Assert.Equal("a::", _expression.GetValue(context)); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("a::", _expression.GetValue(context)); - - _expression = Parser.ParseExpression("#append4('a',#stringArray)"); - Assert.Equal("a::xyz", _expression.GetValue(context).ToString()); - Assert.True(((SpelNode)((SpelExpression)_expression).Ast).IsCompilable()); - AssertCanCompile(_expression); - Assert.Equal("a::xyz", _expression.GetValue(context).ToString()); - } - - [Fact] - public void FunctionReferenceVarargs() - { - var ctx = new StandardEvaluationContext(); - - MethodInfo m = GetType().GetMethod(nameof(Join), new[] - { - typeof(string[]) - }); - - ctx.SetVariable("join", m); - _expression = Parser.ParseExpression("#join('a','b','c')"); - Assert.Equal("abc", _expression.GetValue(ctx)); - AssertCanCompile(_expression); - Assert.Equal("abc", _expression.GetValue(ctx)); - } - - [Fact] - public void VariableReferenceUserDefined() - { - var ctx = new StandardEvaluationContext(); - ctx.SetVariable("target", "abc"); - _expression = Parser.ParseExpression("#target"); - Assert.Equal("abc", _expression.GetValue(ctx)); - AssertCanCompile(_expression); - Assert.Equal("abc", _expression.GetValue(ctx)); - ctx.SetVariable("target", "123"); - Assert.Equal("123", _expression.GetValue(ctx)); - ctx.SetVariable("target", 42); - var ex = Assert.Throws(() => _expression.GetValue(ctx)); - Assert.IsType(ex.InnerException); - - ctx.SetVariable("target", "abc"); - _expression = Parser.ParseExpression("#target.get_Chars(0)"); - Assert.Equal('a', _expression.GetValue(ctx)); - AssertCanCompile(_expression); - Assert.Equal('a', _expression.GetValue(ctx)); - ctx.SetVariable("target", "1"); - Assert.Equal('1', _expression.GetValue(ctx)); - ctx.SetVariable("target", 42); - ex = Assert.Throws(() => _expression.GetValue(ctx)); - Assert.IsType(ex.InnerException); - } - - [Fact] - public void OpLt() - { - _expression = Parse("3.0d < 4.0d"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3446.0d < 1123.0d"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3 < 1"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("2 < 4"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3.0f < 1.0f"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("1.0f < 5.0f"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("30L < 30L"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("15L < 20L"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("1 < 3.0d"); - AssertCantCompile(_expression); - - object d = 3.0d; - _expression = Parse("#root<3.0d"); - Assert.False(_expression.GetValue(d)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(d)); - - object i = 3; - _expression = Parse("#root<3"); - Assert.False(_expression.GetValue(i)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(i)); - - object f = 3.0f; - _expression = Parse("#root<3.0f"); - Assert.False(_expression.GetValue(f)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(f)); - - const long l = 300L; - _expression = Parse("#root<300l"); - Assert.False(_expression.GetValue(l)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(l)); - - _expression = Parse("T(int).Parse('3') < 4"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(int).Parse('3') < T(Int32).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("5 < T(int).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) < 4.0f"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) < T(Single).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"5.0f < T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') < 4L"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') < T(long).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("5L < T(long).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) < 4.0d"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) < T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"5.0d < T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - } - - [Fact] - public void OpLE() - { - _expression = Parse("3.0d <= 4.0d"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3446.0d <= 1123.0d"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3 <= 1"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("2 <= 4"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3.0f <= 1.0f"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("1.0f <= 5.0f"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("30L <= 30L"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("15L <= 20L"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("1 <= 3.0d"); - AssertCantCompile(_expression); - - object d = 3.0d; - _expression = Parse("#root<=3.0d"); - Assert.True(_expression.GetValue(d)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(d)); - - object i = 3; - _expression = Parse("#root<=3"); - Assert.True(_expression.GetValue(i)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(i)); - - object f = 3.0f; - _expression = Parse("#root<=3.0f"); - Assert.True(_expression.GetValue(f)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(f)); - - const long l = 300L; - _expression = Parse("#root<=300l"); - Assert.True(_expression.GetValue(l)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(l)); - - _expression = Parse("T(int).Parse('3') <= 4"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(int).Parse('3') <= T(Int32).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("5 <= T(int).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) <= 4.0f"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) <= T(Single).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"5.0f <= T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') <= 4L"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') <= T(long).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("5L <= T(long).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) <= 4.0d"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) <= T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"5.0d <= T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - } - - [Fact] - public void OpGT() - { - _expression = Parse("3.0d > 4.0d"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3446.0d > 1123.0d"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3 > 1"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("2 > 4"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3.0f > 1.0f"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("1.0f > 5.0f"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("30L > 30L"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("15L > 20L"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("1 > 3.0d"); - AssertCantCompile(_expression); - - object d = 3.0d; - _expression = Parse("#root>3.0d"); - Assert.False(_expression.GetValue(d)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(d)); - - object i = 3; - _expression = Parse("#root>3"); - Assert.False(_expression.GetValue(i)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(i)); - - object f = 3.0f; - _expression = Parse("#root>3.0f"); - Assert.False(_expression.GetValue(f)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(f)); - - const long l = 300L; - _expression = Parse("#root>300l"); - Assert.False(_expression.GetValue(l)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(l)); - - _expression = Parse("T(int).Parse('3') > 4"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(int).Parse('3') > T(Int32).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("5 > T(int).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) > 4.0f"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) > T(Single).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"5.0f > T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') > 4L"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') > T(long).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("5L > T(long).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) > 4.0d"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) > T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"5.0d > T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - } - - [Fact] - public void OpGE() - { - _expression = Parse("3.0d >= 4.0d"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3446.0d >= 1123.0d"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3 >= 1"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("2 >= 4"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3.0f >= 1.0f"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("1.0f >= 5.0f"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("30L >= 30L"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("15L >= 20L"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("1 >= 3.0d"); - AssertCantCompile(_expression); - - object d = 3.0d; - _expression = Parse("#root>=3.0d"); - Assert.True(_expression.GetValue(d)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(d)); - - object i = 3; - _expression = Parse("#root>=3"); - Assert.True(_expression.GetValue(i)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(i)); - - object f = 3.0f; - _expression = Parse("#root>=3.0f"); - Assert.True(_expression.GetValue(f)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(f)); - - const long l = 300L; - _expression = Parse("#root>=300l"); - Assert.True(_expression.GetValue(l)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(l)); - - _expression = Parse("T(int).Parse('3') >= 4"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(int).Parse('3') >= T(Int32).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("5 >= T(int).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) >= 4.0f"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) >= T(Single).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"5.0f >= T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') >= 4L"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') >= T(long).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("5L >= T(long).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) >= 4.0d"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) >= T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"5.0d >= T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - } - - [Fact] - public void OpEq() - { - const string value = "35"; - _expression = Parse("#root == 35"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("35 == #root"); - _expression.GetValue(value); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - var tc7 = new TestClass7(); - _expression = Parse("Property == 'UK'"); - Assert.True(_expression.GetValue(tc7)); - TestClass7.Property = null; - Assert.False(_expression.GetValue(tc7)); - AssertCanCompile(_expression); - TestClass7.Reset(); - Assert.True(_expression.GetValue(tc7)); - TestClass7.Property = "UK"; - Assert.True(_expression.GetValue(tc7)); - TestClass7.Reset(); - TestClass7.Property = null; - Assert.False(_expression.GetValue(tc7)); - _expression = Parse("Property == null"); - Assert.True(_expression.GetValue(tc7)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(tc7)); - - _expression = Parse("3.0d == 4.0d"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3446.0d == 3446.0d"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3 == 1"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3 == 3"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3.0f == 1.0f"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("2.0f == 2.0f"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("30L == 30L"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("15L == 20L"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("1 == 3.0d"); - AssertCantCompile(_expression); - - object d = 3.0d; - _expression = Parse("#root==3.0d"); - Assert.True(_expression.GetValue(d)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(d)); - - object i = 3; - _expression = Parse("#root==3"); - Assert.True(_expression.GetValue(i)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(i)); - - object f = 3.0f; - _expression = Parse("#root==3.0f"); - Assert.True(_expression.GetValue(f)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(f)); - - const long l = 300L; - _expression = Parse("#root==300l"); - Assert.True(_expression.GetValue(l)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(l)); - - const bool b = true; - _expression = Parse("#root==true"); - Assert.True(_expression.GetValue(b)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(b)); - - _expression = Parse("T(int).Parse('3') == 4"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(int).Parse('3') == T(Int32).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("5 == T(int).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) == 4.0f"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) == T(Single).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"5.0f == T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') == 4L"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') == T(long).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("5L == T(long).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) == 4.0d"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) == T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"5.0d == T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("False == True"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(boolean).Parse('True') == T(boolean).Parse('True')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(boolean).Parse('True') == True"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("False == T(boolean).Parse('False')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - } - - [Fact] - public void OpNe() - { - _expression = Parse("3.0d != 4.0d"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3446.0d != 3446.0d"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3 != 1"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("3 != 3"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("3.0f != 1.0f"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("2.0f != 2.0f"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("30L != 30L"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("15L != 20L"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("1 != 3.0d"); - AssertCantCompile(_expression); - - object d = 3.0d; - _expression = Parse("#root!=3.0d"); - Assert.False(_expression.GetValue(d)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(d)); - - object i = 3; - _expression = Parse("#root!=3"); - Assert.False(_expression.GetValue(i)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(i)); - - object f = 3.0f; - _expression = Parse("#root!=3.0f"); - Assert.False(_expression.GetValue(f)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(f)); - - const long l = 300L; - _expression = Parse("#root!=300l"); - Assert.False(_expression.GetValue(l)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(l)); - - const bool b = true; - _expression = Parse("#root!=true"); - Assert.False(_expression.GetValue(b)); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(b)); - - _expression = Parse("T(int).Parse('3') != 4"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(int).Parse('3') != T(Int32).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("5 != T(int).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) != 4.0f"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(float).Parse('3.0',{InvariantCulturePropertyAccess}) != T(Single).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"5.0f != T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') != 4L"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(long).Parse('3') != T(long).Parse('3')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("5L != T(long).Parse('3')"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) != 4.0d"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse($"T(double).Parse('3.0',{InvariantCulturePropertyAccess}) != T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse($"5.0d != T(double).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("False != True"); - Assert.True(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue()); - - _expression = Parse("T(boolean).Parse('True') != T(boolean).Parse('True')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("T(boolean).Parse('True') != True"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - - _expression = Parse("False != T(boolean).Parse('False')"); - Assert.False(_expression.GetValue()); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue()); - } - - [Fact] - public void OpNe_SPR14863() - { - var configuration = new SpelParserOptions(SpelCompilerMode.Mixed); - var parser = new SpelExpressionParser(configuration); - IExpression expression = parser.ParseExpression("Data['my-key'] != 'my-value'"); - - var data = new Dictionary - { - { "my-key", "my-value" } - }; - - var context = new StandardEvaluationContext(new MyContext(data)); - Assert.False(expression.GetValue(context)); - AssertCanCompile(expression); - Assert.False(expression.GetValue(context)); - - var ls = new List - { - "foo" - }; - - context = new StandardEvaluationContext(ls); - expression = Parse("get_Item(0) != 'foo'"); - Assert.False(expression.GetValue(context)); - AssertCanCompile(expression); - Assert.False(expression.GetValue(context)); - - ls.RemoveAt(0); - ls.Add("goo"); - Assert.True(expression.GetValue(context)); - } - - [Fact] - public void OpEq_SPR14863() - { - // Exercise the comparator invocation code that runs in - // equalityCheck() (called from interpreted and compiled code) - _expression = Parser.ParseExpression("#aa==#bb"); - var sec = new StandardEvaluationContext(); - var aa = new Apple(1); - var bb = new Apple(2); - sec.SetVariable("aa", aa); - sec.SetVariable("bb", bb); - - bool b = _expression.GetValue(sec); - Assert.Same(bb, aa.GotComparedTo); - Assert.False(b); - bb.SetValue(1); - b = _expression.GetValue(sec); - Assert.Same(bb, aa.GotComparedTo); - Assert.True(b); - - AssertCanCompile(_expression); - - // Similar test with compiled expression - aa = new Apple(99); - bb = new Apple(100); - sec.SetVariable("aa", aa); - sec.SetVariable("bb", bb); - b = _expression.GetValue(sec); - Assert.Same(bb, aa.GotComparedTo); - Assert.False(b); - bb.SetValue(99); - b = _expression.GetValue(sec); - Assert.Same(bb, aa.GotComparedTo); - Assert.True(b); - - var ls = new List - { - "foo" - }; - - var context = new StandardEvaluationContext(ls); - IExpression expression = Parse("get_Item(0) == 'foo'"); - Assert.True(expression.GetValue(context)); - AssertCanCompile(expression); - Assert.True(expression.GetValue(context)); - - ls.RemoveAt(0); - ls.Add("goo"); - Assert.False(expression.GetValue(context)); - } - - [Fact] - public void OpDivide_MixedNumberTypes() - { - var p = new PayloadX(); - - // right is a double - CheckCalc(p, "payload.valueSB/60D", 2d); - CheckCalc(p, "payload.valueBB/60D", 2d); - CheckCalc(p, "payload.valueFB/60D", 2d); - CheckCalc(p, "payload.valueDB/60D", 2d); - CheckCalc(p, "payload.valueJB/60D", 2d); - CheckCalc(p, "payload.valueIB/60D", 2d); - - CheckCalc(p, "payload.valueS/60D", 2d); - CheckCalc(p, "payload.valueB/60D", 2d); - CheckCalc(p, "payload.valueF/60D", 2d); - CheckCalc(p, "payload.valueD/60D", 2d); - CheckCalc(p, "payload.valueJ/60D", 2d); - CheckCalc(p, "payload.valueI/60D", 2d); - - CheckCalc(p, "payload.valueSB/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueBB/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueFB/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueDB/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueJB/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueIB/payload.valueDB60", 2d); - - CheckCalc(p, "payload.valueS/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueB/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueF/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueD/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueJ/payload.valueDB60", 2d); - CheckCalc(p, "payload.valueI/payload.valueDB60", 2d); - - // right is a float - CheckCalc(p, "payload.valueSB/60F", 2F); - CheckCalc(p, "payload.valueBB/60F", 2F); - CheckCalc(p, "payload.valueFB/60F", 2f); - CheckCalc(p, "payload.valueDB/60F", 2d); - CheckCalc(p, "payload.valueJB/60F", 2F); - CheckCalc(p, "payload.valueIB/60F", 2F); - - CheckCalc(p, "payload.valueS/60F", 2F); - CheckCalc(p, "payload.valueB/60F", 2F); - CheckCalc(p, "payload.valueF/60F", 2f); - CheckCalc(p, "payload.valueD/60F", 2d); - CheckCalc(p, "payload.valueJ/60F", 2F); - CheckCalc(p, "payload.valueI/60F", 2F); - - CheckCalc(p, "payload.valueSB/payload.valueFB60", 2F); - CheckCalc(p, "payload.valueBB/payload.valueFB60", 2F); - CheckCalc(p, "payload.valueFB/payload.valueFB60", 2f); - CheckCalc(p, "payload.valueDB/payload.valueFB60", 2d); - CheckCalc(p, "payload.valueJB/payload.valueFB60", 2F); - CheckCalc(p, "payload.valueIB/payload.valueFB60", 2F); - - CheckCalc(p, "payload.valueS/payload.valueFB60", 2F); - CheckCalc(p, "payload.valueB/payload.valueFB60", 2F); - CheckCalc(p, "payload.valueF/payload.valueFB60", 2f); - CheckCalc(p, "payload.valueD/payload.valueFB60", 2d); - CheckCalc(p, "payload.valueJ/payload.valueFB60", 2F); - CheckCalc(p, "payload.valueI/payload.valueFB60", 2F); - - // right is a long - CheckCalc(p, "payload.valueSB/60L", 2L); - CheckCalc(p, "payload.valueBB/60L", 2L); - CheckCalc(p, "payload.valueFB/60L", 2f); - CheckCalc(p, "payload.valueDB/60L", 2d); - CheckCalc(p, "payload.valueJB/60L", 2L); - CheckCalc(p, "payload.valueIB/60L", 2L); - - CheckCalc(p, "payload.valueS/60L", 2L); - CheckCalc(p, "payload.valueB/60L", 2L); - CheckCalc(p, "payload.valueF/60L", 2f); - CheckCalc(p, "payload.valueD/60L", 2d); - CheckCalc(p, "payload.valueJ/60L", 2L); - CheckCalc(p, "payload.valueI/60L", 2L); - - CheckCalc(p, "payload.valueSB/payload.valueJB60", 2L); - CheckCalc(p, "payload.valueBB/payload.valueJB60", 2L); - CheckCalc(p, "payload.valueFB/payload.valueJB60", 2f); - CheckCalc(p, "payload.valueDB/payload.valueJB60", 2d); - CheckCalc(p, "payload.valueJB/payload.valueJB60", 2L); - CheckCalc(p, "payload.valueIB/payload.valueJB60", 2L); - - CheckCalc(p, "payload.valueS/payload.valueJB60", 2L); - CheckCalc(p, "payload.valueB/payload.valueJB60", 2L); - CheckCalc(p, "payload.valueF/payload.valueJB60", 2f); - CheckCalc(p, "payload.valueD/payload.valueJB60", 2d); - CheckCalc(p, "payload.valueJ/payload.valueJB60", 2L); - CheckCalc(p, "payload.valueI/payload.valueJB60", 2L); - - // right is an int - CheckCalc(p, "payload.valueSB/60", 2); - CheckCalc(p, "payload.valueBB/60", 2); - CheckCalc(p, "payload.valueFB/60", 2f); - CheckCalc(p, "payload.valueDB/60", 2d); - CheckCalc(p, "payload.valueJB/60", 2L); - CheckCalc(p, "payload.valueIB/60", 2); - - CheckCalc(p, "payload.valueS/60", 2); - CheckCalc(p, "payload.valueB/60", 2); - CheckCalc(p, "payload.valueF/60", 2f); - CheckCalc(p, "payload.valueD/60", 2d); - CheckCalc(p, "payload.valueJ/60", 2L); - CheckCalc(p, "payload.valueI/60", 2); - - CheckCalc(p, "payload.valueSB/payload.valueIB60", 2); - CheckCalc(p, "payload.valueBB/payload.valueIB60", 2); - CheckCalc(p, "payload.valueFB/payload.valueIB60", 2f); - CheckCalc(p, "payload.valueDB/payload.valueIB60", 2d); - CheckCalc(p, "payload.valueJB/payload.valueIB60", 2L); - CheckCalc(p, "payload.valueIB/payload.valueIB60", 2); - - CheckCalc(p, "payload.valueS/payload.valueIB60", 2); - CheckCalc(p, "payload.valueB/payload.valueIB60", 2); - CheckCalc(p, "payload.valueF/payload.valueIB60", 2f); - CheckCalc(p, "payload.valueD/payload.valueIB60", 2d); - CheckCalc(p, "payload.valueJ/payload.valueIB60", 2L); - CheckCalc(p, "payload.valueI/payload.valueIB60", 2); - - // right is a short - CheckCalc(p, "payload.valueSB/payload.valueS", 1); - CheckCalc(p, "payload.valueBB/payload.valueS", 1); - CheckCalc(p, "payload.valueFB/payload.valueS", 1f); - CheckCalc(p, "payload.valueDB/payload.valueS", 1d); - CheckCalc(p, "payload.valueJB/payload.valueS", 1L); - CheckCalc(p, "payload.valueIB/payload.valueS", 1); - - CheckCalc(p, "payload.valueS/payload.valueS", 1); - CheckCalc(p, "payload.valueB/payload.valueS", 1); - CheckCalc(p, "payload.valueF/payload.valueS", 1f); - CheckCalc(p, "payload.valueD/payload.valueS", 1d); - CheckCalc(p, "payload.valueJ/payload.valueS", 1L); - CheckCalc(p, "payload.valueI/payload.valueS", 1); - - CheckCalc(p, "payload.valueSB/payload.valueSB", 1); - CheckCalc(p, "payload.valueBB/payload.valueSB", 1); - CheckCalc(p, "payload.valueFB/payload.valueSB", 1f); - CheckCalc(p, "payload.valueDB/payload.valueSB", 1d); - CheckCalc(p, "payload.valueJB/payload.valueSB", 1L); - CheckCalc(p, "payload.valueIB/payload.valueSB", 1); - - CheckCalc(p, "payload.valueS/payload.valueSB", 1); - CheckCalc(p, "payload.valueB/payload.valueSB", 1); - CheckCalc(p, "payload.valueF/payload.valueSB", 1f); - CheckCalc(p, "payload.valueD/payload.valueSB", 1d); - CheckCalc(p, "payload.valueJ/payload.valueSB", 1L); - CheckCalc(p, "payload.valueI/payload.valueSB", 1); - - // right is a byte - CheckCalc(p, "payload.valueSB/payload.valueB", 1); - CheckCalc(p, "payload.valueBB/payload.valueB", 1); - CheckCalc(p, "payload.valueFB/payload.valueB", 1f); - CheckCalc(p, "payload.valueDB/payload.valueB", 1d); - CheckCalc(p, "payload.valueJB/payload.valueB", 1L); - CheckCalc(p, "payload.valueIB/payload.valueB", 1); - - CheckCalc(p, "payload.valueS/payload.valueB", 1); - CheckCalc(p, "payload.valueB/payload.valueB", 1); - CheckCalc(p, "payload.valueF/payload.valueB", 1f); - CheckCalc(p, "payload.valueD/payload.valueB", 1d); - CheckCalc(p, "payload.valueJ/payload.valueB", 1L); - CheckCalc(p, "payload.valueI/payload.valueB", 1); - - CheckCalc(p, "payload.valueSB/payload.valueBB", 1); - CheckCalc(p, "payload.valueBB/payload.valueBB", 1); - CheckCalc(p, "payload.valueFB/payload.valueBB", 1f); - CheckCalc(p, "payload.valueDB/payload.valueBB", 1d); - CheckCalc(p, "payload.valueJB/payload.valueBB", 1L); - CheckCalc(p, "payload.valueIB/payload.valueBB", 1); - - CheckCalc(p, "payload.valueS/payload.valueBB", 1); - CheckCalc(p, "payload.valueB/payload.valueBB", 1); - CheckCalc(p, "payload.valueF/payload.valueBB", 1f); - CheckCalc(p, "payload.valueD/payload.valueBB", 1d); - CheckCalc(p, "payload.valueJ/payload.valueBB", 1L); - CheckCalc(p, "payload.valueI/payload.valueBB", 1); - } - - [Fact] - public void OpPlus() - { - _expression = Parse("2+2"); - Assert.Equal(4, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4, _expression.GetValue()); - - _expression = Parse("2L+2L"); - Assert.Equal(4L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4L, _expression.GetValue()); - - _expression = Parse("2.0f+2.0f"); - Assert.Equal(4F, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4F, _expression.GetValue()); - - _expression = Parse("3.0d+4.0d"); - Assert.Equal(7D, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(7D, _expression.GetValue()); - - _expression = Parse("+1"); - Assert.Equal(1, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1, _expression.GetValue()); - - _expression = Parse("+1L"); - Assert.Equal(1L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1L, _expression.GetValue()); - - _expression = Parse("+1.5f"); - Assert.Equal(1.5f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1.5f, _expression.GetValue()); - - _expression = Parse("+2.5d"); - Assert.Equal(2.5d, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2.5d, _expression.GetValue()); - - _expression = Parse($"+T(Double).Parse('2.5',{InvariantCulturePropertyAccess})"); - Assert.Equal(2.5d, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2.5d, _expression.GetValue()); - - _expression = Parse("T(int).Parse('2')+6"); - Assert.Equal(8, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(8, _expression.GetValue()); - - _expression = Parse("T(int).Parse('1') + T(int).Parse('3')"); - Assert.Equal(4, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4, _expression.GetValue()); - - _expression = Parse("1+T(int).Parse('3')"); - Assert.Equal(4, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4, _expression.GetValue()); - - _expression = Parse($"T(Single).Parse('2.0',{InvariantCulturePropertyAccess})+6"); - Assert.Equal(8.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(8.0f, _expression.GetValue()); - - _expression = Parse($"T(float).Parse('2.0',{InvariantCulturePropertyAccess})+T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.Equal(5.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(5.0f, _expression.GetValue()); - - _expression = Parse("3L+T(Int64).Parse('4')"); - Assert.Equal(7L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(7L, _expression.GetValue()); - - _expression = Parse("T(long).Parse('2')+6"); - Assert.Equal(8L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(8L, _expression.GetValue()); - - _expression = Parse("T(Int64).Parse('2')+T(long).Parse('3')"); - Assert.Equal(5L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(5L, _expression.GetValue()); - - _expression = Parse("1L+T(long).Parse('2')"); - Assert.Equal(3L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(3L, _expression.GetValue()); - } - - [Fact] - public void OpPlus_MixedNumberTypes() - { - var p = new PayloadX(); - - // right is a double - CheckCalc(p, "payload.valueSB+60D", 180d); - CheckCalc(p, "payload.valueBB+60D", 180d); - CheckCalc(p, "payload.valueFB+60D", 180d); - CheckCalc(p, "payload.valueDB+60D", 180d); - CheckCalc(p, "payload.valueJB+60D", 180d); - CheckCalc(p, "payload.valueIB+60D", 180d); - - CheckCalc(p, "payload.valueS+60D", 180d); - CheckCalc(p, "payload.valueB+60D", 180d); - CheckCalc(p, "payload.valueF+60D", 180d); - CheckCalc(p, "payload.valueD+60D", 180d); - CheckCalc(p, "payload.valueJ+60D", 180d); - CheckCalc(p, "payload.valueI+60D", 180d); - - CheckCalc(p, "payload.valueSB+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueBB+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueFB+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueDB+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueJB+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueIB+payload.valueDB60", 180d); - - CheckCalc(p, "payload.valueS+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueB+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueF+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueD+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueJ+payload.valueDB60", 180d); - CheckCalc(p, "payload.valueI+payload.valueDB60", 180d); - - // right is a float - CheckCalc(p, "payload.valueSB+60F", 180F); - CheckCalc(p, "payload.valueBB+60F", 180F); - CheckCalc(p, "payload.valueFB+60F", 180f); - CheckCalc(p, "payload.valueDB+60F", 180d); - CheckCalc(p, "payload.valueJB+60F", 180F); - CheckCalc(p, "payload.valueIB+60F", 180F); - - CheckCalc(p, "payload.valueS+60F", 180F); - CheckCalc(p, "payload.valueB+60F", 180F); - CheckCalc(p, "payload.valueF+60F", 180f); - CheckCalc(p, "payload.valueD+60F", 180d); - CheckCalc(p, "payload.valueJ+60F", 180F); - CheckCalc(p, "payload.valueI+60F", 180F); - - CheckCalc(p, "payload.valueSB+payload.valueFB60", 180F); - CheckCalc(p, "payload.valueBB+payload.valueFB60", 180F); - CheckCalc(p, "payload.valueFB+payload.valueFB60", 180f); - CheckCalc(p, "payload.valueDB+payload.valueFB60", 180d); - CheckCalc(p, "payload.valueJB+payload.valueFB60", 180F); - CheckCalc(p, "payload.valueIB+payload.valueFB60", 180F); - - CheckCalc(p, "payload.valueS+payload.valueFB60", 180F); - CheckCalc(p, "payload.valueB+payload.valueFB60", 180F); - CheckCalc(p, "payload.valueF+payload.valueFB60", 180f); - CheckCalc(p, "payload.valueD+payload.valueFB60", 180d); - CheckCalc(p, "payload.valueJ+payload.valueFB60", 180F); - CheckCalc(p, "payload.valueI+payload.valueFB60", 180F); - - // right is a long - CheckCalc(p, "payload.valueSB+60L", 180L); - CheckCalc(p, "payload.valueBB+60L", 180L); - CheckCalc(p, "payload.valueFB+60L", 180f); - CheckCalc(p, "payload.valueDB+60L", 180d); - CheckCalc(p, "payload.valueJB+60L", 180L); - CheckCalc(p, "payload.valueIB+60L", 180L); - - CheckCalc(p, "payload.valueS+60L", 180L); - CheckCalc(p, "payload.valueB+60L", 180L); - CheckCalc(p, "payload.valueF+60L", 180f); - CheckCalc(p, "payload.valueD+60L", 180d); - CheckCalc(p, "payload.valueJ+60L", 180L); - CheckCalc(p, "payload.valueI+60L", 180L); - - CheckCalc(p, "payload.valueSB+payload.valueJB60", 180L); - CheckCalc(p, "payload.valueBB+payload.valueJB60", 180L); - CheckCalc(p, "payload.valueFB+payload.valueJB60", 180f); - CheckCalc(p, "payload.valueDB+payload.valueJB60", 180d); - CheckCalc(p, "payload.valueJB+payload.valueJB60", 180L); - CheckCalc(p, "payload.valueIB+payload.valueJB60", 180L); - - CheckCalc(p, "payload.valueS+payload.valueJB60", 180L); - CheckCalc(p, "payload.valueB+payload.valueJB60", 180L); - CheckCalc(p, "payload.valueF+payload.valueJB60", 180f); - CheckCalc(p, "payload.valueD+payload.valueJB60", 180d); - CheckCalc(p, "payload.valueJ+payload.valueJB60", 180L); - CheckCalc(p, "payload.valueI+payload.valueJB60", 180L); - - // right is an int - CheckCalc(p, "payload.valueSB+60", 180); - CheckCalc(p, "payload.valueBB+60", 180); - CheckCalc(p, "payload.valueFB+60", 180f); - CheckCalc(p, "payload.valueDB+60", 180d); - CheckCalc(p, "payload.valueJB+60", 180L); - CheckCalc(p, "payload.valueIB+60", 180); - - CheckCalc(p, "payload.valueS+60", 180); - CheckCalc(p, "payload.valueB+60", 180); - CheckCalc(p, "payload.valueF+60", 180f); - CheckCalc(p, "payload.valueD+60", 180d); - CheckCalc(p, "payload.valueJ+60", 180L); - CheckCalc(p, "payload.valueI+60", 180); - - CheckCalc(p, "payload.valueSB+payload.valueIB60", 180); - CheckCalc(p, "payload.valueBB+payload.valueIB60", 180); - CheckCalc(p, "payload.valueFB+payload.valueIB60", 180f); - CheckCalc(p, "payload.valueDB+payload.valueIB60", 180d); - CheckCalc(p, "payload.valueJB+payload.valueIB60", 180L); - CheckCalc(p, "payload.valueIB+payload.valueIB60", 180); - - CheckCalc(p, "payload.valueS+payload.valueIB60", 180); - CheckCalc(p, "payload.valueB+payload.valueIB60", 180); - CheckCalc(p, "payload.valueF+payload.valueIB60", 180f); - CheckCalc(p, "payload.valueD+payload.valueIB60", 180d); - CheckCalc(p, "payload.valueJ+payload.valueIB60", 180L); - CheckCalc(p, "payload.valueI+payload.valueIB60", 180); - - // right is a short - CheckCalc(p, "payload.valueSB+payload.valueS", 240); - CheckCalc(p, "payload.valueBB+payload.valueS", 240); - CheckCalc(p, "payload.valueFB+payload.valueS", 240f); - CheckCalc(p, "payload.valueDB+payload.valueS", 240d); - CheckCalc(p, "payload.valueJB+payload.valueS", 240L); - CheckCalc(p, "payload.valueIB+payload.valueS", 240); - - CheckCalc(p, "payload.valueS+payload.valueS", 240); - CheckCalc(p, "payload.valueB+payload.valueS", 240); - CheckCalc(p, "payload.valueF+payload.valueS", 240f); - CheckCalc(p, "payload.valueD+payload.valueS", 240d); - CheckCalc(p, "payload.valueJ+payload.valueS", 240L); - CheckCalc(p, "payload.valueI+payload.valueS", 240); - - CheckCalc(p, "payload.valueSB+payload.valueSB", 240); - CheckCalc(p, "payload.valueBB+payload.valueSB", 240); - CheckCalc(p, "payload.valueFB+payload.valueSB", 240f); - CheckCalc(p, "payload.valueDB+payload.valueSB", 240d); - CheckCalc(p, "payload.valueJB+payload.valueSB", 240L); - CheckCalc(p, "payload.valueIB+payload.valueSB", 240); - - CheckCalc(p, "payload.valueS+payload.valueSB", 240); - CheckCalc(p, "payload.valueB+payload.valueSB", 240); - CheckCalc(p, "payload.valueF+payload.valueSB", 240f); - CheckCalc(p, "payload.valueD+payload.valueSB", 240d); - CheckCalc(p, "payload.valueJ+payload.valueSB", 240L); - CheckCalc(p, "payload.valueI+payload.valueSB", 240); - - // right is a byte - CheckCalc(p, "payload.valueSB+payload.valueB", 240); - CheckCalc(p, "payload.valueBB+payload.valueB", 240); - CheckCalc(p, "payload.valueFB+payload.valueB", 240f); - CheckCalc(p, "payload.valueDB+payload.valueB", 240d); - CheckCalc(p, "payload.valueJB+payload.valueB", 240L); - CheckCalc(p, "payload.valueIB+payload.valueB", 240); - - CheckCalc(p, "payload.valueS+payload.valueB", 240); - CheckCalc(p, "payload.valueB+payload.valueB", 240); - CheckCalc(p, "payload.valueF+payload.valueB", 240f); - CheckCalc(p, "payload.valueD+payload.valueB", 240d); - CheckCalc(p, "payload.valueJ+payload.valueB", 240L); - CheckCalc(p, "payload.valueI+payload.valueB", 240); - - CheckCalc(p, "payload.valueSB+payload.valueBB", 240); - CheckCalc(p, "payload.valueBB+payload.valueBB", 240); - CheckCalc(p, "payload.valueFB+payload.valueBB", 240f); - CheckCalc(p, "payload.valueDB+payload.valueBB", 240d); - CheckCalc(p, "payload.valueJB+payload.valueBB", 240L); - CheckCalc(p, "payload.valueIB+payload.valueBB", 240); - - CheckCalc(p, "payload.valueS+payload.valueBB", 240); - CheckCalc(p, "payload.valueB+payload.valueBB", 240); - CheckCalc(p, "payload.valueF+payload.valueBB", 240f); - CheckCalc(p, "payload.valueD+payload.valueBB", 240d); - CheckCalc(p, "payload.valueJ+payload.valueBB", 240L); - CheckCalc(p, "payload.valueI+payload.valueBB", 240); - } - - [Fact] - public void OpPlusString() - { - _expression = Parse("'hello' + 'world'"); - Assert.Equal("helloworld", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("helloworld", _expression.GetValue()); - - _expression = Parse("'hello' + World"); - Assert.Equal("helloworld", _expression.GetValue(new Greeter())); - AssertCanCompile(_expression); - Assert.Equal("helloworld", _expression.GetValue(new Greeter())); - - _expression = Parse("World + 'hello'"); - Assert.Equal("worldhello", _expression.GetValue(new Greeter())); - AssertCanCompile(_expression); - Assert.Equal("worldhello", _expression.GetValue(new Greeter())); - - _expression = Parse("'hello' + World + ' spring'"); - Assert.Equal("helloworld spring", _expression.GetValue(new Greeter())); - AssertCanCompile(_expression); - Assert.Equal("helloworld spring", _expression.GetValue(new Greeter())); - - _expression = Parse("'hello' + 3 + ' spring'"); - Assert.Equal("hello3 spring", _expression.GetValue(new Greeter())); - AssertCantCompile(_expression); - - _expression = Parse("GetObject() + 'a'"); - Assert.Equal("objecta", _expression.GetValue(new Greeter())); - AssertCanCompile(_expression); - Assert.Equal("objecta", _expression.GetValue(new Greeter())); - - _expression = Parse("'a'+GetObject()"); - Assert.Equal("aobject", _expression.GetValue(new Greeter())); - AssertCanCompile(_expression); - Assert.Equal("aobject", _expression.GetValue(new Greeter())); - - _expression = Parse("'a'+GetObject()+'a'"); - Assert.Equal("aobjecta", _expression.GetValue(new Greeter())); - AssertCanCompile(_expression); - Assert.Equal("aobjecta", _expression.GetValue(new Greeter())); - - _expression = Parse("GetObject()+'a'+GetObject()"); - Assert.Equal("objectaobject", _expression.GetValue(new Greeter())); - AssertCanCompile(_expression); - Assert.Equal("objectaobject", _expression.GetValue(new Greeter())); - - _expression = Parse("GetObject()+GetObject()"); - Assert.Equal("objectobject", _expression.GetValue(new Greeter())); - AssertCanCompile(_expression); - Assert.Equal("objectobject", _expression.GetValue(new Greeter())); - } - - [Fact] - public void OpMinus() - { - _expression = Parse("2-2"); - Assert.Equal(0, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(0, _expression.GetValue()); - - _expression = Parse("4L - 2L"); - Assert.Equal(2L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2L, _expression.GetValue()); - - _expression = Parse("4.0f-2.0f"); - Assert.Equal(2F, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2F, _expression.GetValue()); - - _expression = Parse("3.0d-4.0d"); - Assert.Equal(-1d, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-1d, _expression.GetValue()); - - _expression = Parse("-1"); - Assert.Equal(-1, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-1, _expression.GetValue()); - - _expression = Parse("-1L"); - Assert.Equal(-1L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-1L, _expression.GetValue()); - - _expression = Parse("-1.5f"); - Assert.Equal(-1.5f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-1.5f, _expression.GetValue()); - - _expression = Parse("-2.5d"); - Assert.Equal(-2.5d, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-2.5d, _expression.GetValue()); - - _expression = Parse("T(int).Parse('2')-6"); - Assert.Equal(-4, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-4, _expression.GetValue()); - - _expression = Parse("T(int).Parse('1')-T(Int32).Parse('3')"); - Assert.Equal(-2, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-2, _expression.GetValue()); - - _expression = Parse("4-T(Int32).Parse('3')"); - Assert.Equal(1, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1, _expression.GetValue()); - - _expression = Parse($"T(Single).Parse('2.0',{InvariantCulturePropertyAccess})-6"); - Assert.Equal(-4.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-4.0f, _expression.GetValue()); - - _expression = Parse($"T(float).Parse('8.0',{InvariantCulturePropertyAccess})-T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.Equal(5.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(5.0f, _expression.GetValue()); - - _expression = Parse("11L-T(Int64).Parse('4')"); - Assert.Equal(7L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(7L, _expression.GetValue()); - - _expression = Parse("T(long).Parse('9')-6"); - Assert.Equal(3L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(3L, _expression.GetValue()); - - _expression = Parse("T(long).Parse('4')-T(long).Parse('3')"); - Assert.Equal(1L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1L, _expression.GetValue()); - - _expression = Parse("8L-T(Int64).Parse('2')"); - Assert.Equal(6L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(6L, _expression.GetValue()); - } - - [Fact] - public void OpMinus_MixedNumberTypes() - { - var p = new PayloadX(); - - // right is a double - CheckCalc(p, "payload.valueSB-60D", 60d); - CheckCalc(p, "payload.valueBB-60D", 60d); - CheckCalc(p, "payload.valueFB-60D", 60d); - CheckCalc(p, "payload.valueDB-60D", 60d); - CheckCalc(p, "payload.valueJB-60D", 60d); - CheckCalc(p, "payload.valueIB-60D", 60d); - - CheckCalc(p, "payload.valueS-60D", 60d); - CheckCalc(p, "payload.valueB-60D", 60d); - CheckCalc(p, "payload.valueF-60D", 60d); - CheckCalc(p, "payload.valueD-60D", 60d); - CheckCalc(p, "payload.valueJ-60D", 60d); - CheckCalc(p, "payload.valueI-60D", 60d); - - CheckCalc(p, "payload.valueSB-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueBB-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueFB-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueDB-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueJB-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueIB-payload.valueDB60", 60d); - - CheckCalc(p, "payload.valueS-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueB-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueF-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueD-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueJ-payload.valueDB60", 60d); - CheckCalc(p, "payload.valueI-payload.valueDB60", 60d); - - // right is a float - CheckCalc(p, "payload.valueSB-60F", 60F); - CheckCalc(p, "payload.valueBB-60F", 60F); - CheckCalc(p, "payload.valueFB-60F", 60f); - CheckCalc(p, "payload.valueDB-60F", 60d); - CheckCalc(p, "payload.valueJB-60F", 60F); - CheckCalc(p, "payload.valueIB-60F", 60F); - - CheckCalc(p, "payload.valueS-60F", 60F); - CheckCalc(p, "payload.valueB-60F", 60F); - CheckCalc(p, "payload.valueF-60F", 60f); - CheckCalc(p, "payload.valueD-60F", 60d); - CheckCalc(p, "payload.valueJ-60F", 60F); - CheckCalc(p, "payload.valueI-60F", 60F); - - CheckCalc(p, "payload.valueSB-payload.valueFB60", 60F); - CheckCalc(p, "payload.valueBB-payload.valueFB60", 60F); - CheckCalc(p, "payload.valueFB-payload.valueFB60", 60f); - CheckCalc(p, "payload.valueDB-payload.valueFB60", 60d); - CheckCalc(p, "payload.valueJB-payload.valueFB60", 60F); - CheckCalc(p, "payload.valueIB-payload.valueFB60", 60F); - - CheckCalc(p, "payload.valueS-payload.valueFB60", 60F); - CheckCalc(p, "payload.valueB-payload.valueFB60", 60F); - CheckCalc(p, "payload.valueF-payload.valueFB60", 60f); - CheckCalc(p, "payload.valueD-payload.valueFB60", 60d); - CheckCalc(p, "payload.valueJ-payload.valueFB60", 60F); - CheckCalc(p, "payload.valueI-payload.valueFB60", 60F); - - // right is a long - CheckCalc(p, "payload.valueSB-60L", 60L); - CheckCalc(p, "payload.valueBB-60L", 60L); - CheckCalc(p, "payload.valueFB-60L", 60f); - CheckCalc(p, "payload.valueDB-60L", 60d); - CheckCalc(p, "payload.valueJB-60L", 60L); - CheckCalc(p, "payload.valueIB-60L", 60L); - - CheckCalc(p, "payload.valueS-60L", 60L); - CheckCalc(p, "payload.valueB-60L", 60L); - CheckCalc(p, "payload.valueF-60L", 60f); - CheckCalc(p, "payload.valueD-60L", 60d); - CheckCalc(p, "payload.valueJ-60L", 60L); - CheckCalc(p, "payload.valueI-60L", 60L); - - CheckCalc(p, "payload.valueSB-payload.valueJB60", 60L); - CheckCalc(p, "payload.valueBB-payload.valueJB60", 60L); - CheckCalc(p, "payload.valueFB-payload.valueJB60", 60f); - CheckCalc(p, "payload.valueDB-payload.valueJB60", 60d); - CheckCalc(p, "payload.valueJB-payload.valueJB60", 60L); - CheckCalc(p, "payload.valueIB-payload.valueJB60", 60L); - - CheckCalc(p, "payload.valueS-payload.valueJB60", 60L); - CheckCalc(p, "payload.valueB-payload.valueJB60", 60L); - CheckCalc(p, "payload.valueF-payload.valueJB60", 60f); - CheckCalc(p, "payload.valueD-payload.valueJB60", 60d); - CheckCalc(p, "payload.valueJ-payload.valueJB60", 60L); - CheckCalc(p, "payload.valueI-payload.valueJB60", 60L); - - // right is an int - CheckCalc(p, "payload.valueSB-60", 60); - CheckCalc(p, "payload.valueBB-60", 60); - CheckCalc(p, "payload.valueFB-60", 60f); - CheckCalc(p, "payload.valueDB-60", 60d); - CheckCalc(p, "payload.valueJB-60", 60L); - CheckCalc(p, "payload.valueIB-60", 60); - - CheckCalc(p, "payload.valueS-60", 60); - CheckCalc(p, "payload.valueB-60", 60); - CheckCalc(p, "payload.valueF-60", 60f); - CheckCalc(p, "payload.valueD-60", 60d); - CheckCalc(p, "payload.valueJ-60", 60L); - CheckCalc(p, "payload.valueI-60", 60); - - CheckCalc(p, "payload.valueSB-payload.valueIB60", 60); - CheckCalc(p, "payload.valueBB-payload.valueIB60", 60); - CheckCalc(p, "payload.valueFB-payload.valueIB60", 60f); - CheckCalc(p, "payload.valueDB-payload.valueIB60", 60d); - CheckCalc(p, "payload.valueJB-payload.valueIB60", 60L); - CheckCalc(p, "payload.valueIB-payload.valueIB60", 60); - - CheckCalc(p, "payload.valueS-payload.valueIB60", 60); - CheckCalc(p, "payload.valueB-payload.valueIB60", 60); - CheckCalc(p, "payload.valueF-payload.valueIB60", 60f); - CheckCalc(p, "payload.valueD-payload.valueIB60", 60d); - CheckCalc(p, "payload.valueJ-payload.valueIB60", 60L); - CheckCalc(p, "payload.valueI-payload.valueIB60", 60); - - // right is a short - CheckCalc(p, "payload.valueSB-payload.valueS20", 100); - CheckCalc(p, "payload.valueBB-payload.valueS20", 100); - CheckCalc(p, "payload.valueFB-payload.valueS20", 100f); - CheckCalc(p, "payload.valueDB-payload.valueS20", 100d); - CheckCalc(p, "payload.valueJB-payload.valueS20", 100L); - CheckCalc(p, "payload.valueIB-payload.valueS20", 100); - - CheckCalc(p, "payload.valueS-payload.valueS20", 100); - CheckCalc(p, "payload.valueB-payload.valueS20", 100); - CheckCalc(p, "payload.valueF-payload.valueS20", 100f); - CheckCalc(p, "payload.valueD-payload.valueS20", 100d); - CheckCalc(p, "payload.valueJ-payload.valueS20", 100L); - CheckCalc(p, "payload.valueI-payload.valueS20", 100); - - CheckCalc(p, "payload.valueSB-payload.valueSB20", 100); - CheckCalc(p, "payload.valueBB-payload.valueSB20", 100); - CheckCalc(p, "payload.valueFB-payload.valueSB20", 100f); - CheckCalc(p, "payload.valueDB-payload.valueSB20", 100d); - CheckCalc(p, "payload.valueJB-payload.valueSB20", 100L); - CheckCalc(p, "payload.valueIB-payload.valueSB20", 100); - - CheckCalc(p, "payload.valueS-payload.valueSB20", 100); - CheckCalc(p, "payload.valueB-payload.valueSB20", 100); - CheckCalc(p, "payload.valueF-payload.valueSB20", 100f); - CheckCalc(p, "payload.valueD-payload.valueSB20", 100d); - CheckCalc(p, "payload.valueJ-payload.valueSB20", 100L); - CheckCalc(p, "payload.valueI-payload.valueSB20", 100); - - // right is a byte - CheckCalc(p, "payload.valueSB-payload.valueB20", 100); - CheckCalc(p, "payload.valueBB-payload.valueB20", 100); - CheckCalc(p, "payload.valueFB-payload.valueB20", 100f); - CheckCalc(p, "payload.valueDB-payload.valueB20", 100d); - CheckCalc(p, "payload.valueJB-payload.valueB20", 100L); - CheckCalc(p, "payload.valueIB-payload.valueB20", 100); - - CheckCalc(p, "payload.valueS-payload.valueB20", 100); - CheckCalc(p, "payload.valueB-payload.valueB20", 100); - CheckCalc(p, "payload.valueF-payload.valueB20", 100f); - CheckCalc(p, "payload.valueD-payload.valueB20", 100d); - CheckCalc(p, "payload.valueJ-payload.valueB20", 100L); - CheckCalc(p, "payload.valueI-payload.valueB20", 100); - - CheckCalc(p, "payload.valueSB-payload.valueBB20", 100); - CheckCalc(p, "payload.valueBB-payload.valueBB20", 100); - CheckCalc(p, "payload.valueFB-payload.valueBB20", 100f); - CheckCalc(p, "payload.valueDB-payload.valueBB20", 100d); - CheckCalc(p, "payload.valueJB-payload.valueBB20", 100L); - CheckCalc(p, "payload.valueIB-payload.valueBB20", 100); - - CheckCalc(p, "payload.valueS-payload.valueBB20", 100); - CheckCalc(p, "payload.valueB-payload.valueBB20", 100); - CheckCalc(p, "payload.valueF-payload.valueBB20", 100f); - CheckCalc(p, "payload.valueD-payload.valueBB20", 100d); - CheckCalc(p, "payload.valueJ-payload.valueBB20", 100L); - CheckCalc(p, "payload.valueI-payload.valueBB20", 100); - } - - [Fact] - public void OpMultiply_MixedNumberTypes() - { - var p = new PayloadX(); - - // right is a double - CheckCalc(p, "payload.valueSB*60D", 7200d); - CheckCalc(p, "payload.valueBB*60D", 7200d); - CheckCalc(p, "payload.valueFB*60D", 7200d); - CheckCalc(p, "payload.valueDB*60D", 7200d); - CheckCalc(p, "payload.valueJB*60D", 7200d); - CheckCalc(p, "payload.valueIB*60D", 7200d); - - CheckCalc(p, "payload.valueS*60D", 7200d); - CheckCalc(p, "payload.valueB*60D", 7200d); - CheckCalc(p, "payload.valueF*60D", 7200d); - CheckCalc(p, "payload.valueD*60D", 7200d); - CheckCalc(p, "payload.valueJ*60D", 7200d); - CheckCalc(p, "payload.valueI*60D", 7200d); - - CheckCalc(p, "payload.valueSB*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueBB*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueFB*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueDB*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueJB*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueIB*payload.valueDB60", 7200d); - - CheckCalc(p, "payload.valueS*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueB*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueF*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueD*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueJ*payload.valueDB60", 7200d); - CheckCalc(p, "payload.valueI*payload.valueDB60", 7200d); - - // right is a float - CheckCalc(p, "payload.valueSB*60F", 7200F); - CheckCalc(p, "payload.valueBB*60F", 7200F); - CheckCalc(p, "payload.valueFB*60F", 7200f); - CheckCalc(p, "payload.valueDB*60F", 7200d); - CheckCalc(p, "payload.valueJB*60F", 7200F); - CheckCalc(p, "payload.valueIB*60F", 7200F); - - CheckCalc(p, "payload.valueS*60F", 7200F); - CheckCalc(p, "payload.valueB*60F", 7200F); - CheckCalc(p, "payload.valueF*60F", 7200f); - CheckCalc(p, "payload.valueD*60F", 7200d); - CheckCalc(p, "payload.valueJ*60F", 7200F); - CheckCalc(p, "payload.valueI*60F", 7200F); - - CheckCalc(p, "payload.valueSB*payload.valueFB60", 7200F); - CheckCalc(p, "payload.valueBB*payload.valueFB60", 7200F); - CheckCalc(p, "payload.valueFB*payload.valueFB60", 7200f); - CheckCalc(p, "payload.valueDB*payload.valueFB60", 7200d); - CheckCalc(p, "payload.valueJB*payload.valueFB60", 7200F); - CheckCalc(p, "payload.valueIB*payload.valueFB60", 7200F); - - CheckCalc(p, "payload.valueS*payload.valueFB60", 7200F); - CheckCalc(p, "payload.valueB*payload.valueFB60", 7200F); - CheckCalc(p, "payload.valueF*payload.valueFB60", 7200f); - CheckCalc(p, "payload.valueD*payload.valueFB60", 7200d); - CheckCalc(p, "payload.valueJ*payload.valueFB60", 7200F); - CheckCalc(p, "payload.valueI*payload.valueFB60", 7200F); - - // right is a long - CheckCalc(p, "payload.valueSB*60L", 7200L); - CheckCalc(p, "payload.valueBB*60L", 7200L); - CheckCalc(p, "payload.valueFB*60L", 7200f); - CheckCalc(p, "payload.valueDB*60L", 7200d); - CheckCalc(p, "payload.valueJB*60L", 7200L); - CheckCalc(p, "payload.valueIB*60L", 7200L); - - CheckCalc(p, "payload.valueS*60L", 7200L); - CheckCalc(p, "payload.valueB*60L", 7200L); - CheckCalc(p, "payload.valueF*60L", 7200f); - CheckCalc(p, "payload.valueD*60L", 7200d); - CheckCalc(p, "payload.valueJ*60L", 7200L); - CheckCalc(p, "payload.valueI*60L", 7200L); - - CheckCalc(p, "payload.valueSB*payload.valueJB60", 7200L); - CheckCalc(p, "payload.valueBB*payload.valueJB60", 7200L); - CheckCalc(p, "payload.valueFB*payload.valueJB60", 7200f); - CheckCalc(p, "payload.valueDB*payload.valueJB60", 7200d); - CheckCalc(p, "payload.valueJB*payload.valueJB60", 7200L); - CheckCalc(p, "payload.valueIB*payload.valueJB60", 7200L); - - CheckCalc(p, "payload.valueS*payload.valueJB60", 7200L); - CheckCalc(p, "payload.valueB*payload.valueJB60", 7200L); - CheckCalc(p, "payload.valueF*payload.valueJB60", 7200f); - CheckCalc(p, "payload.valueD*payload.valueJB60", 7200d); - CheckCalc(p, "payload.valueJ*payload.valueJB60", 7200L); - CheckCalc(p, "payload.valueI*payload.valueJB60", 7200L); - - // right is an int - CheckCalc(p, "payload.valueSB*60", 7200); - CheckCalc(p, "payload.valueBB*60", 7200); - CheckCalc(p, "payload.valueFB*60", 7200f); - CheckCalc(p, "payload.valueDB*60", 7200d); - CheckCalc(p, "payload.valueJB*60", 7200L); - CheckCalc(p, "payload.valueIB*60", 7200); - - CheckCalc(p, "payload.valueS*60", 7200); - CheckCalc(p, "payload.valueB*60", 7200); - CheckCalc(p, "payload.valueF*60", 7200f); - CheckCalc(p, "payload.valueD*60", 7200d); - CheckCalc(p, "payload.valueJ*60", 7200L); - CheckCalc(p, "payload.valueI*60", 7200); - - CheckCalc(p, "payload.valueSB*payload.valueIB60", 7200); - CheckCalc(p, "payload.valueBB*payload.valueIB60", 7200); - CheckCalc(p, "payload.valueFB*payload.valueIB60", 7200f); - CheckCalc(p, "payload.valueDB*payload.valueIB60", 7200d); - CheckCalc(p, "payload.valueJB*payload.valueIB60", 7200L); - CheckCalc(p, "payload.valueIB*payload.valueIB60", 7200); - - CheckCalc(p, "payload.valueS*payload.valueIB60", 7200); - CheckCalc(p, "payload.valueB*payload.valueIB60", 7200); - CheckCalc(p, "payload.valueF*payload.valueIB60", 7200f); - CheckCalc(p, "payload.valueD*payload.valueIB60", 7200d); - CheckCalc(p, "payload.valueJ*payload.valueIB60", 7200L); - CheckCalc(p, "payload.valueI*payload.valueIB60", 7200); - - // right is a short - CheckCalc(p, "payload.valueSB*payload.valueS20", 2400); - CheckCalc(p, "payload.valueBB*payload.valueS20", 2400); - CheckCalc(p, "payload.valueFB*payload.valueS20", 2400f); - CheckCalc(p, "payload.valueDB*payload.valueS20", 2400d); - CheckCalc(p, "payload.valueJB*payload.valueS20", 2400L); - CheckCalc(p, "payload.valueIB*payload.valueS20", 2400); - - CheckCalc(p, "payload.valueS*payload.valueS20", 2400); - CheckCalc(p, "payload.valueB*payload.valueS20", 2400); - CheckCalc(p, "payload.valueF*payload.valueS20", 2400f); - CheckCalc(p, "payload.valueD*payload.valueS20", 2400d); - CheckCalc(p, "payload.valueJ*payload.valueS20", 2400L); - CheckCalc(p, "payload.valueI*payload.valueS20", 2400); - - CheckCalc(p, "payload.valueSB*payload.valueSB20", 2400); - CheckCalc(p, "payload.valueBB*payload.valueSB20", 2400); - CheckCalc(p, "payload.valueFB*payload.valueSB20", 2400f); - CheckCalc(p, "payload.valueDB*payload.valueSB20", 2400d); - CheckCalc(p, "payload.valueJB*payload.valueSB20", 2400L); - CheckCalc(p, "payload.valueIB*payload.valueSB20", 2400); - - CheckCalc(p, "payload.valueS*payload.valueSB20", 2400); - CheckCalc(p, "payload.valueB*payload.valueSB20", 2400); - CheckCalc(p, "payload.valueF*payload.valueSB20", 2400f); - CheckCalc(p, "payload.valueD*payload.valueSB20", 2400d); - CheckCalc(p, "payload.valueJ*payload.valueSB20", 2400L); - CheckCalc(p, "payload.valueI*payload.valueSB20", 2400); - - // right is a byte - CheckCalc(p, "payload.valueSB*payload.valueB20", 2400); - CheckCalc(p, "payload.valueBB*payload.valueB20", 2400); - CheckCalc(p, "payload.valueFB*payload.valueB20", 2400f); - CheckCalc(p, "payload.valueDB*payload.valueB20", 2400d); - CheckCalc(p, "payload.valueJB*payload.valueB20", 2400L); - CheckCalc(p, "payload.valueIB*payload.valueB20", 2400); - - CheckCalc(p, "payload.valueS*payload.valueB20", 2400); - CheckCalc(p, "payload.valueB*payload.valueB20", 2400); - CheckCalc(p, "payload.valueF*payload.valueB20", 2400f); - CheckCalc(p, "payload.valueD*payload.valueB20", 2400d); - CheckCalc(p, "payload.valueJ*payload.valueB20", 2400L); - CheckCalc(p, "payload.valueI*payload.valueB20", 2400); - - CheckCalc(p, "payload.valueSB*payload.valueBB20", 2400); - CheckCalc(p, "payload.valueBB*payload.valueBB20", 2400); - CheckCalc(p, "payload.valueFB*payload.valueBB20", 2400f); - CheckCalc(p, "payload.valueDB*payload.valueBB20", 2400d); - CheckCalc(p, "payload.valueJB*payload.valueBB20", 2400L); - CheckCalc(p, "payload.valueIB*payload.valueBB20", 2400); - - CheckCalc(p, "payload.valueS*payload.valueBB20", 2400); - CheckCalc(p, "payload.valueB*payload.valueBB20", 2400); - CheckCalc(p, "payload.valueF*payload.valueBB20", 2400f); - CheckCalc(p, "payload.valueD*payload.valueBB20", 2400d); - CheckCalc(p, "payload.valueJ*payload.valueBB20", 2400L); - CheckCalc(p, "payload.valueI*payload.valueBB20", 2400); - } - - [Fact] - public void OpModulus_MixedNumberTypes() - { - var p = new PayloadX(); - - // right is a double - CheckCalc(p, "payload.valueSB%58D", 4d); - CheckCalc(p, "payload.valueBB%58D", 4d); - CheckCalc(p, "payload.valueFB%58D", 4d); - CheckCalc(p, "payload.valueDB%58D", 4d); - CheckCalc(p, "payload.valueJB%58D", 4d); - CheckCalc(p, "payload.valueIB%58D", 4d); - - CheckCalc(p, "payload.valueS%58D", 4d); - CheckCalc(p, "payload.valueB%58D", 4d); - CheckCalc(p, "payload.valueF%58D", 4d); - CheckCalc(p, "payload.valueD%58D", 4d); - CheckCalc(p, "payload.valueJ%58D", 4d); - CheckCalc(p, "payload.valueI%58D", 4d); - - CheckCalc(p, "payload.valueSB%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueBB%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueFB%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueDB%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueJB%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueIB%payload.valueDB58", 4d); - - CheckCalc(p, "payload.valueS%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueB%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueF%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueD%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueJ%payload.valueDB58", 4d); - CheckCalc(p, "payload.valueI%payload.valueDB58", 4d); - - // right is a float - CheckCalc(p, "payload.valueSB%58F", 4F); - CheckCalc(p, "payload.valueBB%58F", 4F); - CheckCalc(p, "payload.valueFB%58F", 4f); - CheckCalc(p, "payload.valueDB%58F", 4d); - CheckCalc(p, "payload.valueJB%58F", 4F); - CheckCalc(p, "payload.valueIB%58F", 4F); - - CheckCalc(p, "payload.valueS%58F", 4F); - CheckCalc(p, "payload.valueB%58F", 4F); - CheckCalc(p, "payload.valueF%58F", 4f); - CheckCalc(p, "payload.valueD%58F", 4d); - CheckCalc(p, "payload.valueJ%58F", 4F); - CheckCalc(p, "payload.valueI%58F", 4F); - - CheckCalc(p, "payload.valueSB%payload.valueFB58", 4F); - CheckCalc(p, "payload.valueBB%payload.valueFB58", 4F); - CheckCalc(p, "payload.valueFB%payload.valueFB58", 4f); - CheckCalc(p, "payload.valueDB%payload.valueFB58", 4d); - CheckCalc(p, "payload.valueJB%payload.valueFB58", 4F); - CheckCalc(p, "payload.valueIB%payload.valueFB58", 4F); - - CheckCalc(p, "payload.valueS%payload.valueFB58", 4F); - CheckCalc(p, "payload.valueB%payload.valueFB58", 4F); - CheckCalc(p, "payload.valueF%payload.valueFB58", 4f); - CheckCalc(p, "payload.valueD%payload.valueFB58", 4d); - CheckCalc(p, "payload.valueJ%payload.valueFB58", 4F); - CheckCalc(p, "payload.valueI%payload.valueFB58", 4F); - - // right is a long - CheckCalc(p, "payload.valueSB%58L", 4L); - CheckCalc(p, "payload.valueBB%58L", 4L); - CheckCalc(p, "payload.valueFB%58L", 4f); - CheckCalc(p, "payload.valueDB%58L", 4d); - CheckCalc(p, "payload.valueJB%58L", 4L); - CheckCalc(p, "payload.valueIB%58L", 4L); - - CheckCalc(p, "payload.valueS%58L", 4L); - CheckCalc(p, "payload.valueB%58L", 4L); - CheckCalc(p, "payload.valueF%58L", 4f); - CheckCalc(p, "payload.valueD%58L", 4d); - CheckCalc(p, "payload.valueJ%58L", 4L); - CheckCalc(p, "payload.valueI%58L", 4L); - - CheckCalc(p, "payload.valueSB%payload.valueJB58", 4L); - CheckCalc(p, "payload.valueBB%payload.valueJB58", 4L); - CheckCalc(p, "payload.valueFB%payload.valueJB58", 4f); - CheckCalc(p, "payload.valueDB%payload.valueJB58", 4d); - CheckCalc(p, "payload.valueJB%payload.valueJB58", 4L); - CheckCalc(p, "payload.valueIB%payload.valueJB58", 4L); - - CheckCalc(p, "payload.valueS%payload.valueJB58", 4L); - CheckCalc(p, "payload.valueB%payload.valueJB58", 4L); - CheckCalc(p, "payload.valueF%payload.valueJB58", 4f); - CheckCalc(p, "payload.valueD%payload.valueJB58", 4d); - CheckCalc(p, "payload.valueJ%payload.valueJB58", 4L); - CheckCalc(p, "payload.valueI%payload.valueJB58", 4L); - - // right is an int - CheckCalc(p, "payload.valueSB%58", 4); - CheckCalc(p, "payload.valueBB%58", 4); - CheckCalc(p, "payload.valueFB%58", 4f); - CheckCalc(p, "payload.valueDB%58", 4d); - CheckCalc(p, "payload.valueJB%58", 4L); - CheckCalc(p, "payload.valueIB%58", 4); - - CheckCalc(p, "payload.valueS%58", 4); - CheckCalc(p, "payload.valueB%58", 4); - CheckCalc(p, "payload.valueF%58", 4f); - CheckCalc(p, "payload.valueD%58", 4d); - CheckCalc(p, "payload.valueJ%58", 4L); - CheckCalc(p, "payload.valueI%58", 4); - - CheckCalc(p, "payload.valueSB%payload.valueIB58", 4); - CheckCalc(p, "payload.valueBB%payload.valueIB58", 4); - CheckCalc(p, "payload.valueFB%payload.valueIB58", 4f); - CheckCalc(p, "payload.valueDB%payload.valueIB58", 4d); - CheckCalc(p, "payload.valueJB%payload.valueIB58", 4L); - CheckCalc(p, "payload.valueIB%payload.valueIB58", 4); - - CheckCalc(p, "payload.valueS%payload.valueIB58", 4); - CheckCalc(p, "payload.valueB%payload.valueIB58", 4); - CheckCalc(p, "payload.valueF%payload.valueIB58", 4f); - CheckCalc(p, "payload.valueD%payload.valueIB58", 4d); - CheckCalc(p, "payload.valueJ%payload.valueIB58", 4L); - CheckCalc(p, "payload.valueI%payload.valueIB58", 4); - - // right is a short - CheckCalc(p, "payload.valueSB%payload.valueS18", 12); - CheckCalc(p, "payload.valueBB%payload.valueS18", 12); - CheckCalc(p, "payload.valueFB%payload.valueS18", 12f); - CheckCalc(p, "payload.valueDB%payload.valueS18", 12d); - CheckCalc(p, "payload.valueJB%payload.valueS18", 12L); - CheckCalc(p, "payload.valueIB%payload.valueS18", 12); - - CheckCalc(p, "payload.valueS%payload.valueS18", 12); - CheckCalc(p, "payload.valueB%payload.valueS18", 12); - CheckCalc(p, "payload.valueF%payload.valueS18", 12f); - CheckCalc(p, "payload.valueD%payload.valueS18", 12d); - CheckCalc(p, "payload.valueJ%payload.valueS18", 12L); - CheckCalc(p, "payload.valueI%payload.valueS18", 12); - - CheckCalc(p, "payload.valueSB%payload.valueSB18", 12); - CheckCalc(p, "payload.valueBB%payload.valueSB18", 12); - CheckCalc(p, "payload.valueFB%payload.valueSB18", 12f); - CheckCalc(p, "payload.valueDB%payload.valueSB18", 12d); - CheckCalc(p, "payload.valueJB%payload.valueSB18", 12L); - CheckCalc(p, "payload.valueIB%payload.valueSB18", 12); - - CheckCalc(p, "payload.valueS%payload.valueSB18", 12); - CheckCalc(p, "payload.valueB%payload.valueSB18", 12); - CheckCalc(p, "payload.valueF%payload.valueSB18", 12f); - CheckCalc(p, "payload.valueD%payload.valueSB18", 12d); - CheckCalc(p, "payload.valueJ%payload.valueSB18", 12L); - CheckCalc(p, "payload.valueI%payload.valueSB18", 12); - - // right is a byte - CheckCalc(p, "payload.valueSB%payload.valueB18", 12); - CheckCalc(p, "payload.valueBB%payload.valueB18", 12); - CheckCalc(p, "payload.valueFB%payload.valueB18", 12f); - CheckCalc(p, "payload.valueDB%payload.valueB18", 12d); - CheckCalc(p, "payload.valueJB%payload.valueB18", 12L); - CheckCalc(p, "payload.valueIB%payload.valueB18", 12); - - CheckCalc(p, "payload.valueS%payload.valueB18", 12); - CheckCalc(p, "payload.valueB%payload.valueB18", 12); - CheckCalc(p, "payload.valueF%payload.valueB18", 12f); - CheckCalc(p, "payload.valueD%payload.valueB18", 12d); - CheckCalc(p, "payload.valueJ%payload.valueB18", 12L); - CheckCalc(p, "payload.valueI%payload.valueB18", 12); - - CheckCalc(p, "payload.valueSB%payload.valueBB18", 12); - CheckCalc(p, "payload.valueBB%payload.valueBB18", 12); - CheckCalc(p, "payload.valueFB%payload.valueBB18", 12f); - CheckCalc(p, "payload.valueDB%payload.valueBB18", 12d); - CheckCalc(p, "payload.valueJB%payload.valueBB18", 12L); - CheckCalc(p, "payload.valueIB%payload.valueBB18", 12); - - CheckCalc(p, "payload.valueS%payload.valueBB18", 12); - CheckCalc(p, "payload.valueB%payload.valueBB18", 12); - CheckCalc(p, "payload.valueF%payload.valueBB18", 12f); - CheckCalc(p, "payload.valueD%payload.valueBB18", 12d); - CheckCalc(p, "payload.valueJ%payload.valueBB18", 12L); - CheckCalc(p, "payload.valueI%payload.valueBB18", 12); - } - - [Fact] - public void OpMultiply() - { - _expression = Parse("2*2"); - Assert.Equal(4, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4, _expression.GetValue()); - - _expression = Parse("2L*2L"); - Assert.Equal(4L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4L, _expression.GetValue()); - - _expression = Parse("2.0f*2.0f"); - Assert.Equal(4F, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4F, _expression.GetValue()); - - _expression = Parse("3.0d*4.0d"); - Assert.Equal(12D, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(12D, _expression.GetValue()); - - _expression = Parse($"T(float).Parse('2.0',{InvariantCulturePropertyAccess})*6"); - Assert.Equal(12.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(12.0f, _expression.GetValue()); - - _expression = Parse($"T(Single).Parse('8.0',{InvariantCulturePropertyAccess})*T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.Equal(24.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(24.0f, _expression.GetValue()); - - _expression = Parse("11L*T(long).Parse('4')"); - Assert.Equal(44L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(44L, _expression.GetValue()); - - _expression = Parse("T(long).Parse('9')*6"); - Assert.Equal(54L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(54L, _expression.GetValue()); - - _expression = Parse("T(long).Parse('4')*T(long).Parse('3')"); - Assert.Equal(12L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(12L, _expression.GetValue()); - - _expression = Parse("8L*T(long).Parse('2')"); - Assert.Equal(16L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(16L, _expression.GetValue()); - - _expression = Parse($"T(float).Parse('8.0',{InvariantCulturePropertyAccess})*-T(Single).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.Equal(-24.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-24.0f, _expression.GetValue()); - } - - [Fact] - public void OpDivide() - { - _expression = Parse("2/2"); - Assert.Equal(1, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1, _expression.GetValue()); - - _expression = Parse("2L/2L"); - Assert.Equal(1L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1L, _expression.GetValue()); - - _expression = Parse("2.0f/2.0f"); - Assert.Equal(1f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1f, _expression.GetValue()); - - _expression = Parse("4.0d/4.0d"); - Assert.Equal(1d, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1d, _expression.GetValue()); - - _expression = Parse($"T(float).Parse('6.0',{InvariantCulturePropertyAccess})/2"); - Assert.Equal(3.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(3.0f, _expression.GetValue()); - - _expression = Parse($"T(Single).Parse('8.0',{InvariantCulturePropertyAccess})/T(float).Parse('2.0',{InvariantCulturePropertyAccess})"); - Assert.Equal(4.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4.0f, _expression.GetValue()); - - _expression = Parse("12L/T(long).Parse('4')"); - Assert.Equal(3L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(3L, _expression.GetValue()); - - _expression = Parse("T(long).Parse('44')/11"); - Assert.Equal(4L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4L, _expression.GetValue()); - - _expression = Parse("T(long).Parse('4')/T(long).Parse('2')"); - Assert.Equal(2L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2L, _expression.GetValue()); - - _expression = Parse("8L/T(long).Parse('2')"); - Assert.Equal(4L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(4L, _expression.GetValue()); - - _expression = Parse($"T(float).Parse('8.0',{InvariantCulturePropertyAccess})/-T(Single).Parse('4.0',{InvariantCulturePropertyAccess})"); - Assert.Equal(-2.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(-2.0f, _expression.GetValue()); - } - - [Fact] - public void OpModulus_12041() - { - _expression = Parse("2%2"); - Assert.Equal(0, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(0, _expression.GetValue()); - - _expression = Parse("Payload%2==0"); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(4))); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(5))); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(4))); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(5))); - AssertCanCompile(_expression); - - _expression = Parse("8%3"); - Assert.Equal(2, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2, _expression.GetValue()); - - _expression = Parse("17L%5L"); - Assert.Equal(2L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2L, _expression.GetValue()); - - _expression = Parse("3.0f%2.0f"); - Assert.Equal(1.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1.0f, _expression.GetValue()); - - _expression = Parse("3.0d%4.0d"); - Assert.Equal(3.0d, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(3.0d, _expression.GetValue()); - - _expression = Parse($"T(float).Parse('6.0',{InvariantCulturePropertyAccess})%2"); - Assert.Equal(0.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(0.0f, _expression.GetValue()); - - _expression = Parse($"T(Single).Parse('6.0',{InvariantCulturePropertyAccess})%4"); - Assert.Equal(2.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2.0f, _expression.GetValue()); - - _expression = Parse($"T(Single).Parse('8.0',{InvariantCulturePropertyAccess})%T(float).Parse('3.0',{InvariantCulturePropertyAccess})"); - Assert.Equal(2.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2.0f, _expression.GetValue()); - - _expression = Parse("13L%T(long).Parse('4')"); - Assert.Equal(1L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1L, _expression.GetValue()); - - _expression = Parse("T(Int64).Parse('44')%12"); - Assert.Equal(8L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(8L, _expression.GetValue()); - - _expression = Parse("T(long).Parse('9')%T(long).Parse('2')"); - Assert.Equal(1L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1L, _expression.GetValue()); - - _expression = Parse("7L%T(long).Parse('2')"); - Assert.Equal(1L, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1L, _expression.GetValue()); - - _expression = Parse($"T(float).Parse('9.0',{InvariantCulturePropertyAccess})%-T(float).Parse('4.0',{InvariantCulturePropertyAccess})"); - Assert.Equal(1.0f, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(1.0f, _expression.GetValue()); - } - - [Fact] - public void FailsWhenSettingContextForExpression_SPR12326() - { - var parser = new SpelExpressionParser(new SpelParserOptions(SpelCompilerMode.Off)); - var person = new Person3("foo", 1); - var expression = parser.ParseRaw("#it?.Age?.Equals([0])") as SpelExpression; - - var context = new StandardEvaluationContext(new object[] - { - 1 - }); - - context.SetVariable("it", person); - expression.EvaluationContext = context; - Assert.True(expression.GetValue()); - - // This will trigger compilation (second usage) - Assert.True(expression.GetValue()); - context.SetVariable("it", null); - Assert.Null(expression.GetValue()); - - AssertCanCompile(expression); - - context.SetVariable("it", person); - Assert.True(expression.GetValue()); - context.SetVariable("it", null); - Assert.Null(expression.GetValue()); - } - - // Test variants of using T(...) and static/non-static method/property/field references. - - [Fact] - public void ConstructorReference_SPR13781() - { - // Static const field access on a T() referenced type - _expression = Parser.ParseExpression("T(Int32).MaxValue"); - Assert.Equal(2_147_483_647, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(2_147_483_647, _expression.GetValue()); - - // Static field access on a T() referenced type - _expression = Parser.ParseExpression("T(String).Empty"); - Assert.Equal(string.Empty, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(string.Empty, _expression.GetValue()); - - // Property access on an instance of System.Type object - _expression = Parser.ParseExpression("T(String).Name"); - Assert.Equal("String", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("String", _expression.GetValue()); - - // Now the type reference isn't on the stack, and needs loading - var context = new StandardEvaluationContext(typeof(string)); - _expression = Parser.ParseExpression("Name"); - Assert.Equal("String", _expression.GetValue(context)); - AssertCanCompile(_expression); - Assert.Equal("String", _expression.GetValue(context)); - - _expression = Parser.ParseExpression("T(String).get_Name()"); - Assert.Equal("String", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("String", _expression.GetValue()); - - // These tests below verify that the chain of static accesses (either method/property or field) - // leave the right thing on top of the stack for processing by any outer consuming code. - // Here the consuming code is the String.valueOf() function. If the wrong thing were on - // the stack (for example if the compiled code for static methods wasn't popping the - // previous thing off the stack) the valueOf() would operate on the wrong value. - _expression = Parser.ParseExpression("T(String).Format('Format:{0}', T(String).Name.Format('Format:{0}', 1))"); - Assert.Equal("Format:Format:1", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("Format:Format:1", _expression.GetValue()); - - _expression = Parser.ParseExpression( - $"T(String).Format('Format:{{0}}', T({typeof(SpelCompilationCoverageTests).FullName}$StaticsHelper).MethodA().MethodA().MethodB())"); - - Assert.Equal("Format:mb", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("Format:mb", _expression.GetValue()); - - _expression = Parser.ParseExpression( - $"T(String).Format('Format:{{0}}', T({typeof(SpelCompilationCoverageTests).FullName}$StaticsHelper).Fielda.Fielda.Fieldb)"); - - Assert.Equal("Format:fb", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("Format:fb", _expression.GetValue()); - - _expression = Parser.ParseExpression( - $"T(String).Format('Format:{{0}}', T({typeof(SpelCompilationCoverageTests).FullName}$StaticsHelper).PropertyA.PropertyA.PropertyB)"); - - Assert.Equal("Format:pb", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("Format:pb", _expression.GetValue()); - - _expression = Parser.ParseExpression( - $"T(String).Format('Format:{{0}}', T({typeof(SpelCompilationCoverageTests).FullName}$StaticsHelper).Fielda.MethodA().PropertyA.Fieldb)"); - - Assert.Equal("Format:fb", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("Format:fb", _expression.GetValue()); - - _expression = Parser.ParseExpression("T(String).Format('Format:{0}', Fielda.Fieldb)"); - Assert.Equal("Format:fb", _expression.GetValue(StaticsHelper.sh)); - AssertCanCompile(_expression); - Assert.Equal("Format:fb", _expression.GetValue(StaticsHelper.sh)); - - _expression = Parser.ParseExpression("T(String).Format('Format:{0}', PropertyA.PropertyB)"); - Assert.Equal("Format:pb", _expression.GetValue(StaticsHelper.sh)); - AssertCanCompile(_expression); - Assert.Equal("Format:pb", _expression.GetValue(StaticsHelper.sh)); - - _expression = Parser.ParseExpression("T(String).Format('Format:{0}', MethodA().MethodB())"); - Assert.Equal("Format:mb", _expression.GetValue(StaticsHelper.sh)); - AssertCanCompile(_expression); - Assert.Equal("Format:mb", _expression.GetValue(StaticsHelper.sh)); - } - - [Fact] - public void ConstructorReference_SPR12326() - { - string type = GetType().FullName; - string prefix = $"new {type}$Obj"; - - _expression = Parser.ParseExpression($"{prefix}([0])"); - - Assert.Equal("test", ((Obj)_expression.GetValue(new object[] - { - "test" - })).Param1); - - AssertCanCompile(_expression); - - Assert.Equal("test", ((Obj)_expression.GetValue(new object[] - { - "test" - })).Param1); - - _expression = Parser.ParseExpression($"{prefix}2('foo','bar').Output"); - Assert.Equal("foobar", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("foobar", _expression.GetValue()); - - _expression = Parser.ParseExpression($"{prefix}2('foo').Output"); - Assert.Equal("foo", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("foo", _expression.GetValue()); - - _expression = Parser.ParseExpression($"{prefix}2().Output"); - Assert.Equal(string.Empty, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(string.Empty, _expression.GetValue()); - - _expression = Parser.ParseExpression($"{prefix}3(1,2,3).Output"); - Assert.Equal("123", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("123", _expression.GetValue()); - - _expression = Parser.ParseExpression($"{prefix}3(1).Output"); - Assert.Equal("1", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("1", _expression.GetValue()); - - _expression = Parser.ParseExpression($"{prefix}3().Output"); - Assert.Equal(string.Empty, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(string.Empty, _expression.GetValue()); - - _expression = Parser.ParseExpression($"{prefix}3('abc',5.0f,1,2,3).Output"); - Assert.Equal("abc:5:123", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("abc:5:123", _expression.GetValue()); - - _expression = Parser.ParseExpression($"{prefix}3('abc',5.0f,1).Output"); - Assert.Equal("abc:5:1", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("abc:5:1", _expression.GetValue()); - - _expression = Parser.ParseExpression($"{prefix}3('abc',5.0f).Output"); - Assert.Equal("abc:5:", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("abc:5:", _expression.GetValue()); - - _expression = Parser.ParseExpression($"{prefix}4(#root).Output"); - - Assert.Equal("123", _expression.GetValue(new[] - { - 1, - 2, - 3 - })); - - AssertCanCompile(_expression); - - Assert.Equal("123", _expression.GetValue(new[] - { - 1, - 2, - 3 - })); - } - - [Fact] - public void MethodReferenceMissingCastAndRootObjectAccessing_SPR12326() - { - // Need boxing code on the 1 so that toString() can be called - _expression = Parser.ParseExpression("1.ToString()"); - Assert.Equal("1", _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal("1", _expression.GetValue()); - - _expression = Parser.ParseExpression("#it?.Age.Equals([0])"); - var person = new Person(1); - - var context = new StandardEvaluationContext(new object[] - { - person.Age - }); - - context.SetVariable("it", person); - Assert.True(_expression.GetValue(context)); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(context)); - - var parser2 = new SpelExpressionParser(new SpelParserOptions(SpelCompilerMode.Immediate)); - IExpression ex = parser2.ParseExpression("#it?.Age.Equals([0])"); - - context = new StandardEvaluationContext(new object[] - { - person.Age - }); - - context.SetVariable("it", person); - Assert.True(ex.GetValue(context)); - Assert.True(ex.GetValue(context)); - - var person2 = new PersonInOtherPackage(1); - ex = parser2.ParseRaw("#it?.Age.Equals([0])"); - - context = new StandardEvaluationContext(new object[] - { - person2.Age - }); - - context.SetVariable("it", person2); - Assert.True(ex.GetValue(context)); - Assert.True(ex.GetValue(context)); - - ex = parser2.ParseRaw("#it?.Age.Equals([0])"); - - context = new StandardEvaluationContext(new object[] - { - person2.Age - }); - - context.SetVariable("it", person2); - Assert.True(ex.GetValue(context)); - Assert.True(ex.GetValue(context)); - } - - [Fact] - public void ConstructorReference() - { - string testClass8 = $"{typeof(SpelCompilationCoverageTests).FullName}$TestClass8"; - - // multi arg ctor that includes primitives - _expression = Parser.ParseExpression($"new {testClass8}(42,'123',4.0d,True)"); - Assert.IsType(_expression.GetValue()); - AssertCanCompile(_expression); - object o = _expression.GetValue(); - Assert.IsType(o); - var tc8 = (TestClass8)o; - Assert.Equal(42, tc8.I); - Assert.Equal("123", tc8.S); - Assert.Equal(4.0d, tc8.D); - Assert.True(tc8.Z); - - // pass primitive to reference type ctor - _expression = Parser.ParseExpression($"new {testClass8}(42)"); - Assert.IsType(_expression.GetValue()); - AssertCanCompile(_expression); - o = _expression.GetValue(); - Assert.IsType(o); - tc8 = (TestClass8)o; - Assert.Equal(42, tc8.I); - - // private class, can't compile it - string testClass9 = $"{typeof(SpelCompilationCoverageTests).FullName}$TestClass9"; - _expression = Parser.ParseExpression($"new {testClass9}(42)"); - Assert.IsType(_expression.GetValue()); - AssertCantCompile(_expression); - } - - [Fact] - public void MethodReferenceReflectiveMethodSelectionWithVarargs() - { - var tc = new TestClass10(); - - // Should call the non varargs version of Concat1 - // (which causes the '::' prefix in test output) - _expression = Parser.ParseExpression("Concat1('test')"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("::test", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("::test", tc.S); - tc.Reset(); - - // This will call the varargs Concat1 with an empty array - _expression = Parser.ParseExpression("Concat1()"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal(string.Empty, tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal(string.Empty, tc.S); - tc.Reset(); - - // Should call the non varargs version of Concat2 - // (which causes the '::' prefix in test output) - _expression = Parser.ParseExpression("Concat2('test')"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("::test", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("::test", tc.S); - tc.Reset(); - - // This will call the varargs Concat2 with an empty array - _expression = Parser.ParseExpression("Concat2()"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal(string.Empty, tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal(string.Empty, tc.S); - tc.Reset(); - } - - [Fact] - public void MethodReferenceVarargs() - { - var tc = new TestClass5(); - - // varargs string - _expression = Parser.ParseExpression("Eleven()"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal(string.Empty, tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal(string.Empty, tc.S); - tc.Reset(); - - // varargs string - _expression = Parser.ParseExpression("Eleven('aaa')"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("aaa", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("aaa", tc.S); - tc.Reset(); - - // varargs string - _expression = Parser.ParseExpression("Eleven(StringArray)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("aaabbbccc", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("aaabbbccc", tc.S); - tc.Reset(); - - // varargs string - _expression = Parser.ParseExpression("Eleven('aaa','bbb','ccc')"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("aaabbbccc", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("aaabbbccc", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Sixteen('aaa','bbb','ccc')"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("aaabbbccc", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("aaabbbccc", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Twelve(1,2,3)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal(6, tc.I); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal(6, tc.I); - tc.Reset(); - - _expression = Parser.ParseExpression("Twelve(1)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal(1, tc.I); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal(1, tc.I); - tc.Reset(); - - _expression = Parser.ParseExpression("Thirteen('aaa','bbb','ccc')"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("aaa::bbbccc", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("aaa::bbbccc", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Thirteen('aaa')"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("aaa::", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("aaa::", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Fourteen('aaa',StringArray,StringArray)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("aaa::{aaabbbccc}{aaabbbccc}", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("aaa::{aaabbbccc}{aaabbbccc}", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Fifteen('aaa',IntArray,IntArray)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("aaa::{112233}{112233}", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("aaa::{112233}{112233}", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayz(True,True,False)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("TrueTrueFalse", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("TrueTrueFalse", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayz(True)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("True", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("True", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrays(S1,S2,S3)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrays(S1)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("1", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("1", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayd(1.0d,2.0d,3.0d)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayd(1.0d)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("1", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("1", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayj(L1,L2,L3)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayj(L1)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("1", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("1", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayc(C1,C2,C3)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("abc", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("abc", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayc(C1)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("a", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("a", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayb(B1,B2,B3)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("656667", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("656667", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayb(B1)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("65", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("65", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayf(F1,F2,F3)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("Arrayf(F1)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("1", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("1", tc.S); - tc.Reset(); - } - - [Fact] - public void MethodReference() - { - var tc = new TestClass5(); - - _expression = Parser.ParseExpression("One()"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal(1, tc.I); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal(1, tc.I); - tc.Reset(); - - _expression = Parser.ParseExpression("Two()"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal(1, TestClass5._I); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal(1, TestClass5._I); - tc.Reset(); - - _expression = Parser.ParseExpression("Three()"); - AssertCantCompile(_expression); - Assert.Equal("hello", _expression.GetValue(tc)); - AssertCanCompile(_expression); - tc.Reset(); - Assert.Equal("hello", _expression.GetValue(tc)); - tc.Reset(); - - _expression = Parser.ParseExpression("Four()"); - AssertCantCompile(_expression); - Assert.Equal(3_277_700L, _expression.GetValue(tc)); - AssertCanCompile(_expression); - tc.Reset(); - Assert.Equal(3_277_700L, _expression.GetValue(tc)); - tc.Reset(); - - // static method, reference type return - _expression = Parser.ParseExpression("Five()"); - AssertCantCompile(_expression); - Assert.Equal("hello", _expression.GetValue(tc)); - AssertCanCompile(_expression); - tc.Reset(); - Assert.Equal("hello", _expression.GetValue(tc)); - tc.Reset(); - - // static method, primitive type return - _expression = Parser.ParseExpression("Six()"); - AssertCantCompile(_expression); - Assert.Equal(3_277_700L, _expression.GetValue(tc)); - AssertCanCompile(_expression); - tc.Reset(); - Assert.Equal(3_277_700L, _expression.GetValue(tc)); - tc.Reset(); - - // non-static method, one parameter of reference type - _expression = Parser.ParseExpression("Seven(\"foo\")"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("foo", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("foo", tc.S); - tc.Reset(); - - // static method, one parameter of reference type - _expression = Parser.ParseExpression("Eight(\"bar\")"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("bar", TestClass5._S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("bar", TestClass5._S); - tc.Reset(); - - // non-static method, one parameter of primitive type - _expression = Parser.ParseExpression("Nine(231)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal(231, tc.I); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal(231, tc.I); - tc.Reset(); - - // static method, one parameter of reference type - _expression = Parser.ParseExpression("Ten(111)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal(111, TestClass5._I); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal(111, TestClass5._I); - tc.Reset(); - - // method that gets type converted parameters - - // Converting from an int to a string - _expression = Parser.ParseExpression("Seven(123)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - AssertCantCompile(_expression); // Uncompilable as argument conversion is occurring - tc.Reset(); - - IExpression expression = Parser.ParseExpression("'abcd'.Substring(Index1,Index2)"); - string resultI = expression.GetValue(new TestClass1()); - AssertCanCompile(expression); - string resultC = expression.GetValue(new TestClass1()); - Assert.Equal("bcd", resultI); - Assert.Equal("bcd", resultC); - - // Converting from an int to a Number - _expression = Parser.ParseExpression("TakeNumber(123)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("123", tc.S); - tc.Reset(); - - // Passing a subtype - _expression = Parser.ParseExpression("TakeNumber(T(int).Parse('42'))"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("42", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - _expression.GetValue(tc); - Assert.Equal("42", tc.S); - tc.Reset(); - - _expression = Parser.ParseExpression("TakeString(T(int).Parse('42'))"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("42", tc.S); - AssertCantCompile(_expression); - tc.Reset(); - AssertCantCompile(_expression); - } - - [Fact] - public void ErrorHandling() - { - var tc = new TestClass5(); - - // changing target - // from primitive array to reference type array - int[] intArray = - { - 1, - 2, - 3 - }; - - string[] strings = - { - "a", - "b", - "c" - }; - - _expression = Parser.ParseExpression("[1]"); - Assert.Equal(2, _expression.GetValue(intArray)); - AssertCanCompile(_expression); - Assert.Throws(() => _expression.GetValue(strings)); - SpelCompiler.RevertToInterpreted(_expression); - Assert.Equal("b", _expression.GetValue(strings)); - AssertCanCompile(_expression); - Assert.Equal("b", _expression.GetValue(strings)); - - tc.Field = "foo"; - _expression = Parser.ParseExpression("Seven(Field)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("foo", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - tc.Field = "bar"; - _expression.GetValue(tc); - - // method with changing parameter types (change reference type) - tc.Obj = "foo"; - _expression = Parser.ParseExpression("Seven(Obj)"); - AssertCantCompile(_expression); - _expression.GetValue(tc); - Assert.Equal("foo", tc.S); - AssertCanCompile(_expression); - tc.Reset(); - tc.Obj = 42; - Assert.Throws(() => _expression.GetValue(tc)); - - // method with changing target - _expression = Parser.ParseExpression("#root.get_Chars(0)"); - Assert.Equal('a', _expression.GetValue("abc")); - AssertCanCompile(_expression); - Assert.Throws(() => _expression.GetValue(42)); - } - - [Fact] - public void MethodReference_StaticMethod() - { - IExpression expression = Parser.ParseExpression("T(int).Parse('42')"); - int resultI = expression.GetValue(new TestClass1()); - AssertCanCompile(expression); - int resultC = expression.GetValue(new TestClass1()); - Assert.Equal(42, resultI); - Assert.Equal(42, resultC); - } - - [Fact] - public void MethodReference_LiteralArguments_int() - { - IExpression expression = Parser.ParseExpression("'abcd'.Substring(1,3)"); - string resultI = expression.GetValue(new TestClass1()); - AssertCanCompile(expression); - string resultC = expression.GetValue(new TestClass1()); - Assert.Equal("bcd", resultI); - Assert.Equal("bcd", resultC); - } - - [Fact] - public void MethodReference_SimpleInstanceMethodNoArg() - { - IExpression expression = Parser.ParseExpression("ToString()"); - string resultI = expression.GetValue(42); - AssertCanCompile(expression); - string resultC = expression.GetValue(42); - Assert.Equal("42", resultI); - Assert.Equal("42", resultC); - } - - [Fact] - public void MethodReference_SimpleInstanceMethodNoArgReturnPrimitive() - { - IExpression expression = Parser.ParseExpression("GetHashCode()"); - int resultI = expression.GetValue(42); - AssertCanCompile(expression); - int resultC = expression.GetValue(42); - Assert.Equal(resultI, resultC); - } - - [Fact] - public void MethodReference_SimpleInstanceMethodOneArgReturnPrimitive1() - { - IExpression expression = Parser.ParseExpression("IndexOf('b')"); - int resultI = expression.GetValue("abc"); - AssertCanCompile(expression); - int resultC = expression.GetValue("abc"); - Assert.Equal(1, resultI); - Assert.Equal(1, resultC); - } - - [Fact] - public void MethodReference_SimpleInstanceMethodOneArgReturnPrimitive2() - { - IExpression expression = Parser.ParseExpression("get_Chars(2)"); - char resultI = expression.GetValue("abc"); - AssertCanCompile(expression); - char resultC = expression.GetValue("abc"); - Assert.Equal('c', resultI); - Assert.Equal('c', resultC); - } - - [Fact] - public void CompoundExpression() - { - var payload = new Payload(); - _expression = Parser.ParseExpression("DR[0]"); - Assert.Equal("instanceof Two", _expression.GetValue(payload).ToString()); - AssertCanCompile(_expression); - Assert.Equal("instanceof Two", _expression.GetValue(payload).ToString()); - - _expression = Parser.ParseExpression("Holder.Threeee"); - Assert.IsType(_expression.GetValue(payload)); - AssertCanCompile(_expression); - Assert.IsType(_expression.GetValue(payload)); - - _expression = Parser.ParseExpression("DR[0]"); - Assert.IsType(_expression.GetValue(payload)); - AssertCanCompile(_expression); - Assert.IsType(_expression.GetValue(payload)); - - _expression = Parser.ParseExpression("DR[0].Threeee"); - Assert.IsType(_expression.GetValue(payload)); - AssertCanCompile(_expression); - Assert.IsType(_expression.GetValue(payload)); - - _expression = Parser.ParseExpression("DR[0].Threeee.Four"); - Assert.Equal(.04d, _expression.GetValue(payload)); - AssertCanCompile(_expression); - Assert.Equal(.04d, _expression.GetValue(payload)); - } - - [Fact] - public void MixingItUp_IndexerOpEqTernary() - { - var m = new Dictionary - { - { "andy", "778" } - }; - - _expression = Parse("['andy']==null?1:2"); - Assert.Equal(2, _expression.GetValue(m)); - AssertCanCompile(_expression); - Assert.Equal(2, _expression.GetValue(m)); - m.Remove("andy"); - Assert.Equal(1, _expression.GetValue(m)); - } - - [Fact] - public void PropertyReference() - { - var tc = new TestClass6(); - - // non static field - _expression = Parser.ParseExpression("Orange"); - AssertCantCompile(_expression); - Assert.Equal("value1", _expression.GetValue(tc)); - AssertCanCompile(_expression); - Assert.Equal("value1", _expression.GetValue(tc)); - - // static field - _expression = Parser.ParseExpression("Apple"); - AssertCantCompile(_expression); - Assert.Equal("value2", _expression.GetValue(tc)); - AssertCanCompile(_expression); - Assert.Equal("value2", _expression.GetValue(tc)); - - // non static getter - _expression = Parser.ParseExpression("Banana"); - AssertCantCompile(_expression); - Assert.Equal("value3", _expression.GetValue(tc)); - AssertCanCompile(_expression); - Assert.Equal("value3", _expression.GetValue(tc)); - - // static getter - _expression = Parser.ParseExpression("Plum"); - AssertCantCompile(_expression); - Assert.Equal("value4", _expression.GetValue(tc)); - AssertCanCompile(_expression); - Assert.Equal("value4", _expression.GetValue(tc)); - } - - [Fact] - public void Indexer() - { - string[] sss = - { - "a", - "b", - "c" - }; - - int[] iss = - { - 8, - 9, - 10 - }; - - double[] ds = - { - 3.0d, - 4.0d, - 5.0d - }; - - long[] ls = - { - 2L, - 3L, - 4L - }; - - short[] ss = - { - 33, - 44, - 55 - }; - - float[] fs = - { - 6.0f, - 7.0f, - 8.0f - }; - - byte[] bs = - { - 2, - 3, - 4 - }; - - char[] cs = - { - 'a', - 'b', - 'c' - }; - - _expression = Parser.ParseExpression("[0]"); - Assert.Equal("a", _expression.GetValue(sss)); - AssertCanCompile(_expression); - Assert.Equal("a", _expression.GetValue(sss)); - - _expression = Parser.ParseExpression("[2]"); - Assert.Equal(10, _expression.GetValue(iss)); - AssertCanCompile(_expression); - Assert.Equal(10, _expression.GetValue(iss)); - - _expression = Parser.ParseExpression("[1]"); - Assert.Equal(4.0d, _expression.GetValue(ds)); - AssertCanCompile(_expression); - Assert.Equal(4.0d, _expression.GetValue(ds)); - - _expression = Parser.ParseExpression("[0]"); - Assert.Equal(2L, _expression.GetValue(ls)); - AssertCanCompile(_expression); - Assert.Equal(2L, _expression.GetValue(ls)); - - _expression = Parser.ParseExpression("[2]"); - Assert.Equal((short)55, _expression.GetValue(ss)); - AssertCanCompile(_expression); - Assert.Equal((short)55, _expression.GetValue(ss)); - - _expression = Parser.ParseExpression("[0]"); - Assert.Equal(6.0f, _expression.GetValue(fs)); - AssertCanCompile(_expression); - Assert.Equal(6.0f, _expression.GetValue(fs)); - - _expression = Parser.ParseExpression("[2]"); - Assert.Equal((byte)4, _expression.GetValue(bs)); - AssertCanCompile(_expression); - Assert.Equal((byte)4, _expression.GetValue(bs)); - - _expression = Parser.ParseExpression("[1]"); - Assert.Equal('b', _expression.GetValue(cs)); - AssertCanCompile(_expression); - Assert.Equal('b', _expression.GetValue(cs)); - - // Collections - var strings = new List - { - "aaa", - "bbb", - "ccc" - }; - - _expression = Parser.ParseExpression("[1]"); - Assert.Equal("bbb", _expression.GetValue(strings)); - AssertCanCompile(_expression); - Assert.Equal("bbb", _expression.GetValue(strings)); - - var intArray = new List - { - 123, - 456, - 789 - }; - - _expression = Parser.ParseExpression("[2]"); - Assert.Equal(789, _expression.GetValue(intArray)); - AssertCanCompile(_expression); - Assert.Equal(789, _expression.GetValue(intArray)); - - var map1 = new Dictionary - { - { "aaa", 111 }, - { "bbb", 222 }, - { "ccc", 333 } - }; - - _expression = Parser.ParseExpression("['aaa']"); - Assert.Equal(111, _expression.GetValue(map1)); - AssertCanCompile(_expression); - Assert.Equal(111, _expression.GetValue(map1)); - - var tc = new TestClass6(); - _expression = Parser.ParseExpression("['Orange']"); - Assert.Equal("value1", _expression.GetValue(tc)); - AssertCanCompile(_expression); - Assert.Equal("value1", _expression.GetValue(tc)); - - _expression = Parser.ParseExpression("['Peach']"); - Assert.Equal(34L, _expression.GetValue(tc)); - AssertCanCompile(_expression); - Assert.Equal(34L, _expression.GetValue(tc)); - - _expression = Parser.ParseExpression("['Banana']"); - Assert.Equal("value3", _expression.GetValue(tc)); - AssertCanCompile(_expression); - Assert.Equal("value3", _expression.GetValue(tc)); - - // list of arrays - var listOfStringArrays = new List - { - new[] - { - "a", - "b", - "c" - }, - new[] - { - "d", - "e", - "f" - } - }; - - _expression = Parser.ParseExpression("[1]"); - Assert.Equal("d e f", Stringify(_expression.GetValue(listOfStringArrays))); - AssertCanCompile(_expression); - Assert.Equal("d e f", Stringify(_expression.GetValue(listOfStringArrays))); - - _expression = Parser.ParseExpression("[1][0]"); - Assert.Equal("d", Stringify(_expression.GetValue(listOfStringArrays))); - AssertCanCompile(_expression); - Assert.Equal("d", Stringify(_expression.GetValue(listOfStringArrays))); - - var listOfIntegerArrays = new List - { - new[] - { - 1, - 2, - 3 - }, - new[] - { - 4, - 5, - 6 - } - }; - - _expression = Parser.ParseExpression("[0]"); - Assert.Equal("1 2 3", Stringify(_expression.GetValue(listOfIntegerArrays))); - AssertCanCompile(_expression); - Assert.Equal("1 2 3", Stringify(_expression.GetValue(listOfIntegerArrays))); - - _expression = Parser.ParseExpression("[0][1]"); - Assert.Equal(2, _expression.GetValue(listOfIntegerArrays)); - AssertCanCompile(_expression); - Assert.Equal(2, _expression.GetValue(listOfIntegerArrays)); - - // array of lists - var stringArrayOfLists = new List[2]; - - stringArrayOfLists[0] = new List - { - "a", - "b", - "c" - }; - - stringArrayOfLists[1] = new List - { - "d", - "e", - "f" - }; - - _expression = Parser.ParseExpression("[1]"); - Assert.Equal("d e f", Stringify(_expression.GetValue(stringArrayOfLists))); - AssertCanCompile(_expression); - Assert.Equal("d e f", Stringify(_expression.GetValue(stringArrayOfLists))); - - _expression = Parser.ParseExpression("[1][2]"); - Assert.Equal("f", Stringify(_expression.GetValue(stringArrayOfLists))); - AssertCanCompile(_expression); - Assert.Equal("f", Stringify(_expression.GetValue(stringArrayOfLists))); - - // array of arrays - string[][] referenceTypeArrayOfArrays = - { - new[] - { - "a", - "b", - "c" - }, - new[] - { - "d", - "e", - "f" - } - }; - - _expression = Parser.ParseExpression("[1]"); - Assert.Equal("d e f", Stringify(_expression.GetValue(referenceTypeArrayOfArrays))); - AssertCanCompile(_expression); - Assert.Equal("d e f", Stringify(_expression.GetValue(referenceTypeArrayOfArrays))); - - _expression = Parser.ParseExpression("[1][2]"); - Assert.Equal("f", Stringify(_expression.GetValue(referenceTypeArrayOfArrays))); - AssertCanCompile(_expression); - Assert.Equal("f", Stringify(_expression.GetValue(referenceTypeArrayOfArrays))); - - int[][] primitiveTypeArrayOfArrays = - { - new[] - { - 1, - 2, - 3 - }, - new[] - { - 4, - 5, - 6 - } - }; - - _expression = Parser.ParseExpression("[1]"); - Assert.Equal("4 5 6", Stringify(_expression.GetValue(primitiveTypeArrayOfArrays))); - AssertCanCompile(_expression); - Assert.Equal("4 5 6", Stringify(_expression.GetValue(primitiveTypeArrayOfArrays))); - - _expression = Parser.ParseExpression("[1][2]"); - Assert.Equal("6", Stringify(_expression.GetValue(primitiveTypeArrayOfArrays))); - AssertCanCompile(_expression); - Assert.Equal("6", Stringify(_expression.GetValue(primitiveTypeArrayOfArrays))); - - // list of lists of reference types - var listOfListOfStrings = new List>(); - - var list = new List - { - "a", - "b", - "c" - }; - - listOfListOfStrings.Add(list); - - list = new List - { - "d", - "e", - "f" - }; - - listOfListOfStrings.Add(list); - _expression = Parser.ParseExpression("[1]"); - Assert.Equal("d e f", Stringify(_expression.GetValue(listOfListOfStrings))); - AssertCanCompile(_expression); - Assert.Equal("d e f", Stringify(_expression.GetValue(listOfListOfStrings))); - - _expression = Parser.ParseExpression("[1][2]"); - Assert.Equal("f", Stringify(_expression.GetValue(listOfListOfStrings))); - AssertCanCompile(_expression); - Assert.Equal("f", Stringify(_expression.GetValue(listOfListOfStrings))); - - // Map of lists - var mapToLists = new Dictionary>(); - - list = new List - { - "a", - "b", - "c" - }; - - mapToLists.Add("foo", list); - - _expression = Parser.ParseExpression("['foo']"); - Assert.Equal("a b c", Stringify(_expression.GetValue(mapToLists))); - AssertCanCompile(_expression); - Assert.Equal("a b c", Stringify(_expression.GetValue(mapToLists))); - - _expression = Parser.ParseExpression("['foo'][2]"); - Assert.Equal("c", Stringify(_expression.GetValue(mapToLists))); - AssertCanCompile(_expression); - Assert.Equal("c", Stringify(_expression.GetValue(mapToLists))); - - // Map to array - var mapToIntArray = new Dictionary(); - var ctx = new StandardEvaluationContext(); - ctx.AddPropertyAccessor(new CompilableMapAccessor()); - - mapToIntArray.Add("foo", new[] - { - 1, - 2, - 3 - }); - - _expression = Parser.ParseExpression("['foo']"); - Assert.Equal("1 2 3", Stringify(_expression.GetValue(mapToIntArray))); - AssertCanCompile(_expression); - Assert.Equal("1 2 3", Stringify(_expression.GetValue(mapToIntArray))); - - _expression = Parser.ParseExpression("['foo'][1]"); - Assert.Equal(2, _expression.GetValue(mapToIntArray)); - AssertCanCompile(_expression); - Assert.Equal(2, _expression.GetValue(mapToIntArray)); - - _expression = Parser.ParseExpression("foo"); - Assert.Equal("1 2 3", Stringify(_expression.GetValue(ctx, mapToIntArray))); - AssertCanCompile(_expression); - Assert.Equal("1 2 3", Stringify(_expression.GetValue(ctx, mapToIntArray))); - - _expression = Parser.ParseExpression("foo[1]"); - Assert.Equal(2, _expression.GetValue(ctx, mapToIntArray)); - AssertCanCompile(_expression); - Assert.Equal(2, _expression.GetValue(ctx, mapToIntArray)); - - _expression = Parser.ParseExpression("['foo'][2]"); - Assert.Equal("3", Stringify(_expression.GetValue(ctx, mapToIntArray))); - AssertCanCompile(_expression); - Assert.Equal("3", Stringify(_expression.GetValue(ctx, mapToIntArray))); - - // Map array - var mapArray = new Dictionary[1]; - - mapArray[0] = new Dictionary - { - { "key", "value1" } - }; - - _expression = Parser.ParseExpression("[0]"); - Assert.Equal("{key=value1}", Stringify(_expression.GetValue(mapArray))); - AssertCanCompile(_expression); - Assert.Equal("{key=value1}", Stringify(_expression.GetValue(mapArray))); - - _expression = Parser.ParseExpression("[0]['key']"); - Assert.Equal("value1", Stringify(_expression.GetValue(mapArray))); - AssertCanCompile(_expression); - Assert.Equal("value1", Stringify(_expression.GetValue(mapArray))); - } - - [Fact] - public void PlusNeedingCheckCast_SPR12426() - { - _expression = Parser.ParseExpression("TheObject + ' world'"); - object v = _expression.GetValue(new FooObject()); - Assert.Equal("hello world", v); - AssertCanCompile(_expression); - Assert.Equal("hello world", v); - - _expression = Parser.ParseExpression("Object + ' world'"); - v = _expression.GetValue(new FooString()); - Assert.Equal("hello world", v); - AssertCanCompile(_expression); - Assert.Equal("hello world", v); - } - - [Fact] - public void MixingItUp_PropertyAccessIndexerOpLtTernaryRootNull() - { - var payload = new Payload(); - _expression = Parser.ParseExpression("DR[0].Threeee"); - _expression.GetValue(payload); - - IExpression expression = Parser.ParseExpression("DR[0].Threeee.Four lt 0.1d?#root:null"); - object v = expression.GetValue(payload); - - AssertCanCompile(expression); - object vc = expression.GetValue(payload); - Assert.Equal(payload, v); - Assert.Equal(payload, vc); - payload.DR[0].Threeee.four = 0.13d; - vc = expression.GetValue(payload); - Assert.Null(vc); - } - - [Fact] - public void VariantGetter() - { - var holder = new Payload2Holder(); - var ctx = new StandardEvaluationContext(); - ctx.AddPropertyAccessor(new MyAccessor()); - _expression = Parser.ParseExpression("Payload2.Var1"); - object v = _expression.GetValue(ctx, holder); - Assert.Equal("abc", v); - - AssertCanCompile(_expression); - v = _expression.GetValue(ctx, holder); - Assert.Equal("abc", v); - } - - [Fact] - public void CompilerWithGenerics_12040() - { - _expression = Parser.ParseExpression("Payload!=2"); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(2))); - - _expression = Parser.ParseExpression("2!=Payload"); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(2))); - - _expression = Parser.ParseExpression("Payload!=6L"); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(4L))); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(6L))); - - _expression = Parser.ParseExpression("Payload==2"); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(2))); - - _expression = Parser.ParseExpression("2==Payload"); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(2))); - - _expression = Parser.ParseExpression("Payload==6L"); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(4L))); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(6L))); - - _expression = Parser.ParseExpression("2==Payload"); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(2))); - - _expression = Parser.ParseExpression("Payload/2"); - Assert.Equal(2, _expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.Equal(3, _expression.GetValue(new GenericMessageTestHelper(6))); - - _expression = Parser.ParseExpression("100/Payload"); - Assert.Equal(25, _expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.Equal(10, _expression.GetValue(new GenericMessageTestHelper(10))); - - _expression = Parser.ParseExpression("Payload+2"); - Assert.Equal(6, _expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.Equal(8, _expression.GetValue(new GenericMessageTestHelper(6))); - - _expression = Parser.ParseExpression("100+Payload"); - Assert.Equal(104, _expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.Equal(110, _expression.GetValue(new GenericMessageTestHelper(10))); - - _expression = Parser.ParseExpression("Payload-2"); - Assert.Equal(2, _expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.Equal(4, _expression.GetValue(new GenericMessageTestHelper(6))); - - _expression = Parser.ParseExpression("100-Payload"); - Assert.Equal(96, _expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.Equal(90, _expression.GetValue(new GenericMessageTestHelper(10))); - - _expression = Parser.ParseExpression("Payload*2"); - Assert.Equal(8, _expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.Equal(12, _expression.GetValue(new GenericMessageTestHelper(6))); - - _expression = Parser.ParseExpression("100*Payload"); - Assert.Equal(400, _expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.Equal(1000, _expression.GetValue(new GenericMessageTestHelper(10))); - - _expression = Parser.ParseExpression("Payload/2L"); - Assert.Equal(2L, _expression.GetValue(new GenericMessageTestHelper(4L))); - AssertCanCompile(_expression); - Assert.Equal(3L, _expression.GetValue(new GenericMessageTestHelper(6L))); - - _expression = Parser.ParseExpression("100L/Payload"); - Assert.Equal(25L, _expression.GetValue(new GenericMessageTestHelper(4L))); - AssertCanCompile(_expression); - Assert.Equal(10L, _expression.GetValue(new GenericMessageTestHelper(10L))); - - _expression = Parser.ParseExpression("Payload/2f"); - Assert.Equal(2f, _expression.GetValue(new GenericMessageTestHelper(4f))); - AssertCanCompile(_expression); - Assert.Equal(3f, _expression.GetValue(new GenericMessageTestHelper(6f))); - - _expression = Parser.ParseExpression("100f/Payload"); - Assert.Equal(25f, _expression.GetValue(new GenericMessageTestHelper(4f))); - AssertCanCompile(_expression); - Assert.Equal(10f, _expression.GetValue(new GenericMessageTestHelper(10f))); - - _expression = Parser.ParseExpression("Payload/2d"); - Assert.Equal(2d, _expression.GetValue(new GenericMessageTestHelper(4d))); - AssertCanCompile(_expression); - Assert.Equal(3d, _expression.GetValue(new GenericMessageTestHelper(6d))); - - _expression = Parser.ParseExpression("100d/Payload"); - Assert.Equal(25d, _expression.GetValue(new GenericMessageTestHelper(4d))); - AssertCanCompile(_expression); - Assert.Equal(10d, _expression.GetValue(new GenericMessageTestHelper(10d))); - } - - [Fact] - public void CompilerWithGenerics_12040_2() - { - _expression = Parser.ParseExpression("Payload/2"); - Assert.Equal(2, _expression.GetValue(new GenericMessageTestHelper2(4))); - AssertCanCompile(_expression); - Assert.Equal(3, _expression.GetValue(new GenericMessageTestHelper2(6))); - - _expression = Parser.ParseExpression("9/Payload"); - Assert.Equal(1, _expression.GetValue(new GenericMessageTestHelper2(9))); - AssertCanCompile(_expression); - Assert.Equal(3, _expression.GetValue(new GenericMessageTestHelper2(3))); - - _expression = Parser.ParseExpression("Payload+2"); - Assert.Equal(6, _expression.GetValue(new GenericMessageTestHelper2(4))); - AssertCanCompile(_expression); - Assert.Equal(8, _expression.GetValue(new GenericMessageTestHelper2(6))); - - _expression = Parser.ParseExpression("100+Payload"); - Assert.Equal(104, _expression.GetValue(new GenericMessageTestHelper2(4))); - AssertCanCompile(_expression); - Assert.Equal(110, _expression.GetValue(new GenericMessageTestHelper2(10))); - - _expression = Parser.ParseExpression("Payload-2"); - Assert.Equal(2, _expression.GetValue(new GenericMessageTestHelper2(4))); - AssertCanCompile(_expression); - Assert.Equal(4, _expression.GetValue(new GenericMessageTestHelper2(6))); - - _expression = Parser.ParseExpression("100-Payload"); - Assert.Equal(96, _expression.GetValue(new GenericMessageTestHelper2(4))); - AssertCanCompile(_expression); - Assert.Equal(90, _expression.GetValue(new GenericMessageTestHelper2(10))); - - _expression = Parser.ParseExpression("Payload*2"); - Assert.Equal(8, _expression.GetValue(new GenericMessageTestHelper2(4))); - AssertCanCompile(_expression); - Assert.Equal(12, _expression.GetValue(new GenericMessageTestHelper2(6))); - - _expression = Parser.ParseExpression("100*Payload"); - Assert.Equal(400, _expression.GetValue(new GenericMessageTestHelper2(4))); - AssertCanCompile(_expression); - Assert.Equal(1000, _expression.GetValue(new GenericMessageTestHelper2(10))); - } - - [Fact] - public void CompilerWithGenerics_12040_3() - { - _expression = Parser.ParseExpression("Payload >= 2"); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(1))); - - _expression = Parser.ParseExpression("2 >= Payload"); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(5))); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(1))); - - _expression = Parser.ParseExpression("Payload > 2"); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(4))); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(1))); - - _expression = Parser.ParseExpression("2 > Payload"); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(5))); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(1))); - - _expression = Parser.ParseExpression("Payload <=2"); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(1))); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(6))); - - _expression = Parser.ParseExpression("2 <= Payload"); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(1))); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(6))); - - _expression = Parser.ParseExpression("Payload < 2"); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(1))); - AssertCanCompile(_expression); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(6))); - - _expression = Parser.ParseExpression("2 < Payload"); - Assert.False(_expression.GetValue(new GenericMessageTestHelper(1))); - AssertCanCompile(_expression); - Assert.True(_expression.GetValue(new GenericMessageTestHelper(6))); - } - - [Fact] - public void IndexerMapAccessor_12045() - { - var spc = new SpelParserOptions(SpelCompilerMode.Immediate); - var sep = new SpelExpressionParser(spc); - _expression = sep.ParseExpression("Headers[command]"); - var root = new MyMessage(); - Assert.Equal("wibble", _expression.GetValue(root)); - - // This next call was failing because the isCompilable check in Indexer - // did not check on the key being compilable (and also generateCode in the - // Indexer was missing the optimization that it didn't need necessarily - // need to call generateCode for that accessor) - Assert.Equal("wibble", _expression.GetValue(root)); - AssertCanCompile(_expression); - _expression = sep.ParseExpression("Headers[GetKey()]"); - Assert.Equal("wobble", _expression.GetValue(root)); - Assert.Equal("wobble", _expression.GetValue(root)); - - _expression = sep.ParseExpression("List[GetKey2()]"); - Assert.Equal("wobble", _expression.GetValue(root)); - Assert.Equal("wobble", _expression.GetValue(root)); - - _expression = sep.ParseExpression("IArray[GetKey2()]"); - Assert.Equal(3, _expression.GetValue(root)); - Assert.Equal(3, _expression.GetValue(root)); - } - - [Fact] - public void ElvisOperator_SPR15192() - { - var configuration = new SpelParserOptions(SpelCompilerMode.Immediate); - var exp = new SpelExpressionParser(configuration).ParseExpression("Bar()") as SpelExpression; - Assert.Equal("BAR", exp.GetValue(new Foo())); - AssertCanCompile(exp); - Assert.Equal("BAR", exp.GetValue(new Foo())); - AssertIsCompiled(exp); - - exp = new SpelExpressionParser(configuration).ParseExpression("Bar('baz')") as SpelExpression; - Assert.Equal("BAZ", exp.GetValue(new Foo())); - AssertCanCompile(exp); - Assert.Equal("BAZ", exp.GetValue(new Foo())); - AssertIsCompiled(exp); - - var context = new StandardEvaluationContext(); - - context.SetVariable("map", new Dictionary - { - { "foo", "qux" } - }); - - exp = new SpelExpressionParser(configuration).ParseExpression("Bar(#map['foo'])") as SpelExpression; - Assert.Equal("QUX", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("QUX", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - exp = new SpelExpressionParser(configuration).ParseExpression("Bar(#map['foo'] ?: 'qux')") as SpelExpression; - Assert.Equal("QUX", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("QUX", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // When the condition is a primitive - exp = new SpelExpressionParser(configuration).ParseExpression("3?:'foo'") as SpelExpression; - Assert.Equal("3", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("3", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // When the condition is a long primitive - exp = new SpelExpressionParser(configuration).ParseExpression("3L?:'foo'") as SpelExpression; - Assert.Equal("3", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("3", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // When the condition is an empty string - exp = new SpelExpressionParser(configuration).ParseExpression("''?:4L") as SpelExpression; - Assert.Equal("4", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("4", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // null condition - exp = new SpelExpressionParser(configuration).ParseExpression("null?:4L") as SpelExpression; - Assert.Equal("4", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("4", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // variable access returning primitive - exp = new SpelExpressionParser(configuration).ParseExpression("#x?:'foo'") as SpelExpression; - context.SetVariable("x", 50); - Assert.Equal("50", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("50", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - exp = new SpelExpressionParser(configuration).ParseExpression("#x?:'foo'") as SpelExpression; - context.SetVariable("x", null); - Assert.Equal("foo", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("foo", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // variable access returning array - exp = new SpelExpressionParser(configuration).ParseExpression("#x?:'foo'") as SpelExpression; - - context.SetVariable("x", new[] - { - 1, - 2, - 3 - }); - - Assert.Equal("1,2,3", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("1,2,3", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - } - - [Fact] - public void ElvisOperator_SPR17214() - { - var configuration = new SpelParserOptions(SpelCompilerMode.Immediate); - var sep = new SpelExpressionParser(configuration); - - _expression = sep.ParseExpression("Record['abc']?:Record.Add('abc',Expression.SomeLong)"); - var rh = new RecordHolder(); - Assert.Null(_expression.GetValue(rh)); - Assert.Equal(3L, _expression.GetValue(rh)); - AssertCanCompile(_expression); - rh = new RecordHolder(); - Assert.Null(_expression.GetValue(rh)); - Assert.Equal(3L, _expression.GetValue(rh)); - - _expression = sep.ParseExpression("Record['abc']?:Record.Add('abc',3L)"); - rh = new RecordHolder(); - Assert.Null(_expression.GetValue(rh)); - Assert.Equal(3L, _expression.GetValue(rh)); - AssertCanCompile(_expression); - rh = new RecordHolder(); - Assert.Null(_expression.GetValue(rh)); - Assert.Equal(3L, _expression.GetValue(rh)); - - _expression = sep.ParseExpression("Record['abc']?:Record.Add('abc',Expression.SomeLong)"); - - rh = new RecordHolder - { - Expression = - { - SomeLong = 6L - } - }; - - Assert.Null(_expression.GetValue(rh)); - Assert.Equal(6L, rh.Get("abc")); - AssertCanCompile(_expression); - - rh = new RecordHolder - { - Expression = - { - SomeLong = 10L - } - }; - - Assert.Null(_expression.GetValue(rh)); - Assert.Equal(10L, rh.Get("abc")); - } - - [Fact] - public void TestNullComparison_SPR22358() - { - var configuration = new SpelParserOptions(SpelCompilerMode.Off); - var parser = new SpelExpressionParser(configuration); - var ctx = new StandardEvaluationContext(); - ctx.SetRootObject(new Reg(1)); - VerifyCompilationAndBehaviourWithNull("Value>1", parser, ctx); - VerifyCompilationAndBehaviourWithNull("Value<1", parser, ctx); - VerifyCompilationAndBehaviourWithNull("Value>=1", parser, ctx); - VerifyCompilationAndBehaviourWithNull("Value<=1", parser, ctx); - - VerifyCompilationAndBehaviourWithNull2("Value>Value2", parser, ctx); - VerifyCompilationAndBehaviourWithNull2("Value=Value2", parser, ctx); - VerifyCompilationAndBehaviourWithNull2("Value<=Value2", parser, ctx); - - VerifyCompilationAndBehaviourWithNull("ValueD>1.0d", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueD<1.0d", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueD>=1.0d", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueD<=1.0d", parser, ctx); - - VerifyCompilationAndBehaviourWithNull2("ValueD>ValueD2", parser, ctx); - VerifyCompilationAndBehaviourWithNull2("ValueD=ValueD2", parser, ctx); - VerifyCompilationAndBehaviourWithNull2("ValueD<=ValueD2", parser, ctx); - - VerifyCompilationAndBehaviourWithNull("ValueL>1L", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueL<1L", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueL>=1L", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueL<=1L", parser, ctx); - - VerifyCompilationAndBehaviourWithNull2("ValueL>ValueL2", parser, ctx); - VerifyCompilationAndBehaviourWithNull2("ValueL=ValueL2", parser, ctx); - VerifyCompilationAndBehaviourWithNull2("ValueL<=ValueL2", parser, ctx); - - VerifyCompilationAndBehaviourWithNull("ValueF>1.0f", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueF<1.0f", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueF>=1.0f", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueF<=1.0f", parser, ctx); - - VerifyCompilationAndBehaviourWithNull("ValueF>ValueF2", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueF=ValueF2", parser, ctx); - VerifyCompilationAndBehaviourWithNull("ValueF<=ValueF2", parser, ctx); - } - - [Fact] - public void TernaryOperator_SPR15192() - { - var configuration = new SpelParserOptions(SpelCompilerMode.Immediate); - var context = new StandardEvaluationContext(); - - context.SetVariable("map", new Dictionary - { - { "foo", "qux" } - }); - - var exp = new SpelExpressionParser(configuration).ParseExpression("Bar(#map['foo'] != null ? #map['foo'] : 'qux')") as SpelExpression; - Assert.Equal("QUX", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("QUX", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - exp = new SpelExpressionParser(configuration).ParseExpression("3==3?3:'foo'") as SpelExpression; - Assert.Equal("3", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("3", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - exp = new SpelExpressionParser(configuration).ParseExpression("3!=3?3:'foo'") as SpelExpression; - Assert.Equal("foo", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("foo", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // When the condition is a long - exp = new SpelExpressionParser(configuration).ParseExpression("3==3?3L:'foo'") as SpelExpression; - Assert.Equal("3", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("3", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - exp = new SpelExpressionParser(configuration).ParseExpression("3!=3?3L:'foo'") as SpelExpression; - Assert.Equal("foo", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("foo", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // When the condition is an empty string - exp = new SpelExpressionParser(configuration).ParseExpression("''==''?'abc':4L") as SpelExpression; - Assert.Equal("abc", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("abc", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // null condition - exp = new SpelExpressionParser(configuration).ParseExpression("3==3?null:4L") as SpelExpression; - Assert.Null(exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Null(exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // variable access returning primitive - exp = new SpelExpressionParser(configuration).ParseExpression("#x==#x?50:'foo'") as SpelExpression; - context.SetVariable("x", 50); - Assert.Equal("50", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("50", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - exp = new SpelExpressionParser(configuration).ParseExpression("#x!=#x?50:'foo'") as SpelExpression; - context.SetVariable("x", null); - Assert.Equal("foo", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("foo", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - - // variable access returning array - exp = new SpelExpressionParser(configuration).ParseExpression("#x==#x?'1,2,3':'foo'") as SpelExpression; - - context.SetVariable("x", new[] - { - 1, - 2, - 3 - }); - - Assert.Equal("1,2,3", exp.GetValue(context, new Foo())); - AssertCanCompile(exp); - Assert.Equal("1,2,3", exp.GetValue(context, new Foo())); - AssertIsCompiled(exp); - } - - [Fact] - public void RepeatedCompilations() - { - for (int i = 0; i < 1500; i++) - { - var expression = Parser.ParseExpression("4 + 5") as SpelExpression; - Assert.Equal(9, expression.GetValue()); - AssertCanCompile(expression); - Assert.Equal(9, expression.GetValue()); - AssertIsCompiled(expression); - } - } - - [Fact] - public void CompilationKicksInAfterThreshold() - { - var parser = new SpelExpressionParser(new SpelParserOptions(SpelCompilerMode.Mixed)); - var expression = parser.ParseExpression("4 + 5") as SpelExpression; - - for (int i = 0; i < 200; i++) - { - Assert.Equal(9, expression.GetValue()); - - if (i < SpelExpression.InterpretedCountThreshold) - { - AssertNotCompiled(expression); - } - else - { - AssertIsCompiled(expression); - } - } - } - - [Fact] - public void PropertyReferenceValueType() - { - // static property on value type - _expression = Parser.ParseExpression("T(DateTime).UtcNow"); - long start = DateTime.UtcNow.Ticks; - Assert.True(start < _expression.GetValue().Ticks); - AssertCanCompile(_expression); - Assert.True(start < _expression.GetValue().Ticks); - - // instance property on value type - _expression = Parser.ParseExpression("T(DateTime).UtcNow.Second"); - Assert.InRange(_expression.GetValue(), 0, 60); - AssertCanCompile(_expression); - Assert.InRange(_expression.GetValue(), 0, 60); - - // instance property on boxed value type - _expression = Parser.ParseExpression("#a.ValueProperty"); - Context.SetVariable("a", new A(10)); - Assert.Equal(10, _expression.GetValue(Context)); - AssertCanCompile(_expression); - Assert.Equal(10, _expression.GetValue(Context)); - - // static property on boxed value type - _expression = Parser.ParseExpression("#a.ValuePropertyStatic"); - Context.SetVariable("a", new A(10)); - Assert.Equal(30, _expression.GetValue(Context)); - AssertCanCompile(_expression); - Assert.Equal(30, _expression.GetValue(Context)); - } - - [Fact] - public void FieldReferenceValueType() - { - // static field on unboxed value type - _expression = Parser.ParseExpression("T(DateTime).MaxValue"); - var resultI = _expression.GetValue(); - AssertCanCompile(_expression); - var resultC = _expression.GetValue(); - Assert.Equal(resultI, resultC); - - // instance field on unboxed value type - _expression = Parser.ParseExpression($"T({typeof(SpelCompilationCoverageTests).FullName}$AHolder).GetA().Value"); - Assert.Equal(20, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(20, _expression.GetValue()); - - // instance field on boxed value type - _expression = Parser.ParseExpression("#a.Value"); - Context.SetVariable("a", new A(10)); - Assert.Equal(10, _expression.GetValue(Context)); - AssertCanCompile(_expression); - Assert.Equal(10, _expression.GetValue(Context)); - - // static field on boxed value type - _expression = Parser.ParseExpression("#a.ValueFieldStatic"); - Context.SetVariable("a", new A(10)); - Assert.Equal(40, _expression.GetValue(Context)); - AssertCanCompile(_expression); - Assert.Equal(40, _expression.GetValue(Context)); - } - - [Fact] - public void MethodReferenceValueType() - { - // static method on unboxed value type - _expression = Parser.ParseExpression($"T(DateTime).Parse('2/16/2008 12:15:12 PM',{InvariantCulturePropertyAccess})"); - var resultI = _expression.GetValue(); - AssertCanCompile(_expression); - var resultC = _expression.GetValue(); - Assert.Equal(resultI, resultC); - - // instance method on unboxed value type - _expression = Parser.ParseExpression($"T({typeof(SpelCompilationCoverageTests).FullName}$AHolder).GetA().Method()"); - Assert.Equal(20, _expression.GetValue()); - AssertCanCompile(_expression); - Assert.Equal(20, _expression.GetValue()); - - // instance method on boxed value type - _expression = Parser.ParseExpression("#a.Method()"); - Context.SetVariable("a", new A(20)); - Assert.Equal(20, _expression.GetValue(Context)); - AssertCanCompile(_expression); - Assert.Equal(20, _expression.GetValue(Context)); - - // static method on boxed value type - _expression = Parser.ParseExpression("#a.StaticMethod()"); - Context.SetVariable("a", new A(10)); - Assert.Equal(40, _expression.GetValue(Context)); - AssertCanCompile(_expression); - Assert.Equal(40, _expression.GetValue(Context)); - } - - [Fact] - public void MethodArgumentsValueTypes() - { - var testArgs = new TestAArguments(); - _expression = Parser.ParseExpression("TestUnboxed(#a)"); - Context.SetVariable("a", new A(10)); - Assert.Null(_expression.GetValue(Context, testArgs)); - Assert.Equal(10, testArgs.Value); - AssertCanCompile(_expression); - testArgs.Value = 0; - Assert.Null(_expression.GetValue(Context, testArgs)); - Assert.Equal(10, testArgs.Value); - - testArgs = new TestAArguments(); - _expression = Parser.ParseExpression("TestBoxed(#a)"); - Context.SetVariable("a", new A(20)); - Assert.Null(_expression.GetValue(Context, testArgs)); - Assert.Equal(20, testArgs.Value); - AssertCanCompile(_expression); - testArgs.Value = 0; - Assert.Null(_expression.GetValue(Context, testArgs)); - Assert.Equal(20, testArgs.Value); - } - - public static string Concat(string a, string b) - { - return a + b; - } - - public static string Join(params string[] strings) - { - var buf = new StringBuilder(); - - foreach (string value in strings) - { - buf.Append(value); - } - - return buf.ToString(); - } - - private void VerifyCompilationAndBehaviourWithNull(string expressionText, SpelExpressionParser parser, StandardEvaluationContext ctx) - { - var r = (Reg)ctx.RootObject.Value; - r.SetValue2(1); // having a value in value2 fields will enable compilation to succeed, then can switch it to null - var fast = (SpelExpression)parser.ParseExpression(expressionText); - var slow = (SpelExpression)parser.ParseExpression(expressionText); - fast.GetValue(ctx); - Assert.True(fast.CompileExpression()); - r.SetValue2(null); - - // try the numbers 0,1,2,null - for (int i = 0; i < 4; i++) - { - r.SetValue(i < 3 ? i : null); - bool slowResult = slow.GetValue(ctx); - bool fastResult = fast.GetValue(ctx); - - Assert.Equal(slowResult, fastResult); - } - } - - private void VerifyCompilationAndBehaviourWithNull2(string expressionText, SpelExpressionParser parser, StandardEvaluationContext ctx) - { - var fast = (SpelExpression)parser.ParseExpression(expressionText); - var slow = (SpelExpression)parser.ParseExpression(expressionText); - fast.GetValue(ctx); - Assert.True(fast.CompileExpression()); - var r = (Reg)ctx.RootObject.Value; - - // try the numbers 0,1,2,null - for (int i = 0; i < 4; i++) - { - r.SetValue(i < 3 ? i : null); - bool slowResult = slow.GetValue(ctx); - bool fastResult = fast.GetValue(ctx); - - Assert.Equal(slowResult, fastResult); - } - } - - private void AssertNotCompiled(SpelExpression expression) - { - Assert.Null(expression.CompiledAst); - } - - private void AssertIsCompiled(SpelExpression expression) - { - Assert.NotNull(expression.CompiledAst); - } - - private void AssertCanCompile(IExpression expression) - { - Assert.True(SpelCompiler.Compile(expression)); - } - - private void AssertCantCompile(IExpression expression) - { - Assert.False(SpelCompiler.Compile(expression)); - } - - private void AssertGetValueFail(IExpression expression) - { - Assert.Throws(expression.GetValue); - } - - private IExpression Parse(string expression) - { - return Parser.ParseExpression(expression); - } - - private string Stringify(object obj) - { - var s = new StringBuilder(); - - if (obj is IList ls) - { - foreach (object l in ls) - { - s.Append(l); - s.Append(' '); - } - } - else if (obj is IDictionary dict) - { - foreach (DictionaryEntry kvp in dict) - { - s.Append('{'); - s.Append(kvp.Key); - s.Append('='); - s.Append(kvp.Value); - s.Append('}'); - s.Append(' '); - } - } - else - { - s.Append(obj); - } - - return s.ToString().Trim(); - } - - private void CheckCalc(PayloadX p, string expression, int expectedResult) - { - IExpression expr = Parse(expression); - Assert.Equal(expectedResult, expr.GetValue(p)); - AssertCanCompile(expr); - Assert.Equal(expectedResult, expr.GetValue(p)); - } - - private void CheckCalc(PayloadX p, string expression, float expectedResult) - { - IExpression expr = Parse(expression); - Assert.Equal(expectedResult, expr.GetValue(p)); - AssertCanCompile(expr); - Assert.Equal(expectedResult, expr.GetValue(p)); - } - - private void CheckCalc(PayloadX p, string expression, long expectedResult) - { - IExpression expr = Parse(expression); - Assert.Equal(expectedResult, expr.GetValue(p)); - AssertCanCompile(expr); - Assert.Equal(expectedResult, expr.GetValue(p)); - } - - private void CheckCalc(PayloadX p, string expression, double expectedResult) - { - IExpression expr = Parse(expression); - Assert.Equal(expectedResult, expr.GetValue(p)); - AssertCanCompile(expr); - Assert.Equal(expectedResult, expr.GetValue(p)); - } -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter -#pragma warning disable IDE0051 // Remove unused private members -#pragma warning disable S1144 // Unused private types or members should be removed -#pragma warning disable S2326 // Unused type parameters should be removed -#pragma warning disable S1118 // Utility classes should not have public constructors -#pragma warning disable SA1401 // Fields should be private - - public static class AHolder - { - public static A GetA() - { - return new A(20); - } - } - - public static class DelegatingStringFormat - { - public static string Format(string s, params object[] args) - { - return string.Format(CultureInfo.InvariantCulture, s, args); - } - } - - private static class SomeCompareMethod - { -#pragma warning disable S1172 // Unused method parameters should be removed - public static int PublicCompare(object o1, object o2) - { - return -1; - } - - private static int PrivateCompare(object o1, object o2) -#pragma warning restore S1172 // Unused method parameters should be removed - { - return -1; - } - } - -#pragma warning disable S3898 // Value types should implement "IEquatable" - public struct A -#pragma warning restore S3898 // Value types should implement "IEquatable" - { - public int Value; - - public static int ValueFieldStatic => 40; - - public static int ValuePropertyStatic => 30; - - public int ValueProperty => Value; - - public A(int value) - { - Value = value; - } - - public int Method() - { - return Value; - } - - public int StaticMethod() - { - return ValueFieldStatic; - } - } - - public sealed class TestAArguments - { - public int Value; - - public void TestUnboxed(A a) - { - Value = a.Value; - } - - public void TestBoxed(object a) - { - Value = ((A)a).Value; - } - } - - public sealed class MyAccessor : ICompilablePropertyAccessor - { - private static readonly MethodInfo _method = typeof(Payload2).GetMethod(nameof(Payload2.GetField), new[] - { - typeof(string) - }); - - public bool CanRead(IEvaluationContext context, object target, string name) - { - return true; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - var payload2 = (Payload2)target; - return new TypedValue(payload2.GetField(name)); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return false; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - // Ignore - } - - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(Payload2) - }; - } - - public bool IsCompilable() - { - return true; - } - - public Type GetPropertyType() - { - return typeof(object); - } - - public void GenerateCode(string propertyName, ILGenerator gen, CodeFlow cf) - { - TypeDescriptor descriptor = cf.LastDescriptor(); - - if (descriptor == null) - { - CodeFlow.LoadTarget(gen); - } - - if (descriptor == null || descriptor.Value != _method.DeclaringType) - { - gen.Emit(OpCodes.Castclass, _method.DeclaringType); - } - - gen.Emit(OpCodes.Ldstr, propertyName); - gen.Emit(OpCodes.Callvirt, _method); - } - } - - public sealed class CompilableMapAccessor : ICompilablePropertyAccessor - { - private static readonly MethodInfo _getItem = typeof(IDictionary).GetMethod("get_Item", new[] - { - typeof(object) - }); - - public bool CanRead(IEvaluationContext context, object target, string name) - { - var map = (IDictionary)target; - return map.Contains(name); - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - var map = (IDictionary)target; - object value = map[name]; - - if (value == null && !map.Contains(name)) - { - throw new AccessException(name); - } - - return new TypedValue(value); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return true; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - var map = (IDictionary)target; - map.Add(name, newValue); - } - - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(IDictionary) - }; - } - - public bool IsCompilable() - { - return true; - } - - public Type GetPropertyType() - { - return typeof(object); - } - - public void GenerateCode(string propertyName, ILGenerator gen, CodeFlow cf) - { - TypeDescriptor descriptor = cf.LastDescriptor(); - - if (descriptor == null) - { - CodeFlow.LoadTarget(gen); - } - - gen.Emit(OpCodes.Ldstr, propertyName); - gen.Emit(OpCodes.Callvirt, _getItem); - } - } - - public sealed class Reg - { - public int Value { get; private set; } - - public long ValueL { get; private set; } - - public double ValueD { get; private set; } - - public float ValueF { get; private set; } - - public int Value2 { get; private set; } - - public long ValueL2 { get; private set; } - - public double ValueD2 { get; private set; } - - public float ValueF2 { get; private set; } - - public Reg(int v) - { - Value = v; - ValueL = v; - ValueD = v; - ValueF = v; - } - - public void SetValue(object value) - { - Value = value == null ? default : (int)value; - ValueL = value == null ? default : (long)(int)value; - ValueD = value == null ? default : (double)(int)value; - ValueF = value == null ? default : (float)(int)value; - } - - public void SetValue2(object value) - { - Value2 = value == null ? default : (int)value; - ValueL2 = value == null ? default : (long)(int)value; - ValueD2 = value == null ? default : (double)(int)value; - ValueF2 = value == null ? default : (float)(int)value; - } - } - - public sealed class LongHolder - { - public long SomeLong = 3L; - } - - public sealed class RecordHolder - { - public Dictionary Record = new(); - - public LongHolder Expression = new(); - - public void Add(string key, long value) - { - Record.Add(key, value); - } - - public long Get(string key) - { - return Record[key]; - } - } - - public sealed class Foo - { - public string Bar() - { - return "BAR"; - } - - public string Bar(string arg) - { - return arg.ToUpperInvariant(); - } - } - - public sealed class MessageHeaders : Dictionary - { - } - - public interface IMessage - { - MessageHeaders Headers { get; } - - IList List { get; } - - int[] IArray { get; } - } - - public sealed class MyMessage : IMessage - { - public MessageHeaders Headers - { - get - { - var mh = new MessageHeaders - { - { "command", "wibble" }, - { "command2", "wobble" } - }; - - return mh; - } - } - - public int[] IArray - { - get - { - return new[] - { - 5, - 3 - }; - } - } - - public IList List - { - get - { - var l = new List - { - "wibble", - "wobble" - }; - - return l; - } - } - - public string GetKey() - { - return "command2"; - } - - public int GetKey2() - { - return 1; - } - } - - public sealed class Payload2 - { - private const string _var2 = "def"; - - public string Var1 { get; } = "abc"; - - public object GetField(string name) - { - if (name == "Var1") - { - return Var1; - } - - if (name == "Var2") - { - return _var2; - } - - return null; - } - } - - public sealed class Payload2Holder - { - public Payload2 Payload2 = new(); - } - - public sealed class FooString - { - public string Object { get; } = "hello"; - } - - public sealed class TestClass6 - { - public static string Apple = "value2"; - - public string Orange = "value1"; - - public long Peach = 34L; - - public static string Plum { get; } = "value4"; - - public string Banana { get; } = "value3"; - } - - public sealed class Two - { - public Three Threeee { get; } = new(); - - public override string ToString() - { - return "instanceof Two"; - } - } - - public sealed class Payload - { - public Two Holder = new(); - - public Two[] DR { get; } = - { - new() - }; - } - - public sealed class TestClass5 - { - public static int _I; - public static string _S; - - public static short S1 = 1; - public static short S2 = 2; - public static short S3 = 3; - - public static long L1 = 1L; - - public static long L2 = 2L; - public static long L3 = 3L; - - public static float F1 = 1f; - public static float F2 = 2f; - public static float F3 = 3f; - - public static char C1 = 'a'; - public static char C2 = 'b'; - public static char C3 = 'c'; - - public static byte B1 = 65; - public static byte B2 = 66; - public static byte B3 = 67; - - public static string[] StringArray = - { - "aaa", - "bbb", - "ccc" - }; - - public static int[] IntArray = - { - 11, - 22, - 33 - }; - - public int I; - public string S; - - public object Obj; - - public string Field; - - public static void Two() - { - _I = 1; - } - - public static string Five() - { - return "hello"; - } - - public static long Six() - { - return 3_277_700L; - } - - public static void Ten(int toSet) - { - _I = toSet; - } - - public static void Eight(string toSet) - { - _S = toSet; - } - - public void Reset() - { - I = 0; - _I = 0; - S = null; - _S = null; - Field = null; - } - - public void One() - { - I = 1; - } - - public string Three() - { - return "hello"; - } - - public long Four() - { - return 3_277_700L; - } - - public void Seven(string toSet) - { - S = toSet; - } - - public void TakeNumber(object n) - { - S = n.ToString(); - } - - public void TakeString(string s) - { - S = s; - } - - public void Nine(int toSet) - { - I = toSet; - } - - public void Eleven(params string[] args) - { - if (args == null) - { - S = string.Empty; - } - else - { - S = string.Empty; - - foreach (string arg in args) - { - S += arg; - } - } - } - - public void Twelve(params int[] args) - { - if (args == null) - { - I = 0; - } - else - { - I = 0; - - foreach (int arg in args) - { - I += arg; - } - } - } - - public void Thirteen(string a, params string[] args) - { - if (args == null) - { - S = $"{a}::"; - } - else - { - S = $"{a}::"; - - foreach (string arg in args) - { - S += arg; - } - } - } - - public void Arrayz(params bool[] bs) - { - S = string.Empty; - - if (bs != null) - { - S = string.Empty; - - foreach (bool b in bs) - { - S += b.ToString(CultureInfo.InvariantCulture); - } - } - } - - public void Arrays(params short[] ss) - { - S = string.Empty; - - if (ss != null) - { - S = string.Empty; - - foreach (short s in ss) - { - S += s.ToString(CultureInfo.InvariantCulture); - } - } - } - - public void Arrayd(params double[] args) - { - S = string.Empty; - - if (args != null) - { - S = string.Empty; - - foreach (double v in args) - { - S += v.ToString(CultureInfo.InvariantCulture); - } - } - } - - public void Arrayf(params float[] args) - { - S = string.Empty; - - if (args != null) - { - S = string.Empty; - - foreach (float v in args) - { - S += v.ToString(CultureInfo.InvariantCulture); - } - } - } - - public void Arrayj(params long[] args) - { - S = string.Empty; - - if (args != null) - { - S = string.Empty; - - foreach (long v in args) - { - S += v.ToString(CultureInfo.InvariantCulture); - } - } - } - - public void Arrayb(params byte[] args) - { - S = string.Empty; - - if (args != null) - { - S = string.Empty; - - foreach (byte v in args) - { - S += v.ToString(CultureInfo.InvariantCulture); - } - } - } - - public void Arrayc(params char[] args) - { - S = string.Empty; - - if (args != null) - { - S = string.Empty; - - foreach (char v in args) - { - S += v.ToString(CultureInfo.InvariantCulture); - } - } - } - - public void Fourteen(string a, params string[][] args) - { - if (args == null) - { - S = $"{a}::"; - } - else - { - S = $"{a}::"; - - foreach (string[] arg in args) - { - S += "{"; - - foreach (string v in arg) - { - S += v; - } - - S += "}"; - } - } - } - - public void Fifteen(string a, params int[][] args) - { - if (args == null) - { - S = $"{a}::"; - } - else - { - S = $"{a}::"; - - foreach (int[] arg in args) - { - S += "{"; - - foreach (int v in arg) - { - S += v; - } - - S += "}"; - } - } - } - - public void Sixteen(params object[] args) - { - if (args == null) - { - S = string.Empty; - } - else - { - S = string.Empty; - - foreach (object arg in args) - { - S += arg; - } - } - } - } - - public sealed class TestClass10 - { - public string S; - - public void Reset() - { - S = null; - } - - public void Concat1(string arg) - { - S = $"::{arg}"; - } - - public void Concat1(params string[] args) - { - if (args == null) - { - S = string.Empty; - } - else - { - S = string.Empty; - - foreach (string arg in args) - { - S += arg; - } - } - } - - public void Concat2(object arg) - { - S = $"::{arg}"; - } - - public void Concat2(params object[] args) - { - if (args == null) - { - S = string.Empty; - } - else - { - S = string.Empty; - - foreach (object arg in args) - { - S += arg; - } - } - } - } - - public sealed class TestClass8 - { - public int I { get; } - - public string S { get; } - - public double D { get; } - - public bool Z { get; } - - public TestClass8(int i, string s, double d, bool z) - { - I = i; - S = s; - D = d; - Z = z; - } - - public TestClass8() - { - } - - public TestClass8(object i) - { - I = (int)i; - } - - private TestClass8(string a, string b) - { - S = a + b; - } - } - - public sealed class Obj - { - public readonly string Param1; - - public Obj(string param1) - { - Param1 = param1; - } - } - - public sealed class Obj2 - { - public readonly string Output; - - public Obj2(params string[] strings) - { - var b = new StringBuilder(); - - foreach (string p in strings) - { - b.Append(p); - } - - Output = b.ToString(); - } - } - - public sealed class Obj3 - { - public readonly string Output; - - public Obj3(params int[] integers) - { - var b = new StringBuilder(); - - foreach (int p in integers) - { - b.Append(p); - } - - Output = b.ToString(); - } - - public Obj3(string s, float f, params int[] integers) - { - var b = new StringBuilder(); - b.Append(s); - b.Append(':'); - b.Append(f); - b.Append(':'); - - foreach (int p in integers) - { - b.Append(p); - } - - Output = b.ToString(); - } - } - - public sealed class Obj4 - { - public readonly string Output; - - public Obj4(int[] integers) - { - var b = new StringBuilder(); - - foreach (int p in integers) - { - b.Append(p); - } - - Output = b.ToString(); - } - } - - public sealed class Person - { - public int Age { get; set; } - - public Person(int age) - { - Age = age; - } - } - - public sealed class Person3 - { - public string Name { get; } - public object Age { get; } - - public Person3(string name, int age) - { - Name = name; - Age = age; - } - } - - public sealed class Apple : IComparable - { - public object GotComparedTo; - public int I; - - public Apple(int i) - { - I = i; - } - - public void SetValue(int i) - { - I = i; - } - - public int CompareTo(object obj) - { - GotComparedTo = obj; - var that = obj as Apple; - - if (I < that.I) - { - return -1; - } - - if (I > that.I) - { - return +1; - } - - return 0; - } - } - - // For OpNe_SPR14863 - public sealed class MyContext - { - public Dictionary Data { get; } - - public MyContext(Dictionary data) - { - Data = data; - } - } - - public sealed class TestClass7 - { - public static string Property = "UK 123".Split(' ')[0]; - - public static void Reset() - { - const string s = "UK 123"; - Property = s.Split(' ')[0]; - } - } - - public sealed class GenericMessageTestHelper - { - public T Payload { get; } - - public GenericMessageTestHelper(T value) - { - Payload = value; - } - } - - public sealed class GenericMessageTestHelper2 - where T : struct - { - public object Payload { get; } - - public GenericMessageTestHelper2(T value) - { - Payload = value; - } - } - - public sealed class Greeter - { - public string World => "world"; - - public object GetObject() - { - return "object"; - } - } - - public sealed class PayloadX - { - public int valueI = 120; - - public object valueIB = 120; - public object valueIB58 = 58; - public object valueIB60 = 60; - public long valueJ = 120L; - public object valueJB = 120L; - public object valueJB58 = 58L; - public object valueJB60 = 60L; - public double valueD = 120D; - public object valueDB = 120D; - public object valueDB58 = 58D; - public object valueDB60 = 60D; - public float valueF = 120F; - public object valueFB = 120F; - public object valueFB58 = 58F; - public object valueFB60 = 60F; - public byte valueB = 120; - public byte valueB18 = 18; - public byte valueB20 = 20; - public object valueBB = (byte)120; - public object valueBB18 = (byte)18; - public object valueBB20 = (byte)20; - public char valueC = (char)120; - public object valueCB = (char)120; - public short valueS = 120; - public short valueS18 = 18; - public short valueS20 = 20; - public object valueSB = (short)120; - public object valueSB18 = (short)18; - public object valueSB20 = (short)20; - - public PayloadX payload; - - public PayloadX() - { - payload = this; - } - } - - public sealed class SomeCompareMethod2 - { - public static int Negate(int i1) - { - return -i1; - } - - public static string Append(params string[] strings) - { - var b = new StringBuilder(); - - foreach (string str in strings) - { - b.Append(str); - } - - return b.ToString(); - } - - public static string Append2(params object[] objects) - { - var b = new StringBuilder(); - - foreach (object obj in objects) - { - b.Append(obj); - } - - return b.ToString(); - } - - public static string Append3(string[] strings) - { - var b = new StringBuilder(); - - foreach (string str in strings) - { - b.Append(str); - } - - return b.ToString(); - } - - public static string Append4(string s, params string[] strings) - { - var b = new StringBuilder(); - b.Append(s).Append("::"); - - foreach (string str in strings) - { - b.Append(str); - } - - return b.ToString(); - } - - public static string AppendChar(params char[] values) - { - var b = new StringBuilder(); - - foreach (char ch in values) - { - b.Append(ch); - } - - return b.ToString(); - } - - public static int Sum(params int[] integers) - { - int total = 0; - - foreach (int i in integers) - { - total += i; - } - - return total; - } - - public static int SumDouble(params double[] values) - { - int total = 0; - - foreach (double i in values) - { - total += (int)i; - } - - return total; - } - - public static int SumFloat(params float[] values) - { - int total = 0; - - foreach (float i in values) - { - total += (int)i; - } - - return total; - } - } - - public sealed class Three - { - public double four = 0.04d; - - public double Four => four; - } - - public sealed class StaticsHelper - { - public static StaticsHelper sh = new(); - public static StaticsHelper Fielda = sh; - public static string Fieldb = "fb"; - - public static StaticsHelper PropertyA => sh; - - public static string PropertyB => "pb"; - - public static StaticsHelper MethodA() - { - return sh; - } - - public static string MethodB() - { - return "mb"; - } - - public override string ToString() - { - return "sh"; - } - } - - public sealed class TestClass1 - { - public int Index1 = 1; - public int Index2 = 3; - public string Word = "abcd"; - } - - public sealed class FooObjectHolder - { - public FooObject Foo { get; set; } = new(); - } - - public sealed class FooObject - { - public object TheObject => "hello"; - - public object GetObject() - { - return TheObject; - } - } - - public sealed class TestClass4 - { - public bool A { get; set; } - - public bool B { get; set; } - - public bool GetTrue() - { - return true; - } - - public bool GetFalse() - { - return false; - } - } - - private sealed class TestClass9 - { - public TestClass9(int i) - { - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SpelCompilationPerformanceTests.cs b/src/Common/test/Common.Expression.Test/Spring/SpelCompilationPerformanceTests.cs deleted file mode 100644 index b48a70e5e5..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SpelCompilationPerformanceTests.cs +++ /dev/null @@ -1,950 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Xunit; -using Xunit.Abstractions; - -// ReSharper disable InconsistentNaming - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class SpelCompilationPerformanceTests : AbstractExpressionTests -{ - private const bool NoisyTests = true; - - private const int Count = 50000; // number of evaluations that are timed in one run - - private const int Iterations = 10; // number of times to repeat 'count' evaluations (for averaging) - - private readonly ITestOutputHelper _output; - - private IExpression _expression; - - public SpelCompilationPerformanceTests(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - public void CompilingMathematicalExpressionsWithDifferentOperandTypes() - { - var nh = new NumberHolder(); - _expression = Parser.ParseExpression("(T(Convert).ToDouble(Payload))/18D"); - object o = _expression.GetValue(nh); - Assert.Equal(2d, o); - _output.WriteLine("Performance check for SpEL expression: 'Convert.ToDouble(Payload)/18D'"); - - long startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - Compile(_expression); - _output.WriteLine("Now compiled:"); - o = _expression.GetValue(nh); - Assert.Equal(2d, o); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - _expression = Parser.ParseExpression("Payload/18D"); - o = _expression.GetValue(nh); - Assert.Equal(2d, o); - _output.WriteLine("Performance check for SpEL expression: 'Payload / 18D"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - Compile(_expression); - _output.WriteLine("Now compiled:"); - o = _expression.GetValue(nh); - Assert.Equal(2d, o); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(nh); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - } - - [Fact] - public void InlineLists() - { - _expression = Parser.ParseExpression("{'abcde','ijklm'}[0].Substring({1,3,4}[0],{1,3,4}[1])"); - object o = _expression.GetValue(); - Assert.Equal("bcd", o); - _output.WriteLine("Performance check for SpEL expression: '{'abcde','ijklm'}[0].substring({1,3,4}[0],{1,3,4}[1])'"); - - long startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - Compile(_expression); - _output.WriteLine("Now compiled:"); - o = _expression.GetValue(); - Assert.Equal("bcd", o); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - } - - [Fact] - public void InlineNestedLists() - { - _expression = Parser.ParseExpression("{'abcde',{'ijklm','nopqr'}}[1][0].Substring({1,3,4}[0],{1,3,4}[1])"); - object o = _expression.GetValue(); - Assert.Equal("jkl", o); - _output.WriteLine("Performance check for SpEL expression: '{'abcde',{'ijklm','nopqr'}}[1][0].Substring({1,3,4}[0],{1,3,4}[1])'"); - - long startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - Compile(_expression); - _output.WriteLine("Now compiled:"); - o = _expression.GetValue(); - Assert.Equal("jkl", o); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - } - - [Fact] - public void StringConcatenation() - { - var g = new Greeter(); - _expression = Parser.ParseExpression("'hello' + World + ' spring'"); - object o = _expression.GetValue(g); - Assert.Equal("helloworld spring", o); - _output.WriteLine("Performance check for SpEL expression: 'hello' + World + ' spring'"); - - long startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(g); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(g); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(g); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - Compile(_expression); - _output.WriteLine("Now compiled:"); - o = _expression.GetValue(g); - Assert.Equal("helloworld spring", o); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(g); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(g); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - - startTime = DateTime.UtcNow.Ticks; - - for (int i = 0; i < 1_000_000; i++) - { - _expression.GetValue(g); - } - - _output.WriteLine("One million iterations: " + (DateTime.UtcNow.Ticks - startTime) / 10000 + "ms"); - } - - [Fact] - public void ComplexExpressionPerformance() - { - var payload = new Payload(); - IExpression expression = Parser.ParseExpression("DR[0].DRFixedSection.Duration lt 0.1"); - bool b = false; - long iTotal = 0; - const long cTotal = 0; - - // warmup - for (int i = 0; i < Count; i++) - { - b = expression.GetValue(payload); - } - - // Verify the result - Assert.False(b); - - Log("timing interpreted: "); - - for (int i = 0; i < Iterations; i++) - { - long startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - b = expression.GetValue(payload); - } - - long endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - iTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Compile(expression); - bool bc = false; - expression.GetValue(payload); - Log("timing compiled: "); - - for (int i = 0; i < Iterations; i++) - { - long startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - bc = expression.GetValue(payload); - } - - long endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - iTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - ReportPerformance("complex expression", iTotal, cTotal); - - // Verify the result - Assert.False(b); - - // Verify the same result for compiled vs interpreted - Assert.Equal(b, bc); - - // Verify if the input changes, the result changes - payload.DR[0].DRFixedSection.Duration = 0.04d; - bc = expression.GetValue(payload); - Assert.True(bc); - } - - [Fact] - public void CompilingMethodReference() - { - long interpretedTotal = 0; - const long compiledTotal = 0; - long startTime; - long endTime; - string interpretedResult = null; - string compiledResult = null; - var testData = new HW(); - IExpression expression = Parser.ParseExpression("Hello()"); - - // warmup - for (int i = 0; i < Count; i++) - { - interpretedResult = expression.GetValue(testData); - } - - Log("timing interpreted: "); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - interpretedResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Compile(expression); - - Log("timing compiled: "); - expression.GetValue(testData); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - compiledResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Assert.Equal(interpretedResult, compiledResult); - ReportPerformance("method reference", interpretedTotal, compiledTotal); - - if (interpretedTotal <= compiledTotal) - { - throw new Exception("Compiled version is slower than interpreted!"); - } - } - - [Fact] - public void CompilingPropertyReferenceField() - { - long interpretedTotal = 0; - const long compiledTotal = 0; - long startTime; - long endTime; - string interpretedResult = null; - string compiledResult = null; - var testData = new TestClass2(); - IExpression expression = Parser.ParseExpression("Name"); - - // warmup - for (int i = 0; i < Count; i++) - { - interpretedResult = expression.GetValue(testData); - } - - Log("timing interpreted: "); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - interpretedResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Compile(expression); - - Log("timing compiled: "); - expression.GetValue(testData); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - compiledResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Assert.Equal(interpretedResult, compiledResult); - ReportPerformance("property reference (field)", interpretedTotal, compiledTotal); - - if (interpretedTotal <= compiledTotal) - { - throw new Exception("Compiled version is slower than interpreted!"); - } - } - - [Fact] - public void CompilingPropertyReferenceNestedField() - { - long interpretedTotal = 0; - const long compiledTotal = 0; - long startTime; - long endTime; - string interpretedResult = null; - string compiledResult = null; - var testData = new TestClass2(); - IExpression expression = Parser.ParseExpression("Foo.Bar.Boo"); - - // warmup - for (int i = 0; i < Count; i++) - { - interpretedResult = expression.GetValue(testData); - } - - Log("timing interpreted: "); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - interpretedResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Compile(expression); - - Log("timing compiled: "); - expression.GetValue(testData); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - compiledResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Assert.Equal(interpretedResult, compiledResult); - ReportPerformance("property reference (nested field)", interpretedTotal, compiledTotal); - - if (interpretedTotal <= compiledTotal) - { - throw new Exception("Compiled version is slower than interpreted!"); - } - } - - [Fact] - public void CompilingPropertyReferenceNestedMixedFieldGetter() - { - long interpretedTotal = 0; - const long compiledTotal = 0; - long startTime; - long endTime; - string interpretedResult = null; - string compiledResult = null; - var testData = new TestClass2(); - IExpression expression = Parser.ParseExpression("Foo.Baz.Boo"); - - // warmup - for (int i = 0; i < Count; i++) - { - interpretedResult = expression.GetValue(testData); - } - - Log("timing interpreted: "); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - interpretedResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Compile(expression); - - Log("timing compiled: "); - expression.GetValue(testData); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - compiledResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Assert.Equal(interpretedResult, compiledResult); - ReportPerformance("nested property reference (mixed field/getter)", interpretedTotal, compiledTotal); - - if (interpretedTotal <= compiledTotal) - { - throw new Exception("Compiled version is slower than interpreted!"); - } - } - - [Fact] - public void CompilingNestedMixedFieldPropertyReferenceMethodReference() - { - long interpretedTotal = 0; - const long compiledTotal = 0; - long startTime; - long endTime; - string interpretedResult = null; - string compiledResult = null; - var testData = new TestClass2(); - IExpression expression = Parser.ParseExpression("Foo.Bay().Boo"); - - // warmup - for (int i = 0; i < Count; i++) - { - interpretedResult = expression.GetValue(testData); - } - - Log("timing interpreted: "); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - interpretedResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Compile(expression); - - Log("timing compiled: "); - expression.GetValue(testData); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - compiledResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Assert.Equal(interpretedResult, compiledResult); - ReportPerformance("nested reference(mixed field / method)", interpretedTotal, compiledTotal); - - if (interpretedTotal <= compiledTotal) - { - throw new Exception("Compiled version is slower than interpreted!"); - } - } - - [Fact] - public void CompilingPropertyReferenceGetter() - { - long interpretedTotal = 0; - const long compiledTotal = 0; - long startTime; - long endTime; - string interpretedResult = null; - string compiledResult = null; - var testData = new TestClass2(); - IExpression expression = Parser.ParseExpression("Name2"); - - // warmup - for (int i = 0; i < Count; i++) - { - interpretedResult = expression.GetValue(testData); - } - - Log("timing interpreted: "); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - interpretedResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Compile(expression); - - Log("timing compiled: "); - expression.GetValue(testData); - - for (int i = 0; i < Iterations; i++) - { - startTime = DateTime.UtcNow.Ticks; - - for (int j = 0; j < Count; j++) - { - compiledResult = expression.GetValue(testData); - } - - endTime = DateTime.UtcNow.Ticks; - long interpretedSpeed = endTime - startTime; - interpretedTotal += interpretedSpeed; - Log($"{interpretedSpeed}ticks "); - } - - LogLn(); - - Assert.Equal(interpretedResult, compiledResult); - ReportPerformance("property reference (getter)", interpretedTotal, compiledTotal); - - if (interpretedTotal <= compiledTotal) - { - throw new Exception("Compiled version is slower than interpreted!"); - } - } - - private void ReportPerformance(string title, long interpretedTotalTicks, long compiledTotalTicks) - { - double interpretedTotal = (double)interpretedTotalTicks / 10000; - double compiledTotal = (double)compiledTotalTicks / 10000; - double averageInterpreted = interpretedTotal / Iterations; - double averageCompiled = compiledTotal / Iterations; - double ratio = averageCompiled / averageInterpreted * 100.0d; - - LogLn( - $">>{title}: average for {Count}: compiled={averageCompiled}ms interpreted={averageInterpreted}ms: compiled takes {(int)ratio}% of the interpreted time"); - - if (averageCompiled > averageInterpreted) - { - throw new Exception($"Compiled version took longer than interpreted! CompiledSpeed=~{averageCompiled}ms InterpretedSpeed={averageInterpreted}ms"); - } - - LogLn(); - } - - private void Log(string message) - { - if (NoisyTests) - { - _output.WriteLine(message); - } - } - - private void LogLn(params string[] messages) - { - if (NoisyTests) - { - _output.WriteLine(messages.Length > 0 ? messages[0] : string.Empty); - } - } - - private void Compile(IExpression expression) - { - Assert.True(SpelCompiler.Compile(expression)); - } - - public sealed class HW - { - public string Hello() - { - return "foobar"; - } - } - - public sealed class Payload - { - public Two[] DR { get; } = - { - new() - }; - } - - public sealed class Two - { - public Three DRFixedSection { get; } = new(); - } - - public sealed class Three - { - public double Duration { get; set; } = 0.4d; - } - - public sealed class NumberHolder - { - public int Payload { get; } = 36; - } - - public sealed class Greeter - { - public string World => "world"; - } - - public sealed class TestClass2 - { - public string Name { get; } = "Santa"; - - public string Name2 => "foobar"; - - public Foo Foo => new(); - } - - public sealed class Foo - { - public Bar Bar { get; } = new(); - - public Bar Baz { get; } = new(); - - public Bar Bay() - { - return Baz; - } - } - - public sealed class Bar - { - public string Boo { get; } = "oranges"; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SpelDocumentationTests.cs b/src/Common/test/Common.Expression.Test/Spring/SpelDocumentationTests.cs deleted file mode 100644 index 3e20c2d035..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SpelDocumentationTests.cs +++ /dev/null @@ -1,510 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestResources; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class SpelDocumentationTests : AbstractExpressionTests -{ - private static readonly Inventor Tesla = new("Nikola Tesla", new DateTime(1856, 7, 9, 0, 0, 0, DateTimeKind.Local), "Serbian") - { - PlaceOfBirth = new PlaceOfBirth("SmilJan"), - Inventions = new[] - { - "Telephone repeater", - "Rotating magnetic field principle", - "Polyphase alternating-current system", - "Induction motor", - "Alternating-current power transmission", - "Tesla coil transformer", - "Wireless communication", - "Radio", - "Fluorescent lights" - } - }; - - private static readonly Inventor Pupin = new("Pupin", new DateTime(1856, 7, 9, 0, 0, 0, DateTimeKind.Local), "Idvor") - { - PlaceOfBirth = new PlaceOfBirth("Idvor") - }; - - [Fact] - public void TestMethodInvocation() - { - Evaluate("'Hello World'.ToUpperInvariant()", "HELLO WORLD", typeof(string)); - } - - [Fact] - public void TestBeanPropertyAccess() - { - Evaluate("new String('Hello World'[0])", "H", typeof(string)); - } - - [Fact] - public void TestArrayLengthAccess() - { - Evaluate("'Hello World'.ToCharArray().Length", 11, typeof(int)); - } - - [Fact] - public void TestRootObject() - { - // The constructor arguments are name, birthday, and nationality. - var tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9, 0, 0, 0, DateTimeKind.Local), "Serbian"); - - var parser = new SpelExpressionParser(); - IExpression exp = parser.ParseExpression("Name"); - - var context = new StandardEvaluationContext(); - context.SetRootObject(tesla); - - string name = (string)exp.GetValue(context); - Assert.Equal("Nikola Tesla", name); - } - - [Fact] - public void TestEqualityCheck() - { - var parser = new SpelExpressionParser(); - - var context = new StandardEvaluationContext(); - context.SetRootObject(Tesla); - - IExpression exp = parser.ParseExpression("Name == 'Nikola Tesla'"); - bool isEqual = exp.GetValue(context); - Assert.True(isEqual); - } - - // Section 7.4.1 - [Fact] - public void TestXmlBasedConfig() - { - Evaluate("(T(Random).Shared.Next() * 100.0)>0", true, typeof(bool)); - } - - // Section 7.5 - [Fact] - public void TestLiterals() - { - var parser = new SpelExpressionParser(); - - string helloWorld = (string)parser.ParseExpression("'Hello World'").GetValue(); // evaluates to "Hello World" - Assert.Equal("Hello World", helloWorld); - - double avogadrosNumber = parser.ParseExpression("6.0221415E+23").GetValue(); - Assert.InRange(avogadrosNumber, 6.0221415E+23, 6.0221415E+23); - - int maxValue = parser.ParseExpression("0x7FFFFFFF").GetValue(); // evaluates to 2147483647 - Assert.Equal(int.MaxValue, maxValue); - - bool trueValue = parser.ParseExpression("true").GetValue(); - Assert.True(trueValue); - - object nullValue = parser.ParseExpression("null").GetValue(); - Assert.Null(nullValue); - } - - [Fact] - public void TestPropertyAccess() - { - StandardEvaluationContext context = TestScenarioCreator.GetTestEvaluationContext(); - int year = Parser.ParseExpression("BirthDate.Year + 1900").GetValue(context); // 1856 - Assert.Equal(3756, year); - - string city = (string)Parser.ParseExpression("PlaceOfBirth.City").GetValue(context); - Assert.Equal("SmilJan", city); - } - - [Fact] - public void TestPropertyNavigation() - { - var parser = new SpelExpressionParser(); - - // Inventions Array - StandardEvaluationContext teslaContext = TestScenarioCreator.GetTestEvaluationContext(); - - string invention = parser.ParseExpression("Inventions[3]").GetValue(teslaContext); - Assert.Equal("Induction motor", invention); - - // Members List - var societyContext = new StandardEvaluationContext(); - - var ieee = new InstituteOfElectricalAndElectronicsEngineers - { - Members = - { - [0] = Tesla - } - }; - - societyContext.SetRootObject(ieee); - - // Evaluates to "Nikola Tesla" - string name = parser.ParseExpression("Members[0].Name").GetValue(societyContext); - Assert.Equal("Nikola Tesla", name); - - // List and Array navigation - // Evaluates to "Wireless communication" - invention = parser.ParseExpression("Members[0].Inventions[6]").GetValue(societyContext); - Assert.Equal("Wireless communication", invention); - } - - [Fact] - public void TestDictionaryAccess() - { - var societyContext = new StandardEvaluationContext(); - societyContext.SetRootObject(new InstituteOfElectricalAndElectronicsEngineers()); - - // Officer's Dictionary - var president = Parser.ParseExpression("Officers['president']").GetValue(societyContext); - Assert.NotNull(president); - - // Evaluates to "Idvor" - string city = Parser.ParseExpression("Officers['president'].PlaceOfBirth.City").GetValue(societyContext); - Assert.NotNull(city); - - // setting values - var i = Parser.ParseExpression("Officers['advisors'][0]").GetValue(societyContext); - Assert.Equal("Nikola Tesla", i.Name); - - Parser.ParseExpression("Officers['advisors'][0].PlaceOfBirth.Country").SetValue(societyContext, "Croatia"); - - var i2 = Parser.ParseExpression("Reverse[0]['advisors'][0]").GetValue(societyContext); - Assert.Equal("Nikola Tesla", i2.Name); - } - - // 7.5.3 - [Fact] - public void TestMethodInvocation2() - { - // string literal, Evaluates to "bc" - string c = Parser.ParseExpression("'abc'.Substring(1, 2)").GetValue(); - Assert.Equal("bc", c); - - var societyContext = new StandardEvaluationContext(); - societyContext.SetRootObject(new InstituteOfElectricalAndElectronicsEngineers()); - - // Evaluates to true - bool isMember = Parser.ParseExpression("IsMember('Mihajlo Pupin')").GetValue(societyContext); - Assert.True(isMember); - } - - // 7.5.4.1 - [Fact] - public void TestRelationalOperators() - { - bool result = Parser.ParseExpression("2 == 2").GetValue(); - Assert.True(result); - - // Evaluates to false - result = Parser.ParseExpression("2 < -5.0").GetValue(); - Assert.False(result); - - // Evaluates to true - result = Parser.ParseExpression("'black' < 'block'").GetValue(); - Assert.True(result); - } - - [Fact] - public void TestOtherOperators() - { - // Evaluates to false - bool falseValue = Parser.ParseExpression("'xyz' instanceof T(int)").GetValue(); - Assert.False(falseValue); - - // Evaluates to true - bool trueValue = Parser.ParseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").GetValue(); - Assert.True(trueValue); - - // Evaluates to false - falseValue = Parser.ParseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").GetValue(); - Assert.False(falseValue); - } - - // 7.5.4.2 - [Fact] - public void TestLogicalOperators() - { - var societyContext = new StandardEvaluationContext(); - societyContext.SetRootObject(new InstituteOfElectricalAndElectronicsEngineers()); - - // -- AND -- - - // Evaluates to false - bool falseValue = Parser.ParseExpression("true and false").GetValue(); - Assert.False(falseValue); - - // Evaluates to true - string expression = "IsMember('Nikola Tesla') and IsMember('Mihajlo Pupin')"; - bool trueValue = Parser.ParseExpression(expression).GetValue(societyContext); - Assert.True(trueValue); - - // -- OR -- - - // Evaluates to true - trueValue = Parser.ParseExpression("true or false").GetValue(); - Assert.True(trueValue); - - // Evaluates to true - expression = "IsMember('Nikola Tesla') or IsMember('Albert Einstein')"; - trueValue = Parser.ParseExpression(expression).GetValue(societyContext); - Assert.True(trueValue); - - // -- NOT -- - - // Evaluates to false - falseValue = Parser.ParseExpression("!true").GetValue(); - Assert.False(falseValue); - - // -- AND and NOT -- - expression = "IsMember('Nikola Tesla') and !IsMember('Mihajlo Pupin')"; - falseValue = Parser.ParseExpression(expression).GetValue(societyContext); - Assert.False(falseValue); - } - - // 7.5.4.3 - [Fact] - public void TestNumericalOperators() - { - // Addition - int two = Parser.ParseExpression("1 + 1").GetValue(); // 2 - Assert.Equal(2, two); - - string testString = Parser.ParseExpression("'Test' + ' ' + 'string'").GetValue(); // 'Test string' - Assert.Equal("Test string", testString); - - // Subtraction - int four = Parser.ParseExpression("1 - -3").GetValue(); // 4 - Assert.Equal(4, four); - - double d = Parser.ParseExpression("1000.00 - 1e4").GetValue(); // -9000 - Assert.InRange(d, -9000.0d, -9000.0d); - - // Multiplication - int six = Parser.ParseExpression("-2 * -3").GetValue(); // 6 - Assert.Equal(6, six); - - double twentyFour = Parser.ParseExpression("2.0 * 3e0 * 4").GetValue(); // 24.0 - Assert.InRange(twentyFour, 24.0d, 24.0d); - - // Division - int minusTwo = Parser.ParseExpression("6 / -3").GetValue(); // -2 - Assert.Equal(-2, minusTwo); - - double one = Parser.ParseExpression("8.0 / 4e0 / 2").GetValue(); // 1.0 - Assert.InRange(one, 1.0d, 1.0d); - - // Modulus - int three = Parser.ParseExpression("7 % 4").GetValue(); // 3 - Assert.Equal(3, three); - - int oneInt = Parser.ParseExpression("8 / 5 % 2").GetValue(); // 1 - Assert.Equal(1, oneInt); - - // Operator precedence - int minusTwentyOne = Parser.ParseExpression("1+2-3*8").GetValue(); // -21 - Assert.Equal(-21, minusTwentyOne); - } - - // 7.5.5 - [Fact] - public void TestAssignment() - { - var inventor = new Inventor(); - var inventorContext = new StandardEvaluationContext(); - inventorContext.SetRootObject(inventor); - - Parser.ParseExpression("Foo").SetValue(inventorContext, "Alexander Seovic2"); - - Assert.Equal("Alexander Seovic2", Parser.ParseExpression("Foo").GetValue(inventorContext)); - - // alternatively - string alexander = Parser.ParseExpression("Foo = 'Alexandar Seovic'").GetValue(inventorContext); - Assert.Equal("Alexandar Seovic", Parser.ParseExpression("Foo").GetValue(inventorContext)); - Assert.Equal("Alexandar Seovic", alexander); - } - - // 7.5.6 - [Fact] - public void TestTypes() - { - var dateClass = Parser.ParseExpression("T(DateTime)").GetValue(); - Assert.Equal(typeof(DateTime), dateClass); - bool trueValue = Parser.ParseExpression("T(TypeCode).Double < T(TypeCode).Decimal").GetValue(); - Assert.True(trueValue); - } - - // 7.5.7 - [Fact] - public void TestConstructors() - { - var societyContext = new StandardEvaluationContext(); - societyContext.SetRootObject(new InstituteOfElectricalAndElectronicsEngineers()); - - var einstein = Parser.ParseExpression($"new {typeof(Inventor).FullName}('Albert Einstein',new DateTime(1879, 3, 14), 'German')").GetValue(); - - Assert.Equal("Albert Einstein", einstein.Name); - - // create new inventor instance within add method of List - Parser.ParseExpression($"Members2.Add(new {typeof(Inventor).FullName}('Albert Einstein', 'German'))").GetValue(societyContext); - } - - // 7.5.8 - [Fact] - public void TestVariables() - { - var tesla = new Inventor("Nikola Tesla", "Serbian"); - var context = new StandardEvaluationContext(); - context.SetVariable("newName", "Mike Tesla"); - - context.SetRootObject(tesla); - - Parser.ParseExpression("Foo = #newName").GetValue(context); - - Assert.Equal("Mike Tesla", tesla.Foo); - } - - [Fact] - public void TestSpecialVariables() - { - // create an array of integers - var primes = new List - { - 2, - 3, - 5, - 7, - 11, - 13, - 17 - }; - - // create parser and set variable 'primes' as the array of integers - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - context.SetVariable("primes", primes); - - // all prime numbers > 10 from the list (using selection ?{...}) - var primesGreaterThanTen = parser.ParseExpression("#primes.?[#this>10]").GetValue>(context); - Assert.Equal(3, primesGreaterThanTen.Count); - Assert.Equal(11, primesGreaterThanTen[0]); - Assert.Equal(13, primesGreaterThanTen[1]); - Assert.Equal(17, primesGreaterThanTen[2]); - } - - // 7.5.9 - [Fact] - public void TestFunctions() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - context.RegisterFunction("reversestring", typeof(StringUtils).GetMethod(nameof(StringUtils.ReverseString), BindingFlags.Public | BindingFlags.Static)); - - string helloWorldReversed = parser.ParseExpression("#reversestring('hello world')").GetValue(context); - Assert.Equal("dlrow olleh", helloWorldReversed); - } - - // 7.5.10 - [Fact] - public void TestTernary() - { - string falseString = Parser.ParseExpression("false ? 'trueExp' : 'falseExp'").GetValue(); - Assert.Equal("falseExp", falseString); - - var societyContext = new StandardEvaluationContext(); - societyContext.SetRootObject(new InstituteOfElectricalAndElectronicsEngineers()); - - Parser.ParseExpression("Name").SetValue(societyContext, "IEEE"); - societyContext.SetVariable("queryName", "Nikola Tesla"); - - const string expression = "IsMember(#queryName)? #queryName + ' is a member of the ' " + - "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'"; - - string queryResultString = Parser.ParseExpression(expression).GetValue(societyContext); - Assert.Equal("Nikola Tesla is a member of the IEEE Society", queryResultString); - - // queryResultstring = "Nikola Tesla is a member of the IEEE Society" - } - - // 7.5.11 - [Fact] - public void TestSelection() - { - var societyContext = new StandardEvaluationContext(); - societyContext.SetRootObject(new InstituteOfElectricalAndElectronicsEngineers()); - var list = (List)Parser.ParseExpression("Members2.?[Nationality == 'Serbian']").GetValue(societyContext); - Assert.Single(list); - Assert.Equal("Nikola Tesla", ((Inventor)list[0]).Name); - } - - // 7.5.12 - [Fact] - public void TestTemplating() - { - string randomPhrase = Parser.ParseExpression("random number is ${T(Random).Shared.Next()}", new TemplatedParserContext()).GetValue(); - Assert.StartsWith("random number", randomPhrase, StringComparison.Ordinal); - } - - public static class StringUtils - { - public static string ReverseString(string input) - { - var backwards = new StringBuilder(); - - for (int i = 0; i < input.Length; i++) - { - backwards.Append(input[input.Length - 1 - i]); - } - - return backwards.ToString(); - } - } - - public sealed class InstituteOfElectricalAndElectronicsEngineers - { - public Inventor[] Members { get; } = new Inventor[1]; - public List Members2 { get; } = new(); - public Dictionary Officers { get; } = new(); - public List> Reverse { get; } = new(); - - public string Name { get; set; } - - public InstituteOfElectricalAndElectronicsEngineers() - { - Officers.Add("president", Pupin); - - var list = new List - { - Tesla - }; - - Officers.Add("advisors", list); - Members2.Add(Tesla); - Members2.Add(Pupin); - - Reverse.Add(Officers); - } - - public bool IsMember(string name) - { - return true; - } - } - - public sealed class TemplatedParserContext : IParserContext - { - public string ExpressionPrefix => "${"; - - public string ExpressionSuffix => "}"; - - public bool IsTemplate => true; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SpelExceptionTests.cs b/src/Common/test/Common.Expression.Test/Spring/SpelExceptionTests.cs deleted file mode 100644 index 10fc01ea9f..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SpelExceptionTests.cs +++ /dev/null @@ -1,152 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class SpelExceptionTests -{ - [Fact] - public void SpelExpressionMapNullVariables() - { - var parser = new SpelExpressionParser(); - IExpression spelExpression = parser.ParseExpression("#aMap.containsKey('one')"); - Assert.Throws(() => spelExpression.GetValue()); - } - - [Fact] - public void SpelExpressionMapIndexAccessNullVariables() - { - var parser = new SpelExpressionParser(); - IExpression spelExpression = parser.ParseExpression("#aMap['one'] eq 1"); - Assert.Throws(() => spelExpression.GetValue()); - } - - [Fact] - public void SpelExpressionMapWithVariables() - { - var parser = new SpelExpressionParser(); - IExpression spelExpression = parser.ParseExpression("#aMap['one'] eq 1"); - var ctx = new StandardEvaluationContext(); - - var map = new Dictionary - { - { - "aMap", new Dictionary - { - { "one", 1 }, - { "two", 2 }, - { "three", 3 } - } - } - }; - - ctx.SetVariables(map); - - bool result = spelExpression.GetValue(ctx); - Assert.True(result); - } - - [Fact] - public void SpelExpressionListNullVariables() - { - var parser = new SpelExpressionParser(); - IExpression spelExpression = parser.ParseExpression("#aList.contains('one')"); - Assert.Throws(() => spelExpression.GetValue()); - } - - [Fact] - public void SpelExpressionListIndexAccessNullVariables() - { - var parser = new SpelExpressionParser(); - IExpression spelExpression = parser.ParseExpression("#aList[0] eq 'one'"); - Assert.Throws(() => spelExpression.GetValue()); - } - - [Fact] - public void SpelExpressionListWithVariables() - { - var parser = new SpelExpressionParser(); - IExpression spelExpression = parser.ParseExpression("#aList.Contains('one')"); - var ctx = new StandardEvaluationContext(); - - var map = new Dictionary - { - { - "aList", new List - { - "one", - "two", - "three" - } - } - }; - - ctx.SetVariables(map); - bool result = spelExpression.GetValue(ctx); - Assert.True(result); - } - - [Fact] - public void SpelExpressionListIndexAccessWithVariables() - { - var parser = new SpelExpressionParser(); - IExpression spelExpression = parser.ParseExpression("#aList[0] eq 'one'"); - var ctx = new StandardEvaluationContext(); - - var map = new Dictionary - { - { - "aList", new List - { - "one", - "two", - "three" - } - } - }; - - ctx.SetVariables(map); - bool result = spelExpression.GetValue(ctx); - Assert.True(result); - } - - [Fact] - public void SpelExpressionArrayIndexAccessNullVariables() - { - var parser = new SpelExpressionParser(); - IExpression spelExpression = parser.ParseExpression("#anArray[0] eq 1"); - Assert.Throws(() => spelExpression.GetValue()); - } - - [Fact] - public void SpelExpressionArrayWithVariables() - { - var parser = new SpelExpressionParser(); - IExpression spelExpression = parser.ParseExpression("#anArray[0] eq 1"); - var ctx = new StandardEvaluationContext(); - - var map = new Dictionary - { - { - "anArray", new[] - { - 1, - 2, - 3 - } - } - }; - - ctx.SetVariables(map); - - bool result = spelExpression.GetValue(ctx); - Assert.True(result); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SpelReproTests.cs b/src/Common/test/Common.Expression.Test/Spring/SpelReproTests.cs deleted file mode 100644 index b3ae157df1..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SpelReproTests.cs +++ /dev/null @@ -1,2342 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Globalization; -using System.Reflection; -using System.Text; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Util; -using Xunit; - -// ReSharper disable InconsistentNaming -#pragma warning disable S100 // Methods and properties should be named in PascalCase -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter -#pragma warning disable SA1401 // Fields should be private - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class SpelReproTests : AbstractExpressionTests -{ - [Fact] - public void NPE_SPR5661() - { - Evaluate("JoinThreeStrings('a',null,'c')", "ac", typeof(string)); - } - - [Fact] - public void SWF1086() - { - Evaluate("PrintDouble(T(Decimal).Parse('14.35',T(System.Globalization.CultureInfo).InvariantCulture))", "14.35", typeof(string)); - } - - [Fact] - public void DoubleCoercion() - { - Evaluate("PrintDouble(14.35)", "14.35", typeof(string)); - } - - [Fact] - public void DoubleArrayCoercion() - { - Evaluate("PrintDoubles(GetDoublesAsStringList())", "{14.35, 15.45}", typeof(string)); - } - - [Fact] - public void SPR5899() - { - var context = new StandardEvaluationContext(new Spr5899Class()); - IExpression expr = new SpelExpressionParser().ParseRaw("TryToInvokeWithNull(12)"); - Assert.Equal(12, expr.GetValue(context)); - expr = new SpelExpressionParser().ParseRaw("TryToInvokeWithNull(null)"); - Assert.Equal(0, expr.GetValue(context)); - expr = new SpelExpressionParser().ParseRaw("TryToInvokeWithNull2(null)"); - Assert.Throws(() => expr.GetValue()); - context.TypeLocator = new MyTypeLocator(); - - // varargs - expr = new SpelExpressionParser().ParseRaw("TryToInvokeWithNull3(null,'a','b')"); - Assert.Equal("ab", expr.GetValue(context)); - - // varargs 2 - null is packed into the varargs - expr = new SpelExpressionParser().ParseRaw("TryToInvokeWithNull3(12,'a',null,'c')"); - Assert.Equal("anullc", expr.GetValue(context)); - - // check we can find the ctor ok - expr = new SpelExpressionParser().ParseRaw("new Spr5899Class().ToString()"); - Assert.Equal("instance", expr.GetValue(context)); - - expr = new SpelExpressionParser().ParseRaw("new Spr5899Class(null).ToString()"); - Assert.Equal("instance", expr.GetValue(context)); - - // ctor varargs - expr = new SpelExpressionParser().ParseRaw("new Spr5899Class(null,'a','b').ToString()"); - Assert.Equal("instance", expr.GetValue(context)); - - // ctor varargs 2 - expr = new SpelExpressionParser().ParseRaw("new Spr5899Class(null,'a', null, 'b').ToString()"); - Assert.Equal("instance", expr.GetValue(context)); - } - - [Fact] - public void SPR5905_InnerTypeReferences() - { - var context = new StandardEvaluationContext(new Spr5899Class()); - - IExpression expr = new SpelExpressionParser().ParseRaw($"T({typeof(SpelReproTests).FullName}$Outer$Inner).Run()"); - Assert.Equal(12, expr.GetValue(context)); - - expr = new SpelExpressionParser().ParseRaw($"new {typeof(SpelReproTests).FullName}$Outer$Inner().Run2()"); - Assert.Equal(13, expr.GetValue(context)); - } - - [Fact] - public void SPR5804() - { - var m = new Dictionary - { - { "foo", "bar" } - }; - - var context = new StandardEvaluationContext(m); // root is a map instance - context.AddPropertyAccessor(new MapAccessor()); - IExpression expr = new SpelExpressionParser().ParseRaw("['foo']"); - Assert.Equal("bar", expr.GetValue(context)); - } - - [Fact] - public void SPR5847() - { - var context = new StandardEvaluationContext(new TestProperties()); - string name = null; - IExpression expr = null; - - expr = new SpelExpressionParser().ParseRaw("JdbcProperties['username']"); - name = expr.GetValue(context); - Assert.Equal("Dave", name); - - expr = new SpelExpressionParser().ParseRaw("JdbcProperties[username]"); - name = expr.GetValue(context); - Assert.Equal("Dave", name); - - // MapAccessor required for this to work - expr = new SpelExpressionParser().ParseRaw("JdbcProperties.username"); - context.AddPropertyAccessor(new MapAccessor()); - name = expr.GetValue(context); - Assert.Equal("Dave", name); - - // --- dotted property names - - // lookup foo on the root, then bar on that, then use that as the key into - // jdbcProperties - expr = new SpelExpressionParser().ParseRaw("JdbcProperties[Foo.bar]"); - context.AddPropertyAccessor(new MapAccessor()); - name = expr.GetValue(context); - Assert.Equal("Dave2", name); - - // key is foo.bar - expr = new SpelExpressionParser().ParseRaw("JdbcProperties['foo.bar']"); - context.AddPropertyAccessor(new MapAccessor()); - name = expr.GetValue(context); - Assert.Equal("Elephant", name); - } - - [Fact] - public void NPE_SPR5673() - { - IParserContext hashes = TemplateExpressionParsingTests.HashDelimitedParserContextSingleton; - IParserContext dollars = TemplateExpressionParsingTests.DefaultTemplateParserContextSingleton; - - CheckTemplateParsing("abc${'def'} ghi", "abcdef ghi"); - - CheckTemplateParsingError("abc${ {}( 'abc'", "Missing closing ')' for '(' at position 8"); - CheckTemplateParsingError("abc${ {}[ 'abc'", "Missing closing ']' for '[' at position 8"); - CheckTemplateParsingError("abc${ {}{ 'abc'", "Missing closing '}' for '{' at position 8"); - CheckTemplateParsingError("abc${ ( 'abc' }", "Found closing '}' at position 14 but most recent opening is '(' at position 6"); - CheckTemplateParsingError("abc${ '... }", "Found non terminating string literal starting at position 6"); - CheckTemplateParsingError("abc${ \"... }", "Found non terminating string literal starting at position 6"); - CheckTemplateParsingError("abc${ ) }", "Found closing ')' at position 6 without an opening '('"); - CheckTemplateParsingError("abc${ ] }", "Found closing ']' at position 6 without an opening '['"); - CheckTemplateParsingError("abc${ } }", "No expression defined within delimiter '${}' at character 3"); - CheckTemplateParsingError("abc$[ } ]", new DollarSquareTemplateParserContext(), "Found closing '}' at position 6 without an opening '{'"); - - CheckTemplateParsing("abc ${\"def''g}hi\"} jkl", "abc def'g}hi jkl"); - CheckTemplateParsing("abc ${'def''g}hi'} jkl", "abc def'g}hi jkl"); - CheckTemplateParsing("}", "}"); - CheckTemplateParsing("${'hello'} world", "hello world"); - CheckTemplateParsing("Hello ${'}'}]", "Hello }]"); - CheckTemplateParsing("Hello ${'}'}", "Hello }"); - CheckTemplateParsingError("Hello ${ ( ", "No ending suffix '}' for expression starting at character 6: ${ ( "); - CheckTemplateParsingError("Hello ${ ( }", "Found closing '}' at position 11 but most recent opening is '(' at position 9"); - CheckTemplateParsing("#{'Unable to render embedded object: File ({#this == 2}'}", hashes, "Unable to render embedded object: File ({#this == 2}"); - - CheckTemplateParsing("This is the last odd number in the list: ${ListOfNumbersUpToTen.$[#this%2==1]}", dollars, - "This is the last odd number in the list: 9"); - - CheckTemplateParsing("Hello ${'here is a curly bracket }'}", dollars, "Hello here is a curly bracket }"); - CheckTemplateParsing("He${'${'}llo ${'here is a curly bracket }'}}", dollars, "He${llo here is a curly bracket }}"); - CheckTemplateParsing("Hello ${'()()()}{}{}{][]{}{][}[][][}{()()'} World", dollars, "Hello ()()()}{}{}{][]{}{][}[][][}{()() World"); - - CheckTemplateParsing("Hello ${'inner literal that''s got {[(])]}an escaped quote in it'} World", - "Hello inner literal that's got {[(])]}an escaped quote in it World"); - - CheckTemplateParsingError("Hello ${", "No ending suffix '}' for expression starting at character 6: ${"); - } - - [Fact] - public void PropertyAccessOnNullTarget_SPR5663() - { - var accessor = new ReflectivePropertyAccessor(); - StandardEvaluationContext context = TestScenarioCreator.GetTestEvaluationContext(); - Assert.False(accessor.CanRead(context, null, "abc")); - Assert.False(accessor.CanWrite(context, null, "abc")); - Assert.Throws(() => accessor.Read(context, null, "abc")); - Assert.Throws(() => accessor.Write(context, null, "abc", "foo")); - } - - [Fact] - public void NestedProperties_SPR6923() - { - var context = new StandardEvaluationContext(new Foo()); - IExpression expr = new SpelExpressionParser().ParseRaw("Resource.Resource.Server"); - string name = expr.GetValue(context); - Assert.Equal("abc", name); - } - - [Fact] - public void IndexingAsAPropertyAccess_SPR6968_1() - { - var context = new StandardEvaluationContext(new Goo()); - string name = null; - IExpression expr = null; - expr = new SpelExpressionParser().ParseRaw("Instance[Bar]"); - name = expr.GetValue(context); - Assert.Equal("hello", name); - name = expr.GetValue(context); - Assert.Equal("hello", name); - } - - [Fact] - public void IndexingAsAPropertyAccess_SPR6968_2() - { - var context = new StandardEvaluationContext(new Goo()); - context.SetVariable("bar", "Key"); - string name = null; - IExpression expr = null; - expr = new SpelExpressionParser().ParseRaw("Instance[#bar]"); - name = expr.GetValue(context); - Assert.Equal("hello", name); - name = expr.GetValue(context); - Assert.Equal("hello", name); - } - - [Fact] - public void DollarPrefixedIdentifier_SPR7100() - { - var h = new Holder(); - var context = new StandardEvaluationContext(h); - context.AddPropertyAccessor(new MapAccessor()); - h.Map.Add("$foo", "wibble"); - h.Map.Add("foo$bar", "wobble"); - h.Map.Add("foobar$$", "wabble"); - h.Map.Add("$", "wubble"); - h.Map.Add("$$", "webble"); - h.Map.Add("$_$", "tribble"); - string name = null; - IExpression expr = null; - - expr = new SpelExpressionParser().ParseRaw("Map.$foo"); - name = expr.GetValue(context); - Assert.Equal("wibble", name); - - expr = new SpelExpressionParser().ParseRaw("Map.foo$bar"); - name = expr.GetValue(context); - Assert.Equal("wobble", name); - - expr = new SpelExpressionParser().ParseRaw("Map.foobar$$"); - name = expr.GetValue(context); - Assert.Equal("wabble", name); - - expr = new SpelExpressionParser().ParseRaw("Map.$"); - name = expr.GetValue(context); - Assert.Equal("wubble", name); - - expr = new SpelExpressionParser().ParseRaw("Map.$$"); - name = expr.GetValue(context); - Assert.Equal("webble", name); - - expr = new SpelExpressionParser().ParseRaw("Map.$_$"); - name = expr.GetValue(context); - Assert.Equal("tribble", name); - } - - [Fact] - public void IndexingAsAPropertyAccess_SPR6968_3() - { - var context = new StandardEvaluationContext(new Goo()); - Goo.Instance.Wibble = "wobble"; - context.SetVariable("bar", "Wibble"); - string name = null; - IExpression expr = null; - expr = new SpelExpressionParser().ParseRaw("Instance[#bar]"); - - // will access the field 'wibble' and not use a getter - name = expr.GetValue(context); - Assert.Equal("wobble", name); - name = expr.GetValue(context); - Assert.Equal("wobble", name); - } - - [Fact] - public void IndexingAsAPropertyAccess_SPR6968_4() - { - Goo.Instance.Wibble = "wobble"; - var g = Goo.Instance; - var context = new StandardEvaluationContext(g); - context.SetVariable("bar", "Wibble"); - IExpression expr = null; - expr = new SpelExpressionParser().ParseRaw("Instance[#bar]='world'"); - - // will access the field 'wibble' and not use a getter - expr.GetValue(context); - Assert.Equal("world", g.Wibble); - expr.GetValue(context); - Assert.Equal("world", g.Wibble); - } - - [Fact] - public void IndexingAsAPropertyAccess_SPR6968_5() - { - var g = Goo.Instance; - var context = new StandardEvaluationContext(g); - IExpression expr = null; - expr = new SpelExpressionParser().ParseRaw("Instance[Bar]='world'"); - expr.GetValue(context); - Assert.Equal("world", g.Value); - expr.GetValue(context); - Assert.Equal("world", g.Value); - } - - [Fact] - public void Dollars() - { - var context = new StandardEvaluationContext(new XX()); - IExpression expr = null; - expr = new SpelExpressionParser().ParseRaw("M['$foo']"); - context.SetVariable("file_name", "$foo"); - Assert.Equal("wibble", expr.GetValue(context)); - } - - [Fact] - public void Dollars2() - { - var context = new StandardEvaluationContext(new XX()); - IExpression expr = null; - expr = new SpelExpressionParser().ParseRaw("M[$foo]"); - context.SetVariable("file_name", "$foo"); - Assert.Equal("wibble", expr.GetValue(context)); - } - - [Fact] - public void BeanResolution() - { - var context = new StandardEvaluationContext(new XX()); - IExpression expr = null; - - // no Resolver registered == exception - try - { - expr = new SpelExpressionParser().ParseRaw("@foo"); - Assert.Equal("custard", expr.GetValue(context)); - } - catch (SpelEvaluationException see) - { - Assert.Equal(SpelMessage.NoServiceResolverRegistered, see.MessageCode); - Assert.Equal("foo", see.Inserts[0]); - } - - context.ServiceResolver = new MyBeanResolver(); - - // bean exists - expr = new SpelExpressionParser().ParseRaw("@foo"); - Assert.Equal("custard", expr.GetValue(context)); - - // bean does not exist - expr = new SpelExpressionParser().ParseRaw("@bar"); - Assert.Null(expr.GetValue(context)); - - // bean name will cause AccessException - expr = new SpelExpressionParser().ParseRaw("@goo"); - - try - { - Assert.Null(expr.GetValue(context)); - } - catch (SpelEvaluationException see) - { - Assert.Equal(SpelMessage.ExceptionDuringServiceResolution, see.MessageCode); - Assert.Equal("goo", see.Inserts[0]); - Assert.True(see.InnerException is AccessException); - Assert.StartsWith("DONT", see.InnerException.Message, StringComparison.Ordinal); - } - - // bean exists - expr = new SpelExpressionParser().ParseRaw("@'foo.bar'"); - Assert.Equal("trouble", expr.GetValue(context)); - - // bean exists - try - { - expr = new SpelExpressionParser().ParseRaw("@378"); - Assert.Equal("trouble", expr.GetValue(context)); - } - catch (SpelParseException spe) - { - Assert.Equal(SpelMessage.InvalidServiceReference, spe.MessageCode); - } - } - - [Fact] - public void Elvis_SPR7209_1() - { - var context = new StandardEvaluationContext(new XX()); - IExpression expr = null; - - // Different parts of elvis expression are null - expr = new SpelExpressionParser().ParseRaw("(?:'default')"); - Assert.Equal("default", expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("?:'default'"); - Assert.Equal("default", expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("?:"); - Assert.Null(expr.GetValue()); - - // Different parts of ternary expression are null - var ex = Assert.Throws(() => new SpelExpressionParser().ParseRaw("(?'abc':'default')").GetValue(context)); - Assert.Equal(SpelMessage.TypeConversionError, ex.MessageCode); - expr = new SpelExpressionParser().ParseRaw("(false?'abc':null)"); - Assert.Null(expr.GetValue()); - - // Assignment - ex = Assert.Throws(() => new SpelExpressionParser().ParseRaw("(='default')").GetValue(context)); - Assert.Equal(SpelMessage.SetValueNotSupported, ex.MessageCode); - } - - [Fact] - public void Elvis_SPR7209_2() - { - IExpression expr = null; - - // Have empty string treated as null for elvis - expr = new SpelExpressionParser().ParseRaw("?:'default'"); - Assert.Equal("default", expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("\"\"?:'default'"); - Assert.Equal("default", expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("''?:'default'"); - Assert.Equal("default", expr.GetValue()); - } - - [Fact] - public void MapOfMap_SPR7244() - { - var map = new Dictionary - { - { "uri", "http:" } - }; - - var nameMap = new Dictionary - { - { "givenName", "Arthur" } - }; - - map.Add("value", nameMap); - - var context = new StandardEvaluationContext(map); - var parser = new SpelExpressionParser(); - - const string el2 = "#root['value']['givenName']"; - IExpression exp = parser.ParseExpression(el2); - object evaluated = exp.GetValue(context); - Assert.Equal("Arthur", evaluated); - } - - [Fact] - public void ProjectionTypes_1() - { - var context = new StandardEvaluationContext(new C()); - var parser = new SpelExpressionParser(); - const string el1 = "Ls.![#this.Equals('abc')]"; - IExpression exp = parser.ParseRaw(el1); - var value = (List)exp.GetValue(context); - - // value is list containing [true,false] - Assert.IsType(value[0]); - Type evaluated = exp.GetValueType(context); - Assert.Equal(typeof(List), evaluated); - } - - [Fact] - public void ProjectionTypes_2() - { - var context = new StandardEvaluationContext(new C()); - var parser = new SpelExpressionParser(); - const string el1 = "As.![#this.Equals('abc')]"; - IExpression exp = parser.ParseRaw(el1); - bool[] value = (bool[])exp.GetValue(context); - - // value is array containing [true,false] - Assert.IsType(value[0]); - Type evaluated = exp.GetValueType(context); - Assert.Equal(typeof(bool[]), evaluated); - } - - [Fact] - public void ProjectionTypes_3() - { - var context = new StandardEvaluationContext(new C()); - var parser = new SpelExpressionParser(); - const string el1 = "Ms.![Key.Equals('abc')]"; - IExpression exp = parser.ParseRaw(el1); - var value = (List)exp.GetValue(context); - - // value is list containing [true,false] - Assert.IsType(value[0]); - Type evaluated = exp.GetValueType(context); - Assert.Equal(typeof(List), evaluated); - } - - [Fact] - public void GreaterThanWithNulls_SPR7840() - { - var list = new List - { - new("aaa"), - new("bbb"), - new(null), - new("ccc"), - new(null), - new("zzz") - }; - - var context = new StandardEvaluationContext(list); - var parser = new SpelExpressionParser(); - - const string el1 = "#root.?[A < 'hhh']"; - IExpression exp = parser.ParseRaw(el1); - var value = exp.GetValue(context) as IEnumerable; - Assert.Equal("D(aaa),D(bbb),D(),D(ccc),D()", string.Join(",", value)); - - const string el2 = "#root.?[A > 'hhh']"; - IExpression exp2 = parser.ParseRaw(el2); - var value2 = exp2.GetValue(context) as IEnumerable; - Assert.Equal("D(zzz)", string.Join(",", value2)); - - // trim out the nulls first - const string el3 = "#root.?[A!=null].?[A < 'hhh']"; - IExpression exp3 = parser.ParseRaw(el3); - var value3 = exp3.GetValue(context) as IEnumerable; - Assert.Equal("D(aaa),D(bbb),D(ccc)", string.Join(",", value3)); - } - - [Fact] - public void ConversionPriority_SPR8224() - { - const int integer = 7; - - var emptyEvalContext = new StandardEvaluationContext(); - - var args = new List - { - typeof(int) - }; - - var target = new ConversionPriority1(); - IMethodExecutor me = new ReflectiveMethodResolver(true).Resolve(emptyEvalContext, target, "GetX", args); - - // MethodInvoker chooses getX(int i) when passing Integer - int actual = (int)me.Execute(emptyEvalContext, target, 42).Value; - - // Compiler chooses getX(Number i) when passing Integer - int compiler = target.GetX(integer); - - // Fails! - Assert.Equal(compiler, actual); - - var target2 = new ConversionPriority2(); - IMethodExecutor me2 = new ReflectiveMethodResolver(true).Resolve(emptyEvalContext, target2, "GetX", args); - - // MethodInvoker chooses getX(int i) when passing Integer - int actual2 = (int)me2.Execute(emptyEvalContext, target2, 42).Value; - - // Compiler chooses getX(Number i) when passing Integer - int compiler2 = target2.GetX(integer); - - // Fails! - Assert.Equal(compiler2, actual2); - } - - [Fact] - public void WideningPrimitiveConversion_SPR8224() - { - const int integerValue = 7; - var target = new WideningPrimitiveConversion(); - var emptyEvalContext = new StandardEvaluationContext(); - - var args = new List - { - typeof(int) - }; - - IMethodExecutor me = new ReflectiveMethodResolver(true).Resolve(emptyEvalContext, target, "GetX", args); - int actual = (int)me.Execute(emptyEvalContext, target, integerValue).Value; - - int compiler = target.GetX(integerValue); - Assert.Equal(compiler, actual); - } - - [Fact] - public void ReservedWords_SPR8228() - { - var context = new StandardEvaluationContext(new Reserver()); - var parser = new SpelExpressionParser(); - string ex = "GetReserver().NE"; - IExpression exp = parser.ParseRaw(ex); - string value = exp.GetValue(context); - Assert.Equal("abc", value); - - ex = "GetReserver().ne"; - exp = parser.ParseRaw(ex); - value = exp.GetValue(context); - Assert.Equal("def", value); - - ex = "GetReserver().M[NE]"; - exp = parser.ParseRaw(ex); - value = exp.GetValue(context); - Assert.Equal("xyz", value); - - ex = "GetReserver().DIV"; - exp = parser.ParseRaw(ex); - Assert.Equal(1, exp.GetValue(context)); - - ex = "GetReserver().div"; - exp = parser.ParseRaw(ex); - Assert.Equal(3, exp.GetValue(context)); - - exp = parser.ParseRaw("NE"); - Assert.Equal("abc", exp.GetValue(context)); - } - - [Fact] - public void ReservedWordProperties_SPR9862() - { - var context = new StandardEvaluationContext(); - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseRaw($"T({typeof(TestResources.le.div.mod.reserved.Reserver).FullName}).Const"); - object value = expression.GetValue(context); - Assert.Equal(TestResources.le.div.mod.reserved.Reserver.Const, value); - } - - [Fact] - public void PropertyAccessorOrder_SPR8211() - { - var expressionParser = new SpelExpressionParser(); - var evaluationContext = new StandardEvaluationContext(new ContextObject()); - - evaluationContext.AddPropertyAccessor(new TestPropertyAccessor("FirstContext")); - evaluationContext.AddPropertyAccessor(new TestPropertyAccessor("SecondContext")); - evaluationContext.AddPropertyAccessor(new TestPropertyAccessor("ThirdContext")); - evaluationContext.AddPropertyAccessor(new TestPropertyAccessor("FourthContext")); - - Assert.Equal("first", expressionParser.ParseExpression("shouldBeFirst").GetValue(evaluationContext)); - Assert.Equal("second", expressionParser.ParseExpression("shouldBeSecond").GetValue(evaluationContext)); - Assert.Equal("third", expressionParser.ParseExpression("shouldBeThird").GetValue(evaluationContext)); - Assert.Equal("fourth", expressionParser.ParseExpression("shouldBeFourth").GetValue(evaluationContext)); - } - - [Fact] - public void CustomStaticFunctions_SPR9038() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - - var methodResolvers = new List - { - new ParseReflectiveMethodResolver() - }; - - context.MethodResolvers = methodResolvers; - context.SetVariable("parseFormat", NumberStyles.HexNumber); - IExpression expression = parser.ParseExpression("-Parse('FF', #parseFormat)"); - - int result = expression.GetValue(context, typeof(int)); - Assert.Equal(-255, result); - } - - [Fact] - public void Array() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = null; - object result = null; - - expression = parser.ParseExpression("new Int64(0).GetType()"); - result = expression.GetValue(context, string.Empty); - Assert.Equal("System.Int64", result.ToString()); - - expression = parser.ParseExpression("T(System.Int64[])"); - result = expression.GetValue(context, string.Empty); - Assert.Equal("System.Int64[]", result.ToString()); - - expression = parser.ParseExpression("T(System.String[][][])"); - result = expression.GetValue(context, string.Empty); - Assert.Equal("System.String[][][]", result.ToString()); - Assert.Equal("T(System.String[][][])", ((SpelExpression)expression).ToStringAst()); - - expression = parser.ParseExpression("new Int32[0].GetType()"); - result = expression.GetValue(context, string.Empty); - Assert.Equal("System.Int32[]", result.ToString()); - - expression = parser.ParseExpression("T(Int32[][])"); - result = expression.GetValue(context, string.Empty); - Assert.Equal("System.Int32[][]", result.ToString()); - } - - [Fact] - public void SPR9486_FloatFunctionResolver() - { - float expectedResult = Math.Abs(-10.2f); - var parser = new SpelExpressionParser(); - var testObject = new FunctionsClass(); - - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("Abs(-10.2f)"); - float result = expression.GetValue(context, testObject); - Assert.Equal(expectedResult, result); - } - - [Fact] - public void SPR9486_AddFloatWithDouble() - { - const double expectedNumber = 10.21f + 10.2; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f + 10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_AddFloatWithFloat() - { - const float expectedNumber = 10.21f + 10.2f; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f + 10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_SubtractFloatWithDouble() - { - const double expectedNumber = 10.21f - 10.2; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f - 10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_SubtractFloatWithFloat() - { - const float expectedNumber = 10.21f - 10.2f; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f - 10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_MultiplyFloatWithDouble() - { - const double expectedNumber = 10.21f * 10.2; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f * 10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_MultiplyFloatWithFloat() - { - const float expectedNumber = 10.21f * 10.2f; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f * 10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_FloatDivideByFloat() - { - const float expectedNumber = -10.21f / -10.2f; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f / -10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_FloatDivideByDouble() - { - const double expectedNumber = -10.21f / -10.2; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f / -10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_FloatEqFloatUnaryMinus() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f == -10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(false, result); - } - - [Fact] - public void SPR9486_FloatEqDoubleUnaryMinus() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f == -10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(false, result); - } - - [Fact] - public void SPR9486_FloatEqFloat() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.215f == 10.2109f"); - object result = expression.GetValue(context, null); - Assert.Equal(false, result); - } - - [Fact] - public void SPR9486_FloatEqDouble() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.215f == 10.2109"); - object result = expression.GetValue(context, null); - Assert.Equal(false, result); - } - - [Fact] - public void SPR9486_FloatNotEqFloat() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.215f != 10.2109f"); - object result = expression.GetValue(context, null); - Assert.Equal(true, result); - } - - [Fact] - public void SPR9486_FloatNotEqDouble() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.215f != 10.2109"); - object result = expression.GetValue(context, null); - Assert.Equal(true, result); - } - - [Fact] - public void SPR9486_FloatLessThanFloat() - { - const bool expectedNumber = -10.21f < -10.2f; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f < -10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_FloatLessThanDouble() - { - const bool expectedNumber = -10.21f < -10.2; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f < -10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_FloatLessThanOrEqualFloat() - { - const bool expectedNumber = -10.21f <= -10.22f; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f <= -10.22f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_FloatLessThanOrEqualDouble() - { - const bool expectedNumber = -10.21f <= -10.2; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f <= -10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_FloatGreaterThanFloat() - { - const bool expectedNumber = -10.21f > -10.2f; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f > -10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_FloatGreaterThanDouble() - { - const bool expectedResult = -10.21f > -10.2; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f > -10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedResult, result); - } - - [Fact] - public void SPR9486_FloatGreaterThanOrEqualFloat() - { - const bool expectedNumber = -10.21f >= -10.2f; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f >= -10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedNumber, result); - } - - [Fact] - public void SPR9486_FloatGreaterThanEqualDouble() - { - const bool expectedResult = -10.21f >= -10.2; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("-10.21f >= -10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedResult, result); - } - - [Fact] - public void SPR9486_FloatModulusFloat() - { - const float expectedResult = 10.21f % 10.2f; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f % 10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedResult, result); - } - - [Fact] - public void SPR9486_FloatModulusDouble() - { - const double expectedResult = 10.21f % 10.2; - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f % 10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedResult, result); - } - - [Fact] - public void SPR9486_FloatPowerFloat() - { - double expectedResult = Math.Pow(10.21f, -10.2f); - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f ^ -10.2f"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedResult, result); - } - - [Fact] - public void SPR9486_FloatPowerDouble() - { - double expectedResult = Math.Pow(10.21f, 10.2); - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression expression = parser.ParseExpression("10.21f ^ 10.2"); - object result = expression.GetValue(context, null); - Assert.Equal(expectedResult, result); - } - - [Fact] - public void SPR10091_simpleTestValueType() - { - var parser = new SpelExpressionParser(); - var evaluationContext = new StandardEvaluationContext(new BooleanHolder()); - Type valueType = parser.ParseExpression("SimpleProperty").GetValueType(evaluationContext); - Assert.Equal(typeof(bool), valueType); - } - - [Fact] - public void SPR10091_simpleTestValue() - { - var parser = new SpelExpressionParser(); - var evaluationContext = new StandardEvaluationContext(new BooleanHolder()); - object value = parser.ParseExpression("SimpleProperty").GetValue(evaluationContext); - Assert.IsType(value); - } - - [Fact] - public void SPR10091_primitiveTestValueType() - { - var parser = new SpelExpressionParser(); - var evaluationContext = new StandardEvaluationContext(new BooleanHolder()); - Type valueType = parser.ParseExpression("PrimitiveProperty").GetValueType(evaluationContext); - Assert.Equal(typeof(bool), valueType); - } - - [Fact] - public void SPR10091_primitiveTestValue() - { - var parser = new SpelExpressionParser(); - var evaluationContext = new StandardEvaluationContext(new BooleanHolder()); - object value = parser.ParseExpression("PrimitiveProperty").GetValue(evaluationContext); - Assert.IsType(value); - } - - [Fact] - public void SPR16123() - { - var parser = new SpelExpressionParser(); - parser.ParseExpression("SimpleProperty").SetValue(new BooleanHolder(), null); - Assert.Throws(() => parser.ParseExpression("PrimitiveProperty").SetValue(new BooleanHolder(), null)); - } - - [Fact] - public void SPR10146_MalformedExpressions() - { - DoTestSpr10146("/foo", "EL1070E: Problem parsing left operand"); - DoTestSpr10146("*foo", "EL1070E: Problem parsing left operand"); - DoTestSpr10146("%foo", "EL1070E: Problem parsing left operand"); - DoTestSpr10146("foo", "EL1070E: Problem parsing left operand"); - DoTestSpr10146("&&foo", "EL1070E: Problem parsing left operand"); - DoTestSpr10146("||foo", "EL1070E: Problem parsing left operand"); - DoTestSpr10146("|foo", "EL1069E: Missing expected character ''|''"); - } - - [Fact] - public void SPR10328() - { - var ex = Assert.Throws(() => Parser.ParseExpression("$[]")); - Assert.Contains("EL1071E: A required selection expression has not been specified", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void SPR10452() - { - var configuration = new SpelParserOptions(false, false); - var parser = new SpelExpressionParser(configuration); - - var context = new StandardEvaluationContext(); - IExpression spel = parser.ParseExpression("T(Enum).GetValues(#enumType)"); - - context.SetVariable("enumType", typeof(Abc)); - object result = spel.GetValue(context); - Assert.NotNull(result); - Assert.True(result.GetType().IsArray); - var asArray = result as Array; - Assert.Equal(Abc.A, asArray.GetValue(0)); - Assert.Equal(Abc.B, asArray.GetValue(1)); - Assert.Equal(Abc.C, asArray.GetValue(2)); - - context.SetVariable("enumType", typeof(Xyz)); - result = spel.GetValue(context); - Assert.NotNull(result); - Assert.True(result.GetType().IsArray); - asArray = result as Array; - Assert.Equal(Xyz.X, asArray.GetValue(0)); - Assert.Equal(Xyz.Y, asArray.GetValue(1)); - Assert.Equal(Xyz.Z, asArray.GetValue(2)); - } - - [Fact] - public void SPR9495() - { - var configuration = new SpelParserOptions(false, false); - var parser = new SpelExpressionParser(configuration); - - var context = new StandardEvaluationContext(); - IExpression spel = parser.ParseExpression("T(Enum).GetValues(#enumType)"); - - context.SetVariable("enumType", typeof(Abc)); - object result = spel.GetValue(context); - Assert.NotNull(result); - Assert.True(result.GetType().IsArray); - var asArray = result as Array; - Assert.Equal(Abc.A, asArray.GetValue(0)); - Assert.Equal(Abc.B, asArray.GetValue(1)); - Assert.Equal(Abc.C, asArray.GetValue(2)); - - context.AddMethodResolver(new ValuesMethodResolver()); - result = spel.GetValue(context); - Assert.NotNull(result); - Assert.True(result.GetType().IsArray); - asArray = result as Array; - Assert.Equal(Xyz.X, asArray.GetValue(0)); - Assert.Equal(Xyz.Y, asArray.GetValue(1)); - Assert.Equal(Xyz.Z, asArray.GetValue(2)); - } - - [Fact] - public void SPR10486() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - var rootObject = new Spr10486(); - IExpression classNameExpression = parser.ParseExpression("GetType().FullName"); - IExpression nameExpression = parser.ParseExpression("Name"); - Assert.Equal(typeof(Spr10486).FullName, classNameExpression.GetValue(context, rootObject)); - Assert.Equal("name", nameExpression.GetValue(context, rootObject)); - } - - [Fact] - public void SPR11142() - { - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - var rootObject = new Spr11142(); - IExpression expression = parser.ParseExpression("Something"); - var ex = Assert.Throws(() => expression.GetValue(context, rootObject)); - Assert.Contains("''Something'' cannot be found", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void SPR9194() - { - var one = new TestClass2("abc"); - var two = new TestClass2("abc"); - - var map = new Dictionary - { - { "one", one }, - { "two", two } - }; - - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("['one'] == ['two']"); - Assert.True(expr.GetValue(map)); - } - - [Fact] - public void SPR11348() - { - var coll = new HashSet - { - "one", - "two" - }; - - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("new System.Collections.ArrayList(#root)"); - object value = expr.GetValue(coll); - Assert.IsType(value); - - var list = (ArrayList)value; - Assert.Equal("one", list[0]); - Assert.Equal("two", list[1]); - } - - [Fact] - public void SPR11445_Simple() - { - var context = new StandardEvaluationContext(new Spr11445Class()); - IExpression expr = new SpelExpressionParser().ParseRaw("Echo(Parameter())"); - Assert.Equal(1, expr.GetValue(context)); - } - - [Fact] - public void SPR11445_BeanReference() - { - var context = new StandardEvaluationContext - { - ServiceResolver = new Spr11445Class() - }; - - IExpression expr = new SpelExpressionParser().ParseRaw("@bean.Echo(@bean.Parameter())"); - Assert.Equal(1, expr.GetValue(context)); - } - - [Fact] - public void SPR11609() - { - var sec = new StandardEvaluationContext(); - sec.AddPropertyAccessor(new MapAccessor()); - IExpression exp = new SpelExpressionParser().ParseExpression($"T({typeof(SpelReproTests).FullName}$MapWithConstant).X"); - Assert.Equal(1, exp.GetValue(sec)); - } - - [Fact] - public void SPR9735() - { - var item = new Item - { - Name = "parent" - }; - - var item1 = new Item - { - Name = "child1" - }; - - var item2 = new Item - { - Name = "child2" - }; - - item.Add(item1); - item.Add(item2); - - var parser = new SpelExpressionParser(); - var context = new StandardEvaluationContext(); - IExpression exp = parser.ParseExpression("#item[0].Name"); - context.SetVariable("item", item); - - Assert.Equal("child1", exp.GetValue(context)); - } - - [Fact] - public void SPR12502() - { - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("#root.GetType().Name"); - Assert.Equal(nameof(UnnamedUser), expression.GetValue(new UnnamedUser())); - Assert.Equal(nameof(NamedUser), expression.GetValue(new NamedUser())); - } - - [Fact] - public void SPR12522() - { - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression("T(Array).CreateInstance(T(String), 0)"); - object value = expression.GetValue(); - Assert.True(value is IList); - Assert.Empty((IList)value); - } - - [Fact] - public void SPR12808() - { - var parser = new SpelExpressionParser(); - IExpression expression = parser.ParseExpression($"T({typeof(SpelReproTests).FullName}$DistanceEnforcer).From(#no)"); - var sec = new StandardEvaluationContext(); - sec.SetVariable("no", 1); - Assert.StartsWith("Integer", expression.GetValue(sec).ToString(), StringComparison.Ordinal); - sec = new StandardEvaluationContext(); - sec.SetVariable("no", 1.0F); - Assert.StartsWith("ValueType", expression.GetValue(sec).ToString(), StringComparison.Ordinal); - sec = new StandardEvaluationContext(); - sec.SetVariable("no", "1.0"); - Assert.StartsWith("Object", expression.GetValue(sec).ToString(), StringComparison.Ordinal); - } - - [Fact] - public void SPR13055() - { - var myPayload = new List>(); - - var v1 = new Dictionary(); - var v2 = new Dictionary(); - - v1.Add("test11", "test11"); - v1.Add("test12", "test12"); - v2.Add("test21", "test21"); - v2.Add("test22", "test22"); - - myPayload.Add(v1); - myPayload.Add(v2); - - var context = new StandardEvaluationContext(myPayload); - - var parser = new SpelExpressionParser(); - - const string ex = "#root.![T(String).Join(',', #this.Values)]"; - string res = parser.ParseExpression(ex).GetValue(context); - Assert.Equal("test11,test12,test21,test22", res); - - res = parser.ParseExpression("#root.![#this.Values]").GetValue(context); - Assert.Equal("test11,test12,test21,test22", res); - - res = parser.ParseExpression("#root.![Values]").GetValue(context); - Assert.Equal("test11,test12,test21,test22", res); - } - - [Fact] - public void AccessingFactoryBean_spr9511() - { - var context = new StandardEvaluationContext - { - ServiceResolver = new MyBeanResolver() - }; - - IExpression expr = new SpelExpressionParser().ParseRaw("@foo"); - Assert.Equal("custard", expr.GetValue(context)); - expr = new SpelExpressionParser().ParseRaw("&foo"); - Assert.Equal("foo factory", expr.GetValue(context)); - - var ex = Assert.Throws(() => new SpelExpressionParser().ParseRaw("&@foo")); - Assert.Equal(SpelMessage.InvalidServiceReference, ex.MessageCode); - Assert.Equal(0, ex.Position); - - ex = Assert.Throws(() => new SpelExpressionParser().ParseRaw("@&foo")); - Assert.Equal(SpelMessage.InvalidServiceReference, ex.MessageCode); - Assert.Equal(0, ex.Position); - } - - [Fact] - public void SPR12035() - { - var parser = new SpelExpressionParser(); - - IExpression expression1 = parser.ParseExpression("List.?[ Value>2 ].Count!=0"); - Assert.True(expression1.GetValue(new BeanClass(new ListOf(1.1), new ListOf(2.2)))); - - IExpression expression2 = parser.ParseExpression("List.?[ T(Math).Abs(Value) > 2 ].Count!=0"); - Assert.True(expression2.GetValue(new BeanClass(new ListOf(1.1), new ListOf(-2.2)))); - } - - [Fact] - public void SPR13055_maps() - { - var context = new StandardEvaluationContext(); - var parser = new SpelExpressionParser(); - - IExpression ex = parser.ParseExpression("{'a':'y','b':'n'}.![Value=='y'?Key:null]"); - Assert.Equal("a,", string.Join(",", ex.GetValue>(context))); - - ex = parser.ParseExpression("{2:4,3:6}.![T(Math).Abs(#this.Key) + 5]"); - Assert.Equal("7,8", string.Join(",", ex.GetValue>(context))); - - ex = parser.ParseExpression("{2:4,3:6}.![T(Math).Abs(#this.Value) + 5]"); - Assert.Equal("9,11", string.Join(",", ex.GetValue>(context))); - } - - [Fact] - public void SPR10417() - { - var list1 = new ArrayList - { - "a", - "b", - "x" - }; - - var list2 = new ArrayList - { - "c", - "x" - }; - - var context = new StandardEvaluationContext(); - context.SetVariable("list1", list1); - context.SetVariable("list2", list2); - - // #this should be the element from list1 - IExpression ex = Parser.ParseExpression("#list1.?[#list2.Contains(#this)]"); - var result = ex.GetValue>(context); - Assert.Equal("x", string.Join(",", result)); - - // toString() should be called on the element from list1 - ex = Parser.ParseExpression("#list1.?[#list2.Contains(ToString())]"); - result = ex.GetValue>(context); - Assert.Equal("x", string.Join(",", result)); - - var list3 = new ArrayList - { - 1, - 2, - 3, - 4 - }; - - context = new StandardEvaluationContext(); - context.SetVariable("list3", list3); - ex = Parser.ParseExpression("#list3.?[#this > 2]"); - result = ex.GetValue>(context); - Assert.Equal("3,4", string.Join(",", result)); - - ex = Parser.ParseExpression("#list3.?[#this >= T(Math).Abs(T(Math).Abs(#this))]"); - result = ex.GetValue>(context); - Assert.Equal("1,2,3,4", string.Join(",", result)); - } - - [Fact] - public void SPR10417_maps() - { - var map1 = new Dictionary - { - { "A", 65 }, - { "B", 66 }, - { "X", 66 } - }; - - var map2 = new Dictionary - { - { "X", 66 } - }; - - var context = new StandardEvaluationContext(); - context.SetVariable("map1", map1); - context.SetVariable("map2", map2); - - // #this should be the element from list1 - IExpression ex = Parser.ParseExpression("#map1.?[#map2.ContainsKey(#this.Key)]"); - var result = ex.GetValue(context); - Assert.Single(result); - Assert.Equal(66, result["X"]); - - ex = Parser.ParseExpression("#map1.?[#map2.ContainsKey(Key)]"); - result = ex.GetValue(context); - Assert.Single(result); - Assert.Equal(66, result["X"]); - } - - [Fact] - public void SPR13918() - { - var context = new StandardEvaluationContext(); - context.SetVariable("encoding", "UTF-8"); - - IExpression ex = Parser.ParseExpression("T(System.Text.Encoding).GetEncoding(#encoding)"); - object result = ex.GetValue(context); - Assert.Equal(Encoding.UTF8, result); - } - - [Fact] - public void SPR16032() - { - var context = new StandardEvaluationContext(); - context.SetVariable("str", "a\0b"); - - IExpression ex = Parser.ParseExpression("#str?.Split('\0')"); - object result = ex.GetValue(context); - - string[] resultArray = result as string[]; - Assert.NotNull(resultArray); - - Assert.True(resultArray.SequenceEqual(new[] - { - "a", - "b" - })); - } - - private void DoTestSpr10146(string expression, string expectedMessage) - { - var ex = Assert.Throws(() => new SpelExpressionParser().ParseExpression(expression)); - Assert.Contains(expectedMessage, ex.Message, StringComparison.Ordinal); - } - - private void CheckTemplateParsing(string expression, string expectedValue) - { - CheckTemplateParsing(expression, TemplateExpressionParsingTests.DefaultTemplateParserContextSingleton, expectedValue); - } - - private void CheckTemplateParsing(string expression, IParserContext context, string expectedValue) - { - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression(expression, context); - Assert.Equal(expectedValue, expr.GetValue(TestScenarioCreator.GetTestEvaluationContext())); - } - - private void CheckTemplateParsingError(string expression, string expectedMessage) - { - CheckTemplateParsingError(expression, TemplateExpressionParsingTests.DefaultTemplateParserContextSingleton, expectedMessage); - } - - private void CheckTemplateParsingError(string expression, IParserContext context, string expectedMessage) - { - var parser = new SpelExpressionParser(); - var parseException = Assert.Throws(() => parser.ParseExpression(expression, context)); - Assert.Equal(expectedMessage, parseException.SimpleMessage); - } - - public static class FooLists - { - public static List NewArrayList(IEnumerable iterable) - { - return new List(iterable); - } - - public static List NewArrayList(params object[] elements) - { - throw new InvalidOperationException(); - } - } - - public static class DistanceEnforcer - { - public static string From(ValueType no) - { - return $"ValueType:{no}"; - } - - public static string From(int no) - { - return $"Integer:{no}"; - } - - public static string From(object no) - { - return $"Object:{no}"; - } - } - - public sealed class ValuesMethodResolver : IMethodResolver - { - public IMethodExecutor Resolve(IEvaluationContext context, object targetObject, string name, List argumentTypes) - { - return new ValuesMethodExecutor(); - } - } - - public sealed class ValuesMethodExecutor : IMethodExecutor - { - public ITypedValue Execute(IEvaluationContext context, object target, params object[] arguments) - { - try - { - MethodInfo method = typeof(Enum).GetMethod(nameof(Enum.GetValues), new[] - { - typeof(Type) - }); - - object value = method.Invoke(null, new object[] - { - typeof(Xyz) - }); - - return new TypedValue(value, value == null ? typeof(object) : value.GetType()); - } - catch (Exception ex) - { - throw new AccessException(ex.Message, ex); - } - } - } - - public sealed class ParseReflectiveMethodResolver : ReflectiveMethodResolver - { - protected override MethodInfo[] GetMethods(Type type) - { - try - { - return new[] - { - typeof(int).GetMethod(nameof(int.Parse), new[] - { - typeof(string), - typeof(NumberStyles) - }) - }; - } - catch (Exception) - { - return System.Array.Empty(); - } - } - } - - public sealed class Reserver - { - public string NE = "abc"; - public string ne = "def"; - public int DIV = 1; - - public int div = 3; - public Dictionary M = new(); - - public Reserver() - { - M.Add("NE", "xyz"); - } - - public Reserver GetReserver() - { - return this; - } - } - - public sealed class WideningPrimitiveConversion - { - public int GetX(long i) - { - return 10; - } - } - - public sealed class ConversionPriority1 - { - public int GetX(ValueType i) - { - return 20; - } - - public int GetX(int i) - { - return 10; - } - } - - public sealed class ConversionPriority2 - { - public int GetX(int i) - { - return 10; - } - - public int GetX(ValueType i) - { - return 20; - } - } - - public sealed class DollarSquareTemplateParserContext : IParserContext - { - public bool IsTemplate => true; - - public string ExpressionPrefix => "$["; - - public string ExpressionSuffix => "]"; - } - - public sealed class MyTypeLocator : StandardTypeLocator - { - public override Type FindType(string typeName) - { - if (typeName == "Spr5899Class") - { - return typeof(Spr5899Class); - } - - if (typeName == "Outer") - { - return typeof(Outer); - } - - return base.FindType(typeName); - } - } - - public sealed class Spr5899Class - { - public Spr5899Class() - { - } - - public Spr5899Class(object i) - { - } - - public Spr5899Class(object i, params string[] s) - { - } - - public int TryToInvokeWithNull(object value) - { - return value == null ? default : (int)value; - } - - public int TryToInvokeWithNull2(int i) - { - return i; - } - - public string TryToInvokeWithNull3(object value, params string[] strings) - { - var sb = new StringBuilder(); - - foreach (string str in strings) - { - sb.Append(str ?? "null"); - } - - return sb.ToString(); - } - - public override string ToString() - { - return "instance"; - } - } - - public sealed class TestProperties - { - public Dictionary JdbcProperties = new(); - - public Dictionary Foo = new(); - - public TestProperties() - { - JdbcProperties.Add("username", "Dave"); - JdbcProperties.Add("alias", "Dave2"); - JdbcProperties.Add("foo.bar", "Elephant"); - Foo.Add("bar", "alias"); - } - } - - public sealed class MapAccessor : IPropertyAccessor - { - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(IDictionary) - }; - } - - public bool CanRead(IEvaluationContext context, object target, string name) - { - return ((IDictionary)target).Contains(name); - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - return new TypedValue(((IDictionary)target)[name]); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return true; - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - ((IDictionary)target).Add(name, newValue); - } - } - - public sealed class Outer - { - public sealed class Inner - { - public static int Run() - { - return 12; - } - - public int Run2() - { - return 13; - } - } - } - - public sealed class XX - { - public Dictionary M; - - public string Floo = "bar"; - - public XX() - { - M = new Dictionary - { - { "$foo", "wibble" }, - { "bar", "siddle" } - }; - } - } - - public sealed class MyBeanResolver : IServiceResolver - { - public object Resolve(IEvaluationContext context, string serviceName) - { - if (serviceName == "foo") - { - return "custard"; - } - - if (serviceName == "foo.bar") - { - return "trouble"; - } - - if (serviceName == "&foo") - { - return "foo factory"; - } - - if (serviceName == "goo") - { - throw new AccessException("DONT ASK ME ABOUT GOO"); - } - - return null; - } - } - - public sealed class C - { - public List Ls; - - public string[] As; - - public Dictionary Ms; - - public C() - { - Ls = new List - { - "abc", - "def" - }; - - As = new[] - { - "abc", - "def" - }; - - Ms = new Dictionary - { - ["abc"] = "xyz", - ["def"] = "pqr" - }; - } - } - - public sealed class D - { - public string A; - - public D(string s) - { - A = s; - } - - public override string ToString() - { - return $"D({A})"; - } - } - - public sealed class Resource - { - public string Server => "abc"; - } - - public sealed class ResourceSummary - { - public Resource Resource { get; } - - public ResourceSummary() - { - Resource = new Resource(); - } - } - - public sealed class Foo - { - public ResourceSummary Resource = new(); - } - - public sealed class Foo2 - { - public void Execute(string str) - { - Console.WriteLine($"Value: {str}"); - } - } - - public sealed class Message - { - public string Payload { get; set; } - } - - public sealed class Goo - { - public static Goo Instance = new(); - - public string Bar = "Key"; - - public string Value; - - public string Wibble = "wobble"; - - public string Key - { - get => "hello"; - set => Value = value; - } - } - - public sealed class Holder - { - public Dictionary Map = new(); - } - - public sealed class FunctionsClass // SPR9486 - { - public int Abs(int value) - { - return Math.Abs(value); - } - - public float Abs(float value) - { - return Math.Abs(value); - } - } - - public interface IVarargsInterface - { - string Process(params string[] args); - } - - public sealed class VarargsReceiver : IVarargsInterface - { - public string Process(params string[] args) - { - return "OK"; - } - } - - public sealed class ReflectionUtil - { - public object MethodToCall(T param) - { - Console.WriteLine($"{param} {param.GetType()}"); - return "object MethodToCall(T param)"; - } - - public void Foo(params int[] array) - { - if (array.Length == 0) - { - throw new SystemException(); - } - } - - public void Foo(params float[] array) - { - if (array.Length == 0) - { - throw new SystemException(); - } - } - - public void Foo(params double[] array) - { - if (array.Length == 0) - { - throw new SystemException(); - } - } - - public void Foo(params short[] array) - { - if (array.Length == 0) - { - throw new SystemException(); - } - } - - public void Foo(params long[] array) - { - if (array.Length == 0) - { - throw new SystemException(); - } - } - - public void Foo(params bool[] array) - { - if (array.Length == 0) - { - throw new SystemException(); - } - } - - public void Foo(params char[] array) - { - if (array.Length == 0) - { - throw new SystemException(); - } - } - - public void Foo(params byte[] array) - { - if (array.Length == 0) - { - throw new SystemException(); - } - } - - public void Bar(params int[] array) - { - if (array.Length == 0) - { - throw new SystemException(); - } - } - } - - public sealed class TestPropertyAccessor : IPropertyAccessor - { - private readonly string _mapName; - - public TestPropertyAccessor(string mapName) - { - _mapName = mapName; - } - - public Dictionary GetMap(object target) - { - FieldInfo f = target.GetType().GetField(_mapName); - return (Dictionary)f.GetValue(target); - } - - public bool CanRead(IEvaluationContext context, object target, string name) - { - return GetMap(target).ContainsKey(name); - } - - public bool CanWrite(IEvaluationContext context, object target, string name) - { - return GetMap(target).ContainsKey(name); - } - - public IList GetSpecificTargetClasses() - { - return new List - { - typeof(ContextObject) - }; - } - - public ITypedValue Read(IEvaluationContext context, object target, string name) - { - return new TypedValue(GetMap(target)[name]); - } - - public void Write(IEvaluationContext context, object target, string name, object newValue) - { - GetMap(target)[name] = (string)newValue; - } - } - - public sealed class ContextObject - { - public Dictionary FirstContext = new(); - - public Dictionary SecondContext = new(); - - public Dictionary ThirdContext = new(); - - public Dictionary FourthContext = new(); - - public ContextObject() - { - FirstContext.Add("shouldBeFirst", "first"); - SecondContext.Add("shouldBeFirst", "second"); - ThirdContext.Add("shouldBeFirst", "third"); - FourthContext.Add("shouldBeFirst", "fourth"); - - SecondContext.Add("shouldBeSecond", "second"); - ThirdContext.Add("shouldBeSecond", "third"); - FourthContext.Add("shouldBeSecond", "fourth"); - - ThirdContext.Add("shouldBeThird", "third"); - FourthContext.Add("shouldBeThird", "fourth"); - - FourthContext.Add("shouldBeFourth", "fourth"); - } - } - - public sealed class ListOf - { - public double Value { get; } - - public ListOf(double v) - { - Value = v; - } - } - - public sealed class BeanClass - { - public List List { get; } - - public BeanClass(params ListOf[] list) - { - List = new List(list); - } - } - - public enum Abc - { - A, - B, - C - } - - public enum Xyz - { - X, - Y, - Z - } - - public sealed class BooleanHolder - { - public object SimpleProperty { get; set; } = true; - - public bool PrimitiveProperty { get; set; } = true; - - public object IsSimpleProperty => SimpleProperty; - - public bool IsPrimitiveProperty => PrimitiveProperty; - } - - public interface IGenericInterface - { - T Property { get; set; } - } - - public sealed class GenericImplementation : IGenericInterface - { - public int Property { get; set; } - } - - public class PackagePrivateClassWithGetter - { - public int Property { get; } - } - - public sealed class OnlyBridgeMethod : PackagePrivateClassWithGetter - { - } - - public interface IStaticFinal - { - string VALUE => "interfaceValue"; - } - - public abstract class AbstractStaticFinal : IStaticFinal - { - } - - public sealed class StaticFinalImpl1 : AbstractStaticFinal - { - } - - public sealed class StaticFinalImpl2 : AbstractStaticFinal - { - } - - public sealed class Spr10486 - { - public string Name { get; set; } = "name"; - } - - public sealed class Spr11142 - { - public string IsSomething => string.Empty; - } - - public sealed class TestClass2 - { - // SPR-9194 - private readonly string _str; - - public TestClass2(string str) - { - _str = str; - } - - public override bool Equals(object obj) - { - return ReferenceEquals(this, obj) || (obj is TestClass2 other && _str == other._str); - } - - public override int GetHashCode() - { - return _str.GetHashCode(); - } - } - - public sealed class Spr11445Class : IServiceResolver - { - private readonly AtomicInteger _counter = new(); - - public int Echo(int invocation) - { - return invocation; - } - - public int Parameter() - { - return _counter.IncrementAndGet(); - } - - public object Resolve(IEvaluationContext context, string serviceName) - { - return serviceName == "bean" ? this : null; - } - } - - public sealed class MapWithConstant : Hashtable - { - public static readonly int X = 1; - } - - public sealed class Item : IList, IList - { - private readonly List _children = new(); - - public string Name { get; set; } - - public bool IsReadOnly => false; - - public int Count => _children.Count; - - public bool IsFixedSize => false; - - public bool IsSynchronized => false; - - public object SyncRoot => ((IList)_children).SyncRoot; - - object IList.this[int index] - { - get => _children[index]; - set => _children[index] = (Item)value; - } - - public Item this[int index] - { - get => _children[index]; - set => _children[index] = value; - } - - public void Add(Item item) - { - _children.Add(item); - } - - public int Add(object value) - { - throw new NotImplementedException(); - } - - public void Clear() - { - _children.Clear(); - } - - public bool Contains(Item o) - { - return _children.Contains(o); - } - - public bool Contains(object value) - { - return ((IList)_children).Contains(value); - } - - public void CopyTo(Item[] array, int arrayIndex) - { - _children.CopyTo(array, arrayIndex); - } - - public void CopyTo(Array array, int index) - { - ((IList)_children).CopyTo(array, index); - } - - public IEnumerator GetEnumerator() - { - return _children.GetEnumerator(); - } - - public int IndexOf(Item item) - { - return _children.IndexOf(item); - } - - public int IndexOf(object value) - { - return ((IList)_children).IndexOf(value); - } - - public void Insert(int index, Item item) - { - _children.Insert(index, item); - } - - public void Insert(int index, object value) - { - ((IList)_children).Insert(index, value); - } - - public bool Remove(Item item) - { - return _children.Remove(item); - } - - public void Remove(object value) - { - ((IList)_children).Remove(value); - } - - public void RemoveAt(int index) - { - _children.RemoveAt(index); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _children.GetEnumerator(); - } - } - - public sealed class UnnamedUser - { - } - - public sealed class NamedUser - { - public string Name => "foo"; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/SpelUtilities.cs b/src/Common/test/Common.Expression.Test/Spring/SpelUtilities.cs deleted file mode 100644 index 60902b069e..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/SpelUtilities.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public static class SpelUtilities -{ - public static void PrintAbstractSyntaxTree(TextWriter printStream, IExpression expression) - { - printStream.WriteLine($"===> Expression '{expression.ExpressionString}' - AST start"); - PrintAst(printStream, ((SpelExpression)expression).Ast, string.Empty); - printStream.WriteLine($"===> Expression '{expression.ExpressionString}' - AST end"); - } - - private static void PrintAst(TextWriter output, ISpelNode t, string indent) - { - if (t != null) - { - var sb = new StringBuilder(); - sb.Append(indent).Append(t.GetType().Name); - sb.Append(" value:").Append(t.ToStringAst()); - sb.Append(t.ChildCount < 2 ? string.Empty : $" #children:{t.ChildCount}"); - output.WriteLine(sb.ToString()); - - for (int i = 0; i < t.ChildCount; i++) - { - PrintAst(output, t.GetChild(i), $"{indent} "); - } - } - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/Standard/PropertiesConversionSpelTests.cs b/src/Common/test/Common.Expression.Test/Spring/Standard/PropertiesConversionSpelTests.cs deleted file mode 100644 index e27b0e2640..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/Standard/PropertiesConversionSpelTests.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring.Standard; - -public sealed class PropertiesConversionSpelTests -{ - private static readonly SpelExpressionParser Parser = new(); - - [Fact] - public void Props() - { - var props = new Dictionary - { - { "x", "1" }, - { "y", "2" }, - { "z", "3" } - }; - - IExpression expression = Parser.ParseExpression("Foo(#props)"); - var context = new StandardEvaluationContext(); - context.SetVariable("props", props); - string result = expression.GetValue(context, new TestBean()); - Assert.Equal("123", result); - } - - [Fact] - public void MapWithAllStringValues() - { - var map = new Dictionary - { - { "x", "1" }, - { "y", "2" }, - { "z", "3" } - }; - - IExpression expression = Parser.ParseExpression("Foo(#props)"); - var context = new StandardEvaluationContext(); - context.SetVariable("props", map); - string result = expression.GetValue(context, new TestBean()); - Assert.Equal("123", result); - } - - [Fact] - public void MapWithNonStringValue() - { - var map = new Dictionary - { - { "x", "1" }, - { "y", 2 }, - { "z", "3" }, - { "a", Guid.NewGuid() } - }; - - IExpression expression = Parser.ParseExpression("Foo(#props)"); - var context = new StandardEvaluationContext(); - context.SetVariable("props", map); - string result = expression.GetValue(context, new TestBean()); - Assert.Equal("123", result); - } - - [Fact] - public void CustomMapWithNonStringValue() - { - var map = new CustomMap - { - { "x", "1" }, - { "y", 2 }, - { "z", "3" } - }; - - IExpression expression = Parser.ParseExpression("Foo(#props)"); - var context = new StandardEvaluationContext(); - context.SetVariable("props", map); - string result = expression.GetValue(context, new TestBean()); - Assert.Equal("123", result); - } - - public sealed class TestBean - { - public string Foo(IDictionary props) - { - return props["x"]?.ToString() + props["y"] + props["z"]; - } - } - - public sealed class CustomMap : Dictionary - { - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/Standard/SpelParserTests.cs b/src/Common/test/Common.Expression.Test/Spring/Standard/SpelParserTests.cs deleted file mode 100644 index 468ef557da..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/Standard/SpelParserTests.cs +++ /dev/null @@ -1,401 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Ast; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring.Standard; - -public sealed class SpelParserTests -{ - [Fact] - public void TheMostBasic() - { - var parser = new SpelExpressionParser(); - var expr = parser.ParseRaw("2") as SpelExpression; - Assert.NotNull(expr); - Assert.NotNull(expr.Ast); - Assert.Equal(2, expr.GetValue()); - Assert.Equal(typeof(int), expr.GetValueType()); - Assert.Equal(2, expr.Ast.GetValue(null)); - } - - [Fact] - public void ValueType() - { - var parser = new SpelExpressionParser(); - var ctx = new StandardEvaluationContext(); - Type type = parser.ParseRaw("2").GetValueType(); - Assert.Equal(typeof(int), type); - type = parser.ParseRaw("12").GetValueType(ctx); - Assert.Equal(typeof(int), type); - type = parser.ParseRaw("null").GetValueType(); - Assert.Null(type); - type = parser.ParseRaw("null").GetValueType(ctx); - Assert.Null(type); - object o = parser.ParseRaw("null").GetValue(ctx, typeof(object)); - Assert.Null(o); - } - - [Fact] - public void Whitespace() - { - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseRaw("2 + 3"); - Assert.Equal(5, expr.GetValue()); - expr = parser.ParseRaw("2 + 3"); - Assert.Equal(5, expr.GetValue()); - expr = parser.ParseRaw("2\n+\t3"); - Assert.Equal(5, expr.GetValue()); - expr = parser.ParseRaw("2\r\n+\t3"); - Assert.Equal(5, expr.GetValue()); - } - - [Fact] - public void ArithmeticPlus1() - { - var parser = new SpelExpressionParser(); - var expr = parser.ParseRaw("2+2") as SpelExpression; - Assert.NotNull(expr); - Assert.NotNull(expr.Ast); - Assert.Equal(4, expr.GetValue()); - } - - [Fact] - public void ArithmeticPlus2() - { - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseRaw("37+41"); - Assert.Equal(78, expr.GetValue()); - } - - [Fact] - public void ArithmeticMultiply1() - { - var parser = new SpelExpressionParser(); - var expr = parser.ParseRaw("2*3") as SpelExpression; - Assert.NotNull(expr); - Assert.NotNull(expr.Ast); - - Assert.Equal(6, expr.GetValue()); - } - - [Fact] - public void ArithmeticPrecedence1() - { - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseRaw("2*3+5"); - Assert.Equal(11, expr.GetValue()); - } - - [Fact] - public void GeneralExpressions() - { - var ex = Assert.Throws(() => - { - var parser = new SpelExpressionParser(); - parser.ParseRaw("new String"); - }); - - ParseExceptionRequirements(ex, SpelMessage.MissingConstructorArgs, 10); - - ex = Assert.Throws(() => - { - var parser = new SpelExpressionParser(); - parser.ParseRaw("new String(3,"); - }); - - ParseExceptionRequirements(ex, SpelMessage.RunOutOfArguments, 10); - - ex = Assert.Throws(() => - { - var parser = new SpelExpressionParser(); - parser.ParseRaw("new String(3"); - }); - - ParseExceptionRequirements(ex, SpelMessage.RunOutOfArguments, 10); - - ex = Assert.Throws(() => - { - var parser = new SpelExpressionParser(); - parser.ParseRaw("new String("); - }); - - ParseExceptionRequirements(ex, SpelMessage.RunOutOfArguments, 10); - - ex = Assert.Throws(() => - { - var parser = new SpelExpressionParser(); - parser.ParseRaw("\"abc"); - }); - - ParseExceptionRequirements(ex, SpelMessage.NonTerminatingDoubleQuotedString, 0); - - ex = Assert.Throws(() => - { - var parser = new SpelExpressionParser(); - parser.ParseRaw("'abc"); - }); - - ParseExceptionRequirements(ex, SpelMessage.NonTerminatingQuotedString, 0); - } - - [Fact] - public void ArithmeticPrecedence2() - { - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseRaw("2+3*5"); - Assert.Equal(17, expr.GetValue()); - } - - [Fact] - public void ArithmeticPrecedence3() - { - IExpression expr = new SpelExpressionParser().ParseRaw("3+10/2"); - Assert.Equal(8, expr.GetValue()); - } - - [Fact] - public void ArithmeticPrecedence4() - { - IExpression expr = new SpelExpressionParser().ParseRaw("10/2+3"); - Assert.Equal(8, expr.GetValue()); - } - - [Fact] - public void ArithmeticPrecedence5() - { - IExpression expr = new SpelExpressionParser().ParseRaw("(4+10)/2"); - Assert.Equal(7, expr.GetValue()); - } - - [Fact] - public void ArithmeticPrecedence6() - { - IExpression expr = new SpelExpressionParser().ParseRaw("(3+2)*2"); - Assert.Equal(10, expr.GetValue()); - } - - [Fact] - public void BooleanOperators() - { - IExpression expr = new SpelExpressionParser().ParseRaw("true"); - Assert.True(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("false"); - Assert.False(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("false and false"); - Assert.False(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("true and (true or false)"); - Assert.True(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("true and true or false"); - Assert.True(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("!true"); - Assert.False(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("!(false or true)"); - Assert.False(expr.GetValue()); - } - - [Fact] - public void BooleanOperators_symbolic_spr9614() - { - IExpression expr = new SpelExpressionParser().ParseRaw("true"); - Assert.True(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("false"); - Assert.False(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("false && false"); - Assert.False(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("true && (true || false)"); - Assert.True(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("true && true || false"); - Assert.True(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("!true"); - Assert.False(expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("!(false || true)"); - Assert.False(expr.GetValue()); - } - - [Fact] - public void StringLiterals() - { - IExpression expr = new SpelExpressionParser().ParseRaw("'howdy'"); - Assert.Equal("howdy", expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("'hello '' world'"); - Assert.Equal("hello ' world", expr.GetValue()); - } - - [Fact] - public void StringLiterals2() - { - IExpression expr = new SpelExpressionParser().ParseRaw("'howdy'.Substring(0,2)"); - Assert.Equal("ho", expr.GetValue()); - } - - [Fact] - public void TestStringLiterals_DoubleQuotes_spr9620() - { - IExpression expr = new SpelExpressionParser().ParseRaw("\"double quote: \"\".\""); - Assert.Equal("double quote: \".", expr.GetValue()); - expr = new SpelExpressionParser().ParseRaw("\"hello \"\" world\""); - Assert.Equal("hello \" world", expr.GetValue()); - } - - [Fact] - public void TestStringLiterals_DoubleQuotes_spr9620_2() - { - var ex = Assert.Throws(() => new SpelExpressionParser().ParseRaw("\"double quote: \\\"\\\".\"")); - - Assert.Equal(17, ex.Position); - Assert.Equal(SpelMessage.UnexpectedEscapeChar, ex.MessageCode); - } - - [Fact] - public void PositionalInformation() - { - var expr = new SpelExpressionParser().ParseRaw("true and true or false") as SpelExpression; - ISpelNode rootAst = expr.Ast; - var operatorOr = (OpOr)rootAst; - var operatorAnd = (OpAnd)operatorOr.LeftOperand; - SpelNode rightOrOperand = operatorOr.RightOperand; - - // check position for final 'false' - Assert.Equal(17, rightOrOperand.StartPosition); - Assert.Equal(22, rightOrOperand.EndPosition); - - // check position for first 'true' - Assert.Equal(0, operatorAnd.LeftOperand.StartPosition); - Assert.Equal(4, operatorAnd.LeftOperand.EndPosition); - - // check position for second 'true' - Assert.Equal(9, operatorAnd.RightOperand.StartPosition); - Assert.Equal(13, operatorAnd.RightOperand.EndPosition); - - // check position for OperatorAnd - Assert.Equal(5, operatorAnd.StartPosition); - Assert.Equal(8, operatorAnd.EndPosition); - - // check position for OperatorOr - Assert.Equal(14, operatorOr.StartPosition); - Assert.Equal(16, operatorOr.EndPosition); - } - - [Fact] - public void Test_TokenKind() - { - TokenKind tk = TokenKind.Not; - Assert.False(tk.HasPayload); - Assert.Equal("NOT(!)", tk.ToString()); - - tk = TokenKind.Minus; - Assert.False(tk.HasPayload); - Assert.Equal("MINUS(-)", tk.ToString()); - - tk = TokenKind.LiteralString; - Assert.Equal("LITERAL_STRING", tk.ToString()); - Assert.True(tk.HasPayload); - } - - [Fact] - public void Test_Token() - { - var token = new Token(TokenKind.Not, 0, 3); - Assert.Equal(TokenKind.Not, token.Kind); - Assert.Equal(0, token.StartPos); - Assert.Equal(3, token.EndPos); - Assert.Equal("[NOT(!)](0,3)", token.ToString()); - - token = new Token(TokenKind.LiteralString, "abc".ToCharArray(), 0, 3); - Assert.Equal(TokenKind.LiteralString, token.Kind); - Assert.Equal(0, token.StartPos); - Assert.Equal(3, token.EndPos); - Assert.Equal("[LITERAL_STRING:abc](0,3)", token.ToString()); - } - - [Fact] - public void Exceptions() - { - var exprEx = new ExpressionException("test"); - Assert.Equal("test", exprEx.SimpleMessage); - Assert.Equal("test", exprEx.ToDetailedString()); - Assert.Equal("test", exprEx.Message); - - exprEx = new ExpressionException("wibble", "test"); - Assert.Equal("test", exprEx.SimpleMessage); - Assert.Equal("Expression [wibble]: test", exprEx.ToDetailedString()); - Assert.Equal("Expression [wibble]: test", exprEx.Message); - - exprEx = new ExpressionException("wibble", 3, "test"); - Assert.Equal("test", exprEx.SimpleMessage); - Assert.Equal("Expression [wibble] @3: test", exprEx.ToDetailedString()); - Assert.Equal("Expression [wibble] @3: test", exprEx.Message); - } - - [Fact] - public void ParseMethodsOnNumbers() - { - CheckNumber("3.14.ToString(T(System.Globalization.CultureInfo).InvariantCulture)", "3.14", typeof(string)); - CheckNumber("3.ToString(T(System.Globalization.CultureInfo).InvariantCulture)", "3", typeof(string)); - } - - [Fact] - public void Numerics() - { - CheckNumber("2", 2, typeof(int)); - CheckNumber("22", 22, typeof(int)); - CheckNumber("+22", 22, typeof(int)); - CheckNumber("-22", -22, typeof(int)); - CheckNumber("2L", 2L, typeof(long)); - CheckNumber("22l", 22L, typeof(long)); - - CheckNumber("0x1", 1, typeof(int)); - CheckNumber("0x1L", 1L, typeof(long)); - CheckNumber("0xa", 10, typeof(int)); - CheckNumber("0xAL", 10L, typeof(long)); - - CheckNumberError("0x", SpelMessage.NotAnInteger); - CheckNumberError("0xL", SpelMessage.NotALong); - CheckNumberError(".324", SpelMessage.UnexpectedDataAfterDot); - CheckNumberError("3.4L", SpelMessage.RealCannotBeLong); - - CheckNumber("3.5f", 3.5f, typeof(float)); - CheckNumber("1.2e3", 1.2e3d, typeof(double)); - CheckNumber("1.2e+3", 1.2e3d, typeof(double)); - CheckNumber("1.2e-3", 1.2e-3d, typeof(double)); - CheckNumber("1.2e3", 1.2e3d, typeof(double)); - CheckNumber("1e+3", 1e3d, typeof(double)); - } - - private void ParseExceptionRequirements(SpelParseException ex, SpelMessage expectedMessage, int expectedPosition) - { - Assert.Equal(expectedMessage, ex.MessageCode); - Assert.Equal(expectedPosition, ex.Position); - Assert.Contains(ex.ExpressionString, ex.Message, StringComparison.Ordinal); - } - - private void CheckNumber(string expression, object value, Type type) - { - try - { - var parser = new SpelExpressionParser(); - var expr = parser.ParseRaw(expression) as SpelExpression; - object exprVal = expr.GetValue(); - Assert.Equal(value, exprVal); - Assert.Equal(type, exprVal.GetType()); - } - catch (Exception ex) - { - throw new Exception(ex.Message, ex); - } - } - - private void CheckNumberError(string expression, SpelMessage expectedMessage) - { - var parser = new SpelExpressionParser(); - var ex = Assert.Throws(() => parser.ParseRaw(expression)); - Assert.Equal(expectedMessage, ex.MessageCode); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/StandardTypeLocatorTests.cs b/src/Common/test/Common.Expression.Test/Spring/StandardTypeLocatorTests.cs deleted file mode 100644 index 5b0926ce7e..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/StandardTypeLocatorTests.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class StandardTypeLocatorTests -{ - [Fact] - public void TestImports() - { - var locator = new StandardTypeLocator(); - Assert.Equal(typeof(int), locator.FindType("System.Int32")); - Assert.Equal(typeof(string), locator.FindType("System.String")); - - List prefixes = locator.ImportPrefixes; - Assert.Single(prefixes); - Assert.Contains("System", prefixes); - Assert.DoesNotContain("System.Collections", prefixes); - - Assert.Equal(typeof(bool), locator.FindType("Boolean")); - - var ex = Assert.Throws(() => locator.FindType("StringBuilder")); - Assert.Equal(SpelMessage.TypeNotFound, ex.MessageCode); - locator.RegisterImport("System.Text"); - Assert.Equal(typeof(StringBuilder), locator.FindType("StringBuilder")); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/Support/ReflectionHelperTests.cs b/src/Common/test/Common.Expression.Test/Spring/Support/ReflectionHelperTests.cs deleted file mode 100644 index d0ca92f5f1..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/Support/ReflectionHelperTests.cs +++ /dev/null @@ -1,758 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring.Support; - -public sealed class ReflectionHelperTests : AbstractExpressionTests -{ - [Fact] - public void TestUtilities() - { - var expr = Parser.ParseExpression("3+4+5+6+7-2") as SpelExpression; - var ps = new StringWriter(); - SpelUtilities.PrintAbstractSyntaxTree(ps, expr); - ps.Flush(); - string s = ps.ToString(); - - // ===> Expression '3+4+5+6+7-2' - AST start - // OperatorMinus value:(((((3 + 4) + 5) + 6) + 7) - 2) #children:2 - // OperatorPlus value:((((3 + 4) + 5) + 6) + 7) #children:2 - // OperatorPlus value:(((3 + 4) + 5) + 6) #children:2 - // OperatorPlus value:((3 + 4) + 5) #children:2 - // OperatorPlus value:(3 + 4) #children:2 - // CompoundExpression value:3 - // IntLiteral value:3 - // CompoundExpression value:4 - // IntLiteral value:4 - // CompoundExpression value:5 - // IntLiteral value:5 - // CompoundExpression value:6 - // IntLiteral value:6 - // CompoundExpression value:7 - // IntLiteral value:7 - // CompoundExpression value:2 - // IntLiteral value:2 - // ===> Expression '3+4+5+6+7-2' - AST end - Assert.Contains("===> Expression '3+4+5+6+7-2' - AST start", s, StringComparison.Ordinal); - Assert.Contains(" OpPlus value:((((3 + 4) + 5) + 6) + 7) #children:2", s, StringComparison.Ordinal); - } - - [Fact] - public void TestTypedValue() - { - var tv1 = new TypedValue("hello"); - var tv2 = new TypedValue("hello"); - var tv3 = new TypedValue("bye"); - Assert.Equal(typeof(string), tv1.TypeDescriptor); - Assert.Equal("TypedValue: 'hello' of [System.String]", tv1.ToString()); - Assert.Equal(tv1, tv2); - Assert.Equal(tv2, tv1); - Assert.NotEqual(tv1, tv3); - Assert.NotEqual(tv2, tv3); - Assert.NotEqual(tv3, tv1); - Assert.NotEqual(tv3, tv2); - Assert.Equal(tv1.GetHashCode(), tv2.GetHashCode()); - Assert.NotEqual(tv1.GetHashCode(), tv3.GetHashCode()); - Assert.NotEqual(tv2.GetHashCode(), tv3.GetHashCode()); - } - - [Fact] - public void TestReflectionHelperCompareArguments_ExactMatching() - { - var tc = new StandardTypeConverter(); - - // Calling foo(String) with (String) is exact match - CheckMatch(new[] - { - typeof(string) - }, new[] - { - typeof(string) - }, tc, ArgumentsMatchKind.Exact); - - // Calling foo(String,Integer) with (String,Integer) is exact match - CheckMatch(new[] - { - typeof(string), - typeof(int) - }, new[] - { - typeof(string), - typeof(int) - }, tc, ArgumentsMatchKind.Exact); - } - - [Fact] - public void TestReflectionHelperCompareArguments_CloseMatching() - { - var tc = new StandardTypeConverter(); - - // Calling foo(List) with (ArrayList) is close match (no conversion required) - CheckMatch(new[] - { - typeof(ArrayList) - }, new[] - { - typeof(IList) - }, tc, ArgumentsMatchKind.Close); - - // Passing (Sub,String) on call to foo(Super,String) is close match - CheckMatch(new[] - { - typeof(Sub), - typeof(string) - }, new[] - { - typeof(Super), - typeof(string) - }, tc, ArgumentsMatchKind.Close); - - // Passing (String,Sub) on call to foo(String,Super) is close match - CheckMatch(new[] - { - typeof(string), - typeof(Sub) - }, new[] - { - typeof(string), - typeof(Super) - }, tc, ArgumentsMatchKind.Close); - } - - [Fact] - public void TestReflectionHelperCompareArguments_RequiresConversionMatching() - { - var tc = new StandardTypeConverter(); - - // Calling foo(String,int) with (String,Integer) requires boxing conversion of argument one - CheckMatch(new[] - { - typeof(string), - typeof(int) - }, new[] - { - typeof(string), - typeof(object) - }, tc, ArgumentsMatchKind.Close); - - // Passing (int,String) on call to foo(Integer,String) requires boxing conversion of argument zero - CheckMatch(new[] - { - typeof(int), - typeof(string) - }, new[] - { - typeof(object), - typeof(string) - }, tc, ArgumentsMatchKind.Close); - - // Passing (int,Sub) on call to foo(Integer,Super) requires boxing conversion of argument zero - CheckMatch(new[] - { - typeof(int), - typeof(Sub) - }, new[] - { - typeof(object), - typeof(Super) - }, tc, ArgumentsMatchKind.Close); - - // Passing (int,Sub,boolean) on call to foo(Integer,Super,Boolean) requires boxing conversion of arguments zero and two - } - - [Fact] - public void TestReflectionHelperCompareArguments_NotAMatch() - { - var typeConverter = new StandardTypeConverter(); - - // Passing (Super,String) on call to foo(Sub,String) is not a match - CheckMatch(new[] - { - typeof(Super), - typeof(string) - }, new[] - { - typeof(Sub), - typeof(string) - }, typeConverter, null); - } - - [Fact] - public void TestReflectionHelperCompareArguments_Varargs_ExactMatching() - { - var tc = new StandardTypeConverter(); - - // Passing (String[]) on call to (String[]) is exact match - CheckMatch2(new[] - { - typeof(string[]) - }, new[] - { - typeof(string[]) - }, tc, ArgumentsMatchKind.Exact); - - // Passing (Integer, String[]) on call to (Integer, String[]) is exact match - CheckMatch2(new[] - { - typeof(int), - typeof(string[]) - }, new[] - { - typeof(int), - typeof(string[]) - }, tc, ArgumentsMatchKind.Exact); - - // Passing (String, Integer, String[]) on call to (String, String, String[]) is exact match - CheckMatch2(new[] - { - typeof(string), - typeof(int), - typeof(string[]) - }, new[] - { - typeof(string), - typeof(int), - typeof(string[]) - }, tc, ArgumentsMatchKind.Exact); - - // Passing (Sub, String[]) on call to (Super, String[]) is exact match - CheckMatch2(new[] - { - typeof(Sub), - typeof(string[]) - }, new[] - { - typeof(Super), - typeof(string[]) - }, tc, ArgumentsMatchKind.Close); - - // Passing (Integer, String[]) on call to (String, String[]) is exact match - CheckMatch2(new[] - { - typeof(int), - typeof(string[]) - }, new[] - { - typeof(string), - typeof(string[]) - }, tc, ArgumentsMatchKind.RequiresConversion); - - // Passing (Integer, Sub, String[]) on call to (String, Super, String[]) is exact match - CheckMatch2(new[] - { - typeof(int), - typeof(Sub), - typeof(string[]) - }, new[] - { - typeof(string), - typeof(Super), - typeof(string[]) - }, tc, ArgumentsMatchKind.RequiresConversion); - - // Passing (String) on call to (String[]) is exact match - CheckMatch2(new[] - { - typeof(string) - }, new[] - { - typeof(string[]) - }, tc, ArgumentsMatchKind.Exact); - - // Passing (Integer,String) on call to (Integer,String[]) is exact match - CheckMatch2(new[] - { - typeof(int), - typeof(string) - }, new[] - { - typeof(int), - typeof(string[]) - }, tc, ArgumentsMatchKind.Exact); - - // Passing (String) on call to (Integer[]) is conversion match (String to Integer) - CheckMatch2(new[] - { - typeof(string) - }, new[] - { - typeof(int[]) - }, tc, ArgumentsMatchKind.RequiresConversion); - - // Passing (Sub) on call to (Super[]) is close match - CheckMatch2(new[] - { - typeof(Sub) - }, new[] - { - typeof(Super[]) - }, tc, ArgumentsMatchKind.Close); - - // Passing (Super) on call to (Sub[]) is not a match - CheckMatch2(new[] - { - typeof(Super) - }, new[] - { - typeof(Sub[]) - }, tc, null); - - CheckMatch2(new[] - { - typeof(Unconvertable), - typeof(string) - }, new[] - { - typeof(Sub), - typeof(Super[]) - }, tc, null); - - CheckMatch2(new[] - { - typeof(int), - typeof(int), - typeof(string) - }, new[] - { - typeof(string), - typeof(string), - typeof(Super[]) - }, tc, null); - - CheckMatch2(new[] - { - typeof(Unconvertable), - typeof(string) - }, new[] - { - typeof(Sub), - typeof(Super[]) - }, tc, null); - - CheckMatch2(new[] - { - typeof(int), - typeof(int), - typeof(string) - }, new[] - { - typeof(string), - typeof(string), - typeof(Super[]) - }, tc, null); - - CheckMatch2(new[] - { - typeof(int), - typeof(int), - typeof(Sub) - }, new[] - { - typeof(string), - typeof(string), - typeof(Super[]) - }, tc, ArgumentsMatchKind.RequiresConversion); - - CheckMatch2(new[] - { - typeof(int), - typeof(int), - typeof(int) - }, new[] - { - typeof(int), - typeof(string[]) - }, tc, ArgumentsMatchKind.RequiresConversion); - - // what happens on (Integer,String) passed to (Integer[]) ? - } - - [Fact] - public void TestConvertArguments() - { - var tc = new StandardTypeConverter(); - - MethodInfo oneArg = typeof(ITestInterface).GetMethod(nameof(ITestInterface.OneArg), new[] - { - typeof(string) - }); - - MethodInfo twoArg = typeof(ITestInterface).GetMethod(nameof(ITestInterface.TwoArg), new[] - { - typeof(string), - typeof(string[]) - }); - - // basic conversion int>String - object[] args = - { - 3 - }; - - ReflectionHelper.ConvertArguments(tc, args, oneArg, null); - CheckArguments(args, "3"); - - // varargs but nothing to convert - args = new object[] - { - 3 - }; - - ReflectionHelper.ConvertArguments(tc, args, twoArg, 1); - CheckArguments(args, "3"); - - // varargs with nothing needing conversion - args = new object[] - { - 3, - "abc", - "abc" - }; - - ReflectionHelper.ConvertArguments(tc, args, twoArg, 1); - CheckArguments(args, "3", "abc", "abc"); - - // varargs with conversion required - args = new object[] - { - 3, - false, - 3.0d - }; - - ReflectionHelper.ConvertArguments(tc, args, twoArg, 1); - CheckArguments(args, "3", "False", "3"); - } - - [Fact] - public void TestConvertArguments2() - { - var tc = new StandardTypeConverter(); - - MethodInfo oneArg = typeof(ITestInterface).GetMethod(nameof(ITestInterface.OneArg), new[] - { - typeof(string) - }); - - MethodInfo twoArg = typeof(ITestInterface).GetMethod(nameof(ITestInterface.TwoArg), new[] - { - typeof(string), - typeof(string[]) - }); - - // Simple conversion: int to string - object[] args = - { - 3 - }; - - ReflectionHelper.ConvertAllArguments(tc, args, oneArg); - CheckArguments(args, "3"); - - // varargs conversion - args = new object[] - { - 3, - false, - 3.0f - }; - - ReflectionHelper.ConvertAllArguments(tc, args, twoArg); - CheckArguments(args, "3", "False", "3"); - - // varargs conversion but no varargs - args = new object[] - { - 3 - }; - - ReflectionHelper.ConvertAllArguments(tc, args, twoArg); - CheckArguments(args, "3"); - - // null value - args = new object[] - { - 3, - null, - 3.0f - }; - - ReflectionHelper.ConvertAllArguments(tc, args, twoArg); - CheckArguments(args, "3", null, "3"); - } - - [Fact] - public void TestSetupArguments() - { - object[] newArray = ReflectionHelper.SetupArgumentsForVarargsInvocation(new[] - { - typeof(string[]) - }, "a", "b", "c"); - - Assert.Single(newArray); - object firstParam = newArray[0]; - Assert.Equal(typeof(string), firstParam.GetType().GetElementType()); - object[] firstParamArray = (object[])firstParam; - Assert.Equal(3, firstParamArray.Length); - Assert.Equal("a", firstParamArray[0]); - Assert.Equal("b", firstParamArray[1]); - Assert.Equal("c", firstParamArray[2]); - } - - [Fact] - public void TestReflectivePropertyAccessor() - { - var rpa = new ReflectivePropertyAccessor(); - - var t = new Tester - { - Property = "hello" - }; - - var ctx = new StandardEvaluationContext(t); - Assert.True(rpa.CanRead(ctx, t, "Property")); - Assert.Equal("hello", rpa.Read(ctx, t, "Property").Value); - - // cached accessor used - Assert.Equal("hello", rpa.Read(ctx, t, "Property").Value); - - Assert.True(rpa.CanRead(ctx, t, "Field")); - Assert.Equal(3, rpa.Read(ctx, t, "Field").Value); - - // cached accessor used - Assert.Equal(3, rpa.Read(ctx, t, "Field").Value); - - Assert.True(rpa.CanWrite(ctx, t, "Property")); - rpa.Write(ctx, t, "Property", "goodbye"); - rpa.Write(ctx, t, "Property", "goodbye"); // cached accessor used - - Assert.True(rpa.CanWrite(ctx, t, "Field")); - rpa.Write(ctx, t, "Field", 12); - rpa.Write(ctx, t, "Field", 12); - - // Attempted Write as first activity on this field and property to drive testing - // of populating type descriptor cache - rpa.Write(ctx, t, "Field2", 3); - rpa.Write(ctx, t, "Property2", "doodoo"); - Assert.Equal(3, rpa.Read(ctx, t, "Field2").Value); - - // Attempted Read as first activity on this field and property (no CanRead before them) - Assert.Equal(0, rpa.Read(ctx, t, "Field3").Value); - Assert.Equal("doodoo", rpa.Read(ctx, t, "Property3").Value); - - // Access through is method - Assert.Equal(0, rpa.Read(ctx, t, "Field3").Value); - Assert.False((bool)rpa.Read(ctx, t, "Property4").Value); - Assert.True(rpa.CanRead(ctx, t, "Property4")); - - // repro SPR-9123, ReflectivePropertyAccessor JavaBean property names compliance tests - Assert.Equal("iD", rpa.Read(ctx, t, "iD").Value); - Assert.True(rpa.CanRead(ctx, t, "iD")); - Assert.Equal("id", rpa.Read(ctx, t, "Id").Value); - Assert.True(rpa.CanRead(ctx, t, "Id")); - Assert.Equal("ID", rpa.Read(ctx, t, "ID").Value); - Assert.True(rpa.CanRead(ctx, t, "ID")); - - // note: "Id" is not a valid JavaBean name, nevertheless it is treated as "id" - Assert.Equal("id", rpa.Read(ctx, t, "Id").Value); - Assert.True(rpa.CanRead(ctx, t, "Id")); - - // repro SPR-10994 - Assert.Equal("xyZ", rpa.Read(ctx, t, "XyZ").Value); - Assert.True(rpa.CanRead(ctx, t, "XyZ")); - Assert.Equal("xY", rpa.Read(ctx, t, "XY").Value); - Assert.True(rpa.CanRead(ctx, t, "XY")); - - // SPR-10122, ReflectivePropertyAccessor JavaBean property names compliance tests - setters - rpa.Write(ctx, t, "pEBS", "Test String"); - Assert.Equal("Test String", rpa.Read(ctx, t, "pEBS").Value); - } - - [Fact] - public void TestOptimalReflectivePropertyAccessor() - { - var reflective = new ReflectivePropertyAccessor(); - - var tester = new Tester - { - Property = "hello" - }; - - var ctx = new StandardEvaluationContext(tester); - Assert.True(reflective.CanRead(ctx, tester, "Property")); - Assert.Equal("hello", reflective.Read(ctx, tester, "Property").Value); - - // cached accessor used - Assert.Equal("hello", reflective.Read(ctx, tester, "Property").Value); - - IPropertyAccessor property = reflective.CreateOptimalAccessor(ctx, tester, "Property"); - Assert.True(property.CanRead(ctx, tester, "Property")); - Assert.False(property.CanRead(ctx, tester, "Property2")); - Assert.Throws(() => property.CanWrite(ctx, tester, "Property")); - Assert.Throws(() => property.CanWrite(ctx, tester, "Property2")); - Assert.Equal("hello", property.Read(ctx, tester, "Property").Value); - - // cached accessor used - Assert.Equal("hello", property.Read(ctx, tester, "Property").Value); - Assert.Throws(() => property.GetSpecificTargetClasses()); - Assert.Throws(() => property.Write(ctx, tester, "Property", null)); - - IPropertyAccessor field = reflective.CreateOptimalAccessor(ctx, tester, "Field"); - Assert.True(field.CanRead(ctx, tester, "Field")); - Assert.False(field.CanRead(ctx, tester, "Field2")); - Assert.Throws(() => field.CanWrite(ctx, tester, "Field")); - Assert.Throws(() => field.CanWrite(ctx, tester, "Field2")); - Assert.Equal(3, field.Read(ctx, tester, "Field").Value); - - // cached accessor used - Assert.Equal(3, field.Read(ctx, tester, "Field").Value); - Assert.Throws(() => field.GetSpecificTargetClasses()); - Assert.Throws(() => field.Write(ctx, tester, "field", null)); - } - - private void CheckMatch(Type[] inputTypes, Type[] expectedTypes, StandardTypeConverter typeConverter, ArgumentsMatchKind? expectedMatchKind) - { - ArgumentsMatchInfo matchInfo = ReflectionHelper.CompareArguments(GetTypeDescriptors(expectedTypes), GetTypeDescriptors(inputTypes), typeConverter); - - if (expectedMatchKind == null) - { - Assert.Null(matchInfo); - return; - } - - Assert.NotNull(matchInfo); - - if (expectedMatchKind.Value == ArgumentsMatchKind.Exact) - { - Assert.True(matchInfo.IsExactMatch); - } - else if (expectedMatchKind.Value == ArgumentsMatchKind.Close) - { - Assert.True(matchInfo.IsCloseMatch); - } - else if (expectedMatchKind.Value == ArgumentsMatchKind.RequiresConversion) - { - Assert.True(matchInfo.IsMatchRequiringConversion); - } - } - - private void CheckMatch2(Type[] inputTypes, Type[] expectedTypes, StandardTypeConverter typeConverter, ArgumentsMatchKind? expectedMatchKind) - { - ArgumentsMatchInfo matchInfo = - ReflectionHelper.CompareArgumentsVarargs(GetTypeDescriptors(expectedTypes), GetTypeDescriptors(inputTypes), typeConverter); - - if (expectedMatchKind == null) - { - Assert.Null(matchInfo); - return; - } - - Assert.NotNull(matchInfo); - - if (expectedMatchKind.Value == ArgumentsMatchKind.Exact) - { - Assert.True(matchInfo.IsExactMatch); - } - else if (expectedMatchKind.Value == ArgumentsMatchKind.Close) - { - Assert.True(matchInfo.IsCloseMatch); - } - else if (expectedMatchKind.Value == ArgumentsMatchKind.RequiresConversion) - { - Assert.True(matchInfo.IsMatchRequiringConversion); - } - } - - private void CheckArguments(object[] args, params object[] expected) - { - Assert.Equal(expected.Length, args.Length); - - for (int i = 0; i < expected.Length; i++) - { - CheckArgument(expected[i], args[i]); - } - } - - private void CheckArgument(object expected, object actual) - { - Assert.Equal(expected, actual); - } - - private List GetTypeDescriptors(params Type[] types) - { - var typeDescriptors = new List(types.Length); - - foreach (Type type in types) - { - typeDescriptors.Add(type); - } - - return typeDescriptors; - } - - public interface ITestInterface - { - void OneArg(string arg1); - - void TwoArg(string arg1, params string[] arg2); - } - - public class Super - { - } - - public sealed class Sub : Super - { - } - - public sealed class Unconvertable - { - } - - public sealed class Tester - { - public string Property { get; set; } - - public string Property2 { private get; set; } - - public string Property3 { get; } = "doodoo"; - - public bool Property4 { get; } - -#pragma warning disable SA1401 // Fields should be private - public int Field = 3; - public int Field2; - public int Field3; -#pragma warning restore SA1401 // Fields should be private - - // ReSharper disable InconsistentNaming -#pragma warning disable SA1300 // Element should begin with upper-case letter -#pragma warning disable S100 // Methods and properties should be named in PascalCase -#pragma warning disable IDE1006 // Naming Styles - - public string iD { get; } = "iD"; - - public string Id { get; } = "id"; - - public string ID { get; } = "ID"; - - public string XY { get; } = "xY"; - - public string XyZ { get; } = "xyZ"; - - public string pEBS { get; set; } = "pEBS"; - -#pragma warning restore IDE1006 // Naming Styles -#pragma warning restore S100 // Methods and properties should be named in PascalCase -#pragma warning restore SA1300 // Element should begin with upper-case letter - // ReSharper restore InconsistentNaming - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/Support/StandardComponentsTests.cs b/src/Common/test/Common.Expression.Test/Spring/Support/StandardComponentsTests.cs deleted file mode 100644 index 76638bbdae..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/Support/StandardComponentsTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring.Support; - -public sealed class StandardComponentsTests -{ - [Fact] - public void TestStandardEvaluationContext() - { - var context = new StandardEvaluationContext(); - Assert.NotNull(context.TypeComparator); - - var tc = new StandardTypeComparator(); - context.TypeComparator = tc; - Assert.Equal(tc, context.TypeComparator); - - var tl = new StandardTypeLocator(); - context.TypeLocator = tl; - Assert.Equal(tl, context.TypeLocator); - } - - [Fact] - public void TestStandardOperatorOverloader() - { - var oo = new StandardOperatorOverloader(); - Assert.False(oo.OverridesOperation(Operation.Add, null, null)); - Assert.Throws(() => oo.Operate(Operation.Add, 2, 3)); - } - - [Fact] - public void TestStandardTypeLocator() - { - var tl = new StandardTypeLocator(); - List prefixes = tl.ImportPrefixes; - Assert.Single(prefixes); - tl.RegisterImport("System.Collections"); - prefixes = tl.ImportPrefixes; - Assert.Equal(2, prefixes.Count); - tl.RemoveImport("System.Collections"); - prefixes = tl.ImportPrefixes; - Assert.Single(prefixes); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TemplateExpressionParsingTests.cs b/src/Common/test/Common.Expression.Test/Spring/TemplateExpressionParsingTests.cs deleted file mode 100644 index b9f590297d..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TemplateExpressionParsingTests.cs +++ /dev/null @@ -1,208 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Common; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class TemplateExpressionParsingTests : AbstractExpressionTests -{ - internal static readonly IParserContext DefaultTemplateParserContextSingleton = new DefaultTemplateParserContext(); - internal static readonly IParserContext HashDelimitedParserContextSingleton = new HashDelimitedParserContext(); - - [Fact] - public void TestParsingSimpleTemplateExpression01() - { - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("hello ${'world'}", DefaultTemplateParserContextSingleton); - object o = expr.GetValue(); - Assert.Equal("hello world", o.ToString()); - } - - [Fact] - public void TestParsingSimpleTemplateExpression02() - { - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("hello ${'to'} you", DefaultTemplateParserContextSingleton); - object o = expr.GetValue(); - Assert.Equal("hello to you", o.ToString()); - } - - [Fact] - public void TestParsingSimpleTemplateExpression03() - { - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("The quick ${'brown'} fox jumped over the ${'lazy'} dog", DefaultTemplateParserContextSingleton); - object o = expr.GetValue(); - Assert.Equal("The quick brown fox jumped over the lazy dog", o.ToString()); - } - - [Fact] - public void TestParsingSimpleTemplateExpression04() - { - var parser = new SpelExpressionParser(); - IExpression expr = parser.ParseExpression("${'hello'} world", DefaultTemplateParserContextSingleton); - object o = expr.GetValue(); - Assert.Equal("hello world", o.ToString()); - - expr = parser.ParseExpression(string.Empty, DefaultTemplateParserContextSingleton); - o = expr.GetValue(); - Assert.Equal(string.Empty, o.ToString()); - - expr = parser.ParseExpression("abc", DefaultTemplateParserContextSingleton); - o = expr.GetValue(); - Assert.Equal("abc", o.ToString()); - - expr = parser.ParseExpression("abc", DefaultTemplateParserContextSingleton); - o = expr.GetValue((object)null); - Assert.Equal("abc", o.ToString()); - } - - [Fact] - public void TestCompositeStringExpression() - { - var parser = new SpelExpressionParser(); - IExpression ex = parser.ParseExpression("hello ${'world'}", DefaultTemplateParserContextSingleton); - Assert.Equal("hello world", ex.GetValue()); - Assert.Equal("hello world", ex.GetValue(typeof(string))); - Assert.Equal("hello world", ex.GetValue((object)null, typeof(string))); - Assert.Equal("hello world", ex.GetValue(new Rooty())); - Assert.Equal("hello world", ex.GetValue(new Rooty(), typeof(string))); - - var ctx = new StandardEvaluationContext(); - Assert.Equal("hello world", ex.GetValue(ctx)); - Assert.Equal("hello world", ex.GetValue(ctx, typeof(string))); - Assert.Equal("hello world", ex.GetValue(ctx, null, typeof(string))); - Assert.Equal("hello world", ex.GetValue(ctx, new Rooty())); - Assert.Equal("hello world", ex.GetValue(ctx, new Rooty(), typeof(string))); - Assert.Equal("hello world", ex.GetValue(ctx, new Rooty(), typeof(string))); - Assert.Equal("hello ${'world'}", ex.ExpressionString); - Assert.False(ex.IsWritable(new StandardEvaluationContext())); - Assert.False(ex.IsWritable(new Rooty())); - Assert.False(ex.IsWritable(new StandardEvaluationContext(), new Rooty())); - - Assert.Equal(typeof(string), ex.GetValueType()); - Assert.Equal(typeof(string), ex.GetValueType(ctx)); - Assert.Equal(typeof(string), ex.GetValueType(new Rooty())); - Assert.Equal(typeof(string), ex.GetValueType(ctx, new Rooty())); - Assert.Throws(() => ex.SetValue(ctx, null)); - Assert.Throws(() => ex.SetValue((object)null, null)); - Assert.Throws(() => ex.SetValue(ctx, null, null)); - } - - [Fact] - public void TestNestedExpressions() - { - var parser = new SpelExpressionParser(); - - // treat the nested ${..} as a part of the expression - IExpression ex = parser.ParseExpression("hello ${ListOfNumbersUpToTen.$[#this<5]} world", DefaultTemplateParserContextSingleton); - object s = ex.GetValue(TestScenarioCreator.GetTestEvaluationContext(), typeof(string)); - Assert.Equal("hello 4 world", s); - - // not a useful expression but Tests nested expression syntax that clashes with template prefix/suffix - ex = parser.ParseExpression("hello ${ListOfNumbersUpToTen.$[#root.ListOfNumbersUpToTen.$[#this%2==1]==3]} world", - DefaultTemplateParserContextSingleton); - - Assert.IsType(ex); - var cse = (CompositeStringExpression)ex; - List expressions = cse.Expressions; - Assert.Equal(3, expressions.Count); - Assert.Equal("ListOfNumbersUpToTen.$[#root.ListOfNumbersUpToTen.$[#this%2==1]==3]", expressions[1].ExpressionString); - s = ex.GetValue(TestScenarioCreator.GetTestEvaluationContext(), typeof(string)); - Assert.Equal("hello world", s); - - ex = parser.ParseExpression("hello ${ListOfNumbersUpToTen.$[#this<5]} ${ListOfNumbersUpToTen.$[#this>5]} world", DefaultTemplateParserContextSingleton); - s = ex.GetValue(TestScenarioCreator.GetTestEvaluationContext(), typeof(string)); - Assert.Equal("hello 4 10 world", s); - - var pex = Assert.Throws(() => - parser.ParseExpression("hello ${ListOfNumbersUpToTen.$[#this<5]} ${ListOfNumbersUpToTen.$[#this>5] world", DefaultTemplateParserContextSingleton)); - - Assert.Equal("No ending suffix '}' for expression starting at character 41: ${ListOfNumbersUpToTen.$[#this>5] world", pex.SimpleMessage); - - pex = Assert.Throws(() => parser.ParseExpression("hello ${ListOfNumbersUpToTen.$[#root.ListOfNumbersUpToTen.$[#this%2==1==3]} world", - DefaultTemplateParserContextSingleton)); - - Assert.Equal("Found closing '}' at position 74 but most recent opening is '[' at position 30", pex.SimpleMessage); - } - - [Fact] - public void TestClashingWithSuffixes() - { - // Just wanting to use the prefix or suffix within the template: - IExpression ex = Parser.ParseExpression("hello ${3+4} world", DefaultTemplateParserContextSingleton); - object s = ex.GetValue(TestScenarioCreator.GetTestEvaluationContext(), typeof(string)); - Assert.Equal("hello 7 world", s); - - ex = Parser.ParseExpression("hello ${3+4} wo${'${'}rld", DefaultTemplateParserContextSingleton); - s = ex.GetValue(TestScenarioCreator.GetTestEvaluationContext(), typeof(string)); - Assert.Equal("hello 7 wo${rld", s); - - ex = Parser.ParseExpression("hello ${3+4} wo}rld", DefaultTemplateParserContextSingleton); - s = ex.GetValue(TestScenarioCreator.GetTestEvaluationContext(), typeof(string)); - Assert.Equal("hello 7 wo}rld", s); - } - - [Fact] - public void TestParsingNormalExpressionThroughTemplateParser() - { - IExpression expr = Parser.ParseExpression("1+2+3"); - Assert.Equal(6, expr.GetValue()); - } - - [Fact] - public void TestErrorCases() - { - var pex = Assert.Throws(() => Parser.ParseExpression("hello ${'world'", DefaultTemplateParserContextSingleton)); - - Assert.Equal("No ending suffix '}' for expression starting at character 6: ${'world'", pex.SimpleMessage); - Assert.Equal("hello ${'world'", pex.ExpressionString); - - pex = Assert.Throws(() => Parser.ParseExpression("hello ${'wibble'${'world'}", DefaultTemplateParserContextSingleton)); - Assert.Equal("No ending suffix '}' for expression starting at character 6: ${'wibble'${'world'}", pex.SimpleMessage); - pex = Assert.Throws(() => Parser.ParseExpression("hello ${} world", DefaultTemplateParserContextSingleton)); - Assert.Equal("No expression defined within delimiter '${}' at character 6", pex.SimpleMessage); - } - - [Fact] - public void TestTemplateParserContext() - { - var tpc = new TemplateParserContext("abc", "def"); - Assert.Equal("abc", tpc.ExpressionPrefix); - Assert.Equal("def", tpc.ExpressionSuffix); - Assert.True(tpc.IsTemplate); - - tpc = new TemplateParserContext(); - Assert.Equal("#{", tpc.ExpressionPrefix); - Assert.Equal("}", tpc.ExpressionSuffix); - Assert.True(tpc.IsTemplate); - } - - public sealed class DefaultTemplateParserContext : IParserContext - { - public bool IsTemplate => true; - - public string ExpressionPrefix => "${"; - - public string ExpressionSuffix => "}"; - } - - public sealed class HashDelimitedParserContext : IParserContext - { - public bool IsTemplate => true; - - public string ExpressionPrefix => "#{"; - - public string ExpressionSuffix => "}"; - } - - public sealed class Rooty - { - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestData/PersonInOtherPackage.cs b/src/Common/test/Common.Expression.Test/Spring/TestData/PersonInOtherPackage.cs deleted file mode 100644 index 7291d95eb1..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestData/PersonInOtherPackage.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestData; - -public sealed class PersonInOtherPackage -{ - public int Age { get; set; } - - public PersonInOtherPackage(int age) - { - Age = age; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestGenericConversionService.cs b/src/Common/test/Common.Expression.Test/Spring/TestGenericConversionService.cs deleted file mode 100644 index 46f4828351..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestGenericConversionService.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Converter; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class TestGenericConversionService : IConversionService -{ - public bool CanBypassConvert(Type sourceType, Type targetType) - { - return false; - } - - public bool CanConvert(Type sourceType, Type targetType) - { - return true; - } - - public T Convert(object source) - { - return (T)Convert(source, source?.GetType(), typeof(T)); - } - - public object Convert(object source, Type sourceType, Type targetType) - { - if (source == null) - { - return targetType == typeof(bool) ? false : null; - } - - return source; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/ArrayContainer.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/ArrayContainer.cs deleted file mode 100644 index 7c9eac9f4c..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/ArrayContainer.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class ArrayContainer -{ - public int[] Integers { get; } = new int[3]; - public long[] Longs { get; } = new long[3]; - public double[] Doubles { get; } = new double[3]; - public byte[] Bytes { get; } = new byte[3]; - public char[] Chars { get; } = new char[3]; - public short[] Shorts { get; } = new short[3]; - public bool[] Booleans { get; } = new bool[3]; - public float[] Floats { get; } = new float[3]; - - public ArrayContainer() - { - // setup some values - Integers[0] = 42; - Longs[0] = 42L; - Doubles[0] = 42.0d; - Bytes[0] = 42; - Chars[0] = (char)42; - Shorts[0] = 42; - Booleans[0] = true; - Floats[0] = 42.0f; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/Color.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/Color.cs deleted file mode 100644 index 9544003d41..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/Color.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class Color -{ - public static Color Orange { get; } = new(1); - public static Color Yellow { get; } = new(2); - public static Color Green { get; } = new(3); - public static Color Red { get; } = new(4); - public static Color Blue { get; } = new(5); - - public int Rgb { get; } - - private Color(int rgb) - { - Rgb = rgb; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/Company.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/Company.cs deleted file mode 100644 index ae7eb85b2a..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/Company.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class Company -{ - public string Address { get; } - - public Company(string str) - { - Address = str; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/Fruit.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/Fruit.cs deleted file mode 100644 index b611c3b4f2..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/Fruit.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class Fruit -{ -#pragma warning disable SA1401 // Fields should be private - public string Name; // accessible as property field -#pragma warning restore SA1401 // Fields should be private - public string ColorName { get; } // accessible as property through getter/setter - - public Color Color { get; } - - public int StringsCount { get; } = -1; - - public Fruit(string name, Color color, string colorName) - { - Name = name; - Color = color; - ColorName = colorName; - } - - public Fruit(params string[] strings) - { - StringsCount = strings.Length; - } - - public Fruit(int i, params string[] strings) - { - StringsCount = i + strings.Length; - } - - public override string ToString() - { - return $"A{(ColorName != null && ColorName.StartsWith('o') ? "n " : " ")}{ColorName} {Name}"; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/Inventor.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/Inventor.cs deleted file mode 100644 index 5dfa73edf9..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/Inventor.cs +++ /dev/null @@ -1,210 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -#pragma warning disable S4004 // Collection properties should be readonly -// ReSharper disable InconsistentNaming - -using System.Globalization; - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class Inventor -{ - private PlaceOfBirth _placeOfBirth; - public List ListOfInteger { get; set; } = new(); - public List BoolList { get; } = new(); - public Dictionary MapOfStringToBoolean { get; } = new(); - public Dictionary MapOfNumbersUpToTen { get; } = new(); - public List ListOfNumbersUpToTen { get; } = new(); - public List ListOneFive { get; } = new(); - - public string[] StringArrayOfThreeItems { get; } = - { - "1", - "2", - "3" - }; - - public int Counter { get; set; } - public string RandomField { get; } - public Dictionary TestDictionary { get; } - public string PublicName { get; set; } - public ArrayContainer ArrayContainer { get; set; } - public bool PublicBoolean { get; set; } - - public string[] Inventions { get; set; } - - public PlaceOfBirth PlaceOfBirth - { - get => _placeOfBirth; - set - { - _placeOfBirth = value; - - PlacesLived = new[] - { - value - }; - - PlacesLivedList.Add(value); - } - } - - public string Name { get; } - - public bool WonNobelPrize { get; set; } - - public PlaceOfBirth[] PlacesLived { get; set; } - - public List PlacesLivedList { get; set; } = new(); - - public bool SomeProperty { get; set; } - - public DateTime BirthDate { get; } - - public string Foo { get; set; } - - public string Nationality { get; } - - public Inventor(params string[] strings) - { - } - - public Inventor(string name, DateTime birthdate, string nationality) - { - Name = name; - _name = name; - _name_ = name; - BirthDate = birthdate; - Nationality = nationality; - ArrayContainer = new ArrayContainer(); - - TestDictionary = new Dictionary - { - { "monday", "montag" }, - { "tuesday", "dienstag" }, - { "wednesday", "mittwoch" }, - { "thursday", "donnerstag" }, - { "friday", "freitag" }, - { "saturday", "samstag" }, - { "sunday", "sonntag" } - }; - - ListOneFive.Add(1); - ListOneFive.Add(5); - BoolList.Add(false); - BoolList.Add(false); - ListOfNumbersUpToTen.Add(1); - ListOfNumbersUpToTen.Add(2); - ListOfNumbersUpToTen.Add(3); - ListOfNumbersUpToTen.Add(4); - ListOfNumbersUpToTen.Add(5); - ListOfNumbersUpToTen.Add(6); - ListOfNumbersUpToTen.Add(7); - ListOfNumbersUpToTen.Add(8); - ListOfNumbersUpToTen.Add(9); - ListOfNumbersUpToTen.Add(10); - MapOfNumbersUpToTen.Add(1, "one"); - MapOfNumbersUpToTen.Add(2, "two"); - MapOfNumbersUpToTen.Add(3, "three"); - MapOfNumbersUpToTen.Add(4, "four"); - MapOfNumbersUpToTen.Add(5, "five"); - MapOfNumbersUpToTen.Add(6, "six"); - MapOfNumbersUpToTen.Add(7, "seven"); - MapOfNumbersUpToTen.Add(8, "eight"); - MapOfNumbersUpToTen.Add(9, "nine"); - MapOfNumbersUpToTen.Add(10, "ten"); - } - - public int ThrowException(int valueIn) - { - Counter++; - - if (valueIn == 1) - { - throw new ArgumentException("ArgumentException for 1", nameof(valueIn)); - } - - if (valueIn == 2) - { - throw new SystemException("RuntimeException for 2"); - } - - if (valueIn == 4) - { - throw new TestException(); - } - - return valueIn; - } - - public string ThrowException(PlaceOfBirth pob) - { - return pob.City; - } - - public string Echo(object o) - { - return o.ToString(); - } - - public string SayHelloTo(string person) - { - return $"hello {person}"; - } - - public string PrintDouble(double d) - { - return d.ToString("F2", CultureInfo.InvariantCulture); - } - - public string PrintDoubles(double[] d) - { - return $"{{{string.Join(", ", d.Select(x => x.ToString(CultureInfo.InvariantCulture)))}}}"; - } - - public List GetDoublesAsStringList() - { - var result = new List - { - "14.35", - "15.45" - }; - - return result; - } - - public string JoinThreeStrings(string a, string b, string c) - { - return a + b + c; - } - - public int AVarargsMethod(params string[] strings) - { - if (strings == null) - { - return 0; - } - - return strings.Length; - } - - public int AVarargsMethod2(int i, params string[] strings) - { - if (strings == null) - { - return i; - } - - return strings.Length + i; - } - - public sealed class TestException : Exception - { - } -#pragma warning disable SA1401 // Fields should be private - public string _name; - public string _name_; -#pragma warning restore SA1401 // Fields should be private -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/Person.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/Person.cs deleted file mode 100644 index 495d3b3c0f..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/Person.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class Person -{ - public string Name { get; set; } - - public Company Company { get; } - - public Person(string name) - { - Name = name; - } - - public Person(string name, Company company) - { - Name = name; - Company = company; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/PlaceOfBirth.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/PlaceOfBirth.cs deleted file mode 100644 index f5a515ef2b..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/PlaceOfBirth.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class PlaceOfBirth -{ - public string Country { get; set; } - - public string City { get; set; } - - public PlaceOfBirth(string str) - { - City = str; - } - - public override string ToString() - { - return City; - } - - public int DoubleIt(int i) - { - return i * 2; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not PlaceOfBirth other) - { - return false; - } - - return City == other.City; - } - - public override int GetHashCode() - { - return City.GetHashCode(); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/TestAddress.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/TestAddress.cs deleted file mode 100644 index 1b30e7e1d2..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/TestAddress.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -#pragma warning disable S4004 // Collection properties should be readonly - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class TestAddress -{ - public string Street { get; set; } - - public List CrossStreets { get; set; } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/TestPerson.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/TestPerson.cs deleted file mode 100644 index 766b4bfb87..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/TestPerson.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class TestPerson -{ - public string Name { get; set; } - - public TestAddress Address { get; set; } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/WidenDouble.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/WidenDouble.cs deleted file mode 100644 index 93b8f459fd..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/WidenDouble.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class WidenDouble -{ - public double D { get; } - - public WidenDouble(double d) - { - D = d; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/WidenLong.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/WidenLong.cs deleted file mode 100644 index fe24293638..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/WidenLong.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Common.Expression.Test.Spring.TestResources; - -public sealed class WidenLong -{ - public long L { get; } - - public WidenLong(long l) - { - L = l; - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestResources/le/div/mod/reserved/Reserver.cs b/src/Common/test/Common.Expression.Test/Spring/TestResources/le/div/mod/reserved/Reserver.cs deleted file mode 100644 index fd7f879f09..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestResources/le/div/mod/reserved/Reserver.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -#pragma warning disable SA1300 // Element should begin with upper-case letter -namespace Steeltoe.Common.Expression.Test.Spring.TestResources.le.div.mod.reserved; -#pragma warning restore SA1300 // Element should begin with upper-case letter - -public static class Reserver -{ - public const string Const = "Const"; -} diff --git a/src/Common/test/Common.Expression.Test/Spring/TestScenarioCreator.cs b/src/Common/test/Common.Expression.Test/Spring/TestScenarioCreator.cs deleted file mode 100644 index 2195b5a167..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/TestScenarioCreator.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Text; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Expression.Test.Spring.TestResources; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public static class TestScenarioCreator -{ - public static StandardEvaluationContext GetTestEvaluationContext() - { - var testContext = new StandardEvaluationContext(); - SetupRootContextObject(testContext); - PopulateVariables(testContext); - PopulateFunctions(testContext); - return testContext; - } - - public static string IsEven(int i) - { - if (i % 2 == 0) - { - return "y"; - } - - return "n"; - } - - public static int[] ReverseInt(int i, int j, int k) - { - return new[] - { - k, - j, - i - }; - } - - public static string ReverseString(string input) - { - var backwards = new StringBuilder(); - - for (int i = 0; i < input.Length; i++) - { - backwards.Append(input[input.Length - 1 - i]); - } - - return backwards.ToString(); - } - - public static string VarargsFunctionReverseStringsAndMerge(params string[] strings) - { - var sb = new StringBuilder(); - - if (strings != null) - { - for (int i = strings.Length - 1; i >= 0; i--) - { - sb.Append(strings[i]); - } - } - - return sb.ToString(); - } - - public static string VarargsFunctionReverseStringsAndMerge2(int j, params string[] strings) - { - var sb = new StringBuilder(); - sb.Append(j); - - if (strings != null) - { - for (int i = strings.Length - 1; i >= 0; i--) - { - sb.Append(strings[i]); - } - } - - return sb.ToString(); - } - - private static void PopulateFunctions(StandardEvaluationContext testContext) - { - try - { - testContext.RegisterFunction("IsEven", typeof(TestScenarioCreator).GetMethod(nameof(IsEven), new[] - { - typeof(int) - })); - - testContext.RegisterFunction("ReverseInt", typeof(TestScenarioCreator).GetMethod(nameof(ReverseInt), new[] - { - typeof(int), - typeof(int), - typeof(int) - })); - - testContext.RegisterFunction("ReverseString", typeof(TestScenarioCreator).GetMethod(nameof(ReverseString), new[] - { - typeof(string) - })); - - testContext.RegisterFunction("VarargsFunctionReverseStringsAndMerge", typeof(TestScenarioCreator).GetMethod( - nameof(VarargsFunctionReverseStringsAndMerge), new[] - { - typeof(string[]) - })); - - testContext.RegisterFunction("VarargsFunctionReverseStringsAndMerge2", typeof(TestScenarioCreator).GetMethod( - nameof(VarargsFunctionReverseStringsAndMerge2), new[] - { - typeof(int), - typeof(string[]) - })); - } - catch (Exception ex) - { - throw new InvalidOperationException("Populate failed", ex); - } - } - - private static void PopulateVariables(StandardEvaluationContext testContext) - { - testContext.SetVariable("answer", 42); - } - - private static void SetupRootContextObject(StandardEvaluationContext testContext) - { - var c = new GregorianCalendar(); - - var tesla = new Inventor("Nikola Tesla", c.ToDateTime(1856, 7, 9, 0, 0, 0, Calendar.CurrentEra), "Serbian") - { - PlaceOfBirth = new PlaceOfBirth("SmilJan"), - Inventions = new[] - { - "Telephone repeater", - "Rotating magnetic field principle", - "Polyphase alternating-current system", - "Induction motor", - "Alternating-current power transmission", - "Tesla coil transformer", - "Wireless communication", - "Radio", - "Fluorescent lights" - } - }; - - testContext.SetRootObject(tesla); - } -} diff --git a/src/Common/test/Common.Expression.Test/Spring/VariableAndFunctionTests.cs b/src/Common/test/Common.Expression.Test/Spring/VariableAndFunctionTests.cs deleted file mode 100644 index f4b06fa353..0000000000 --- a/src/Common/test/Common.Expression.Test/Spring/VariableAndFunctionTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal.Spring; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Xunit; - -namespace Steeltoe.Common.Expression.Test.Spring; - -public sealed class VariableAndFunctionTests : AbstractExpressionTests -{ - [Fact] - public void TestVariableAccess01() - { - Evaluate("#answer", "42", typeof(int), ShouldBeWritable); - Evaluate("#answer / 2", 21, typeof(int), ShouldNotBeWritable); - } - - [Fact] - public void TestVariableAccess_WellKnownVariables() - { - Evaluate("#this.Name", "Nikola Tesla", typeof(string)); - Evaluate("#root.Name", "Nikola Tesla", typeof(string)); - } - - [Fact] - public void TestFunctionAccess01() - { - Evaluate("#ReverseInt(1,2,3)", "System.Int32[3]{(0)=3,(1)=2,(2)=1,}", typeof(int[])); - Evaluate("#ReverseInt('1',2,3)", "System.Int32[3]{(0)=3,(1)=2,(2)=1,}", typeof(int[])); // requires type conversion of '1' to 1 - EvaluateAndCheckError("#ReverseInt(1)", SpelMessage.IncorrectNumberOfArgumentsToFunction, 0, 1, 3); - } - - [Fact] - public void TestFunctionAccess02() - { - Evaluate("#ReverseString('hello')", "olleh", typeof(string)); - Evaluate("#ReverseString(37)", "73", typeof(string)); // requires type conversion of 37 to '37' - } - - [Fact] - public void TestCallVarargsFunction() - { - Evaluate("#VarargsFunctionReverseStringsAndMerge('a','b','c')", "cba", typeof(string)); - Evaluate("#VarargsFunctionReverseStringsAndMerge('a')", "a", typeof(string)); - Evaluate("#VarargsFunctionReverseStringsAndMerge()", string.Empty, typeof(string)); - Evaluate("#VarargsFunctionReverseStringsAndMerge('b',25)", "25b", typeof(string)); - Evaluate("#VarargsFunctionReverseStringsAndMerge(25)", "25", typeof(string)); - Evaluate("#VarargsFunctionReverseStringsAndMerge2(1,'a','b','c')", "1cba", typeof(string)); - Evaluate("#VarargsFunctionReverseStringsAndMerge2(2,'a')", "2a", typeof(string)); - Evaluate("#VarargsFunctionReverseStringsAndMerge2(3)", "3", typeof(string)); - Evaluate("#VarargsFunctionReverseStringsAndMerge2(4,'b',25)", "425b", typeof(string)); - Evaluate("#VarargsFunctionReverseStringsAndMerge2(5,25)", "525", typeof(string)); - } - - [Fact] - public void TestCallingIllegalFunctions() - { - var parser = new SpelExpressionParser(); - var ctx = new StandardEvaluationContext(); - ctx.SetVariable("notStatic", GetType().GetMethod(nameof(NonStatic))); - var ex = Assert.Throws(() => parser.ParseRaw("#notStatic()").GetValue(ctx)); - Assert.Equal(SpelMessage.FunctionMustBeStatic, ex.MessageCode); - } - - // this method is used by the Test above -#pragma warning disable xUnit1013 // Public method should be marked as test - public void NonStatic() -#pragma warning restore xUnit1013 // Public method should be marked as test - { - } -} diff --git a/src/Common/test/Common.Expression.Test/Steeltoe.Common.Expression.Test.csproj b/src/Common/test/Common.Expression.Test/Steeltoe.Common.Expression.Test.csproj deleted file mode 100644 index f464c2774c..0000000000 --- a/src/Common/test/Common.Expression.Test/Steeltoe.Common.Expression.Test.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - net8.0;net6.0 - - - - - - - - diff --git a/src/Common/test/Common.Test/Converter/ConversionUtilsTest.cs b/src/Common/test/Common.Test/Converter/ConversionUtilsTest.cs deleted file mode 100644 index c54e7653d9..0000000000 --- a/src/Common/test/Common.Test/Converter/ConversionUtilsTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Collections.ObjectModel; -using Steeltoe.Common.Converter; -using Xunit; - -namespace Steeltoe.Common.Test.Converter; - -public sealed class ConversionUtilsTest -{ - [Fact] - public void TestCanCreateCompatListFrom() - { - Assert.False(ConversionUtils.CanCreateCompatListFor(null)); - Assert.False(ConversionUtils.CanCreateCompatListFor(typeof(IList<>))); - Assert.True(ConversionUtils.CanCreateCompatListFor(typeof(IList))); - Assert.True(ConversionUtils.CanCreateCompatListFor(typeof(List))); - Assert.False(ConversionUtils.CanCreateCompatListFor(typeof(List<>))); - Assert.False(ConversionUtils.CanCreateCompatListFor(typeof(LinkedList))); - Assert.True(ConversionUtils.CanCreateCompatListFor(typeof(Collection))); - Assert.True(ConversionUtils.CanCreateCompatListFor(typeof(ArrayList))); - } - - [Fact] - public void TestCreateList() - { - Assert.Null(ConversionUtils.CreateCompatListFor(null)); - Assert.Null(ConversionUtils.CreateCompatListFor(typeof(IList<>))); - Assert.IsType>(ConversionUtils.CreateCompatListFor(typeof(IList))); - Assert.IsType>(ConversionUtils.CreateCompatListFor(typeof(List))); - Assert.Null(ConversionUtils.CreateCompatListFor(typeof(List<>))); - Assert.Null(ConversionUtils.CreateCompatListFor(typeof(LinkedList))); - Assert.IsType>(ConversionUtils.CreateCompatListFor(typeof(Collection))); - Assert.IsType(ConversionUtils.CreateCompatListFor(typeof(ArrayList))); - Assert.IsType(ConversionUtils.CreateCompatListFor(typeof(IList))); - } - - [Fact] - public void TestMakeGenericListType() - { - Assert.Equal(typeof(List), ConversionUtils.MakeGenericListType(typeof(IList))); - } -} diff --git a/src/Common/test/Common.Test/Converter/DefaultConversionServiceTest.cs b/src/Common/test/Common.Test/Converter/DefaultConversionServiceTest.cs deleted file mode 100644 index 6496015738..0000000000 --- a/src/Common/test/Common.Test/Converter/DefaultConversionServiceTest.cs +++ /dev/null @@ -1,1218 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Text; -using Steeltoe.Common.Converter; -using Xunit; - -namespace Steeltoe.Common.Test.Converter; - -public sealed class DefaultConversionServiceTest -{ - // Static so only one is created for this battery of tests.. ensures internal cache is filled by all of the tests - private static readonly DefaultConversionService ConversionService = new(); - - [Fact] - public void TestStringToCharacter() - { - Assert.Equal('1', ConversionService.Convert("1")); - Assert.Equal('1', ConversionService.Convert("1")); - } - - [Fact] - public void TestStringToCharacterEmptyString() - { - Assert.Null(ConversionService.Convert(string.Empty)); - } - - [Fact] - public void TestStringToCharacterInvalidString() - { - Assert.Throws(() => ConversionService.Convert("invalid")); - Assert.Throws(() => ConversionService.Convert("invalid")); - } - - [Fact] - public void TestCharacterToString() - { - Assert.Equal("3", ConversionService.Convert('3')); - } - - [Fact] - public void TestStringToBooleanTrue() - { - Assert.True(ConversionService.Convert("true")); - Assert.True(ConversionService.Convert("on")); - Assert.True(ConversionService.Convert("yes")); - Assert.True(ConversionService.Convert("1")); - Assert.True(ConversionService.Convert("TRUE")); - Assert.True(ConversionService.Convert("ON")); - Assert.True(ConversionService.Convert("YES")); - - Assert.True(ConversionService.Convert("true")); - Assert.True(ConversionService.Convert("on")); - Assert.True(ConversionService.Convert("yes")); - Assert.True(ConversionService.Convert("1")); - Assert.True(ConversionService.Convert("TRUE")); - Assert.True(ConversionService.Convert("ON")); - Assert.True(ConversionService.Convert("YES")); - } - - [Fact] - public void TestStringToBooleanFalse() - { - Assert.False(ConversionService.Convert("false")); - Assert.False(ConversionService.Convert("off")); - Assert.False(ConversionService.Convert("no")); - Assert.False(ConversionService.Convert("0")); - Assert.False(ConversionService.Convert("FALSE")); - Assert.False(ConversionService.Convert("OFF")); - Assert.False(ConversionService.Convert("NO")); - - Assert.False(ConversionService.Convert("false")); - Assert.False(ConversionService.Convert("off")); - Assert.False(ConversionService.Convert("no")); - Assert.False(ConversionService.Convert("0")); - Assert.False(ConversionService.Convert("FALSE")); - Assert.False(ConversionService.Convert("OFF")); - Assert.False(ConversionService.Convert("NO")); - } - - [Fact] - public void TestStringToBooleanEmptyString() - { - Assert.Null(ConversionService.Convert(string.Empty)); - } - - [Fact] - public void TestStringToBooleanInvalidString() - { - Assert.Throws(() => ConversionService.Convert("invalid")); - } - - [Fact] - public void TestBooleanToString() - { - Assert.Equal("True", ConversionService.Convert(true)); - } - - [Fact] - public void TestStringToByte() - { - Assert.Equal((byte)1, ConversionService.Convert("1")); - Assert.Equal((sbyte)-1, ConversionService.Convert("-1")); - Assert.Equal((byte)1, ConversionService.Convert("1")); - Assert.Equal((sbyte)-1, ConversionService.Convert("-1")); - } - - [Fact] - public void TestByteToString() - { - Assert.Equal("65", ConversionService.Convert(Encoding.UTF8.GetBytes("A")[0])); - Assert.Equal("-1", ConversionService.Convert((sbyte)-1)); - } - - [Fact] - public void TestStringToShort() - { - Assert.Equal((short)1, ConversionService.Convert("1")); - Assert.Equal((ushort)1, ConversionService.Convert("1")); - Assert.Equal((short)1, ConversionService.Convert("1")); - Assert.Equal((ushort)1, ConversionService.Convert("1")); - } - - [Fact] - public void TestShortToString() - { - const short signedThree = 3; - const ushort unsignedThree = 3; - Assert.Equal("3", ConversionService.Convert(signedThree)); - Assert.Equal("3", ConversionService.Convert(unsignedThree)); - } - - [Fact] - public void TestStringToInteger() - { - Assert.Equal(1, ConversionService.Convert("1")); - Assert.Equal(1, ConversionService.Convert("1")); - Assert.Equal(1U, ConversionService.Convert("1")); - Assert.Equal(1U, ConversionService.Convert("1")); - } - - [Fact] - public void TestIntegerToString() - { - Assert.Equal("3", ConversionService.Convert(3)); - Assert.Equal("3", ConversionService.Convert(3U)); - } - - [Fact] - public void TestStringToLong() - { - Assert.Equal(1L, ConversionService.Convert("1")); - Assert.Equal(1UL, ConversionService.Convert("1")); - Assert.Equal(1L, ConversionService.Convert("1")); - Assert.Equal(1UL, ConversionService.Convert("1")); - } - - [Fact] - public void TestLongToString() - { - Assert.Equal("3", ConversionService.Convert(3L)); - Assert.Equal("3", ConversionService.Convert(3UL)); - } - - [Fact] - public void TestStringToFloat() - { - Assert.Equal(1.0f, ConversionService.Convert("1.0")); - Assert.Equal(1.0f, ConversionService.Convert("1.0")); - } - - [Fact] - public void TestFloatToString() - { - Assert.Equal("3.1", ConversionService.Convert(3.1f)); - } - - [Fact] - public void TestStringToDouble() - { - Assert.Equal(1.0d, ConversionService.Convert("1.0")); - Assert.Equal(1.0d, ConversionService.Convert("1.0")); - } - - [Fact] - public void TestDoubleToString() - { - Assert.Equal("3.1", ConversionService.Convert(3.1d)); - } - - [Fact] - public void TestStringToDecimal() - { - const decimal result = 1.0m; - Assert.Equal(result, ConversionService.Convert("1.0")); - Assert.Equal(result, ConversionService.Convert("1.0")); - } - - [Fact] - public void TestDecimalToString() - { - const decimal source = 300.00m; - Assert.Equal("300.00", ConversionService.Convert(source)); - } - - [Fact] - public void TestStringToNumberEmptyString() - { - Assert.Null(ConversionService.Convert(string.Empty)); - Assert.Null(ConversionService.Convert(string.Empty)); - } - - [Fact] - public void TestStringToEnum() - { - Assert.Equal(Foo.Bar, ConversionService.Convert("BAR")); - Assert.Equal(Foo.Bar, ConversionService.Convert("BAR")); - Assert.Equal(Foo.Bar, ConversionService.Convert("bar")); - Assert.Equal(Foo.Bar, ConversionService.Convert("bar")); - } - - [Fact] - public void TestStringToEnumEmptyString() - { - Assert.Null(ConversionService.Convert(string.Empty)); - } - - [Fact] - public void TestEnumToString() - { - Assert.Equal("Bar", ConversionService.Convert(Foo.Bar)); - } - - [Fact] - public void TestStringToString() - { - const string str = "test"; - Assert.Same(str, ConversionService.Convert(str)); - } - - [Fact] - public void TestGuidToStringAndStringToGuid() - { - var uuid = Guid.NewGuid(); - string convertToString = ConversionService.Convert(uuid); - var convertToUuid = ConversionService.Convert(convertToString); - Assert.Equal(uuid, convertToUuid); - } - - [Fact] - public void TestNumberToNumber() - { - Assert.Equal(1L, ConversionService.Convert(1L)); - } - - [Fact] - public void TestNumberToNumberNotSupportedNumber() - { - Assert.Throws(() => ConversionService.Convert('a')); - } - - [Fact] - public void TestNumberToCharacter() - { - Assert.Equal('A', ConversionService.Convert(65)); - } - - [Fact] - public void TestCharacterToNumber() - { - Assert.Equal(65, ConversionService.Convert('A')); - } - - [Fact] - public void ConvertArrayToCollectionInterface() - { - var result = ConversionService.Convert>(new[] - { - "1", - "2", - "3" - }); - - Assert.Equal("1", result[0]); - Assert.Equal("2", result[1]); - Assert.Equal("3", result[2]); - } - - [Fact] - public void ConvertArrayToEnumerableInterface() - { - string[] array = - { - "1", - "2", - "3" - }; - - var result = ConversionService.Convert(array); - Assert.IsType(result); - Assert.Same(result, array); - } - - [Fact] - public void ConvertArrayToEnumerableStringInterface() - { - string[] array = - { - "1", - "2", - "3" - }; - - var result = ConversionService.Convert>(array); - Assert.IsType(result); - Assert.Same(result, array); - } - - [Fact] - public void ConvertArrayToEnumerableGenericTypeConversion() - { - string[] array = - { - "1", - "2", - "3" - }; - - var result = ConversionService.Convert>(array); - Assert.Contains(1, result); - Assert.Contains(2, result); - Assert.Contains(3, result); - } - - [Fact] - public void ConvertArrayToCollectionGenericTypeConversion() - { - var result = ConversionService.Convert>(new[] - { - "1", - "2", - "3" - }); - - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertArrayToCollectionImpl() - { - var result = ConversionService.Convert>(new[] - { - "1", - "2", - "3" - }); - - Assert.Equal("1", result[0]); - Assert.Equal("2", result[1]); - Assert.Equal("3", result[2]); - } - - [Fact] - public void ConvertArrayToAbstractCollection() - { - // No public constructor found - Assert.Throws(() => ConversionService.Convert(new[] - { - "1", - "2", - "3" - })); - } - - [Fact] - public void ConvertArrayToString() - { - string result = ConversionService.Convert(new[] - { - "1", - "2", - "3" - }); - - Assert.Equal("1,2,3", result); - } - - [Fact] - public void ConvertArrayToStringWithElementConversion() - { - string result = ConversionService.Convert(new[] - { - 1, - 2, - 3 - }); - - Assert.Equal("1,2,3", result); - } - - [Fact] - public void ConvertEmptyArrayToString() - { - string result = ConversionService.Convert(Array.Empty()); - Assert.Equal(string.Empty, result); - } - - [Fact] - public void ConvertStringToArray() - { - string[] result = ConversionService.Convert("1,2,3"); - Assert.Equal(3, result.Length); - Assert.Equal("1", result[0]); - Assert.Equal("2", result[1]); - Assert.Equal("3", result[2]); - } - - [Fact] - public void ConvertStringToArrayWithElementConversion() - { - int[] result = ConversionService.Convert("1,2,3"); - Assert.Equal(3, result.Length); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertEmptyStringToArray() - { - string[] result = ConversionService.Convert(string.Empty); - Assert.Empty(result); - } - - [Fact] - public void ConvertArrayToObject() - { - long[] array = - { - 3L - }; - - long result = ConversionService.Convert(array); - Assert.Equal(3L, result); - } - - [Fact] - public void ConvertArrayToObjectWithElementConversion() - { - string[] array = - { - "3" - }; - - int result = ConversionService.Convert(array); - Assert.Equal(3, result); - } - - [Fact] - public void ConvertArrayToObjectAssignableTargetType() - { - long[] array = - { - 3L - }; - - long[] result = ConversionService.Convert(array); - Assert.Equal(array, result); - } - - [Fact] - public void ConvertObjectToArray() - { - object[] result = ConversionService.Convert(3L); - Assert.Single(result); - Assert.Equal(3L, result[0]); - } - - [Fact] - public void ConvertObjectToArrayWithElementConversion() - { - int[] result = ConversionService.Convert(3L); - Assert.Single(result); - Assert.Equal(3, result[0]); - } - - [Fact] - public void ConvertCollectionToArray() - { - IList list = new List(); - list.Add("1"); - list.Add("2"); - list.Add("3"); - string[] result = ConversionService.Convert(list); - Assert.Equal(3, result.Length); - Assert.Equal("1", result[0]); - Assert.Equal("2", result[1]); - Assert.Equal("3", result[2]); - } - - [Fact] - public void ConvertSetToArray() - { - ISet set = new HashSet(); - set.Add("1"); - set.Add("2"); - set.Add("3"); - string[] result = ConversionService.Convert(set); - Assert.Equal(3, result.Length); - Assert.Equal("1", result[0]); - Assert.Equal("2", result[1]); - Assert.Equal("3", result[2]); - } - - [Fact] - public void ConvertSetToArrayWithElementConversion() - { - ISet set = new HashSet(); - set.Add("1"); - set.Add("2"); - set.Add("3"); - int[] result = ConversionService.Convert(set); - Assert.Equal(3, result.Length); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertCollectionToArrayWithElementConversion() - { - IList list = new List(); - list.Add(1L); - list.Add(2L); - list.Add(3L); - int[] result = ConversionService.Convert(list); - Assert.Equal(3, result.Length); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertCollectionToString() - { - var list = new List - { - "foo", - "bar" - }; - - string result = ConversionService.Convert(list); - Assert.Equal("foo,bar", result); - } - - [Fact] - public void ConvertCollectionToStringWithElementConversion() - { - var list = new List - { - 3, - 5 - }; - - string result = ConversionService.Convert(list); - Assert.Equal("3,5", result); - } - - [Fact] - public void ConvertStringToCollection() - { - var result = ConversionService.Convert>("1,2,3"); - Assert.Equal(3, result.Count); - Assert.Equal("1", result[0]); - Assert.Equal("2", result[1]); - Assert.Equal("3", result[2]); - } - - [Fact] - public void ConvertStringToEnumerable() - { - var result = ConversionService.Convert>("1,2,3"); - Assert.Equal(3, result.Count()); - Assert.Contains("1", result); - Assert.Contains("2", result); - Assert.Contains("3", result); - } - - [Fact] - public void ConvertStringToEnumerableWithElementConversion() - { - var result = ConversionService.Convert>("1,2,3"); - Assert.Equal(3, result.Count()); - Assert.Contains(1, result); - Assert.Contains(2, result); - Assert.Contains(3, result); - } - - [Fact] - public void ConvertStringToCollectionWithElementConversion() - { - var result = ConversionService.Convert>("1,2,3"); - Assert.Equal(3, result.Count); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertEmptyStringToCollection() - { - var result = ConversionService.Convert>(string.Empty); - Assert.Empty(result); - } - - [Fact] - public void ConvertCollectionToObject() - { - var list = new List - { - 3L - }; - - long result = ConversionService.Convert(list); - Assert.Equal(3L, result); - } - - [Fact] - public void ConvertCollectionToObjectWithElementConversion() - { - var list = new List - { - "3" - }; - - int result = ConversionService.Convert(list); - Assert.Equal(3, result); - } - - [Fact] - public void ConvertCollectionToObjectAssignableTarget() - { - IList source = new List - { - "foo" - }; - - object result = ConversionService.Convert(source); - Assert.Same(source, result); - } - - [Fact] - public void ConvertListToDictionaryAssignableTarget() - { - IList source = new List - { - new Dictionary - { - ["test"] = 3 - } - }; - - var result = ConversionService.Convert>(source); - Assert.Equal(3, result["test"]); - } - - [Fact] - public void ConvertObjectToCollection() - { - var result = ConversionService.Convert>(3L); - Assert.Single(result); - Assert.Equal(3L, result[0]); - } - - [Fact] - public void ConvertObjectToEnumerableInterface() - { - var result = ConversionService.Convert(3L); - Assert.Single(result); - - foreach (object elem in result) - { - Assert.Equal(3L, elem); - } - } - - [Fact] - public void ConvertObjectToCollectionWithElementConversion() - { - var result = ConversionService.Convert>(3L); - Assert.Single(result); - Assert.Equal(3, result[0]); - } - - [Fact] - public void ConvertObjectToEnumerableWithElementConversion() - { - var result = ConversionService.Convert>(3L); - Assert.Single(result); - Assert.Contains(3, result); - } - - [Fact] - public void ConvertStringArrayToIntegerArray() - { - int[] result = ConversionService.Convert(new[] - { - "1", - "2", - "3" - }); - - Assert.Equal(3, result.Length); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertIntegerArrayToIntegerArray() - { - int[] result = ConversionService.Convert(new[] - { - 1, - 2, - 3 - }); - - Assert.Equal(3, result.Length); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertObjectArrayToIntegerArray() - { - int[] result = ConversionService.Convert(new object[] - { - 1, - 2, - 3 - }); - - Assert.Equal(3, result.Length); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertArrayToArrayAssignable() - { - int[] orig = - { - 1, - 2, - 3 - }; - - int[] result = ConversionService.Convert(orig); - Assert.Same(orig, result); - } - - [Fact] - public void ConvertListOfStringToString() - { - var list = new List - { - "Foo", - "Bar" - }; - - string result = ConversionService.Convert(list); - Assert.Equal("Foo,Bar", result); - } - - [Fact] - public void ConvertListOfListToString() - { - var list1 = new List - { - "Foo", - "Bar" - }; - - var list2 = new List - { - "Baz", - "Boop" - }; - - var list = new List> - { - list1, - list2 - }; - - string result = ConversionService.Convert(list); - Assert.Equal("Foo,Bar,Baz,Boop", result); - } - - [Fact] - public void ConvertCollectionToCollectionWithElementConversion() - { - var foo = new Collection - { - "1", - "2", - "3" - }; - - var result = ConversionService.Convert>(foo); - Assert.Equal(3, result.Count); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertSetToCollectionWithElementConversion() - { - ISet foo = new HashSet(); - foo.Add("1"); - foo.Add("2"); - foo.Add("3"); - - var result = ConversionService.Convert>(foo); - Assert.Equal(3, result.Count); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void ConvertCollectionToCollectionNull() - { - var bar = (List)ConversionService.Convert(null, typeof(ICollection), typeof(List)); - Assert.Null(bar); - } - - [Fact] - public void ConvertCollectionToCollectionNotGeneric() - { - var foo = new Collection - { - "1", - "2", - "3" - }; - - var result = ConversionService.Convert(foo); - Assert.Equal(3, result.Count); - Assert.Equal("1", result[0]); - Assert.Equal("2", result[1]); - Assert.Equal("3", result[2]); - } - - [Fact] - public void CollectionToCollectionEnumerableWithElementConversion() - { - var strings = new ArrayList - { - "3", - "9" - }; - - var result = ConversionService.Convert>(strings); - Assert.Contains(3, result); - Assert.Contains(9, result); - Assert.Equal(2, result.Count()); - } - - [Fact] - public void CollectionToCollectionEnumerableString() - { - var strings = new ArrayList - { - "3", - "9" - }; - - var result = ConversionService.Convert>(strings); - Assert.Contains("3", result); - Assert.Contains("9", result); - Assert.Equal(2, result.Count()); - } - - [Fact] - public void CollectionToCollectionStringCollection() - { - var strings = new ArrayList - { - "3", - "9" - }; - - var result = ConversionService.Convert(strings); - Assert.Contains("3", result[0], StringComparison.Ordinal); - Assert.Contains("9", result[1], StringComparison.Ordinal); - Assert.Equal(2, result.Count); - } - - [Fact] - public void ConvertDictToDictWithElementConversion() - { - var foo = new Dictionary - { - { "1", "BAR" }, - { "2", "BAZ" } - }; - - var map = ConversionService.Convert>(foo); - Assert.Equal(Foo.Bar, map[1]); - Assert.Equal(Foo.Baz, map[2]); - } - - [Fact] - public void ConvertDictionaryValuesToList() - { - IDictionary hashMap = new Dictionary(); - hashMap.Add("1", 1); - hashMap.Add("2", 2); - var converted = ConversionService.Convert>(hashMap.Values); - Assert.Contains(1, converted); - Assert.Contains(2, converted); - } - - [Fact] - public void ConvertDictionaryBothElementConversion() - { - var strings = new Dictionary - { - { "3", "9" }, - { "6", "31" } - }; - - var integers = ConversionService.Convert>(strings); - Assert.Equal(9, integers[3]); - Assert.Equal(31, integers[6]); - } - - [Fact] - public void ConvertHashtableToDictionary() - { - var strings = new Hashtable - { - { "3", "9" }, - { "6", "31" } - }; - - var integers = ConversionService.Convert>(strings); - Assert.Equal(9, integers[3]); - Assert.Equal(31, integers[6]); - } - - [Fact] - public void ConvertHashtableToSortedList() - { - var strings = new Hashtable - { - { "3", "9" }, - { "6", "31" } - }; - - var integers = ConversionService.Convert(strings); - Assert.Equal("9", integers["3"]); - Assert.Equal("31", integers["6"]); - } - - [Fact] - public void ConvertDictionaryConcurrentDictionary() - { - var strings = new Dictionary - { - { "3", "9" }, - { "6", "31" } - }; - - var integers = ConversionService.Convert>(strings); - Assert.Equal(9, integers[3]); - Assert.Equal(31, integers[6]); - } - - [Fact] - public void ConvertObjectToStringWithValueOfMethodPresentUsingToString() - { - Isbn.Reset(); - Assert.Equal("123456789", ConversionService.Convert(new Isbn("123456789"))); - - Assert.Equal(1, Isbn.ConstructorCount); - Assert.Equal(0, Isbn.ValueOfCount); - Assert.Equal(1, Isbn.ToStringCount); - } - - [Fact] - public void ConvertObjectToObjectUsingValueOfMethod() - { - Isbn.Reset(); - Assert.Equal(new Isbn("123456789"), ConversionService.Convert("123456789")); - - Assert.Equal(2, Isbn.ConstructorCount); - Assert.Equal(1, Isbn.ValueOfCount); - Assert.Equal(0, Isbn.ToStringCount); - } - - [Fact] - public void ConvertObjectToStringUsingToString() - { - SocialSecurityNumber.Reset(); - Assert.Equal("123456789", ConversionService.Convert(new SocialSecurityNumber("123456789"))); - - Assert.Equal(1, SocialSecurityNumber.ConstructorCount); - Assert.Equal(1, SocialSecurityNumber.ToStringCount); - } - - [Fact] - public void ConvertObjectToObjectUsingObjectConstructor() - { - SocialSecurityNumber.Reset(); - Assert.Equal(new SocialSecurityNumber("123456789"), ConversionService.Convert("123456789")); - - Assert.Equal(2, SocialSecurityNumber.ConstructorCount); - Assert.Equal(0, SocialSecurityNumber.ToStringCount); - } - - [Fact] - public void ConvertObjectToObjectNoValueOfMethodOrConstructor() - { - Assert.Throws(() => ConversionService.Convert(3L)); - } - - [Fact] - public void ConvertCharArrayToString() - { - string converted = ConversionService.Convert(new[] - { - 'a', - 'b', - 'c' - }); - - Assert.Equal("a,b,c", converted); - } - - [Fact] - public void ConvertStringToCharArray() - { - char[] converted = ConversionService.Convert("a,b,c"); - - Assert.Equal(new[] - { - 'a', - 'b', - 'c' - }, converted); - } - - [Fact] - public void MultidimensionalArrayToListConversionShouldConvertEntriesCorrectly() - { - string[][] grid = - { - new[] - { - "1", - "2", - "3", - "4" - }, - new[] - { - "5", - "6", - "7", - "8" - }, - new[] - { - "9", - "10", - "11", - "12" - } - }; - - var converted = ConversionService.Convert>(grid); - string[][] convertedBack = ConversionService.Convert(converted); - Assert.Equal(grid, convertedBack); - } - - [Fact] - public void TestStringToEncoding() - { - Assert.Equal(Encoding.UTF8, ConversionService.Convert("UTF-8")); - } - - [Fact] - public void TestEncodingToString() - { - Assert.Equal("utf-8", ConversionService.Convert(Encoding.UTF8)); - } - - private enum Foo - { - Bar, - Baz - } - - private sealed class Isbn - { - private readonly string _value; - - public static int ConstructorCount { get; private set; } - public static int ToStringCount { get; private set; } - public static int ValueOfCount { get; private set; } - - public Isbn(string value) - { - ConstructorCount++; - _value = value; - } - - public static void Reset() - { - ConstructorCount = 0; - ToStringCount = 0; - ValueOfCount = 0; - } - -#pragma warning disable S1144 // Unused private types or members should be removed - public static Isbn ValueOf(string value) - { - ValueOfCount++; - return new Isbn(value); - } -#pragma warning restore S1144 // Unused private types or members should be removed - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not Isbn other) - { - return false; - } - - return _value == other._value; - } - - public override int GetHashCode() - { - return _value.GetHashCode(); - } - - public override string ToString() - { - ToStringCount++; - return _value; - } - } - - private sealed class SocialSecurityNumber - { - private readonly string _value; - - public static int ConstructorCount { get; private set; } - public static int ToStringCount { get; private set; } - - public SocialSecurityNumber(string value) - { - ConstructorCount++; - _value = value; - } - - public static void Reset() - { - ConstructorCount = 0; - ToStringCount = 0; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not SocialSecurityNumber ssn) - { - return false; - } - - return _value == ssn._value; - } - - public override int GetHashCode() - { - return _value.GetHashCode(); - } - - public override string ToString() - { - ToStringCount++; - return _value; - } - } -} diff --git a/src/Common/test/Common.Test/Lifecycle/DefaultLifecycleProcessorTest.cs b/src/Common/test/Common.Test/Lifecycle/DefaultLifecycleProcessorTest.cs deleted file mode 100644 index b229f0488a..0000000000 --- a/src/Common/test/Common.Test/Lifecycle/DefaultLifecycleProcessorTest.cs +++ /dev/null @@ -1,448 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Xunit; - -namespace Steeltoe.Common.Test.Lifecycle; - -public sealed class DefaultLifecycleProcessorTest -{ - [Fact] - public async Task SingleSmartLifecycleAutoStartup() - { - var startedBeans = new ConcurrentQueue(); - TestSmartLifecycleBean bean = TestSmartLifecycleBean.ForStartupTests(1, startedBeans); - bean.IsAutoStartup = true; - - var processor = new DefaultLifecycleProcessor(CreateApplicationContext(new List - { - bean - }, new List())); - - Assert.False(bean.IsRunning); - await processor.OnRefreshAsync(); - Assert.True(bean.IsRunning); - await processor.StopAsync(); - Assert.False(bean.IsRunning); - Assert.Single(startedBeans); - } - - [Fact] - public async Task SingleSmartLifecycleWithoutAutoStartup() - { - var startedBeans = new ConcurrentQueue(); - TestSmartLifecycleBean bean = TestSmartLifecycleBean.ForStartupTests(1, startedBeans); - bean.IsAutoStartup = false; - - var processor = new DefaultLifecycleProcessor(CreateApplicationContext(new List - { - bean - }, new List())); - - Assert.False(bean.IsRunning); - await processor.OnRefreshAsync(); - Assert.False(bean.IsRunning); - Assert.Empty(startedBeans); - await processor.StartAsync(); - Assert.True(bean.IsRunning); - Assert.Single(startedBeans); - await processor.StopAsync(); - } - - [Fact] - public async Task SmartLifecycleGroupStartup() - { - var startedBeans = new ConcurrentQueue(); - TestSmartLifecycleBean beanMin = TestSmartLifecycleBean.ForStartupTests(int.MinValue, startedBeans); - TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.ForStartupTests(1, startedBeans); - TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.ForStartupTests(2, startedBeans); - TestSmartLifecycleBean bean3 = TestSmartLifecycleBean.ForStartupTests(3, startedBeans); - TestSmartLifecycleBean beanMax = TestSmartLifecycleBean.ForStartupTests(int.MaxValue, startedBeans); - - var processor = new DefaultLifecycleProcessor(CreateApplicationContext(new List - { - bean3, - beanMin, - bean2, - beanMax, - bean1 - }, new List())); - - Assert.False(beanMin.IsRunning); - Assert.False(bean1.IsRunning); - Assert.False(bean2.IsRunning); - Assert.False(bean3.IsRunning); - Assert.False(beanMax.IsRunning); - - await processor.OnRefreshAsync(); - - Assert.True(beanMin.IsRunning); - Assert.True(bean1.IsRunning); - Assert.True(bean2.IsRunning); - Assert.True(bean3.IsRunning); - Assert.True(beanMax.IsRunning); - - await processor.StopAsync(); - - Assert.Equal(5, startedBeans.Count); - ILifecycle[] started = startedBeans.ToArray(); - Assert.Equal(int.MinValue, GetPhase(started[0])); - Assert.Equal(1, GetPhase(started[1])); - Assert.Equal(2, GetPhase(started[2])); - Assert.Equal(3, GetPhase(started[3])); - Assert.Equal(int.MaxValue, GetPhase(started[4])); - } - - [Fact] - public async Task RefreshThenStartWithMixedBeans() - { - var startedBeans = new ConcurrentQueue(); - TestLifecycleBean simpleBean1 = TestLifecycleBean.ForStartupTests(startedBeans); - TestLifecycleBean simpleBean2 = TestLifecycleBean.ForStartupTests(startedBeans); - TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.ForStartupTests(5, startedBeans); - TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.ForStartupTests(-3, startedBeans); - - var processor = new DefaultLifecycleProcessor(CreateApplicationContext(new List - { - simpleBean1, - simpleBean2, - smartBean1, - smartBean2 - }, new List())); - - Assert.False(simpleBean1.IsRunning); - Assert.False(simpleBean2.IsRunning); - Assert.False(smartBean1.IsRunning); - Assert.False(smartBean2.IsRunning); - - await processor.OnRefreshAsync(); - - Assert.False(simpleBean1.IsRunning); - Assert.False(simpleBean2.IsRunning); - Assert.True(smartBean1.IsRunning); - Assert.True(smartBean2.IsRunning); - - Assert.Equal(2, startedBeans.Count); - ILifecycle[] started = startedBeans.ToArray(); - Assert.Equal(-3, GetPhase(started[0])); - Assert.Equal(5, GetPhase(started[1])); - - await processor.StartAsync(); - - Assert.True(simpleBean1.IsRunning); - Assert.True(simpleBean2.IsRunning); - Assert.True(smartBean1.IsRunning); - Assert.True(smartBean2.IsRunning); - - Assert.Equal(4, startedBeans.Count); - started = startedBeans.ToArray(); - Assert.Equal(0, GetPhase(started[2])); - Assert.Equal(0, GetPhase(started[3])); - } - - [Fact] - public async Task RefreshThenStopAndRestartWithMixedBeans() - { - var startedBeans = new ConcurrentQueue(); - TestLifecycleBean simpleBean1 = TestLifecycleBean.ForStartupTests(startedBeans); - TestLifecycleBean simpleBean2 = TestLifecycleBean.ForStartupTests(startedBeans); - TestSmartLifecycleBean smartBean1 = TestSmartLifecycleBean.ForStartupTests(5, startedBeans); - TestSmartLifecycleBean smartBean2 = TestSmartLifecycleBean.ForStartupTests(-3, startedBeans); - - var processor = new DefaultLifecycleProcessor(CreateApplicationContext(new List - { - simpleBean1, - simpleBean2, - smartBean1, - smartBean2 - }, new List())); - - Assert.False(simpleBean1.IsRunning); - Assert.False(simpleBean2.IsRunning); - Assert.False(smartBean1.IsRunning); - Assert.False(smartBean2.IsRunning); - - await processor.OnRefreshAsync(); - - Assert.False(simpleBean1.IsRunning); - Assert.False(simpleBean2.IsRunning); - Assert.True(smartBean1.IsRunning); - Assert.True(smartBean2.IsRunning); - - Assert.Equal(2, startedBeans.Count); - ILifecycle[] started = startedBeans.ToArray(); - Assert.Equal(-3, GetPhase(started[0])); - Assert.Equal(5, GetPhase(started[1])); - - await processor.StopAsync(); - - Assert.False(simpleBean1.IsRunning); - Assert.False(simpleBean2.IsRunning); - Assert.False(smartBean1.IsRunning); - Assert.False(smartBean2.IsRunning); - - await processor.StartAsync(); - - Assert.True(simpleBean1.IsRunning); - Assert.True(simpleBean2.IsRunning); - Assert.True(smartBean1.IsRunning); - Assert.True(smartBean2.IsRunning); - - Assert.Equal(6, startedBeans.Count); - started = startedBeans.ToArray(); - Assert.Equal(-3, GetPhase(started[2])); - Assert.Equal(0, GetPhase(started[3])); - Assert.Equal(0, GetPhase(started[4])); - Assert.Equal(5, GetPhase(started[5])); - } - - [Fact] - public async Task SmartLifecycleGroupShutdown() - { - var stoppedBeans = new ConcurrentQueue(); - - TestSmartLifecycleBean bean1 = TestSmartLifecycleBean.ForShutdownTests(1, 300, stoppedBeans); - TestSmartLifecycleBean bean2 = TestSmartLifecycleBean.ForShutdownTests(3, 100, stoppedBeans); - TestSmartLifecycleBean bean3 = TestSmartLifecycleBean.ForShutdownTests(1, 600, stoppedBeans); - TestSmartLifecycleBean bean4 = TestSmartLifecycleBean.ForShutdownTests(2, 400, stoppedBeans); - TestSmartLifecycleBean bean5 = TestSmartLifecycleBean.ForShutdownTests(2, 700, stoppedBeans); - TestSmartLifecycleBean bean6 = TestSmartLifecycleBean.ForShutdownTests(int.MaxValue, 200, stoppedBeans); - TestSmartLifecycleBean bean7 = TestSmartLifecycleBean.ForShutdownTests(3, 200, stoppedBeans); - - var processor = new DefaultLifecycleProcessor(CreateApplicationContext(new List - { - bean1, - bean2, - bean3, - bean4, - bean5, - bean6, - bean7 - }, new List())); - - await processor.OnRefreshAsync(); - await processor.StopAsync(); - ILifecycle[] stopped = stoppedBeans.ToArray(); - Assert.Equal(int.MaxValue, GetPhase(stopped[0])); - Assert.Equal(3, GetPhase(stopped[1])); - Assert.Equal(3, GetPhase(stopped[2])); - Assert.Equal(2, GetPhase(stopped[3])); - Assert.Equal(2, GetPhase(stopped[4])); - Assert.Equal(1, GetPhase(stopped[5])); - Assert.Equal(1, GetPhase(stopped[6])); - } - - [Fact] - public async Task SingleSmartLifecycleShutdown() - { - var stoppedBeans = new ConcurrentQueue(); - TestSmartLifecycleBean bean = TestSmartLifecycleBean.ForShutdownTests(99, 300, stoppedBeans); - - var processor = new DefaultLifecycleProcessor(CreateApplicationContext(new List - { - bean - }, new List())); - - await processor.OnRefreshAsync(); - Assert.True(bean.IsRunning); - await processor.StopAsync(); - ILifecycle[] stopped = stoppedBeans.ToArray(); - Assert.Same(bean, stopped[0]); - } - - [Fact] - public async Task SingleLifecycleShutdown() - { - var stoppedBeans = new ConcurrentQueue(); - ILifecycle bean = new TestLifecycleBean(null, stoppedBeans); - - var processor = new DefaultLifecycleProcessor(CreateApplicationContext(new List - { - bean - }, new List())); - - Assert.False(bean.IsRunning); - await processor.OnRefreshAsync(); - Assert.False(bean.IsRunning); - await processor.StartAsync(); - Assert.True(bean.IsRunning); - await processor.StopAsync(); - ILifecycle[] stopped = stoppedBeans.ToArray(); - Assert.False(bean.IsRunning); - Assert.Same(bean, stopped[0]); - } - - [Fact] - public async Task MixedShutdown() - { - var stoppedBeans = new ConcurrentQueue(); - ILifecycle bean1 = TestLifecycleBean.ForShutdownTests(stoppedBeans); - ILifecycle bean2 = TestSmartLifecycleBean.ForShutdownTests(500, 200, stoppedBeans); - ILifecycle bean3 = TestSmartLifecycleBean.ForShutdownTests(int.MaxValue, 100, stoppedBeans); - ILifecycle bean4 = TestLifecycleBean.ForShutdownTests(stoppedBeans); - ILifecycle bean5 = TestSmartLifecycleBean.ForShutdownTests(1, 200, stoppedBeans); - ILifecycle bean6 = TestSmartLifecycleBean.ForShutdownTests(-1, 100, stoppedBeans); - ILifecycle bean7 = TestSmartLifecycleBean.ForShutdownTests(int.MinValue, 300, stoppedBeans); - - var processor = new DefaultLifecycleProcessor(CreateApplicationContext(new List - { - bean1, - bean2, - bean3, - bean4, - bean5, - bean6, - bean7 - }, new List())); - - await processor.OnRefreshAsync(); - - Assert.True(bean2.IsRunning); - Assert.True(bean3.IsRunning); - Assert.True(bean5.IsRunning); - Assert.True(bean6.IsRunning); - Assert.True(bean7.IsRunning); - Assert.False(bean1.IsRunning); - Assert.False(bean4.IsRunning); - - await bean1.StartAsync(); - await bean4.StartAsync(); - - Assert.True(bean1.IsRunning); - Assert.True(bean4.IsRunning); - - await processor.StopAsync(); - - Assert.False(bean2.IsRunning); - Assert.False(bean3.IsRunning); - Assert.False(bean5.IsRunning); - Assert.False(bean6.IsRunning); - Assert.False(bean7.IsRunning); - Assert.False(bean1.IsRunning); - Assert.False(bean4.IsRunning); - - ILifecycle[] stopped = stoppedBeans.ToArray(); - Assert.Equal(7, stopped.Length); - Assert.Equal(int.MaxValue, GetPhase(stopped[0])); - Assert.Equal(500, GetPhase(stopped[1])); - Assert.Equal(1, GetPhase(stopped[2])); - Assert.Equal(0, GetPhase(stopped[3])); - Assert.Equal(0, GetPhase(stopped[4])); - Assert.Equal(-1, GetPhase(stopped[5])); - Assert.Equal(int.MinValue, GetPhase(stopped[6])); - } - - private static IApplicationContext CreateApplicationContext(List lifecycles, List smartLifecycles) - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - var serviceCollection = new ServiceCollection(); - - foreach (ILifecycle lifeCycle in lifecycles) - { - serviceCollection.AddSingleton(lifeCycle); - } - - foreach (ISmartLifecycle lifeCycle in smartLifecycles) - { - serviceCollection.AddSingleton(lifeCycle); - } - - return new GenericApplicationContext(serviceCollection.BuildServiceProvider(true), configurationRoot); - } - - private static int GetPhase(ILifecycle lifecycle) - { - if (lifecycle is ISmartLifecycle lifecycle1) - { - return lifecycle1.Phase; - } - - return 0; - } - - private class TestLifecycleBean : ILifecycle - { - private readonly ConcurrentQueue _startedBeans; - private readonly ConcurrentQueue _stoppedBeans; - - public bool IsRunning { get; private set; } - - public TestLifecycleBean(ConcurrentQueue startedBeans, ConcurrentQueue stoppedBeans) - { - _startedBeans = startedBeans; - _stoppedBeans = stoppedBeans; - } - - public static TestLifecycleBean ForStartupTests(ConcurrentQueue startedBeans) - { - return new TestLifecycleBean(startedBeans, null); - } - - public static TestLifecycleBean ForShutdownTests(ConcurrentQueue stoppedBeans) - { - return new TestLifecycleBean(null, stoppedBeans); - } - - public Task StartAsync() - { - if (_startedBeans != null) - { - _startedBeans.Enqueue(this); - } - - IsRunning = true; - return Task.CompletedTask; - } - - public Task StopAsync() - { - if (_stoppedBeans != null) - { - _stoppedBeans.Enqueue(this); - } - - IsRunning = false; - return Task.CompletedTask; - } - } - - private sealed class TestSmartLifecycleBean : TestLifecycleBean, ISmartLifecycle - { - private readonly int _shutdownDelay; - - public bool IsAutoStartup { get; set; } = true; - - public int Phase { get; } - - public TestSmartLifecycleBean(int phase, int shutdownDelay, ConcurrentQueue startedBeans, ConcurrentQueue stoppedBeans) - : base(startedBeans, stoppedBeans) - { - Phase = phase; - _shutdownDelay = shutdownDelay; - } - - public static TestSmartLifecycleBean ForStartupTests(int phase, ConcurrentQueue startedBeans) - { - return new TestSmartLifecycleBean(phase, 0, startedBeans, null); - } - - public static TestSmartLifecycleBean ForShutdownTests(int phase, int shutdownDelay, ConcurrentQueue stoppedBeans) - { - return new TestSmartLifecycleBean(phase, shutdownDelay, null, stoppedBeans); - } - - public async Task StopAsync(Action callback) - { - await StopAsync(); - int delay = _shutdownDelay; - await Task.Delay(delay); - callback(); - } - } -} diff --git a/src/Common/test/Common.Test/Order/OrderCompareTest.cs b/src/Common/test/Common.Test/Order/OrderCompareTest.cs deleted file mode 100644 index 8976783907..0000000000 --- a/src/Common/test/Common.Test/Order/OrderCompareTest.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Order; -using Xunit; - -namespace Steeltoe.Common.Test.Order; - -public sealed class OrderCompareTest -{ - private readonly OrderComparer _orderComparer = OrderComparer.Instance; - - [Fact] - public void CompareOrderedInstancesBefore() - { - Assert.Equal(-1, _orderComparer.Compare(new StubOrdered(100), new StubOrdered(2000))); - } - - [Fact] - public void CompareOrderedInstancesSame() - { - Assert.Equal(0, _orderComparer.Compare(new StubOrdered(100), new StubOrdered(100))); - } - - [Fact] - public void CompareOrderedInstancesAfter() - { - Assert.Equal(1, _orderComparer.Compare(new StubOrdered(982300), new StubOrdered(100))); - } - - [Fact] - public void CompareOrderedInstancesNullFirst() - { - Assert.Equal(1, _orderComparer.Compare(null, new StubOrdered(100))); - } - - [Fact] - public void CompareOrderedInstancesNullLast() - { - Assert.Equal(-1, _orderComparer.Compare(new StubOrdered(100), null)); - } - - [Fact] - public void CompareOrderedInstancesDoubleNull() - { - Assert.Equal(0, _orderComparer.Compare(null, null)); - } - - private sealed class StubOrdered : IOrdered - { - public int Order { get; } - - public StubOrdered(int order) - { - Order = order; - } - } -} diff --git a/src/Common/test/Common.Test/Util/AntPathMatcherTest.cs b/src/Common/test/Common.Test/Util/AntPathMatcherTest.cs deleted file mode 100644 index 3f8eadaf0c..0000000000 --- a/src/Common/test/Common.Test/Util/AntPathMatcherTest.cs +++ /dev/null @@ -1,603 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Xunit; - -namespace Steeltoe.Common.Test.Util; - -public sealed class AntPathMatcherTest -{ - private static readonly Random Rng = new(); - private readonly AntPathMatcher _pathMatcher = new(); - - [Fact] - public void Match() - { - // test exact Matching - Assert.True(_pathMatcher.Match("test", "test")); - Assert.True(_pathMatcher.Match("/test", "/test")); - - // SPR-14141 - Assert.True(_pathMatcher.Match("https://example.org", "https://example.org")); - Assert.False(_pathMatcher.Match("/test.jpg", "test.jpg")); - Assert.False(_pathMatcher.Match("test", "/test")); - Assert.False(_pathMatcher.Match("/test", "test")); - - // test Matching with ?'s - Assert.True(_pathMatcher.Match("t?st", "test")); - Assert.True(_pathMatcher.Match("??st", "test")); - Assert.True(_pathMatcher.Match("tes?", "test")); - Assert.True(_pathMatcher.Match("te??", "test")); - Assert.True(_pathMatcher.Match("?es?", "test")); - Assert.False(_pathMatcher.Match("tes?", "tes")); - Assert.False(_pathMatcher.Match("tes?", "testt")); - Assert.False(_pathMatcher.Match("tes?", "tsst")); - - // test Matching with *'s - Assert.True(_pathMatcher.Match("*", "test")); - Assert.True(_pathMatcher.Match("test*", "test")); - Assert.True(_pathMatcher.Match("test*", "testTest")); - Assert.True(_pathMatcher.Match("test/*", "test/Test")); - Assert.True(_pathMatcher.Match("test/*", "test/t")); - Assert.True(_pathMatcher.Match("test/*", "test/")); - Assert.True(_pathMatcher.Match("*test*", "AnothertestTest")); - Assert.True(_pathMatcher.Match("*test", "Anothertest")); - Assert.True(_pathMatcher.Match("*.*", "test.")); - Assert.True(_pathMatcher.Match("*.*", "test.test")); - Assert.True(_pathMatcher.Match("*.*", "test.test.test")); - Assert.True(_pathMatcher.Match("test*aaa", "testblaaaa")); - Assert.False(_pathMatcher.Match("test*", "tst")); - Assert.False(_pathMatcher.Match("test*", "tsttest")); - Assert.False(_pathMatcher.Match("test*", "test/")); - Assert.False(_pathMatcher.Match("test*", "test/t")); - Assert.False(_pathMatcher.Match("test/*", "test")); - Assert.False(_pathMatcher.Match("*test*", "tsttst")); - Assert.False(_pathMatcher.Match("*test", "tsttst")); - Assert.False(_pathMatcher.Match("*.*", "tsttst")); - Assert.False(_pathMatcher.Match("test*aaa", "test")); - Assert.False(_pathMatcher.Match("test*aaa", "testblaaab")); - - // test Matching with ?'s and /'s - Assert.True(_pathMatcher.Match("/?", "/a")); - Assert.True(_pathMatcher.Match("/?/a", "/a/a")); - Assert.True(_pathMatcher.Match("/a/?", "/a/b")); - Assert.True(_pathMatcher.Match("/??/a", "/aa/a")); - Assert.True(_pathMatcher.Match("/a/??", "/a/bb")); - Assert.True(_pathMatcher.Match("/?", "/a")); - - // test Matching with **'s - Assert.True(_pathMatcher.Match("/**", "/testing/testing")); - Assert.True(_pathMatcher.Match("/*/**", "/testing/testing")); - Assert.True(_pathMatcher.Match("/**/*", "/testing/testing")); - Assert.True(_pathMatcher.Match("/bla/**/bla", "/bla/testing/testing/bla")); - Assert.True(_pathMatcher.Match("/bla/**/bla", "/bla/testing/testing/bla/bla")); - Assert.True(_pathMatcher.Match("/**/test", "/bla/bla/test")); - Assert.True(_pathMatcher.Match("/bla/**/**/bla", "/bla/bla/bla/bla/bla/bla")); - Assert.True(_pathMatcher.Match("/bla*bla/test", "/blaXXXbla/test")); - Assert.True(_pathMatcher.Match("/*bla/test", "/XXXbla/test")); - Assert.False(_pathMatcher.Match("/bla*bla/test", "/blaXXXbl/test")); - Assert.False(_pathMatcher.Match("/*bla/test", "XXXblab/test")); - Assert.False(_pathMatcher.Match("/*bla/test", "XXXbl/test")); - - Assert.False(_pathMatcher.Match("/????", "/bala/bla")); - Assert.False(_pathMatcher.Match("/**/*bla", "/bla/bla/bla/bbb")); - - Assert.True(_pathMatcher.Match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing/")); - Assert.True(_pathMatcher.Match("/*bla*/**/bla/*", "/XXXblaXXXX/testing/testing/bla/testing")); - Assert.True(_pathMatcher.Match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing")); - Assert.True(_pathMatcher.Match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing.jpg")); - - Assert.True(_pathMatcher.Match("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing/")); - Assert.True(_pathMatcher.Match("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing")); - Assert.True(_pathMatcher.Match("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing")); - Assert.False(_pathMatcher.Match("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing/testing")); - - Assert.False(_pathMatcher.Match("/x/x/**/bla", "/x/x/x/")); - - Assert.True(_pathMatcher.Match("/foo/bar/**", "/foo/bar")); - - Assert.True(_pathMatcher.Match(string.Empty, string.Empty)); - - Assert.True(_pathMatcher.Match("/{bla}.*", "/testing.html")); - } - - [Fact] - public void MatchWithTrimTokensEnabled() - { - _pathMatcher.TrimTokens = true; - - Assert.True(_pathMatcher.Match("/foo/bar", "/foo /bar")); - } - - [Fact] - public void WithMatchStart() - { - // test exact Matching - Assert.True(_pathMatcher.MatchStart("test", "test")); - Assert.True(_pathMatcher.MatchStart("/test", "/test")); - Assert.False(_pathMatcher.MatchStart("/test.jpg", "test.jpg")); - Assert.False(_pathMatcher.MatchStart("test", "/test")); - Assert.False(_pathMatcher.MatchStart("/test", "test")); - - // test Matching with ?'s - Assert.True(_pathMatcher.MatchStart("t?st", "test")); - Assert.True(_pathMatcher.MatchStart("??st", "test")); - Assert.True(_pathMatcher.MatchStart("tes?", "test")); - Assert.True(_pathMatcher.MatchStart("te??", "test")); - Assert.True(_pathMatcher.MatchStart("?es?", "test")); - Assert.False(_pathMatcher.MatchStart("tes?", "tes")); - Assert.False(_pathMatcher.MatchStart("tes?", "testt")); - Assert.False(_pathMatcher.MatchStart("tes?", "tsst")); - - // test Matching with *'s - Assert.True(_pathMatcher.MatchStart("*", "test")); - Assert.True(_pathMatcher.MatchStart("test*", "test")); - Assert.True(_pathMatcher.MatchStart("test*", "testTest")); - Assert.True(_pathMatcher.MatchStart("test/*", "test/Test")); - Assert.True(_pathMatcher.MatchStart("test/*", "test/t")); - Assert.True(_pathMatcher.MatchStart("test/*", "test/")); - Assert.True(_pathMatcher.MatchStart("*test*", "AnothertestTest")); - Assert.True(_pathMatcher.MatchStart("*test", "Anothertest")); - Assert.True(_pathMatcher.MatchStart("*.*", "test.")); - Assert.True(_pathMatcher.MatchStart("*.*", "test.test")); - Assert.True(_pathMatcher.MatchStart("*.*", "test.test.test")); - Assert.True(_pathMatcher.MatchStart("test*aaa", "testblaaaa")); - Assert.False(_pathMatcher.MatchStart("test*", "tst")); - Assert.False(_pathMatcher.MatchStart("test*", "test/")); - Assert.False(_pathMatcher.MatchStart("test*", "tsttest")); - Assert.False(_pathMatcher.MatchStart("test*", "test/")); - Assert.False(_pathMatcher.MatchStart("test*", "test/t")); - Assert.True(_pathMatcher.MatchStart("test/*", "test")); - Assert.True(_pathMatcher.MatchStart("test/t*.txt", "test")); - Assert.False(_pathMatcher.MatchStart("*test*", "tsttst")); - Assert.False(_pathMatcher.MatchStart("*test", "tsttst")); - Assert.False(_pathMatcher.MatchStart("*.*", "tsttst")); - Assert.False(_pathMatcher.MatchStart("test*aaa", "test")); - Assert.False(_pathMatcher.MatchStart("test*aaa", "testblaaab")); - - // test Matching with ?'s and /'s - Assert.True(_pathMatcher.MatchStart("/?", "/a")); - Assert.True(_pathMatcher.MatchStart("/?/a", "/a/a")); - Assert.True(_pathMatcher.MatchStart("/a/?", "/a/b")); - Assert.True(_pathMatcher.MatchStart("/??/a", "/aa/a")); - Assert.True(_pathMatcher.MatchStart("/a/??", "/a/bb")); - Assert.True(_pathMatcher.MatchStart("/?", "/a")); - - // test Matching with **'s - Assert.True(_pathMatcher.MatchStart("/**", "/testing/testing")); - Assert.True(_pathMatcher.MatchStart("/*/**", "/testing/testing")); - Assert.True(_pathMatcher.MatchStart("/**/*", "/testing/testing")); - Assert.True(_pathMatcher.MatchStart("test*/**", "test/")); - Assert.True(_pathMatcher.MatchStart("test*/**", "test/t")); - Assert.True(_pathMatcher.MatchStart("/bla/**/bla", "/bla/testing/testing/bla")); - Assert.True(_pathMatcher.MatchStart("/bla/**/bla", "/bla/testing/testing/bla/bla")); - Assert.True(_pathMatcher.MatchStart("/**/test", "/bla/bla/test")); - Assert.True(_pathMatcher.MatchStart("/bla/**/**/bla", "/bla/bla/bla/bla/bla/bla")); - Assert.True(_pathMatcher.MatchStart("/bla*bla/test", "/blaXXXbla/test")); - Assert.True(_pathMatcher.MatchStart("/*bla/test", "/XXXbla/test")); - Assert.False(_pathMatcher.MatchStart("/bla*bla/test", "/blaXXXbl/test")); - Assert.False(_pathMatcher.MatchStart("/*bla/test", "XXXblab/test")); - Assert.False(_pathMatcher.MatchStart("/*bla/test", "XXXbl/test")); - - Assert.False(_pathMatcher.MatchStart("/????", "/bala/bla")); - Assert.True(_pathMatcher.MatchStart("/**/*bla", "/bla/bla/bla/bbb")); - - Assert.True(_pathMatcher.MatchStart("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing/")); - Assert.True(_pathMatcher.MatchStart("/*bla*/**/bla/*", "/XXXblaXXXX/testing/testing/bla/testing")); - Assert.True(_pathMatcher.MatchStart("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing")); - Assert.True(_pathMatcher.MatchStart("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing.jpg")); - - Assert.True(_pathMatcher.MatchStart("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing/")); - Assert.True(_pathMatcher.MatchStart("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing")); - Assert.True(_pathMatcher.MatchStart("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing")); - Assert.True(_pathMatcher.MatchStart("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing/testing")); - - Assert.True(_pathMatcher.MatchStart("/x/x/**/bla", "/x/x/x/")); - - Assert.True(_pathMatcher.MatchStart(string.Empty, string.Empty)); - } - - [Fact] - public void UniqueDeliminator() - { - _pathMatcher.PathSeparator = "."; - - // test exact Matching - Assert.True(_pathMatcher.Match("test", "test")); - Assert.True(_pathMatcher.Match(".test", ".test")); - Assert.False(_pathMatcher.Match(".test/jpg", "test/jpg")); - Assert.False(_pathMatcher.Match("test", ".test")); - Assert.False(_pathMatcher.Match(".test", "test")); - - // test Matching with ?'s - Assert.True(_pathMatcher.Match("t?st", "test")); - Assert.True(_pathMatcher.Match("??st", "test")); - Assert.True(_pathMatcher.Match("tes?", "test")); - Assert.True(_pathMatcher.Match("te??", "test")); - Assert.True(_pathMatcher.Match("?es?", "test")); - Assert.False(_pathMatcher.Match("tes?", "tes")); - Assert.False(_pathMatcher.Match("tes?", "testt")); - Assert.False(_pathMatcher.Match("tes?", "tsst")); - - // test Matching with *'s - Assert.True(_pathMatcher.Match("*", "test")); - Assert.True(_pathMatcher.Match("test*", "test")); - Assert.True(_pathMatcher.Match("test*", "testTest")); - Assert.True(_pathMatcher.Match("*test*", "AnothertestTest")); - Assert.True(_pathMatcher.Match("*test", "Anothertest")); - Assert.True(_pathMatcher.Match("*/*", "test/")); - Assert.True(_pathMatcher.Match("*/*", "test/test")); - Assert.True(_pathMatcher.Match("*/*", "test/test/test")); - Assert.True(_pathMatcher.Match("test*aaa", "testblaaaa")); - Assert.False(_pathMatcher.Match("test*", "tst")); - Assert.False(_pathMatcher.Match("test*", "tsttest")); - Assert.False(_pathMatcher.Match("*test*", "tsttst")); - Assert.False(_pathMatcher.Match("*test", "tsttst")); - Assert.False(_pathMatcher.Match("*/*", "tsttst")); - Assert.False(_pathMatcher.Match("test*aaa", "test")); - Assert.False(_pathMatcher.Match("test*aaa", "testblaaab")); - - // test Matching with ?'s and .'s - Assert.True(_pathMatcher.Match(".?", ".a")); - Assert.True(_pathMatcher.Match(".?.a", ".a.a")); - Assert.True(_pathMatcher.Match(".a.?", ".a.b")); - Assert.True(_pathMatcher.Match(".??.a", ".aa.a")); - Assert.True(_pathMatcher.Match(".a.??", ".a.bb")); - Assert.True(_pathMatcher.Match(".?", ".a")); - - // test Matching with **'s - Assert.True(_pathMatcher.Match(".**", ".testing.testing")); - Assert.True(_pathMatcher.Match(".*.**", ".testing.testing")); - Assert.True(_pathMatcher.Match(".**.*", ".testing.testing")); - Assert.True(_pathMatcher.Match(".bla.**.bla", ".bla.testing.testing.bla")); - Assert.True(_pathMatcher.Match(".bla.**.bla", ".bla.testing.testing.bla.bla")); - Assert.True(_pathMatcher.Match(".**.test", ".bla.bla.test")); - Assert.True(_pathMatcher.Match(".bla.**.**.bla", ".bla.bla.bla.bla.bla.bla")); - Assert.True(_pathMatcher.Match(".bla*bla.test", ".blaXXXbla.test")); - Assert.True(_pathMatcher.Match(".*bla.test", ".XXXbla.test")); - Assert.False(_pathMatcher.Match(".bla*bla.test", ".blaXXXbl.test")); - Assert.False(_pathMatcher.Match(".*bla.test", "XXXblab.test")); - Assert.False(_pathMatcher.Match(".*bla.test", "XXXbl.test")); - } - - [Fact] - public void ExtractPathWithinPattern() - { - Assert.Equal(string.Empty, _pathMatcher.ExtractPathWithinPattern("/docs/commit.html", "/docs/commit.html")); - - Assert.Equal("cvs/commit", _pathMatcher.ExtractPathWithinPattern("/docs/*", "/docs/cvs/commit")); - Assert.Equal("commit.html", _pathMatcher.ExtractPathWithinPattern("/docs/cvs/*.html", "/docs/cvs/commit.html")); - Assert.Equal("cvs/commit", _pathMatcher.ExtractPathWithinPattern("/docs/**", "/docs/cvs/commit")); - Assert.Equal("cvs/commit.html", _pathMatcher.ExtractPathWithinPattern("/docs/**/*.html", "/docs/cvs/commit.html")); - Assert.Equal("commit.html", _pathMatcher.ExtractPathWithinPattern("/docs/**/*.html", "/docs/commit.html")); - Assert.Equal("commit.html", _pathMatcher.ExtractPathWithinPattern("/*.html", "/commit.html")); - Assert.Equal("docs/commit.html", _pathMatcher.ExtractPathWithinPattern("/*.html", "/docs/commit.html")); - Assert.Equal("/commit.html", _pathMatcher.ExtractPathWithinPattern("*.html", "/commit.html")); - Assert.Equal("/docs/commit.html", _pathMatcher.ExtractPathWithinPattern("*.html", "/docs/commit.html")); - Assert.Equal("/docs/commit.html", _pathMatcher.ExtractPathWithinPattern("**/*.*", "/docs/commit.html")); - Assert.Equal("/docs/commit.html", _pathMatcher.ExtractPathWithinPattern("*", "/docs/commit.html")); - - // SPR-10515 - Assert.Equal("/docs/cvs/other/commit.html", _pathMatcher.ExtractPathWithinPattern("**/commit.html", "/docs/cvs/other/commit.html")); - Assert.Equal("cvs/other/commit.html", _pathMatcher.ExtractPathWithinPattern("/docs/**/commit.html", "/docs/cvs/other/commit.html")); - Assert.Equal("cvs/other/commit.html", _pathMatcher.ExtractPathWithinPattern("/docs/**/**/**/**", "/docs/cvs/other/commit.html")); - - Assert.Equal("docs/cvs/commit", _pathMatcher.ExtractPathWithinPattern("/d?cs/*", "/docs/cvs/commit")); - Assert.Equal("cvs/commit.html", _pathMatcher.ExtractPathWithinPattern("/docs/c?s/*.html", "/docs/cvs/commit.html")); - Assert.Equal("docs/cvs/commit", _pathMatcher.ExtractPathWithinPattern("/d?cs/**", "/docs/cvs/commit")); - Assert.Equal("docs/cvs/commit.html", _pathMatcher.ExtractPathWithinPattern("/d?cs/**/*.html", "/docs/cvs/commit.html")); - } - - [Fact] - public void ExtractUriTemplateVariables() - { - IDictionary result = _pathMatcher.ExtractUriTemplateVariables("/hotels/{hotel}", "/hotels/1"); - - Assert.Equal(new Dictionary - { - { "hotel", "1" } - }, result); - - result = _pathMatcher.ExtractUriTemplateVariables("/h?tels/{hotel}", "/hotels/1"); - - Assert.Equal(new Dictionary - { - { "hotel", "1" } - }, result); - - result = _pathMatcher.ExtractUriTemplateVariables("/hotels/{hotel}/bookings/{booking}", "/hotels/1/bookings/2"); - IDictionary expected = new Dictionary(); - expected.Add("hotel", "1"); - expected.Add("booking", "2"); - Assert.Equal(expected, result); - - result = _pathMatcher.ExtractUriTemplateVariables("/**/hotels/**/{hotel}", "/foo/hotels/bar/1"); - - Assert.Equal(new Dictionary - { - { "hotel", "1" } - }, result); - - result = _pathMatcher.ExtractUriTemplateVariables("/{page}.html", "/42.html"); - - Assert.Equal(new Dictionary - { - { "page", "42" } - }, result); - - result = _pathMatcher.ExtractUriTemplateVariables("/{page}.*", "/42.html"); - - Assert.Equal(new Dictionary - { - { "page", "42" } - }, result); - - result = _pathMatcher.ExtractUriTemplateVariables("/A-{B}-C", "/A-b-C"); - - Assert.Equal(new Dictionary - { - { "B", "b" } - }, result); - - result = _pathMatcher.ExtractUriTemplateVariables("/{name}.{extension}", "/test.html"); - expected.Clear(); - expected.Add("name", "test"); - expected.Add("extension", "html"); - Assert.Equal(expected, result); - } - - [Fact] - public void ExtractUriTemplateVariablesRegex() - { - IDictionary result = - _pathMatcher.ExtractUriTemplateVariables("{symbolicName:[\\w\\.]+}-{version:[\\w\\.]+}.jar", "com.example-1.0.0.jar"); - - Assert.Equal("com.example", result["symbolicName"]); - Assert.Equal("1.0.0", result["version"]); - - result = _pathMatcher.ExtractUriTemplateVariables("{symbolicName:[\\w\\.]+}-sources-{version:[\\w\\.]+}.jar", "com.example-sources-1.0.0.jar"); - Assert.Equal("com.example", result["symbolicName"]); - Assert.Equal("1.0.0", result["version"]); - } - - [Fact] - public void ExtractUriTemplateVarsRegexQualifiers() - { - IDictionary result = _pathMatcher.ExtractUriTemplateVariables( - "{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar", "com.example-sources-1.0.0.jar"); - - Assert.Equal("com.example", result["symbolicName"]); - Assert.Equal("1.0.0", result["version"]); - - result = _pathMatcher.ExtractUriTemplateVariables("{symbolicName:[\\w\\.]+}-sources-{version:[\\d\\.]+}-{year:\\d{4}}{month:\\d{2}}{day:\\d{2}}.jar", - "com.example-sources-1.0.0-20100220.jar"); - - Assert.Equal("com.example", result["symbolicName"]); - Assert.Equal("1.0.0", result["version"]); - Assert.Equal("2010", result["year"]); - Assert.Equal("02", result["month"]); - Assert.Equal("20", result["day"]); - - result = _pathMatcher.ExtractUriTemplateVariables("{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.\\{\\}]+}.jar", - "com.example-sources-1.0.0.{12}.jar"); - - Assert.Equal("com.example", result["symbolicName"]); - Assert.Equal("1.0.0.{12}", result["version"]); - } - - [Fact] - public void ExtractUriTemplateVarsRegexCapturingGroups() - { - Assert.Throws(() => _pathMatcher.ExtractUriTemplateVariables("/web/{id:foo(bar)?}", "/web/foobar")); - } - - [Fact] - public void Combine() - { - Assert.Equal(string.Empty, _pathMatcher.Combine(null, null)); - Assert.Equal("/hotels", _pathMatcher.Combine("/hotels", null)); - Assert.Equal("/hotels", _pathMatcher.Combine(null, "/hotels")); - Assert.Equal("/hotels/booking", _pathMatcher.Combine("/hotels/*", "booking")); - Assert.Equal("/hotels/booking", _pathMatcher.Combine("/hotels/*", "/booking")); - Assert.Equal("/hotels/**/booking", _pathMatcher.Combine("/hotels/**", "booking")); - Assert.Equal("/hotels/**/booking", _pathMatcher.Combine("/hotels/**", "/booking")); - Assert.Equal("/hotels/booking", _pathMatcher.Combine("/hotels", "/booking")); - Assert.Equal("/hotels/booking", _pathMatcher.Combine("/hotels", "booking")); - Assert.Equal("/hotels/booking", _pathMatcher.Combine("/hotels/", "booking")); - Assert.Equal("/hotels/{hotel}", _pathMatcher.Combine("/hotels/*", "{hotel}")); - Assert.Equal("/hotels/**/{hotel}", _pathMatcher.Combine("/hotels/**", "{hotel}")); - Assert.Equal("/hotels/{hotel}", _pathMatcher.Combine("/hotels", "{hotel}")); - Assert.Equal("/hotels/{hotel}.*", _pathMatcher.Combine("/hotels", "{hotel}.*")); - Assert.Equal("/hotels/*/booking/{booking}", _pathMatcher.Combine("/hotels/*/booking", "{booking}")); - Assert.Equal("/hotel.html", _pathMatcher.Combine("/*.html", "/hotel.html")); - Assert.Equal("/hotel.html", _pathMatcher.Combine("/*.html", "/hotel")); - Assert.Equal("/hotel.html", _pathMatcher.Combine("/*.html", "/hotel.*")); - Assert.Equal("/*.html", _pathMatcher.Combine("/**", "/*.html")); - Assert.Equal("/*.html", _pathMatcher.Combine("/*", "/*.html")); - Assert.Equal("/*.html", _pathMatcher.Combine("/*.*", "/*.html")); - - // SPR-8858 - Assert.Equal("/{foo}/bar", _pathMatcher.Combine("/{foo}", "/bar")); - - // SPR-7970 - Assert.Equal("/user/user", _pathMatcher.Combine("/user", "/user")); - - // SPR-10062 - Assert.Equal("/{foo:.*[^0-9].*}/edit/", _pathMatcher.Combine("/{foo:.*[^0-9].*}", "/edit/")); - - // SPR-10554 - Assert.Equal("/1.0/foo/test", _pathMatcher.Combine("/1.0", "/foo/test")); - - // SPR-12975 - Assert.Equal("/hotel", _pathMatcher.Combine("/", "/hotel")); - - // SPR-12975 - Assert.Equal("/hotel/booking", _pathMatcher.Combine("/hotel/", "/booking")); - } - - [Fact] - public void PatternComparator() - { - IComparer comparator = _pathMatcher.GetPatternComparer("/hotels/new"); - - Assert.Equal(0, comparator.Compare(null, null)); - Assert.Equal(1, comparator.Compare(null, "/hotels/new")); - Assert.Equal(-1, comparator.Compare("/hotels/new", null)); - - Assert.Equal(0, comparator.Compare("/hotels/new", "/hotels/new")); - - Assert.Equal(-1, comparator.Compare("/hotels/new", "/hotels/*")); - Assert.Equal(1, comparator.Compare("/hotels/*", "/hotels/new")); - Assert.Equal(0, comparator.Compare("/hotels/*", "/hotels/*")); - - Assert.Equal(-1, comparator.Compare("/hotels/new", "/hotels/{hotel}")); - Assert.Equal(1, comparator.Compare("/hotels/{hotel}", "/hotels/new")); - Assert.Equal(0, comparator.Compare("/hotels/{hotel}", "/hotels/{hotel}")); - Assert.Equal(-1, comparator.Compare("/hotels/{hotel}/booking", "/hotels/{hotel}/bookings/{booking}")); - Assert.Equal(1, comparator.Compare("/hotels/{hotel}/bookings/{booking}", "/hotels/{hotel}/booking")); - - // SPR-10550 - Assert.Equal(-1, comparator.Compare("/hotels/{hotel}/bookings/{booking}/customers/{customer}", "/**")); - Assert.Equal(1, comparator.Compare("/**", "/hotels/{hotel}/bookings/{booking}/customers/{customer}")); - Assert.Equal(0, comparator.Compare("/**", "/**")); - - Assert.Equal(-1, comparator.Compare("/hotels/{hotel}", "/hotels/*")); - Assert.Equal(1, comparator.Compare("/hotels/*", "/hotels/{hotel}")); - - Assert.Equal(-1, comparator.Compare("/hotels/*", "/hotels/*/**")); - Assert.Equal(1, comparator.Compare("/hotels/*/**", "/hotels/*")); - - Assert.Equal(-1, comparator.Compare("/hotels/new", "/hotels/new.*")); - Assert.Equal(2, comparator.Compare("/hotels/{hotel}", "/hotels/{hotel}.*")); - - // SPR-6741 - Assert.Equal(-1, comparator.Compare("/hotels/{hotel}/bookings/{booking}/customers/{customer}", "/hotels/**")); - Assert.Equal(1, comparator.Compare("/hotels/**", "/hotels/{hotel}/bookings/{booking}/customers/{customer}")); - Assert.Equal(1, comparator.Compare("/hotels/foo/bar/**", "/hotels/{hotel}")); - Assert.Equal(-1, comparator.Compare("/hotels/{hotel}", "/hotels/foo/bar/**")); - Assert.Equal(2, comparator.Compare("/hotels/**/bookings/**", "/hotels/**")); - Assert.Equal(-2, comparator.Compare("/hotels/**", "/hotels/**/bookings/**")); - - // SPR-8683 - Assert.Equal(1, comparator.Compare("/**", "/hotels/{hotel}")); - - // longer is better - Assert.Equal(1, comparator.Compare("/hotels", "/hotels2")); - - // SPR-13139 - Assert.Equal(-1, comparator.Compare("*", "*/**")); - Assert.Equal(1, comparator.Compare("*/**", "*")); - } - - [Fact] - public void PatternComparatorSort() - { - IComparer comparator = _pathMatcher.GetPatternComparer("/hotels/new"); - - var paths = new List(3); - - paths.Add(null); - paths.Add("/hotels/new"); - paths.Sort(comparator); - Assert.Equal("/hotels/new", paths[0]); - Assert.Null(paths[1]); - paths.Clear(); - - paths.Add("/hotels/new"); - paths.Add(null); - paths.Sort(comparator); - Assert.Equal("/hotels/new", paths[0]); - Assert.Null(paths[1]); - paths.Clear(); - - paths.Add("/hotels/*"); - paths.Add("/hotels/new"); - paths.Sort(comparator); - Assert.Equal("/hotels/new", paths[0]); - Assert.Equal("/hotels/*", paths[1]); - paths.Clear(); - - paths.Add("/hotels/new"); - paths.Add("/hotels/*"); - paths.Sort(comparator); - Assert.Equal("/hotels/new", paths[0]); - Assert.Equal("/hotels/*", paths[1]); - paths.Clear(); - - paths.Add("/hotels/**"); - paths.Add("/hotels/*"); - paths.Sort(comparator); - Assert.Equal("/hotels/*", paths[0]); - Assert.Equal("/hotels/**", paths[1]); - paths.Clear(); - - paths.Add("/hotels/*"); - paths.Add("/hotels/**"); - paths.Sort(comparator); - Assert.Equal("/hotels/*", paths[0]); - Assert.Equal("/hotels/**", paths[1]); - paths.Clear(); - - paths.Add("/hotels/{hotel}"); - paths.Add("/hotels/new"); - paths.Sort(comparator); - Assert.Equal("/hotels/new", paths[0]); - Assert.Equal("/hotels/{hotel}", paths[1]); - paths.Clear(); - - paths.Add("/hotels/new"); - paths.Add("/hotels/{hotel}"); - paths.Sort(comparator); - Assert.Equal("/hotels/new", paths[0]); - Assert.Equal("/hotels/{hotel}", paths[1]); - paths.Clear(); - - paths.Add("/hotels/*"); - paths.Add("/hotels/{hotel}"); - paths.Add("/hotels/new"); - paths.Sort(comparator); - Assert.Equal("/hotels/new", paths[0]); - Assert.Equal("/hotels/{hotel}", paths[1]); - Assert.Equal("/hotels/*", paths[2]); - paths.Clear(); - - paths.Add("/hotels/ne*"); - paths.Add("/hotels/n*"); - Shuffle(paths); - paths.Sort(comparator); - Assert.Equal("/hotels/ne*", paths[0]); - Assert.Equal("/hotels/n*", paths[1]); - paths.Clear(); - - comparator = _pathMatcher.GetPatternComparer("/hotels/new.html"); - paths.Add("/hotels/new.*"); - paths.Add("/hotels/{hotel}"); - Shuffle(paths); - paths.Sort(comparator); - Assert.Equal("/hotels/new.*", paths[0]); - Assert.Equal("/hotels/{hotel}", paths[1]); - paths.Clear(); - - comparator = _pathMatcher.GetPatternComparer("/web/endUser/action/login.html"); - paths.Add("/**/login.*"); - paths.Add("/**/endUser/action/login.*"); - paths.Sort(comparator); - Assert.Equal("/**/endUser/action/login.*", paths[0]); - Assert.Equal("/**/login.*", paths[1]); - paths.Clear(); - } - - private static void Shuffle(List list) - { - int n = list.Count; - - while (n > 1) - { - n--; - int k = Rng.Next(n + 1); - (list[k], list[n]) = (list[n], list[k]); - } - } -} diff --git a/src/Common/test/Common.Test/Util/AttributeAccessorTest.cs b/src/Common/test/Common.Test/Util/AttributeAccessorTest.cs deleted file mode 100644 index a93e8f83f5..0000000000 --- a/src/Common/test/Common.Test/Util/AttributeAccessorTest.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Xunit; - -namespace Steeltoe.Common.Test.Util; - -public sealed class AttributeAccessorTest -{ - private const string Name = "foo"; - - private const string Value = "bar"; - - private readonly SimpleAttributeAccessor _attributeAccessor = new(); - - [Fact] - public void SetAndGet() - { - _attributeAccessor.SetAttribute(Name, Value); - Assert.Equal(Value, _attributeAccessor.GetAttribute(Name)); - } - - [Fact] - public void SetAndHas() - { - Assert.False(_attributeAccessor.HasAttribute(Name)); - _attributeAccessor.SetAttribute(Name, Value); - Assert.True(_attributeAccessor.HasAttribute(Name)); - } - - [Fact] - public void Remove() - { - Assert.False(_attributeAccessor.HasAttribute(Name)); - _attributeAccessor.SetAttribute(Name, Value); - Assert.Equal(Value, _attributeAccessor.RemoveAttribute(Name)); - Assert.False(_attributeAccessor.HasAttribute(Name)); - } - - [Fact] - public void AttributeNames() - { - _attributeAccessor.SetAttribute(Name, Value); - _attributeAccessor.SetAttribute("abc", "123"); - string[] attributeNames = _attributeAccessor.AttributeNames; - Assert.Contains(Name, attributeNames); - Assert.Contains("abc", attributeNames); - } - - private sealed class SimpleAttributeAccessor : AbstractAttributeAccessor - { - } -} diff --git a/src/Common/test/Common.Test/Util/ClassUtilsTest.cs b/src/Common/test/Common.Test/Util/ClassUtilsTest.cs deleted file mode 100644 index 31281158c9..0000000000 --- a/src/Common/test/Common.Test/Util/ClassUtilsTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Xunit; - -namespace Steeltoe.Common.Test.Util; - -public sealed class ClassUtilsTest -{ - [Fact] - public void TestIsAssignable_Throws() - { - Assert.Throws(() => ClassUtils.IsAssignable(null, null)); - Assert.Throws(() => ClassUtils.IsAssignable(typeof(string), null)); - } - - [Fact] - public void TestIsAssignableValue_Throws() - { - Assert.Throws(() => ClassUtils.IsAssignableValue(null, null)); - } - - [Fact] - public void TestIsAssignableValue() - { - Assert.True(ClassUtils.IsAssignableValue(typeof(string), null)); - Assert.True(ClassUtils.IsAssignableValue(typeof(string), string.Empty)); - Assert.True(ClassUtils.IsAssignableValue(typeof(object), string.Empty)); - } - - [Fact] - public void TestIsAssignable() - { - Assert.True(ClassUtils.IsAssignable(typeof(object), typeof(object))); - Assert.True(ClassUtils.IsAssignable(typeof(string), typeof(string))); - Assert.True(ClassUtils.IsAssignable(typeof(object), typeof(string))); - Assert.True(ClassUtils.IsAssignable(typeof(object), typeof(int))); - Assert.True(ClassUtils.IsAssignable(typeof(int), typeof(int))); - Assert.True(ClassUtils.IsAssignable(typeof(int), typeof(int))); - Assert.False(ClassUtils.IsAssignable(typeof(string), typeof(object))); - Assert.False(ClassUtils.IsAssignable(typeof(int), typeof(double))); - Assert.False(ClassUtils.IsAssignable(typeof(double), typeof(int))); - } -} diff --git a/src/Common/test/Common.Test/Util/DefaultIdGeneratorTest.cs b/src/Common/test/Common.Test/Util/DefaultIdGeneratorTest.cs deleted file mode 100644 index 88b983b173..0000000000 --- a/src/Common/test/Common.Test/Util/DefaultIdGeneratorTest.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Xunit; - -namespace Steeltoe.Common.Test.Util; - -public sealed class DefaultIdGeneratorTest -{ - [Fact] - public void TestGenerateId() - { - Assert.NotEqual(Guid.Empty.ToString(), new DefaultIdGenerator().GenerateId()); - } -} diff --git a/src/Common/test/Common.Test/Util/MimeTypeTest.cs b/src/Common/test/Common.Test/Util/MimeTypeTest.cs deleted file mode 100644 index 3b3a13bd34..0000000000 --- a/src/Common/test/Common.Test/Util/MimeTypeTest.cs +++ /dev/null @@ -1,383 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Util; -using Xunit; - -namespace Steeltoe.Common.Test.Util; - -public sealed class MimeTypeTest -{ - [Fact] - public void SlashInSubtype() - { - Assert.Throws(() => new MimeType("text", "/")); - } - - [Fact] - public void ToMimeTypeNoSubtype() - { - Assert.Throws(() => MimeType.ToMimeType("audio")); - } - - [Fact] - public void ToMimeTypeNoSubtypeSlash() - { - Assert.Throws(() => MimeType.ToMimeType("audio/")); - } - - [Fact] - public void ToMimeTypeIllegalType() - { - Assert.Throws(() => MimeType.ToMimeType("audio(/basic")); - } - - [Fact] - public void ToMimeTypeIllegalSubtype() - { - Assert.Throws(() => MimeType.ToMimeType("audio/basic)")); - } - - [Fact] - public void ToMimeTypeIllegalCharset() - { - Assert.Throws(() => MimeType.ToMimeType("text/html; charset=foo-bar")); - } - - [Fact] - public void ParseCharset() - { - const string s = "text/html; charset=iso-8859-1"; - var mimeType = MimeType.ToMimeType(s); - Assert.Equal("text", mimeType.Type); - Assert.Equal("html", mimeType.Subtype); - Assert.Equal(Encoding.GetEncoding("ISO-8859-1"), mimeType.Encoding); - } - - [Fact] - public void ParseQuotedCharset() - { - const string s = "application/xml;charset=\"utf-8\""; - var mimeType = MimeType.ToMimeType(s); - Assert.Equal("application", mimeType.Type); - Assert.Equal("xml", mimeType.Subtype); - Assert.Equal(Encoding.UTF8.EncodingName, mimeType.Encoding.EncodingName); - } - - [Fact] - public void ParseQuotedSeparator() - { - const string s = "application/xop+xml;charset=utf-8;type=\"application/soap+xml;action=\\\"https://x.y.z\\\"\""; - var mimeType = MimeType.ToMimeType(s); - Assert.Equal("application", mimeType.Type); - Assert.Equal("xop+xml", mimeType.Subtype); - Assert.Equal(Encoding.UTF8.EncodingName, mimeType.Encoding.EncodingName); - Assert.Equal("\"application/soap+xml;action=\\\"https://x.y.z\\\"\"", mimeType.GetParameter("type")); - } - - [Fact] - public void Includes() - { - MimeType textPlain = MimeTypeUtils.TextPlain; - Assert.True(textPlain.Includes(textPlain)); - var allText = new MimeType("text"); - - Assert.True(allText.Includes(textPlain)); - Assert.False(textPlain.Includes(allText)); - - Assert.True(MimeTypeUtils.All.Includes(textPlain)); - Assert.False(textPlain.Includes(MimeTypeUtils.All)); - - Assert.True(MimeTypeUtils.All.Includes(textPlain)); - Assert.False(textPlain.Includes(MimeTypeUtils.All)); - - var applicationSoapXml = new MimeType("application", "soap+xml"); - var applicationWildcardXml = new MimeType("application", "*+xml"); - var suffixXml = new MimeType("application", "x.y+z+xml"); - - Assert.True(applicationSoapXml.Includes(applicationSoapXml)); - Assert.True(applicationWildcardXml.Includes(applicationWildcardXml)); - Assert.True(applicationWildcardXml.Includes(suffixXml)); - - Assert.True(applicationWildcardXml.Includes(applicationSoapXml)); - Assert.False(applicationSoapXml.Includes(applicationWildcardXml)); - Assert.False(suffixXml.Includes(applicationWildcardXml)); - - Assert.False(applicationWildcardXml.Includes(MimeTypeUtils.ApplicationJson)); - } - - [Fact] - public void IsCompatible() - { - MimeType textPlain = MimeTypeUtils.TextPlain; - Assert.True(textPlain.IsCompatibleWith(textPlain)); - var allText = new MimeType("text"); - - Assert.True(allText.IsCompatibleWith(textPlain)); - Assert.True(textPlain.IsCompatibleWith(allText)); - - Assert.True(MimeTypeUtils.All.IsCompatibleWith(textPlain)); - Assert.True(textPlain.IsCompatibleWith(MimeTypeUtils.All)); - - Assert.True(MimeTypeUtils.All.IsCompatibleWith(textPlain)); - Assert.True(textPlain.IsCompatibleWith(MimeTypeUtils.All)); - - var applicationSoapXml = new MimeType("application", "soap+xml"); - var applicationWildcardXml = new MimeType("application", "*+xml"); - var suffixXml = new MimeType("application", "x.y+z+xml"); // SPR-15795 - - Assert.True(applicationSoapXml.IsCompatibleWith(applicationSoapXml)); - Assert.True(applicationWildcardXml.IsCompatibleWith(applicationWildcardXml)); - Assert.True(applicationWildcardXml.IsCompatibleWith(suffixXml)); - - Assert.True(applicationWildcardXml.IsCompatibleWith(applicationSoapXml)); - Assert.True(applicationSoapXml.IsCompatibleWith(applicationWildcardXml)); - Assert.True(suffixXml.IsCompatibleWith(applicationWildcardXml)); - - Assert.False(applicationWildcardXml.IsCompatibleWith(MimeTypeUtils.ApplicationJson)); - } - - [Fact] - public void TestToString() - { - var mimeType = new MimeType("text", "plain"); - string result = mimeType.ToString(); - Assert.Equal("text/plain", result); - } - - [Fact] - public void ParseMimeType() - { - const string s = "audio/*"; - MimeType mimeType = MimeTypeUtils.ParseMimeType(s); - Assert.Equal("audio", mimeType.Type); - Assert.Equal("*", mimeType.Subtype); - } - - [Fact] - public void ParseMimeTypeNoSubtype() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("audio")); - } - - [Fact] - public void ParseMimeTypeNoSubtypeSlash() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("audio/")); - } - - [Fact] - public void ParseMimeTypeTypeRange() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("*/json")); - } - - [Fact] - public void ParseMimeTypeIllegalType() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("audio(/basic")); - } - - [Fact] - public void ParseMimeTypeIllegalSubtype() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("audio/basic)")); - } - - [Fact] - public void ParseMimeTypeMissingTypeAndSubtype() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType(" ;a=b")); - } - - [Fact] - public void ParseMimeTypeEmptyParameterAttribute() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("audio/*;=value")); - } - - [Fact] - public void ParseMimeTypeEmptyParameterValue() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("audio/*;attr=")); - } - - [Fact] - public void ParseMimeTypeIllegalParameterAttribute() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("audio/*;attr<=value")); - } - - [Fact] - public void ParseMimeTypeIllegalParameterValue() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("audio/*;attr=v>alue")); - } - - [Fact] - public void ParseMimeTypeIllegalCharset() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("text/html; charset=foo-bar")); - } - - [Fact] - public void ParseMimeTypeQuotedParameterValue() - { - MimeType mimeType = MimeTypeUtils.ParseMimeType("audio/*;attr=\"v>alue\""); - Assert.Equal("\"v>alue\"", mimeType.GetParameter("attr")); - } - - [Fact] - public void ParseMimeTypeSingleQuotedParameterValue() - { - MimeType mimeType = MimeTypeUtils.ParseMimeType("audio/*;attr='v>alue'"); - Assert.Equal("'v>alue'", mimeType.GetParameter("attr")); - } - - [Fact] - public void ParseMimeTypeWithSpacesAroundEquals() - { - MimeType mimeType = MimeTypeUtils.ParseMimeType("multipart/x-mixed-replace;boundary = --myboundary"); - Assert.Equal("--myboundary", mimeType.GetParameter("boundary")); - } - - [Fact] - public void ParseMimeTypeWithSpacesAroundEqualsAndQuotedValue() - { - MimeType mimeType = MimeTypeUtils.ParseMimeType("text/plain; foo = \" bar \" "); - Assert.Equal("\" bar \"", mimeType.GetParameter("foo")); - } - - [Fact] - public void ParseMimeTypeIllegalQuotedParameterValue() - { - Assert.Throws(() => MimeTypeUtils.ParseMimeType("audio/*;attr=\"")); - } - - [Fact] - public void ParseMimeTypes() - { - const string s = "text/plain, text/html, text/x-dvi, text/x-c"; - List mimeTypes = MimeTypeUtils.ParseMimeTypes(s); - Assert.NotNull(mimeTypes); - Assert.Equal(4, mimeTypes.Count); - - mimeTypes = MimeTypeUtils.ParseMimeTypes(null); - Assert.NotNull(mimeTypes); - Assert.Empty(mimeTypes); - } - - [Fact] - public void ParseMimeTypesWithQuotedParameters() - { - TestWithQuotedParameters("foo/bar;param=\",\""); - TestWithQuotedParameters("foo/bar;param=\"s,a,\""); - TestWithQuotedParameters("foo/bar;param=\"s,\"", "text/x-c"); - TestWithQuotedParameters("foo/bar;param=\"a\\\"b,c\""); - TestWithQuotedParameters("foo/bar;param=\"\\\\\""); - TestWithQuotedParameters("foo/bar;param=\"\\,\\\""); - } - - [Fact] - public void CompareTo() - { - var audioBasic = new MimeType("audio", "basic"); - var audio = new MimeType("audio"); - var audioWave = new MimeType("audio", "wave"); - var audioBasicLevel = new MimeType("audio", "basic", SingletonMap("level", "1")); - - // equal - Assert.Equal(0, audioBasic.CompareTo(audioBasic)); - Assert.Equal(0, audio.CompareTo(audio)); - Assert.Equal(0, audioBasicLevel.CompareTo(audioBasicLevel)); - - Assert.True(audioBasicLevel.CompareTo(audio) > 0); - - var expected = new List - { - audio, - audioBasic, - audioBasicLevel, - audioWave - }; - - var result = new List(expected); - - // shuffle & sort 10 times - for (int i = 0; i < 10; i++) - { - Shuffle(result); - result.Sort(); - - for (int j = 0; j < result.Count; j++) - { - Assert.Same(expected[j], result[j]); - } - } - } - - [Fact] - public void CompareToCaseSensitivity() - { - var m1 = new MimeType("audio", "basic"); - var m2 = new MimeType("Audio", "Basic"); - Assert.Equal(0, m1.CompareTo(m2)); - Assert.Equal(0, m2.CompareTo(m1)); - - m1 = new MimeType("audio", "basic", SingletonMap("foo", "bar")); - m2 = new MimeType("audio", "basic", SingletonMap("Foo", "bar")); - Assert.Equal(0, m1.CompareTo(m2)); - Assert.Equal(0, m2.CompareTo(m1)); - - m1 = new MimeType("audio", "basic", SingletonMap("foo", "bar")); - m2 = new MimeType("audio", "basic", SingletonMap("foo", "Bar")); - Assert.True(m1.CompareTo(m2) != 0); - Assert.True(m2.CompareTo(m1) != 0); - } - - [Fact] - public void EqualsIsCaseInsensitiveForCharsets() - { - var m1 = new MimeType("text", "plain", SingletonMap("charset", "UTF-8")); - var m2 = new MimeType("text", "plain", SingletonMap("charset", "utf-8")); - Assert.Equal(m1, m2); - Assert.Equal(m2, m1); - Assert.Equal(0, m1.CompareTo(m2)); - Assert.Equal(0, m2.CompareTo(m1)); - } - - private void TestWithQuotedParameters(params string[] mimeTypes) - { - string s = string.Join(",", mimeTypes); - List actual = MimeTypeUtils.ParseMimeTypes(s); - Assert.Equal(mimeTypes.Length, actual.Count); - - for (int i = 0; i < mimeTypes.Length; i++) - { - Assert.Equal(mimeTypes[i], actual[i].ToString()); - } - } - - private IDictionary SingletonMap(string key, string value) - { - return new Dictionary - { - { key, value } - }; - } - - private void Shuffle(IList list) - { - for (int i = 0; i < list.Count - 1; i++) - { - Swap(list, i, Random.Shared.Next(i, list.Count)); - } - } - - private void Swap(IList list, int i, int j) - { - (list[i], list[j]) = (list[j], list[i]); - } -} diff --git a/src/Common/test/Common.Test/Util/ObjectEqualityTest.cs b/src/Common/test/Common.Test/Util/ObjectEqualityTest.cs deleted file mode 100644 index b979ed8e94..0000000000 --- a/src/Common/test/Common.Test/Util/ObjectEqualityTest.cs +++ /dev/null @@ -1,158 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Util; -using Xunit; - -namespace Steeltoe.Common.Test.Util; - -public sealed class ObjectEqualityTest -{ - [Fact] - public void EqualsWithArrays() - { - Assert.True(ObjectEquality.ObjectOrCollectionEquals(new[] - { - "a", - "b", - "c" - }, new[] - { - "a", - "b", - "c" - })); - - Assert.True(ObjectEquality.ObjectOrCollectionEquals(new[] - { - 1, - 2, - 3 - }, new[] - { - 1, - 2, - 3 - })); - } - - [Fact] - public void NullSafeHashCodeWithCharArray() - { - char[] array = - { - 'a', - 'E' - }; - - int expected = ComputeCollectionHashCode(array); - int actual = ObjectEquality.GetObjectOrCollectionHashCode(array); - - Assert.Equal(expected, actual); - } - - [Fact] - public void NullSafeHashCodeWithCharArrayEqualToNull() - { - const char[] value = null; - Assert.Equal(0, ObjectEquality.GetObjectOrCollectionHashCode(value)); - } - - [Fact] - public void NullSafeHashCodeWithDoubleArray() - { - double[] array = - { - 8449.65, - 9944.923 - }; - - int expected = ComputeCollectionHashCode(array); - int actual = ObjectEquality.GetObjectOrCollectionHashCode(array); - - Assert.Equal(expected, actual); - } - - [Fact] - public void NullSafeHashCodeWithDoubleArrayEqualToNull() - { - const double[] value = null; - Assert.Equal(0, ObjectEquality.GetObjectOrCollectionHashCode(value)); - } - - [Fact] - public void NullSafeHashCodeWithObjectArray() - { - object[] array = - { - "Leia", - "Han" - }; - - int expected = ComputeCollectionHashCode(array); - int actual = ObjectEquality.GetObjectOrCollectionHashCode(array); - - Assert.Equal(expected, actual); - } - - [Fact] - public void NullSafeHashCodeWithObjectArrayEqualToNull() - { - const object[] value = null; - Assert.Equal(0, ObjectEquality.GetObjectOrCollectionHashCode(value)); - } - - [Fact] - public void NullSafeHashCodeWithObjectBeingBooleanArray() - { - object array = new[] - { - true, - false - }; - - int expected = ComputeCollectionHashCode((bool[])array); - AssertEqualHashCodes(expected, array); - } - - [Fact] - public void NullSafeHashCodeWithObjectBeingObjectArray() - { - object array = new object[] - { - "Luke", - "Anakin" - }; - - int expected = ComputeCollectionHashCode((object[])array); - AssertEqualHashCodes(expected, array); - } - - [Fact] - public void NullSafeHashCodeWithObjectEqualToNull() - { - const object value = null; - Assert.Equal(0, ObjectEquality.GetObjectOrCollectionHashCode(value)); - } - - private void AssertEqualHashCodes(int expected, object array) - { - int actual = ObjectEquality.GetObjectOrCollectionHashCode(array); - Assert.Equal(expected, actual); - Assert.True(array.GetHashCode() != actual); - } - - private int ComputeCollectionHashCode(IEnumerable enumerable) - { - HashCode hashCode = default; - - foreach (object item in enumerable) - { - hashCode.Add(item); - } - - return hashCode.ToHashCode(); - } -} diff --git a/src/Common/test/Common.Test/Util/PatternMatchUtilsTest.cs b/src/Common/test/Common.Test/Util/PatternMatchUtilsTest.cs deleted file mode 100644 index 5d6060402f..0000000000 --- a/src/Common/test/Common.Test/Util/PatternMatchUtilsTest.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Xunit; - -namespace Steeltoe.Common.Test.Util; - -public sealed class PatternMatchUtilsTest -{ - [Fact] - public void TestTrivial() - { - Assert.False(PatternMatchUtils.SimpleMatch((string)null, string.Empty)); - Assert.False(PatternMatchUtils.SimpleMatch("1", null)); - DoTest("*", "123", true); - DoTest("123", "123", true); - } - - [Fact] - public void TestStartsWith() - { - DoTest("get*", "getMe", true); - DoTest("get*", "setMe", false); - } - - [Fact] - public void TestEndsWith() - { - DoTest("*Test", "getMeTest", true); - DoTest("*Test", "setMe", false); - } - - [Fact] - public void TestBetween() - { - DoTest("*stuff*", "getMeTest", false); - DoTest("*stuff*", "getstuffTest", true); - DoTest("*stuff*", "stuffTest", true); - DoTest("*stuff*", "getstuff", true); - DoTest("*stuff*", "stuff", true); - } - - [Fact] - public void TestStartsEnds() - { - DoTest("on*Event", "onMyEvent", true); - DoTest("on*Event", "onEvent", true); - DoTest("3*3", "3", false); - DoTest("3*3", "33", true); - } - - [Fact] - public void TestStartsEndsBetween() - { - DoTest("12*45*78", "12345678", true); - DoTest("12*45*78", "123456789", false); - DoTest("12*45*78", "012345678", false); - DoTest("12*45*78", "124578", true); - DoTest("12*45*78", "1245457878", true); - DoTest("3*3*3", "33", false); - DoTest("3*3*3", "333", true); - } - - [Fact] - public void TestRidiculous() - { - DoTest("*1*2*3*", "0011002001010030020201030", true); - DoTest("1*2*3*4", "10300204", false); - DoTest("1*2*3*3", "10300203", false); - DoTest("*1*2*3*", "123", true); - DoTest("*1*2*3*", "132", false); - } - - [Fact] - public void TestPatternVariants() - { - DoTest("*a", "*", false); - DoTest("*a", "a", true); - DoTest("*a", "b", false); - DoTest("*a", "aa", true); - DoTest("*a", "ba", true); - DoTest("*a", "ab", false); - DoTest("**a", "*", false); - DoTest("**a", "a", true); - DoTest("**a", "b", false); - DoTest("**a", "aa", true); - DoTest("**a", "ba", true); - DoTest("**a", "ab", false); - } - - private void DoTest(string pattern, string str, bool shouldMatch) - { - Assert.Equal(shouldMatch, PatternMatchUtils.SimpleMatch(pattern, str)); - } -} diff --git a/src/Common/test/Common.TestResources/CapturingLoggerProvider.cs b/src/Common/test/Common.TestResources/CapturingLoggerProvider.cs deleted file mode 100644 index 462739c469..0000000000 --- a/src/Common/test/Common.TestResources/CapturingLoggerProvider.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Common.TestResources; - -public sealed class CapturingLoggerProvider : ILoggerProvider, ILogger -{ - private readonly ConcurrentBag _messages = new(); - - public ILogger CreateLogger(string categoryName) - { - return this; - } - - public IDisposable BeginScope(TState state) - { - return EmptyDisposable.Instance; - } - - public bool IsEnabled(LogLevel logLevel) - { - return true; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - string message = $"{logLevel.ToString().ToUpperInvariant()}: {formatter(state, exception)}"; - _messages.Add(message); - } - - public IEnumerable GetMessages() - { - return _messages.ToList(); - } - - public void Dispose() - { - // Intentionally left empty. - } -} diff --git a/src/Common/test/Common.TestResources/XunitLogger.cs b/src/Common/test/Common.TestResources/XunitLogger.cs deleted file mode 100644 index 1ff04dbc7d..0000000000 --- a/src/Common/test/Common.TestResources/XunitLogger.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Xunit.Abstractions; - -namespace Steeltoe.Common.TestResources; - -public sealed class XunitLogger : ILogger -{ - private readonly ITestOutputHelper _output; - - public XunitLogger(ITestOutputHelper output) - { - _output = output; - LoggerFactory.Create(builder => builder.AddConsole()); - } - - public IDisposable BeginScope(TState state) - { - return EmptyDisposable.Instance; - } - - public bool IsEnabled(LogLevel logLevel) - { - return logLevel != LogLevel.None; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - _output.WriteLine(formatter(state, exception)); - } -} diff --git a/src/Common/test/Common.TestResources/XunitLoggerFactory.cs b/src/Common/test/Common.TestResources/XunitLoggerFactory.cs deleted file mode 100644 index 9010094bfe..0000000000 --- a/src/Common/test/Common.TestResources/XunitLoggerFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Xunit.Abstractions; - -namespace Steeltoe.Common.TestResources; - -public sealed class XunitLoggerFactory : ILoggerFactory -{ - private readonly ITestOutputHelper _output; - private readonly ILoggerFactory _factory; - - public XunitLoggerFactory(ITestOutputHelper output) - { - _output = output; - - _factory = LoggerFactory.Create(builder => - { - builder.AddDebug(); - }); - } - - public void AddProvider(ILoggerProvider provider) - { - throw new NotImplementedException(); - } - - public ILogger CreateLogger(string categoryName) - { - return new XunitLogger(_output); - } - - public void Dispose() - { - _factory.Dispose(); - } -} diff --git a/src/Common/test/Common.Utils.Test/Diagnostics/CommandExecutorTest.cs b/src/Common/test/Common.Utils.Test/Diagnostics/CommandExecutorTest.cs deleted file mode 100644 index 40e6f4d5ef..0000000000 --- a/src/Common/test/Common.Utils.Test/Diagnostics/CommandExecutorTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using FluentAssertions; -using Steeltoe.Common.Utils.Diagnostics; -using Xunit; -using Xunit.Sdk; - -namespace Steeltoe.Common.Utils.Test.Diagnostics; - -public sealed class CommandExecutorTest -{ - [Fact] - public async Task SuccessfulCommandShouldReturn0() - { - var executor = new CommandExecutor(); - - CommandResult result = await executor.ExecuteAsync("dotnet --help"); - - result.ExitCode.Should().Be(0); - result.Output.Should().Contain("Usage: dotnet"); - } - - [Fact] - public async Task UnsuccessfulCommandShouldNotReturn0() - { - var executor = new CommandExecutor(); - - CommandResult result = await executor.ExecuteAsync("dotnet --no-such-option"); - - result.ExitCode.Should().NotBe(0); - - try - { - result.Error.Should().Contain("Unknown option: --no-such-option"); - } - catch (XunitException) - { - // message changes if .NET 6 sdk is installed - result.Error.Should().Contain("--no-such-option does not exist"); - } - } - - [Fact] - public async Task UnknownCommandShouldThrowException() - { - var executor = new CommandExecutor(); - - Func act = async () => - { - await executor.ExecuteAsync("no-such-command"); - }; - - await act.Should().ThrowExactlyAsync().WithMessage("'no-such-command' failed to start*"); - } -} diff --git a/src/Configuration/src/CloudFoundry.ServiceBinding/Properties/AssemblyInfo.cs b/src/Configuration/src/CloudFoundry.ServiceBinding/Properties/AssemblyInfo.cs index 1508f98658..99d2718f34 100644 --- a/src/Configuration/src/CloudFoundry.ServiceBinding/Properties/AssemblyInfo.cs +++ b/src/Configuration/src/CloudFoundry.ServiceBinding/Properties/AssemblyInfo.cs @@ -7,5 +7,3 @@ [assembly: InternalsVisibleTo("Steeltoe.Bootstrap.AutoConfiguration.Test")] [assembly: InternalsVisibleTo("Steeltoe.Configuration.CloudFoundry.ServiceBinding.Test")] [assembly: InternalsVisibleTo("Steeltoe.Connectors.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Messaging.RabbitMQ.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Stream.Test")] diff --git a/src/Configuration/src/SpringBoot/Properties/AssemblyInfo.cs b/src/Configuration/src/SpringBoot/Properties/AssemblyInfo.cs index 57077a71b0..2c0f0be049 100644 --- a/src/Configuration/src/SpringBoot/Properties/AssemblyInfo.cs +++ b/src/Configuration/src/SpringBoot/Properties/AssemblyInfo.cs @@ -5,4 +5,3 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Steeltoe.Configuration.SpringBoot.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Stream.Test")] diff --git a/src/Integration/src/Abstractions/Acks/IAcknowledgmentCallback.cs b/src/Integration/src/Abstractions/Acks/IAcknowledgmentCallback.cs deleted file mode 100644 index 92dedeb28e..0000000000 --- a/src/Integration/src/Abstractions/Acks/IAcknowledgmentCallback.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Acks; - -/// -/// General abstraction over acknowledgments. -/// -public interface IAcknowledgmentCallback -{ - /// - /// Gets or sets a value indicating whether the ack has been processed by the user so that the framework can auto-ack if needed. - /// - bool IsAcknowledged { get; set; } - - /// - /// Gets or sets a value indicating whether return true if this acknowledgment supports auto ack when it has not been already ack'd by the application. - /// - bool IsAutoAck { get; set; } - - /// - /// Acknowledge the message. - /// - /// - /// true if the message is already acked. - /// - void Acknowledge(Status status); -} diff --git a/src/Integration/src/Abstractions/Acks/IAcknowledgmentCallbackFactory.cs b/src/Integration/src/Abstractions/Acks/IAcknowledgmentCallbackFactory.cs deleted file mode 100644 index 52f39f3c47..0000000000 --- a/src/Integration/src/Abstractions/Acks/IAcknowledgmentCallbackFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Acks; - -public interface IAcknowledgmentCallbackFactory -{ - IAcknowledgmentCallback CreateCallback(T info); -} diff --git a/src/Integration/src/Abstractions/Acks/Status.cs b/src/Integration/src/Abstractions/Acks/Status.cs deleted file mode 100644 index 7555502838..0000000000 --- a/src/Integration/src/Abstractions/Acks/Status.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Acks; - -/// -/// AcknowledgmentCallback status values. -/// -public enum Status -{ - /// - /// Mark message as accepted. - /// - Accept, - - /// - /// Mark message as rejected. - /// - Reject, - - /// - /// Reject message and requeue. - /// - Requeue -} diff --git a/src/Integration/src/Abstractions/Channel/IChannelInterceptorAware.cs b/src/Integration/src/Abstractions/Channel/IChannelInterceptorAware.cs deleted file mode 100644 index c9c1a6a012..0000000000 --- a/src/Integration/src/Abstractions/Channel/IChannelInterceptorAware.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Channel; - -/// -/// A marker interface providing the ability to configure ChannelInterceptors on MessageChannel implementations. -/// -public interface IChannelInterceptorAware -{ - /// - /// Gets or sets the channel interceptors. - /// - List ChannelInterceptors { get; set; } - - /// - /// Add a interceptor to the channel. - /// - /// - /// the interceptor. - /// - void AddInterceptor(IChannelInterceptor interceptor); - - /// - /// Add an interceptor to the channel at the specified index. - /// - /// - /// the index to add the interceptor at. - /// - /// - /// the interceptor. - /// - void AddInterceptor(int index, IChannelInterceptor interceptor); - - /// - /// Remove an interceptor from the channel. - /// - /// - /// the interceptor. - /// - /// - /// success or failure. - /// - bool RemoveInterceptor(IChannelInterceptor interceptor); - - /// - /// Remove an interceptor at the specified index. - /// - /// - /// the index. - /// - /// - /// removed interceptor. - /// - IChannelInterceptor RemoveInterceptor(int index); -} diff --git a/src/Integration/src/Abstractions/Channel/IQueueChannelOperations.cs b/src/Integration/src/Abstractions/Channel/IQueueChannelOperations.cs deleted file mode 100644 index ee87453855..0000000000 --- a/src/Integration/src/Abstractions/Channel/IQueueChannelOperations.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Channel; - -/// -/// Operations available on a channel that has queuing semantics. -/// -public interface IQueueChannelOperations -{ - /// - /// Gets the size of the queue. - /// - int QueueSize { get; } - - /// - /// Gets the remaining capacity of the queue. - /// - int RemainingCapacity { get; } - - /// - /// Clear all items off the queue. - /// - /// - /// list of removed messages. - /// - IList Clear(); - - /// - /// Remove any Messages that are not accepted by the provided selector. - /// - /// - /// the selector to apply. - /// - /// - /// list of purged messages. - /// - IList Purge(IMessageSelector messageSelector); -} diff --git a/src/Integration/src/Abstractions/Channel/ITaskSchedulerChannelInterceptorAware.cs b/src/Integration/src/Abstractions/Channel/ITaskSchedulerChannelInterceptorAware.cs deleted file mode 100644 index f9933d1bc6..0000000000 --- a/src/Integration/src/Abstractions/Channel/ITaskSchedulerChannelInterceptorAware.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Channel; - -/// -/// Interface implemented by task scheduler based channels. -/// -public interface ITaskSchedulerChannelInterceptorAware : IChannelInterceptorAware -{ - /// - /// Gets a value indicating whether there are any task scheduler interceptors on the channel. - /// - bool HasTaskSchedulerInterceptors { get; } -} diff --git a/src/Integration/src/Abstractions/Configuration/IMethodAttributeProcessor.cs b/src/Integration/src/Abstractions/Configuration/IMethodAttributeProcessor.cs deleted file mode 100644 index d9ee2c0727..0000000000 --- a/src/Integration/src/Abstractions/Configuration/IMethodAttributeProcessor.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Integration.Configuration; - -#pragma warning disable S2326 // Unused type parameters should be removed -public interface IMethodAttributeProcessor -#pragma warning restore S2326 // Unused type parameters should be removed - where TAttribute : Attribute -{ - object PostProcess(object service, string serviceName, MethodInfo method, List attributes); - - bool ShouldCreateEndpoint(MethodInfo method, List attributes); -} diff --git a/src/Integration/src/Abstractions/Dispatcher/ILoadBalancingStrategy.cs b/src/Integration/src/Abstractions/Dispatcher/ILoadBalancingStrategy.cs deleted file mode 100644 index 16e1f39258..0000000000 --- a/src/Integration/src/Abstractions/Dispatcher/ILoadBalancingStrategy.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Dispatcher; - -/// -/// Strategy for determining the iteration order of a MessageHandler list. -/// -public interface ILoadBalancingStrategy -{ - /// - /// Gets the next index to be used in selecting a handler from the provided list of handlers. - /// - /// - /// the message to be processed. - /// - /// - /// the current list of handlers. - /// - /// - /// an index into the handler list at which to start load balancing. - /// - int GetNextHandlerStartIndex(IMessage message, List handlers); -} diff --git a/src/Integration/src/Abstractions/Dispatcher/IMessageDispatcher.cs b/src/Integration/src/Abstractions/Dispatcher/IMessageDispatcher.cs deleted file mode 100644 index b58083b486..0000000000 --- a/src/Integration/src/Abstractions/Dispatcher/IMessageDispatcher.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Dispatcher; - -/// -/// Strategy interface for dispatching messages to handlers. -/// -public interface IMessageDispatcher -{ - /// - /// Gets the current handler count. - /// - int HandlerCount { get; } - - /// - /// Gets or sets the maximum number of subscribers this dispatcher supports. - /// - int MaxSubscribers { get; set; } - - /// - /// Gets or sets the load balancing strategy in use by the dispatcher. - /// - ILoadBalancingStrategy LoadBalancingStrategy { get; set; } - - /// - /// Gets or sets a value indicating whether this dispatcher should failover upon a dispatching error. - /// - bool Failover { get; set; } - - /// - /// Gets or sets the message handling decorator that should be applied to the message for processing. - /// - IMessageHandlingDecorator MessageHandlingDecorator { get; set; } - - /// - /// Adds a handler to the dispatcher. - /// - /// - /// the handler to add. - /// - /// - /// true if added. - /// - bool AddHandler(IMessageHandler handler); - - /// - /// Remove the specified handler from the dispatcher. - /// - /// - /// the handler to remove. - /// - /// - /// true if removed. - /// - bool RemoveHandler(IMessageHandler handler); - - /// - /// Dispatch the message to one or more handlers. - /// - /// - /// the message to dispatch. - /// - /// - /// token used to cancel the operation. - /// - /// - /// the value returned from the handler. - /// - bool Dispatch(IMessage message, CancellationToken cancellationToken = default); -} diff --git a/src/Integration/src/Abstractions/Dispatcher/IMessageHandlingDecorator.cs b/src/Integration/src/Abstractions/Dispatcher/IMessageHandlingDecorator.cs deleted file mode 100644 index 5642deba79..0000000000 --- a/src/Integration/src/Abstractions/Dispatcher/IMessageHandlingDecorator.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Dispatcher; - -/// -/// The strategy to decorate message handling tasks. -/// -public interface IMessageHandlingDecorator -{ - /// - /// Decorate the incoming message handling runnable (task). - /// - /// - /// incoming message handling task. - /// - /// - /// the newly decorated task. - /// - IMessageHandlingRunnable Decorate(IMessageHandlingRunnable messageHandlingRunnable); -} diff --git a/src/Integration/src/Abstractions/Handler/IHeaderPropagation.cs b/src/Integration/src/Abstractions/Handler/IHeaderPropagation.cs deleted file mode 100644 index 531ea6d7f9..0000000000 --- a/src/Integration/src/Abstractions/Handler/IHeaderPropagation.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Handler; - -/// -/// MessageHandlers implementing this interface can propagate headers from an input message to an output message. -/// -public interface IHeaderPropagation -{ - /// - /// Gets or sets the headers that should not be copied from inbound message if handler is configured to copy headers. - /// - IList NotPropagatedHeaders { get; set; } - - /// - /// Add headers that will not be copied from the inbound message if handler is configured to copy headers. - /// - /// - /// the headers to not copy. - /// - void AddNotPropagatedHeaders(params string[] headers); -} diff --git a/src/Integration/src/Abstractions/Handler/IMessageProcessor.cs b/src/Integration/src/Abstractions/Handler/IMessageProcessor.cs deleted file mode 100644 index 24211a07bc..0000000000 --- a/src/Integration/src/Abstractions/Handler/IMessageProcessor.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -/// -/// This defines the lowest-level strategy of processing a Message and returning some Object(or null). Implementations will be focused on generic -/// concerns, such as invoking a method, running a script, or evaluating an expression. -/// -public interface IMessageProcessor -{ - /// - /// Process a message and return a value or null. - /// - /// - /// message to process. - /// - /// - /// resulting object. - /// - object ProcessMessage(IMessage message); -} - -/// -/// This defines the lowest-level strategy of processing a Message and returning some Object(or null). Implementations will be focused on generic -/// concerns, such as invoking a method, running a script, or evaluating an expression. -/// -/// -/// the type of the processing result. -/// -public interface IMessageProcessor : IMessageProcessor -{ - /// - /// Process a message and return a value or null. - /// - /// - /// message to process. - /// - /// - /// result after processing. - /// - new T ProcessMessage(IMessage message); -} diff --git a/src/Integration/src/Abstractions/IIntegrationServices.cs b/src/Integration/src/Abstractions/IIntegrationServices.cs deleted file mode 100644 index 9f60c20b37..0000000000 --- a/src/Integration/src/Abstractions/IIntegrationServices.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Converter; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; - -namespace Steeltoe.Integration; - -/// -/// A group of commonly used services used by the integration components. -/// -public interface IIntegrationServices -{ - /// - /// Gets or sets the current message builder factory. - /// - IMessageBuilderFactory MessageBuilderFactory { get; set; } - - /// - /// Gets or sets the current channel resolver. - /// - IDestinationResolver ChannelResolver { get; set; } - - /// - /// Gets or sets the current conversion service. - /// - IConversionService ConversionService { get; set; } - - /// - /// Gets or sets the current id generator. - /// - IIdGenerator IdGenerator { get; set; } - - /// - /// Gets or sets the current id expression parser. - /// - IExpressionParser ExpressionParser { get; set; } -} diff --git a/src/Integration/src/Abstractions/IMessageProducer.cs b/src/Integration/src/Abstractions/IMessageProducer.cs deleted file mode 100644 index 8be6be5979..0000000000 --- a/src/Integration/src/Abstractions/IMessageProducer.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -/// -/// Base interface for any component that can create and send messages. -/// -public interface IMessageProducer -{ - /// - /// Gets or sets the output channel the producer uses. - /// - IMessageChannel OutputChannel { get; set; } - - /// - /// Gets or sets the output channel name the producer uses. - /// - string OutputChannelName { get; set; } -} diff --git a/src/Integration/src/Abstractions/IMessageSelector.cs b/src/Integration/src/Abstractions/IMessageSelector.cs deleted file mode 100644 index 4f569a4188..0000000000 --- a/src/Integration/src/Abstractions/IMessageSelector.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -public interface IMessageSelector : ISelector -{ -} diff --git a/src/Integration/src/Abstractions/IMessageSource.cs b/src/Integration/src/Abstractions/IMessageSource.cs deleted file mode 100644 index eefa71db6d..0000000000 --- a/src/Integration/src/Abstractions/IMessageSource.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -/// -/// Base interface for any source of Messages that can be polled. -/// -public interface IMessageSource -{ - /// - /// Poll for a message from the source. - /// - /// - /// the message. - /// - IMessage Receive(); -} - -/// -/// A typed interface for any source of Messages that can be polled. -/// -/// -/// the type of payload in the message. -/// -public interface IMessageSource : IMessageSource -{ - /// - /// Poll for a message from the source. - /// - /// - /// the message. - /// - new IMessage Receive(); -} diff --git a/src/Integration/src/Abstractions/ISelector.cs b/src/Integration/src/Abstractions/ISelector.cs deleted file mode 100644 index f44462433b..0000000000 --- a/src/Integration/src/Abstractions/ISelector.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration; - -public interface ISelector -{ - bool Accept(T source); -} diff --git a/src/Integration/src/Abstractions/Mapping/IRequestReplyHeaderMapper.cs b/src/Integration/src/Abstractions/Mapping/IRequestReplyHeaderMapper.cs deleted file mode 100644 index 6abecd3cd7..0000000000 --- a/src/Integration/src/Abstractions/Mapping/IRequestReplyHeaderMapper.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Mapping; - -public interface IRequestReplyHeaderMapper -{ - void FromHeadersToRequest(IMessageHeaders headers, T target); - - void FromHeadersToReply(IMessageHeaders headers, T target); - - IDictionary ToHeadersFromRequest(T source); - - IDictionary ToHeadersFromReply(T source); -} diff --git a/src/Integration/src/Abstractions/Router/IMessageRouter.cs b/src/Integration/src/Abstractions/Router/IMessageRouter.cs deleted file mode 100644 index 51ae165224..0000000000 --- a/src/Integration/src/Abstractions/Router/IMessageRouter.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Router; - -/// -/// Routers implementing this interface have a default output channel. -/// -public interface IMessageRouter -{ - /// - /// Gets the default output channel. - /// - IMessageChannel DefaultOutputChannel { get; } -} diff --git a/src/Integration/src/Abstractions/Steeltoe.Integration.Abstractions.csproj b/src/Integration/src/Abstractions/Steeltoe.Integration.Abstractions.csproj deleted file mode 100644 index 3460054492..0000000000 --- a/src/Integration/src/Abstractions/Steeltoe.Integration.Abstractions.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - net8.0;net6.0 - Steeltoe.Integration - - Abstractions for use with Steeltoe Integration libraries - **PLEASE NOTE** The public API of this package is subject to change and is not recommended for direct use outside of Steeltoe. - - - true - - - - - - - - diff --git a/src/Integration/src/Abstractions/Support/Channel/IHeaderChannelRegistry.cs b/src/Integration/src/Abstractions/Support/Channel/IHeaderChannelRegistry.cs deleted file mode 100644 index f780b7d46f..0000000000 --- a/src/Integration/src/Abstractions/Support/Channel/IHeaderChannelRegistry.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support.Channel; - -/// -/// Implementations convert a channel to a name, retaining a reference to the channel keyed by the name. -/// -public interface IHeaderChannelRegistry -{ - /// - /// Converts the channel to a name (String). If the channel is not a MessageChannel, it is returned unchanged. - /// - /// - /// the channel. - /// - /// - /// the channel name or the channel if not a MessageChannel. - /// - object ChannelToChannelName(object channel); - - /// - /// Converts the channel to a name (String). If the channel is not a MessageChannel, it is returned unchanged. - /// - /// - /// the channel. - /// - /// - /// how long in milliseconds the channel mapping should remain in registry. - /// - /// - /// the channel name or the channel if not a MessageChannel. - /// - object ChannelToChannelName(object channel, long timeToLive); - - /// - /// Converts the channel name back to a MessageChannel (if it is registered). - /// - /// - /// the channel name. - /// - /// - /// The channel, or null if there is no channel registered with the name. - /// - IMessageChannel ChannelNameToChannel(string name); -} diff --git a/src/Integration/src/Abstractions/Support/IErrorMessageStrategy.cs b/src/Integration/src/Abstractions/Support/IErrorMessageStrategy.cs deleted file mode 100644 index 35d1100c4f..0000000000 --- a/src/Integration/src/Abstractions/Support/IErrorMessageStrategy.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Support; - -/// -/// A strategy to build an ErrorMessage based on the provided Exception AttributeAccessor as a context. -/// -public interface IErrorMessageStrategy -{ - /// - /// Build the error message. - /// - /// - /// the payload of the error message. - /// - /// - /// the context to use. - /// - /// - /// the error message. - /// - ErrorMessage BuildErrorMessage(Exception exception, IAttributeAccessor attributeAccessor); -} diff --git a/src/Integration/src/Abstractions/Support/IMessageBuilder.cs b/src/Integration/src/Abstractions/Support/IMessageBuilder.cs deleted file mode 100644 index 15dfb509a7..0000000000 --- a/src/Integration/src/Abstractions/Support/IMessageBuilder.cs +++ /dev/null @@ -1,495 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -/// -/// A message builder that creates immutable GenericMessages. -/// -public interface IMessageBuilder -{ - /// - /// Gets the payload of the message. - /// - object Payload { get; } - - /// - /// Gets the current headers of the message. - /// - IDictionary Headers { get; } - - /// - /// Adds an expiration date in the headers. - /// - /// - /// expiration date added to header. - /// - /// - /// the builder. - /// - IMessageBuilder SetExpirationDate(long expirationDate); - - /// - /// Adds an expiration date in the headers. - /// - /// - /// expiration date added to header. - /// - /// - /// the builder. - /// - IMessageBuilder SetExpirationDate(DateTime? expirationDate); - - /// - /// Adds the correlationId to the headers. - /// - /// - /// the id to add. - /// - /// - /// the builder. - /// - IMessageBuilder SetCorrelationId(object correlationId); - - /// - /// Adds a sequence details header to the message. - /// - /// - /// correlation id to use in sequence. - /// - /// - /// the sequence number. - /// - /// - /// the size of the sequence number. - /// - /// - /// the builder. - /// - IMessageBuilder PushSequenceDetails(object correlationId, int sequenceNumber, int sequenceSize); - - /// - /// Removes a sequence details header from the message. - /// - /// - /// the builder. - /// - IMessageBuilder PopSequenceDetails(); - - /// - /// Adds a reply channel to the message. - /// - /// - /// the reply channel. - /// - /// - /// the builder. - /// - IMessageBuilder SetReplyChannel(IMessageChannel replyChannel); - - /// - /// Adds a reply channel name to the message. - /// - /// - /// the reply channel name. - /// - /// - /// the builder. - /// - IMessageBuilder SetReplyChannelName(string replyChannelName); - - /// - /// Adds an error channel to the message. - /// - /// - /// the error channel. - /// - /// - /// the builder. - /// - IMessageBuilder SetErrorChannel(IMessageChannel errorChannel); - - /// - /// Adds an error channel name to the message. - /// - /// - /// the name of the error channel. - /// - /// - /// the builder. - /// - IMessageBuilder SetErrorChannelName(string errorChannelName); - - /// - /// Adds sequence details header to the message. - /// - /// - /// the sequence number. - /// - /// - /// the builder. - /// - IMessageBuilder SetSequenceNumber(int sequenceNumber); - - /// - /// Sets the size of the sequence number. - /// - /// - /// the size. - /// - /// - /// the builder. - /// - IMessageBuilder SetSequenceSize(int sequenceSize); - - /// - /// Adds a priority header to the message. - /// - /// - /// the priority to add. - /// - /// - /// the builder. - /// - IMessageBuilder SetPriority(int priority); - - /// - /// Remove headers from the provided map matching to the provided pattens and only after that copy the result into the target message headers. - /// - /// - /// the set of headers to copy. - /// - /// - /// header patterns to filter before copy. - /// - /// - /// the builder. - /// - IMessageBuilder FilterAndCopyHeadersIfAbsent(IDictionary headersToCopy, params string[] headerPatternsToFilter); - - /// - /// Add a header and value to the message. - /// - /// - /// name of the header. - /// - /// - /// value of the header item. - /// - /// - /// the builder. - /// - IMessageBuilder SetHeader(string headerName, object headerValue); - - /// - /// Add a header and value to the message if not present. - /// - /// - /// name of the header. - /// - /// - /// value of the header item. - /// - /// - /// the builder. - /// - IMessageBuilder SetHeaderIfAbsent(string headerName, object headerValue); - - /// - /// Remove the headers matched by the header patterns from the message. - /// - /// - /// header patterns to match. - /// - /// - /// the builder. - /// - IMessageBuilder RemoveHeaders(params string[] headerPatterns); - - /// - /// Remove the header if present. - /// - /// - /// the name of the header to remove. - /// - /// - /// the builder. - /// - IMessageBuilder RemoveHeader(string headerName); - - /// - /// Adds the headers to the message overwriting any existing values. - /// - /// - /// the headers to add. - /// - /// - /// the builder. - /// - IMessageBuilder CopyHeaders(IDictionary headersToCopy); - - /// - /// Adds the headers to the message but will not overwrite any existing values. - /// - /// - /// the headers to add. - /// - /// - /// the builder. - /// - IMessageBuilder CopyHeadersIfAbsent(IDictionary headersToCopy); - - /// - /// Build the message. - /// - /// - /// the message. - /// - IMessage Build(); -} - -/// -/// A typed MessageBuilder. -/// -/// -/// the type of the payload. -/// -public interface IMessageBuilder : IMessageBuilder -{ - /// - /// Gets the payload of the message. - /// - new T Payload { get; } - - /// - /// Adds an expiration date in the headers. - /// - /// - /// expiration date added to header. - /// - /// - /// the builder. - /// - new IMessageBuilder SetExpirationDate(long expirationDate); - - /// - /// Adds an expiration date in the headers. - /// - /// - /// expiration date added to header. - /// - /// - /// the builder. - /// - new IMessageBuilder SetExpirationDate(DateTime? expirationDate); - - /// - /// Adds the correlationId to the headers. - /// - /// - /// the id to add. - /// - /// - /// the builder. - /// - new IMessageBuilder SetCorrelationId(object correlationId); - - /// - /// Adds a sequence details header to the message. - /// - /// - /// correlation id to use in sequence. - /// - /// - /// the sequence number. - /// - /// - /// the size of the sequence number. - /// - /// - /// the builder. - /// - new IMessageBuilder PushSequenceDetails(object correlationId, int sequenceNumber, int sequenceSize); - - /// - /// Removes a sequence details header from the message. - /// - /// - /// the builder. - /// - new IMessageBuilder PopSequenceDetails(); - - /// - /// Adds a reply channel to the message. - /// - /// - /// the reply channel. - /// - /// - /// the builder. - /// - new IMessageBuilder SetReplyChannel(IMessageChannel replyChannel); - - /// - /// Adds a reply channel name to the message. - /// - /// - /// the reply channel name. - /// - /// - /// the builder. - /// - new IMessageBuilder SetReplyChannelName(string replyChannelName); - - /// - /// Adds an error channel to the message. - /// - /// - /// the error channel. - /// - /// - /// the builder. - /// - new IMessageBuilder SetErrorChannel(IMessageChannel errorChannel); - - /// - /// Adds an error channel name to the message. - /// - /// - /// the name of the error channel. - /// - /// - /// the builder. - /// - new IMessageBuilder SetErrorChannelName(string errorChannelName); - - /// - /// Adds sequence details header to the message. - /// - /// - /// the sequence number. - /// - /// - /// the builder. - /// - new IMessageBuilder SetSequenceNumber(int sequenceNumber); - - /// - /// Sets the size of the sequence number. - /// - /// - /// the size. - /// - /// - /// the builder. - /// - new IMessageBuilder SetSequenceSize(int sequenceSize); - - /// - /// Adds a priority header to the message. - /// - /// - /// the priority to add. - /// - /// - /// the builder. - /// - new IMessageBuilder SetPriority(int priority); - - /// - /// Remove headers from the provided map matching to the provided pattens and only after that copy the result into the target message headers. - /// - /// - /// the set of headers to copy. - /// - /// - /// header patterns to filter before copy. - /// - /// - /// the builder. - /// - new IMessageBuilder FilterAndCopyHeadersIfAbsent(IDictionary headersToCopy, params string[] headerPatternsToFilter); - - /// - /// Add a header and value to the message. - /// - /// - /// name of the header. - /// - /// - /// value of the header item. - /// - /// - /// the builder. - /// - new IMessageBuilder SetHeader(string headerName, object headerValue); - - /// - /// Add a header and value to the message if not present. - /// - /// - /// name of the header. - /// - /// - /// value of the header item. - /// - /// - /// the builder. - /// - new IMessageBuilder SetHeaderIfAbsent(string headerName, object headerValue); - - /// - /// Remove the headers matched by the header patterns from the message. - /// - /// - /// header patterns to match. - /// - /// - /// the builder. - /// - new IMessageBuilder RemoveHeaders(params string[] headerPatterns); - - /// - /// Remove the header if present. - /// - /// - /// the name of the header to remove. - /// - /// - /// the builder. - /// - new IMessageBuilder RemoveHeader(string headerName); - - /// - /// Adds the headers to the message overwriting any existing values. - /// - /// - /// the headers to add. - /// - /// - /// the builder. - /// - new IMessageBuilder CopyHeaders(IDictionary headersToCopy); - - /// - /// Adds the headers to the message but will not overwrite any existing values. - /// - /// - /// the headers to add. - /// - /// - /// the builder. - /// - new IMessageBuilder CopyHeadersIfAbsent(IDictionary headersToCopy); - - /// - /// Build the message. - /// - /// - /// the message. - /// - new IMessage Build(); -} diff --git a/src/Integration/src/Abstractions/Support/IMessageBuilderFactory.cs b/src/Integration/src/Abstractions/Support/IMessageBuilderFactory.cs deleted file mode 100644 index 8c85b5689d..0000000000 --- a/src/Integration/src/Abstractions/Support/IMessageBuilderFactory.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -/// -/// A factory for creating message builders. -/// -public interface IMessageBuilderFactory -{ - /// - /// Create a message builder from the given message. - /// - /// - /// the type of payload. - /// - /// - /// the message to use. - /// - /// - /// the message builder. - /// - IMessageBuilder FromMessage(IMessage message); - - /// - /// Create a message builder from the given message. - /// - /// - /// the message to use. - /// - /// - /// the message builder. - /// - IMessageBuilder FromMessage(IMessage message); - - /// - /// Create a message builder from the given message payload. - /// - /// - /// the type of the payload. - /// - /// - /// the payload of the message. - /// - /// - /// the message builder. - /// - IMessageBuilder WithPayload(T payload); - - /// - /// Create a message builder from the given message payload. - /// - /// - /// the payload of the message. - /// - /// - /// the message builder. - /// - IMessageBuilder WithPayload(object payload); -} diff --git a/src/Integration/src/Abstractions/Support/IMessageDecorator.cs b/src/Integration/src/Abstractions/Support/IMessageDecorator.cs deleted file mode 100644 index 0b165f3efd..0000000000 --- a/src/Integration/src/Abstractions/Support/IMessageDecorator.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -/// -/// A contract for message decorators. -/// -public interface IMessageDecorator -{ - /// - /// Process the incoming message and return the decorated result. - /// - /// - /// the message to process. - /// - /// - /// the resulting message. - /// - IMessage DecorateMessage(IMessage message); -} diff --git a/src/Integration/src/Abstractions/Transformer/ITransformer.cs b/src/Integration/src/Abstractions/Transformer/ITransformer.cs deleted file mode 100644 index 87cb7c7be9..0000000000 --- a/src/Integration/src/Abstractions/Transformer/ITransformer.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Transformer; - -public interface ITransformer -{ - IMessage Transform(IMessage message); -} diff --git a/src/Integration/src/Integration/AbstractMessageProducer.cs b/src/Integration/src/Integration/AbstractMessageProducer.cs deleted file mode 100644 index 7bcda70917..0000000000 --- a/src/Integration/src/Integration/AbstractMessageProducer.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -public abstract class AbstractMessageProducer : IMessageProducer -{ - public abstract IMessageChannel OutputChannel { get; set; } - - public string OutputChannelName - { - get => string.Empty; - set => throw new InvalidOperationException("This MessageProducer does not support setting the channel by name."); - } -} diff --git a/src/Integration/src/Integration/Acks/AbstractAcknowledgmentCallback.cs b/src/Integration/src/Integration/Acks/AbstractAcknowledgmentCallback.cs deleted file mode 100644 index 325ba3286c..0000000000 --- a/src/Integration/src/Integration/Acks/AbstractAcknowledgmentCallback.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Acks; - -public abstract class AbstractAcknowledgmentCallback : IAcknowledgmentCallback -{ - public virtual bool IsAcknowledged { get; set; } - - public virtual bool IsAutoAck - { - get => true; - set => throw new InvalidOperationException("You cannot disable auto acknowledgment with this implementation"); - } - - public abstract void Acknowledge(Status status); -} diff --git a/src/Integration/src/Integration/Acks/AckUtils.cs b/src/Integration/src/Integration/Acks/AckUtils.cs deleted file mode 100644 index 6a6659ee5c..0000000000 --- a/src/Integration/src/Integration/Acks/AckUtils.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Acks; - -public static class AckUtils -{ - public static void AutoAck(IAcknowledgmentCallback ackCallback) - { - if (ackCallback != null && ackCallback.IsAutoAck && !ackCallback.IsAcknowledged) - { - ackCallback.Acknowledge(Status.Accept); - } - } - - public static void AutoNack(IAcknowledgmentCallback ackCallback) - { - if (ackCallback != null && ackCallback.IsAutoAck && !ackCallback.IsAcknowledged) - { - ackCallback.Acknowledge(Status.Reject); - } - } - - public static void Accept(IAcknowledgmentCallback ackCallback) - { - if (ackCallback != null) - { - ackCallback.Acknowledge(Status.Accept); - } - } - - public static void Reject(IAcknowledgmentCallback ackCallback) - { - if (ackCallback != null) - { - ackCallback.Acknowledge(Status.Reject); - } - } - - public static void Requeue(IAcknowledgmentCallback ackCallback) - { - if (ackCallback != null) - { - ackCallback.Acknowledge(Status.Requeue); - } - } -} diff --git a/src/Integration/src/Integration/Attributes/EndpointIdAttribute.cs b/src/Integration/src/Integration/Attributes/EndpointIdAttribute.cs deleted file mode 100644 index f4149ad551..0000000000 --- a/src/Integration/src/Integration/Attributes/EndpointIdAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Attributes; - -[AttributeUsage(AttributeTargets.Method)] -public sealed class EndpointIdAttribute : Attribute -{ - public string Id { get; } - - public EndpointIdAttribute() - { - } - - public EndpointIdAttribute(string id) - { - Id = id; - } -} diff --git a/src/Integration/src/Integration/Attributes/PayloadsAttribute.cs b/src/Integration/src/Integration/Attributes/PayloadsAttribute.cs deleted file mode 100644 index 7bdb01ba94..0000000000 --- a/src/Integration/src/Integration/Attributes/PayloadsAttribute.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Attributes; - -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)] -public sealed class PayloadsAttribute : Attribute -{ - public string Expression { get; set; } - - public PayloadsAttribute() - { - Expression = string.Empty; - } - - public PayloadsAttribute(string expression) - { - Expression = expression; - } -} diff --git a/src/Integration/src/Integration/Attributes/ServiceActivatorAttribute.cs b/src/Integration/src/Integration/Attributes/ServiceActivatorAttribute.cs deleted file mode 100644 index 6330e6b274..0000000000 --- a/src/Integration/src/Integration/Attributes/ServiceActivatorAttribute.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; - -namespace Steeltoe.Integration.Attributes; - -[AttributeUsage(AttributeTargets.Method)] -public sealed class ServiceActivatorAttribute : Attribute -{ - public string InputChannel { get; set; } = string.Empty; - - public string OutputChannel { get; set; } = string.Empty; - - public string RequiresReply { get; set; } = string.Empty; - - public string SendTimeout { get; set; } = string.Empty; - - public string AutoStartup { get; set; } = string.Empty; - - public string Phase { get; set; } = string.Empty; - - public ServiceActivatorAttribute() - { - } - - public ServiceActivatorAttribute(string inputChannel) - { - InputChannel = inputChannel; - } - - public ServiceActivatorAttribute(string inputChannel, string outputChannel) - { - InputChannel = inputChannel; - OutputChannel = outputChannel; - } - - public ServiceActivatorAttribute(string inputChannel, string outputChannel, bool requiresReply) - { - InputChannel = inputChannel; - OutputChannel = outputChannel; - RequiresReply = requiresReply.ToString(CultureInfo.InvariantCulture); - } - - public ServiceActivatorAttribute(string inputChannel, string outputChannel, bool requiresReply, int sendTimeout) - { - InputChannel = inputChannel; - OutputChannel = outputChannel; - RequiresReply = requiresReply.ToString(CultureInfo.InvariantCulture); - SendTimeout = sendTimeout.ToString(CultureInfo.InvariantCulture); - } - - public ServiceActivatorAttribute(string inputChannel, string outputChannel, bool requiresReply, int sendTimeout, bool autoStartup) - { - InputChannel = inputChannel; - OutputChannel = outputChannel; - RequiresReply = requiresReply.ToString(CultureInfo.InvariantCulture); - SendTimeout = sendTimeout.ToString(CultureInfo.InvariantCulture); - AutoStartup = autoStartup.ToString(CultureInfo.InvariantCulture); - } - - public ServiceActivatorAttribute(string inputChannel, string outputChannel, bool requiresReply, int sendTimeout, bool autoStartup, int phase) - { - InputChannel = inputChannel; - OutputChannel = outputChannel; - RequiresReply = requiresReply.ToString(CultureInfo.InvariantCulture); - SendTimeout = sendTimeout.ToString(CultureInfo.InvariantCulture); - AutoStartup = autoStartup.ToString(CultureInfo.InvariantCulture); - Phase = phase.ToString(CultureInfo.InvariantCulture); - } -} diff --git a/src/Integration/src/Integration/Channel/AbstractMessageChannel.cs b/src/Integration/src/Integration/Channel/AbstractMessageChannel.cs deleted file mode 100644 index 976f31a8a5..0000000000 --- a/src/Integration/src/Integration/Channel/AbstractMessageChannel.cs +++ /dev/null @@ -1,419 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Threading.Channels; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Order; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Support.Converter; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Channel; - -public abstract class AbstractMessageChannel : Channel, IMessageChannel, IChannelInterceptorAware -{ - protected const int IndefiniteTimeout = -1; - - protected readonly ILogger Logger; - private IIntegrationServices _integrationServices; - private IMessageConverter _messageConverter; - - internal ChannelInterceptorList Interceptors { get; set; } - - public IApplicationContext ApplicationContext { get; } - - public IIntegrationServices IntegrationServices - { - get - { - _integrationServices ??= IntegrationServicesUtils.GetIntegrationServices(ApplicationContext); - return _integrationServices; - } - } - - public virtual string ComponentType { get; } = "channel"; - - public virtual string ServiceName { get; set; } - - public virtual string ComponentName { get; set; } - - public virtual List DataTypes { get; set; } = new(); - - public virtual IMessageConverter MessageConverter - { - get - { - _messageConverter ??= ApplicationContext.GetService(); - return _messageConverter; - } - set => _messageConverter = value; - } - - public virtual List ChannelInterceptors - { - get => Interceptors.Interceptors; - set - { - value.Sort(new OrderComparer()); - Interceptors.Set(value); - } - } - - protected AbstractMessageChannel(IApplicationContext context, ILogger logger = null) - : this(context, null, logger) - { - } - - protected AbstractMessageChannel(IApplicationContext context, string name, ILogger logger = null) - { - ApplicationContext = context; - Logger = logger; - ServiceName = name ?? $"{GetType().Name}@{GetHashCode()}"; - Interceptors = new ChannelInterceptorList(logger); - } - - public virtual void AddInterceptor(IChannelInterceptor interceptor) - { - Interceptors.Add(interceptor); - } - - public virtual void AddInterceptor(int index, IChannelInterceptor interceptor) - { - Interceptors.Add(index, interceptor); - } - - public virtual bool RemoveInterceptor(IChannelInterceptor interceptor) - { - return Interceptors.Remove(interceptor); - } - - public virtual IChannelInterceptor RemoveInterceptor(int index) - { - return Interceptors.Remove(index); - } - - public virtual bool Send(IMessage message) - { - return Send(message, -1); - } - - public virtual bool Send(IMessage message, int timeout) - { - ArgumentGuard.NotNull(message); - - if (message.Payload == null) - { - throw new ArgumentException("Message payload must not be null.", nameof(message)); - } - - return DoSend(message, timeout); - } - - public ValueTask SendAsync(IMessage message, CancellationToken cancellationToken = default) - { - return new ValueTask(DoSend(message, cancellationToken)); - } - - protected virtual bool DoSend(IMessage message, int timeout) - { - if (timeout <= 0) - { - return DoSend(message, CancellationToken.None); - } - - using var source = new CancellationTokenSource(); - source.CancelAfter(timeout); - return DoSend(message, source.Token); - } - - protected bool DoSend(IMessage message, CancellationToken cancellationToken) - { - Stack interceptorStack = null; - bool sent = false; - - try - { - if (DataTypes.Count > 0) - { - message = ConvertPayloadIfNecessary(message); - } - - Logger?.LogDebug("PreSend on channel '{channel}', message: {message}", ServiceName, message); - - if (Interceptors.Count > 0) - { - interceptorStack = new Stack(); - message = Interceptors.PreSend(message, this, interceptorStack); - - if (message == null) - { - return false; - } - } - - sent = DoSendInternal(message, cancellationToken); - - Logger?.LogDebug("PostSend (sent={sent}) on channel '{channel}', message: {message}", sent, ServiceName, message); - - if (interceptorStack != null) - { - Interceptors.PostSend(message, this, sent); - Interceptors.AfterSendCompletion(message, this, sent, null, interceptorStack); - } - - return sent; - } - catch (Exception e) - { - if (interceptorStack != null) - { - Interceptors.AfterSendCompletion(message, this, sent, e, interceptorStack); - } - - Exception wrapped = IntegrationUtils.WrapInDeliveryExceptionIfNecessary(message, $"failed to send Message to channel '{ServiceName}'", e); - - if (wrapped != e) - { - throw wrapped; - } - - throw; - } - } - - protected abstract bool DoSendInternal(IMessage message, CancellationToken cancellationToken); - - private IMessage ConvertPayloadIfNecessary(IMessage message) - { - // first pass checks if the payload type already matches any of the data types - foreach (Type dataType in DataTypes) - { - if (dataType.IsInstanceOfType(message.Payload)) - { - return message; - } - } - - if (MessageConverter != null) - { - // second pass applies conversion if possible, attempting data types in order - foreach (Type dataType in DataTypes) - { - object converted = MessageConverter.FromMessage(message, dataType); - - if (converted != null) - { - return converted as IMessage ?? IntegrationServices.MessageBuilderFactory.WithPayload(converted).CopyHeaders(message.Headers).Build(); - } - } - } - - throw new MessageDeliveryException(message, - $"Channel '{ServiceName}' expected one of the following data types [{string.Join(",", DataTypes)}], but received [{message.Payload.GetType()}]"); - } - - internal sealed class ChannelInterceptorList - { - private readonly object _lock = new(); - private readonly ILogger _logger; - private IChannelInterceptor[] _interceptors = Array.Empty(); - - public int Count { get; private set; } - - public List Interceptors - { - get - { - lock (_lock) - { - return new List(_interceptors); - } - } - } - - public ChannelInterceptorList(ILogger logger) - { - _logger = logger; - } - - public bool Set(IList interceptors) - { - lock (_lock) - { - _interceptors = interceptors.ToArray(); - return true; - } - } - - public bool Add(IChannelInterceptor interceptor) - { - lock (_lock) - { - var interceptors = new List(_interceptors) - { - interceptor - }; - - _interceptors = interceptors.ToArray(); - Count = _interceptors.Length; - return true; - } - } - - public void Add(int index, IChannelInterceptor interceptor) - { - lock (_lock) - { - var interceptors = new List(_interceptors); - interceptors.Insert(index, interceptor); - _interceptors = interceptors.ToArray(); - Count = _interceptors.Length; - } - } - - public bool Remove(IChannelInterceptor interceptor) - { - lock (_lock) - { - var interceptors = new List(_interceptors); - bool result = interceptors.Remove(interceptor); - _interceptors = interceptors.ToArray(); - Count = _interceptors.Length; - return result; - } - } - - public IChannelInterceptor Remove(int index) - { - lock (_lock) - { - if (index < 0 || index >= _interceptors.Length) - { - return null; - } - - var interceptors = new List(_interceptors); - IChannelInterceptor current = interceptors[index]; - interceptors.RemoveAt(index); - _interceptors = interceptors.ToArray(); - Count = _interceptors.Length; - return current; - } - } - - public IMessage PreSend(IMessage messageArg, IMessageChannel channel, Stack interceptorStack) - { - IMessage message = messageArg; - - IChannelInterceptor[] interceptors = _interceptors; - - foreach (IChannelInterceptor interceptor in interceptors) - { - IMessage previous = message; - message = interceptor.PreSend(message, channel); - - if (message == null) - { - AfterSendCompletion(previous, channel, false, null, interceptorStack); - return null; - } - - interceptorStack.Push(interceptor); - } - - return message; - } - - public void PostSend(IMessage message, IMessageChannel channel, bool sent) - { - IChannelInterceptor[] interceptors = _interceptors; - - foreach (IChannelInterceptor interceptor in interceptors) - { - interceptor.PostSend(message, channel, sent); - } - } - - public void AfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception ex, Stack interceptorStack) - { - if (interceptorStack == null) - { - return; - } - - foreach (IChannelInterceptor interceptor in interceptorStack) - { - try - { - interceptor.AfterSendCompletion(message, channel, sent, ex); - } - catch (Exception e) - { - _logger?.LogError(e, e.Message); - } - } - } - - public bool PreReceive(IMessageChannel channel, Stack interceptorStack) - { - IChannelInterceptor[] interceptors = _interceptors; - - foreach (IChannelInterceptor interceptor in interceptors) - { - if (!interceptor.PreReceive(channel)) - { - AfterReceiveCompletion(null, channel, null, interceptorStack); - return false; - } - - interceptorStack.Push(interceptor); - } - - return true; - } - - public IMessage PostReceive(IMessage messageArg, IMessageChannel channel) - { - IMessage message = messageArg; - IChannelInterceptor[] interceptors = _interceptors; - - foreach (IChannelInterceptor interceptor in interceptors) - { - message = interceptor.PostReceive(message, channel); - - if (message == null) - { - return null; - } - } - - return message; - } - - public void AfterReceiveCompletion(IMessage message, IMessageChannel channel, Exception ex, Stack interceptorStack) - { - if (interceptorStack == null) - { - return; - } - - foreach (IChannelInterceptor interceptor in interceptorStack) - { - try - { - interceptor.AfterReceiveCompletion(message, channel, ex); - } - catch (Exception e) - { - _logger?.LogError(e, e.Message); - } - } - } - } -} diff --git a/src/Integration/src/Integration/Channel/AbstractMessageChannelWriter.cs b/src/Integration/src/Integration/Channel/AbstractMessageChannelWriter.cs deleted file mode 100644 index 744814c179..0000000000 --- a/src/Integration/src/Integration/Channel/AbstractMessageChannelWriter.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Threading.Channels; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Channel; - -public abstract class AbstractMessageChannelWriter : ChannelWriter -{ - protected AbstractMessageChannel channel; - protected ILogger logger; - - protected AbstractMessageChannelWriter(AbstractMessageChannel channel, ILogger logger = null) - { - ArgumentGuard.NotNull(channel); - - this.channel = channel; - this.logger = logger; - } - - public override bool TryComplete(Exception error = null) - { - return false; - } - - public override bool TryWrite(IMessage message) - { - return channel.Send(message, 0); - } - - public override ValueTask WaitToWriteAsync(CancellationToken cancellationToken = default) - { - return cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) : new ValueTask(true); - } - - public override ValueTask WriteAsync(IMessage message, CancellationToken cancellationToken = default) - { - if (TryWrite(message)) - { - return default; - } - - return new ValueTask(Task.FromException(new MessageDeliveryException(message))); - } -} diff --git a/src/Integration/src/Integration/Channel/AbstractPollableChannel.cs b/src/Integration/src/Integration/Channel/AbstractPollableChannel.cs deleted file mode 100644 index 3b8a8054d9..0000000000 --- a/src/Integration/src/Integration/Channel/AbstractPollableChannel.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Channel; - -public abstract class AbstractPollableChannel : AbstractMessageChannel, IPollableChannel, ITaskSchedulerChannelInterceptorAware -{ - public int TaskSchedulerInterceptorsSize { get; private set; } - - public override List ChannelInterceptors - { - get => base.ChannelInterceptors; - set - { - base.ChannelInterceptors = value; - - foreach (IChannelInterceptor interceptor in value) - { - if (interceptor is ITaskSchedulerChannelInterceptor) - { - TaskSchedulerInterceptorsSize++; - } - } - } - } - - public virtual bool HasTaskSchedulerInterceptors => TaskSchedulerInterceptorsSize > 0; - - protected AbstractPollableChannel(IApplicationContext context, ILogger logger = null) - : base(context, logger) - { - } - - protected AbstractPollableChannel(IApplicationContext context, string name, ILogger logger = null) - : base(context, name, logger) - { - } - - public virtual ValueTask ReceiveAsync(CancellationToken cancellationToken = default) - { - return new ValueTask(DoReceive(cancellationToken)); - } - - public virtual IMessage Receive() - { - return Receive(-1); - } - - public virtual IMessage Receive(int timeout) - { - return DoReceive(timeout); - } - - protected virtual IMessage DoReceive(int timeout) - { - if (timeout == 0) - { - return DoReceive(); - } - - using var source = new CancellationTokenSource(); - source.CancelAfter(timeout); - return DoReceive(source.Token); - } - - protected virtual IMessage DoReceive(CancellationToken cancellationToken = default) - { - ChannelInterceptorList interceptorList = Interceptors; - Stack interceptorStack = null; - - try - { - Logger?.LogTrace("PreReceive on channel '{channel}'", this); - - if (interceptorList.Count > 0) - { - interceptorStack = new Stack(); - - if (!interceptorList.PreReceive(this, interceptorStack)) - { - return null; - } - } - - IMessage message = DoReceiveInternal(cancellationToken); - - if (message == null) - { - Logger?.LogTrace("PostReceive on channel '{channel}', message is null", ServiceName); - } - else - { - Logger?.LogDebug("PostReceive on channel '{channel}', message: {message}", ServiceName, message); - } - - if (interceptorStack != null && message != null) - { - message = interceptorList.PostReceive(message, this); - } - - interceptorList.AfterReceiveCompletion(message, this, null, interceptorStack); - return message; - } - catch (Exception ex) - { - interceptorList.AfterReceiveCompletion(null, this, ex, interceptorStack); - throw; - } - } - - protected abstract IMessage DoReceiveInternal(CancellationToken cancellationToken); - - public override void AddInterceptor(IChannelInterceptor interceptor) - { - base.AddInterceptor(interceptor); - - if (interceptor is ITaskSchedulerChannelInterceptor) - { - TaskSchedulerInterceptorsSize++; - } - } - - public override void AddInterceptor(int index, IChannelInterceptor interceptor) - { - base.AddInterceptor(index, interceptor); - - if (interceptor is ITaskSchedulerChannelInterceptor) - { - TaskSchedulerInterceptorsSize++; - } - } - - public override bool RemoveInterceptor(IChannelInterceptor interceptor) - { - bool removed = base.RemoveInterceptor(interceptor); - - if (removed && interceptor is ITaskSchedulerChannelInterceptor) - { - TaskSchedulerInterceptorsSize--; - } - - return removed; - } - - public override IChannelInterceptor RemoveInterceptor(int index) - { - IChannelInterceptor interceptor = base.RemoveInterceptor(index); - - if (interceptor is ITaskSchedulerChannelInterceptor) - { - TaskSchedulerInterceptorsSize--; - } - - return interceptor; - } -} diff --git a/src/Integration/src/Integration/Channel/AbstractSubscribableChannel.cs b/src/Integration/src/Integration/Channel/AbstractSubscribableChannel.cs deleted file mode 100644 index 903dde2f95..0000000000 --- a/src/Integration/src/Integration/Channel/AbstractSubscribableChannel.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Channel; - -public abstract class AbstractSubscribableChannel : AbstractMessageChannel, ISubscribableChannel -{ - public virtual int SubscriberCount => Dispatcher.HandlerCount; - - public virtual int MaxSubscribers - { - get => Dispatcher.MaxSubscribers; - set => Dispatcher.MaxSubscribers = value; - } - - public virtual bool Failover - { - get => Dispatcher.Failover; - set => Dispatcher.Failover = value; - } - - public IMessageDispatcher Dispatcher { get; } - - protected AbstractSubscribableChannel(IApplicationContext context, IMessageDispatcher dispatcher, ILogger logger = null) - : this(context, dispatcher, null, logger) - { - } - - protected AbstractSubscribableChannel(IApplicationContext context, IMessageDispatcher dispatcher, string name, ILogger logger = null) - : base(context, name, logger) - { - ArgumentGuard.NotNull(dispatcher); - - Dispatcher = dispatcher; - } - - public virtual bool Subscribe(IMessageHandler handler) - { - bool added = Dispatcher.AddHandler(handler); - - if (added) - { - Logger?.LogTrace("Channel '{channel}' has handler {name}.", ServiceName, handler.ServiceName); - Logger?.LogInformation("Channel '{channel}' has {count} subscriber(s).", ServiceName, Dispatcher.HandlerCount); - } - - return added; - } - - public virtual bool Unsubscribe(IMessageHandler handler) - { - bool removed = Dispatcher.RemoveHandler(handler); - - if (removed) - { - Logger?.LogInformation("Channel '{channel}' has {count} subscriber(s).", ServiceName, Dispatcher.HandlerCount); - } - - return removed; - } - - protected override bool DoSendInternal(IMessage message, CancellationToken cancellationToken) - { - try - { - return Dispatcher.Dispatch(message, cancellationToken); - } - catch (MessageDispatchingException e) - { - string description = $"{e.Message} for channel '{ServiceName}'."; - throw new MessageDeliveryException(message, description, e); - } - } -} diff --git a/src/Integration/src/Integration/Channel/AbstractSubscribableChannelWriter.cs b/src/Integration/src/Integration/Channel/AbstractSubscribableChannelWriter.cs deleted file mode 100644 index 48d5dc7d86..0000000000 --- a/src/Integration/src/Integration/Channel/AbstractSubscribableChannelWriter.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Integration.Channel; - -public abstract class AbstractSubscribableChannelWriter : AbstractMessageChannelWriter -{ - public virtual AbstractSubscribableChannel Channel => (AbstractSubscribableChannel)channel; - - protected AbstractSubscribableChannelWriter(AbstractSubscribableChannel channel, ILogger logger = null) - : base(channel, logger) - { - } - - public override ValueTask WaitToWriteAsync(CancellationToken cancellationToken = default) - { - if (cancellationToken.IsCancellationRequested) - { - return new ValueTask(Task.FromCanceled(cancellationToken)); - } - - if (Channel.SubscriberCount > 0) - { - return new ValueTask(true); - } - - return new ValueTask(false); - } -} diff --git a/src/Integration/src/Integration/Channel/AbstractTaskSchedulerChannel.cs b/src/Integration/src/Integration/Channel/AbstractTaskSchedulerChannel.cs deleted file mode 100644 index b509a4f9aa..0000000000 --- a/src/Integration/src/Integration/Channel/AbstractTaskSchedulerChannel.cs +++ /dev/null @@ -1,229 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Channel; - -public abstract class AbstractTaskSchedulerChannel : AbstractSubscribableChannel, ITaskSchedulerChannelInterceptorAware -{ - protected TaskScheduler executor; - protected int taskSchedulerInterceptorsSize; - - public override List ChannelInterceptors - { - get => base.ChannelInterceptors; - set - { - base.ChannelInterceptors = value; - - foreach (IChannelInterceptor interceptor in value) - { - if (interceptor is ITaskSchedulerChannelInterceptor) - { - taskSchedulerInterceptorsSize++; - } - } - } - } - - public virtual bool HasTaskSchedulerInterceptors => taskSchedulerInterceptorsSize > 0; - - protected AbstractTaskSchedulerChannel(IApplicationContext context, IMessageDispatcher dispatcher, ILogger logger = null) - : this(context, dispatcher, null, logger) - { - } - - protected AbstractTaskSchedulerChannel(IApplicationContext context, IMessageDispatcher dispatcher, TaskScheduler executor, ILogger logger = null) - : this(context, dispatcher, executor, null, logger) - { - } - - protected AbstractTaskSchedulerChannel(IApplicationContext context, IMessageDispatcher dispatcher, TaskScheduler executor, string name, - ILogger logger = null) - : base(context, dispatcher, name, logger) - { - this.executor = executor; - } - - public override void AddInterceptor(IChannelInterceptor interceptor) - { - base.AddInterceptor(interceptor); - - if (interceptor is ITaskSchedulerChannelInterceptor) - { - taskSchedulerInterceptorsSize++; - } - } - - public override void AddInterceptor(int index, IChannelInterceptor interceptor) - { - base.AddInterceptor(index, interceptor); - - if (interceptor is ITaskSchedulerChannelInterceptor) - { - taskSchedulerInterceptorsSize++; - } - } - - public override bool RemoveInterceptor(IChannelInterceptor interceptor) - { - bool removed = base.RemoveInterceptor(interceptor); - - if (removed && interceptor is ITaskSchedulerChannelInterceptor) - { - taskSchedulerInterceptorsSize--; - } - - return removed; - } - - public override IChannelInterceptor RemoveInterceptor(int index) - { - IChannelInterceptor interceptor = base.RemoveInterceptor(index); - - if (interceptor is ITaskSchedulerChannelInterceptor) - { - taskSchedulerInterceptorsSize--; - } - - return interceptor; - } - - protected class MessageHandlingTask : IMessageHandlingRunnable - { - private readonly IMessageHandlingRunnable _runnable; - private readonly AbstractTaskSchedulerChannel _channel; - private readonly ILogger _logger; - - public IMessage Message => _runnable.Message; - - public IMessageHandler MessageHandler => _runnable.MessageHandler; - - public MessageHandlingTask(AbstractTaskSchedulerChannel channel, IMessageHandlingRunnable task, ILogger logger) - { - _channel = channel; - _runnable = task; - _logger = logger; - } - - public bool Run() - { - IMessage message = _runnable.Message; - IMessageHandler messageHandler = _runnable.MessageHandler; - - if (messageHandler == null) - { - throw new InvalidOperationException("'messageHandler' must not be null"); - } - - var interceptorStack = new Queue(); - - try - { - if (_channel.HasTaskSchedulerInterceptors) - { - message = ApplyBeforeHandle(message, interceptorStack); - - if (message == null) - { - return true; - } - } - - messageHandler.HandleMessage(message); - - if (interceptorStack.Count > 0) - { - TriggerAfterMessageHandled(message, null, interceptorStack); - } - - return true; - } - catch (Exception ex) - { - if (interceptorStack.Count > 0) - { - TriggerAfterMessageHandled(message, ex, interceptorStack); - } - - if (ex is MessagingException exception) - { - throw new MessagingWrapperException(message, exception); - } - - string description = $"Failed to handle {message} to {this} in {messageHandler}"; - throw new MessageDeliveryException(message, description, ex); - } - } - - private IMessage ApplyBeforeHandle(IMessage message, Queue interceptorStack) - { - IMessage theMessage = message; - - foreach (IChannelInterceptor interceptor in _channel.ChannelInterceptors) - { - if (interceptor is ITaskSchedulerChannelInterceptor executorInterceptor) - { - theMessage = executorInterceptor.BeforeHandled(theMessage, _channel, _runnable.MessageHandler); - - if (theMessage == null) - { - _logger?.LogDebug("{name} returned null from beforeHandle, i.e. precluding the send.", executorInterceptor.GetType().Name); - TriggerAfterMessageHandled(null, null, interceptorStack); - return null; - } - - interceptorStack.Enqueue(executorInterceptor); - } - } - - return theMessage; - } - - private void TriggerAfterMessageHandled(IMessage message, Exception ex, Queue interceptorStack) - { - IEnumerable iterator = interceptorStack.Reverse(); - - foreach (ITaskSchedulerChannelInterceptor interceptor in iterator) - { - try - { - interceptor.AfterMessageHandled(message, _channel, _runnable.MessageHandler, ex); - } - catch (Exception ex2) - { - _logger?.LogError(ex2, "Exception from afterMessageHandled in {interceptor}", interceptor); - } - } - } - } - - protected class MessageHandlingDecorator : IMessageHandlingDecorator - { - private readonly AbstractTaskSchedulerChannel _channel; - - public MessageHandlingDecorator(AbstractTaskSchedulerChannel channel) - { - _channel = channel; - } - - public IMessageHandlingRunnable Decorate(IMessageHandlingRunnable messageHandlingRunnable) - { - IMessageHandlingRunnable runnable = messageHandlingRunnable; - - if (_channel.HasTaskSchedulerInterceptors) - { - runnable = new MessageHandlingTask(_channel, messageHandlingRunnable, _channel.Logger); - } - - return runnable; - } - } -} diff --git a/src/Integration/src/Integration/Channel/DirectChannel.cs b/src/Integration/src/Integration/Channel/DirectChannel.cs deleted file mode 100644 index 40d659cd87..0000000000 --- a/src/Integration/src/Integration/Channel/DirectChannel.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Channel; - -/// -/// A channel that invokes a single subscriber for each sent Message. The invocation will occur in the sender's thread. -/// -public class DirectChannel : AbstractSubscribableChannel -{ - public DirectChannel(ILogger logger = null) - : this(null, logger) - { - } - - public DirectChannel(IApplicationContext context, ILogger logger = null) - : this(context, new RoundRobinLoadBalancingStrategy(), null, logger) - { - } - - public DirectChannel(IApplicationContext context, string name, ILogger logger = null) - : this(context, new RoundRobinLoadBalancingStrategy(), name, logger) - { - } - - public DirectChannel(IApplicationContext context, ILoadBalancingStrategy loadBalancingStrategy, string name, ILogger logger = null) - : base(context, new UnicastingDispatcher(context), name, logger) - { - Dispatcher.LoadBalancingStrategy = loadBalancingStrategy; - Dispatcher.MaxSubscribers = int.MaxValue; - Writer = new DirectChannelWriter(this, logger); - Reader = new NotSupportedChannelReader(); - } -} diff --git a/src/Integration/src/Integration/Channel/DirectChannelWriter.cs b/src/Integration/src/Integration/Channel/DirectChannelWriter.cs deleted file mode 100644 index f663bca582..0000000000 --- a/src/Integration/src/Integration/Channel/DirectChannelWriter.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Integration.Channel; - -public class DirectChannelWriter : AbstractSubscribableChannelWriter -{ - public new DirectChannel Channel => (DirectChannel)channel; - - public DirectChannelWriter(DirectChannel channel, ILogger logger = null) - : base(channel, logger) - { - } -} diff --git a/src/Integration/src/Integration/Channel/MessagePublishingErrorHandler.cs b/src/Integration/src/Integration/Channel/MessagePublishingErrorHandler.cs deleted file mode 100644 index 1acdef1289..0000000000 --- a/src/Integration/src/Integration/Channel/MessagePublishingErrorHandler.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Channel; - -public class MessagePublishingErrorHandler : ErrorMessagePublisher, IErrorHandler -{ - private const int DefaultSendTimeout = 1000; - public const string DefaultServiceName = nameof(MessagePublishingErrorHandler); - - private static IErrorMessageStrategy DefaultErrorMessageStrategyInstance { get; } = new DefaultErrorMessageStrategy(); - - public string ServiceName { get; set; } = DefaultServiceName; - - public IMessageChannel DefaultErrorChannel - { - get => Channel; - set => Channel = value; - } - - public string DefaultErrorChannelName - { - get => ChannelName; - set => ChannelName = value; - } - - public MessagePublishingErrorHandler(IApplicationContext context, ILogger logger = null) - : base(context, logger) - { - ErrorMessageStrategy = DefaultErrorMessageStrategyInstance; - SendTimeout = DefaultSendTimeout; - } - - public bool HandleError(Exception exception) - { - IMessageChannel errorChannel = ResolveErrorChannel(exception); - bool sent = false; - - if (errorChannel != null) - { - try - { - MessagingTemplate.Send(errorChannel, ErrorMessageStrategy.BuildErrorMessage(exception, null)); - sent = true; - } - catch (Exception errorDeliveryError) - { - Logger?.LogWarning(errorDeliveryError, "Error message was not delivered."); - } - } - - if (!sent) - { - IMessage failedMessage = exception is MessagingException ex ? ex.FailedMessage : null; - - if (failedMessage != null) - { - Logger?.LogError(exception, "failure occurred in messaging task with message: {message}", failedMessage); - } - else - { - Logger?.LogError(exception, "failure occurred in messaging task"); - } - } - - return sent; - } - - private IMessageChannel ResolveErrorChannel(Exception exception) - { - Exception actualThrowable = exception; - - if (exception is MessagingWrapperException) - { - actualThrowable = exception.InnerException; - } - - IMessage failedMessage = actualThrowable is MessagingException ex ? ex.FailedMessage : null; - - if (DefaultErrorChannel == null && ChannelResolver != null) - { - Channel = ChannelResolver.ResolveDestination(IntegrationContextUtils.ErrorChannelBeanName); - } - - if (failedMessage == null || failedMessage.Headers.ErrorChannel == null) - { - return DefaultErrorChannel; - } - - object errorChannelHeader = failedMessage.Headers.ErrorChannel; - - if (errorChannelHeader is IMessageChannel channel) - { - return channel; - } - - if (errorChannelHeader is not string header) - { - throw new InvalidOperationException( - $"Unsupported error channel header type. Expected {nameof(IMessageChannel)} or {nameof(String)}, but actual type is [{errorChannelHeader.GetType()}]"); - } - - if (ChannelResolver != null) - { - return ChannelResolver.ResolveDestination(header); - } - - return null; - } - - private sealed class DefaultErrorMessageStrategy : IErrorMessageStrategy - { - public ErrorMessage BuildErrorMessage(Exception exception, IAttributeAccessor attributeAccessor) - { - return exception is MessagingWrapperException wrapperException - ? new ErrorMessage(exception.InnerException, wrapperException.FailedMessage) - : new ErrorMessage(exception); - } - } -} diff --git a/src/Integration/src/Integration/Channel/NullChannel.cs b/src/Integration/src/Integration/Channel/NullChannel.cs deleted file mode 100644 index 7fca64e7a1..0000000000 --- a/src/Integration/src/Integration/Channel/NullChannel.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Threading.Channels; -using Microsoft.Extensions.Logging; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Channel; - -public class NullChannel : Channel, IPollableChannel -{ - private readonly ILogger _logger; - - public string ServiceName { get; set; } = IntegrationContextUtils.NullChannelBeanName; - - public NullChannel(ILogger logger = null) - { - _logger = logger; - Writer = new NotSupportedChannelWriter(); - Reader = new NotSupportedChannelReader(); - } - - public IMessage Receive() - { - _logger?.LogDebug("receive called on null channel"); - return null; - } - - public IMessage Receive(int timeout) - { - _logger?.LogDebug("receive called on null channel"); - return null; - } - - public ValueTask ReceiveAsync(CancellationToken cancellationToken = default) - { - _logger?.LogDebug("receive called on null channel"); - return new ValueTask((IMessage)null); - } - - public bool Send(IMessage message) - { - _logger?.LogDebug("message sent to null channel: {message}", message); - return true; - } - - public bool Send(IMessage message, int timeout) - { - _logger?.LogDebug("message sent to null channel: {message}", message); - return Send(message); - } - - public ValueTask SendAsync(IMessage message, CancellationToken cancellationToken = default) - { - _logger?.LogDebug("message sent to null channel: {message}", message); - return new ValueTask(false); - } -} diff --git a/src/Integration/src/Integration/Channel/PublishSubscribeChannel.cs b/src/Integration/src/Integration/Channel/PublishSubscribeChannel.cs deleted file mode 100644 index a674183f7a..0000000000 --- a/src/Integration/src/Integration/Channel/PublishSubscribeChannel.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Channel; - -public class PublishSubscribeChannel : AbstractTaskSchedulerChannel -{ - protected BroadcastingDispatcher BroadcastingDispatcher => (BroadcastingDispatcher)Dispatcher; - - public override string ComponentType => "publish-subscribe-channel"; - - public virtual IErrorHandler ErrorHandler - { - get => BroadcastingDispatcher.ErrorHandler; - set => BroadcastingDispatcher.ErrorHandler = value; - } - - public virtual bool IgnoreFailures - { - get => BroadcastingDispatcher.IgnoreFailures; - set => BroadcastingDispatcher.IgnoreFailures = value; - } - - public virtual bool ApplySequence - { - get => BroadcastingDispatcher.ApplySequence; - set => BroadcastingDispatcher.ApplySequence = value; - } - - public virtual int MinSubscribers - { - get => BroadcastingDispatcher.MinSubscribers; - set => BroadcastingDispatcher.MinSubscribers = value; - } - - public PublishSubscribeChannel(ILogger logger = null) - : this(null, null, null, logger) - { - } - - public PublishSubscribeChannel(IApplicationContext context, ILogger logger = null) - : this(context, null, null, logger) - { - } - - public PublishSubscribeChannel(IApplicationContext context, string name, ILogger logger = null) - : this(context, null, name, logger) - { - } - - public PublishSubscribeChannel(IApplicationContext context, TaskScheduler executor, ILogger logger = null) - : this(context, executor, null, logger) - { - } - - public PublishSubscribeChannel(IApplicationContext context, TaskScheduler executor, string name, ILogger logger = null) - : base(context, new BroadcastingDispatcher(context, executor), executor, name, logger) - { - BroadcastingDispatcher.IgnoreFailures = false; - BroadcastingDispatcher.ApplySequence = false; - BroadcastingDispatcher.MinSubscribers = 0; - Dispatcher.MessageHandlingDecorator = new MessageHandlingDecorator(this); - Writer = new PublishSubscribeChannelWriter(this, logger); - Reader = new NotSupportedChannelReader(); - } -} diff --git a/src/Integration/src/Integration/Channel/PublishSubscribeChannelWriter.cs b/src/Integration/src/Integration/Channel/PublishSubscribeChannelWriter.cs deleted file mode 100644 index a0e8d2ce6d..0000000000 --- a/src/Integration/src/Integration/Channel/PublishSubscribeChannelWriter.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Integration.Channel; - -public class PublishSubscribeChannelWriter : AbstractSubscribableChannelWriter -{ - public new PublishSubscribeChannel Channel => (PublishSubscribeChannel)channel; - - public PublishSubscribeChannelWriter(PublishSubscribeChannel channel, ILogger logger = null) - : base(channel, logger) - { - } -} diff --git a/src/Integration/src/Integration/Channel/QueueChannel.cs b/src/Integration/src/Integration/Channel/QueueChannel.cs deleted file mode 100644 index 2e71535eaa..0000000000 --- a/src/Integration/src/Integration/Channel/QueueChannel.cs +++ /dev/null @@ -1,141 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Threading.Channels; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Channel; - -public class QueueChannel : AbstractPollableChannel, IQueueChannelOperations -{ - private readonly Channel _channel; - private readonly int _capacity = -1; - private int _size; - - public int QueueSize => _size; - - public int RemainingCapacity => _capacity - _size; - - public QueueChannel(ILogger logger = null) - : this(null, logger) - { - } - - public QueueChannel(IApplicationContext context, ILogger logger = null) - : this(context, System.Threading.Channels.Channel.CreateBounded(new BoundedChannelOptions(int.MaxValue) - { - FullMode = BoundedChannelFullMode.Wait - }), null, logger) - { - _capacity = int.MaxValue; - } - - public QueueChannel(IApplicationContext context, string name, ILogger logger = null) - : this(context, System.Threading.Channels.Channel.CreateBounded(new BoundedChannelOptions(int.MaxValue) - { - FullMode = BoundedChannelFullMode.Wait - }), name, logger) - { - _capacity = int.MaxValue; - } - - public QueueChannel(IApplicationContext context, int capacity, ILogger logger = null) - : this(context, System.Threading.Channels.Channel.CreateBounded(new BoundedChannelOptions(capacity) - { - FullMode = BoundedChannelFullMode.Wait - }), null, logger) - { - _capacity = capacity; - } - - public QueueChannel(IApplicationContext context, Channel channel, ILogger logger = null) - : this(context, channel, null, logger) - { - } - - public QueueChannel(IApplicationContext context, Channel channel, string name, ILogger logger = null) - : base(context, name, logger) - { - ArgumentGuard.NotNull(channel); - - _channel = channel; - Writer = new QueueChannelWriter(this, logger); - Reader = new QueueChannelReader(this, logger); - } - - public IList Clear() - { - var messages = new List(); - - while (_channel.Reader.TryRead(out IMessage message)) - { - Interlocked.Decrement(ref _size); - messages.Add(message); - } - - return messages; - } - - public IList Purge(IMessageSelector messageSelector) - { - throw new NotSupportedException(); - } - - protected override IMessage DoReceiveInternal(CancellationToken cancellationToken) - { - if (cancellationToken == default) - { - if (_channel.Reader.TryRead(out IMessage message)) - { - Interlocked.Decrement(ref _size); - } - - return message; - } - - try - { - IMessage message = _channel.Reader.ReadAsync(cancellationToken).AsTask().GetAwaiter().GetResult(); - - if (message != null) - { - Interlocked.Decrement(ref _size); - } - - return message; - } - catch (OperationCanceledException) - { - return null; - } - } - - protected override bool DoSendInternal(IMessage message, CancellationToken cancellationToken) - { - if (cancellationToken == default) - { - if (_channel.Writer.TryWrite(message)) - { - Interlocked.Increment(ref _size); - return true; - } - - return false; - } - - try - { - _channel.Writer.WriteAsync(message, cancellationToken).AsTask().GetAwaiter().GetResult(); - Interlocked.Increment(ref _size); - return true; - } - catch (Exception) - { - return false; - } - } -} diff --git a/src/Integration/src/Integration/Channel/QueueChannelReader.cs b/src/Integration/src/Integration/Channel/QueueChannelReader.cs deleted file mode 100644 index 4d69c5e96f..0000000000 --- a/src/Integration/src/Integration/Channel/QueueChannelReader.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Threading.Channels; -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Channel; - -public class QueueChannelReader : ChannelReader -{ - private readonly ILogger _logger; - - protected QueueChannel Channel { get; } - - public QueueChannelReader(QueueChannel channel, ILogger logger = null) - { - Channel = channel; - _logger = logger; - } - - public override bool TryRead(out IMessage item) - { - _logger?.LogDebug("TryRead issued"); - item = Channel.Receive(0); - return item != null; - } - - public override ValueTask WaitToReadAsync(CancellationToken cancellationToken = default) - { - return Channel.Reader.WaitToReadAsync(cancellationToken); - } -} diff --git a/src/Integration/src/Integration/Channel/QueueChannelWriter.cs b/src/Integration/src/Integration/Channel/QueueChannelWriter.cs deleted file mode 100644 index 63107a72d5..0000000000 --- a/src/Integration/src/Integration/Channel/QueueChannelWriter.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Integration.Channel; - -public class QueueChannelWriter : AbstractMessageChannelWriter -{ - public QueueChannelWriter(AbstractPollableChannel channel, ILogger logger = null) - : base(channel, logger) - { - } -} diff --git a/src/Integration/src/Integration/Channel/TaskSchedulerChannel.cs b/src/Integration/src/Integration/Channel/TaskSchedulerChannel.cs deleted file mode 100644 index 502cb792cd..0000000000 --- a/src/Integration/src/Integration/Channel/TaskSchedulerChannel.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Channel; - -public class TaskSchedulerChannel : AbstractTaskSchedulerChannel -{ - protected UnicastingDispatcher UnicastingDispatcher => (UnicastingDispatcher)Dispatcher; - - public TaskSchedulerChannel(IApplicationContext context, TaskScheduler executor, ILogger logger = null) - : this(context, executor, new RoundRobinLoadBalancingStrategy(), logger) - { - } - - public TaskSchedulerChannel(IApplicationContext context, TaskScheduler executor, ILoadBalancingStrategy loadBalancingStrategy, ILogger logger = null) - : this(context, executor, loadBalancingStrategy, null, logger) - { - } - - public TaskSchedulerChannel(IApplicationContext context, TaskScheduler executor, ILoadBalancingStrategy loadBalancingStrategy, string name, - ILogger logger = null) - : base(context, new UnicastingDispatcher(context, executor, logger), executor, name, logger) - { - ArgumentGuard.NotNull(executor); - - Dispatcher.MessageHandlingDecorator = new MessageHandlingDecorator(this); - Dispatcher.LoadBalancingStrategy = loadBalancingStrategy; - Dispatcher.MessageHandlingDecorator = new MessageHandlingDecorator(this); - Writer = new TaskSchedulerChannelWriter(this, logger); - Reader = new NotSupportedChannelReader(); - } -} diff --git a/src/Integration/src/Integration/Channel/TaskSchedulerChannelWriter.cs b/src/Integration/src/Integration/Channel/TaskSchedulerChannelWriter.cs deleted file mode 100644 index a3615d90d3..0000000000 --- a/src/Integration/src/Integration/Channel/TaskSchedulerChannelWriter.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Integration.Channel; - -public class TaskSchedulerChannelWriter : AbstractSubscribableChannelWriter -{ - public new TaskSchedulerChannel Channel => (TaskSchedulerChannel)channel; - - public TaskSchedulerChannelWriter(TaskSchedulerChannel channel, ILogger logger = null) - : base(channel, logger) - { - } -} diff --git a/src/Integration/src/Integration/Configuration/AbstractMethodAttributeProcessor.cs b/src/Integration/src/Integration/Configuration/AbstractMethodAttributeProcessor.cs deleted file mode 100644 index 0fa0a04702..0000000000 --- a/src/Integration/src/Integration/Configuration/AbstractMethodAttributeProcessor.cs +++ /dev/null @@ -1,284 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Attributes; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Endpoint; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; - -namespace Steeltoe.Integration.Configuration; - -public abstract class AbstractMethodAttributeProcessor : IMethodAttributeProcessor - where TAttribute : Attribute -{ - protected const string SendTimeoutPropertyName = "SendTimeout"; - protected const string InputChannelPropertyName = "InputChannel"; - private readonly ILogger _logger; - - protected virtual List MessageHandlerProperties { get; } = new(); - - protected IApplicationContext ApplicationContext { get; } - - protected virtual IConversionService ConversionService { get; } - - protected virtual IDestinationResolver ChannelResolver { get; } - - protected virtual Type AnnotationType { get; } - - protected virtual string InputChannelProperty { get; } = InputChannelPropertyName; - - protected AbstractMethodAttributeProcessor(IApplicationContext applicationContext, ILogger logger) - { - ArgumentGuard.NotNull(applicationContext); - - ApplicationContext = applicationContext; - _logger = logger; - MessageHandlerProperties.Add(SendTimeoutPropertyName); - ConversionService = ApplicationContext.GetService() ?? DefaultConversionService.Singleton; - - ChannelResolver = new DefaultMessageChannelDestinationResolver(applicationContext); - AnnotationType = typeof(TAttribute); - } - - public object PostProcess(object service, string serviceName, MethodInfo method, List attributes) - { - GetSourceHandlerFromContext(method, out object sourceHandler, out bool skipEndpointCreation); - - if (skipEndpointCreation) - { - return null; - } - - IMessageHandler handler = GetHandler(service, method, attributes); - - if (handler != sourceHandler) - { - string handlerServiceName = GenerateHandlerServiceName(serviceName, method); - - if (handler is ReplyProducingMessageHandlerWrapper && !string.IsNullOrEmpty(MessagingAttributeUtils.EndpointIdValue(method))) - { - handlerServiceName += ".wrapper"; - } - - ApplicationContext.Register(handlerServiceName, handler); - handler = (IMessageHandler)ApplicationContext.GetService(handlerServiceName); - } - - AbstractEndpoint endpoint = CreateEndpoint(handler, method, attributes); - - if (endpoint != null) - { - return endpoint; - } - - return handler; - } - - public virtual bool ShouldCreateEndpoint(MethodInfo method, List attributes) - { - string inputChannel = MessagingAttributeUtils.ResolveAttribute(attributes, InputChannelProperty); - bool createEndpoint = !string.IsNullOrEmpty(inputChannel); - - if (!createEndpoint && ServiceAnnotationAware()) - { - bool isService = method.GetCustomAttribute() != null; - - if (isService) - { - throw new InvalidOperationException( - $"A channel name in '{InputChannelProperty}' is required when {AnnotationType} is used on '[Service]' methods."); - } - } - - return createEndpoint; - } - - protected virtual bool ServiceAnnotationAware() - { - return false; - } - - protected virtual AbstractEndpoint CreateEndpoint(IMessageHandler handler, MethodInfo method, List annotations) - { - AbstractEndpoint endpoint = null; - string inputChannelName = MessagingAttributeUtils.ResolveAttribute(annotations, InputChannelProperty); - - if (!string.IsNullOrEmpty(inputChannelName)) - { - IMessageChannel inputChannel; - - try - { - inputChannel = ChannelResolver.ResolveDestination(inputChannelName); - - if (inputChannel == null) - { - inputChannel = new DirectChannel(ApplicationContext, inputChannelName); - ApplicationContext.Register(inputChannelName, inputChannel); - } - } - catch (DestinationResolutionException) - { - inputChannel = new DirectChannel(ApplicationContext, inputChannelName); - ApplicationContext.Register(inputChannelName, inputChannel); - } - - endpoint = DoCreateEndpoint(handler, inputChannel, annotations); - } - - return endpoint; - } - - protected virtual AbstractEndpoint DoCreateEndpoint(IMessageHandler handler, IMessageChannel inputChannel, List annotations) - { - if (inputChannel is IPollableChannel) - { - throw new InvalidOperationException("No support for IPollableChannel"); - } - - return new EventDrivenConsumerEndpoint(ApplicationContext, (ISubscribableChannel)inputChannel, handler); - } - - protected virtual string GenerateHandlerServiceName(string originalServiceName, MethodInfo method) - { - string name = MessagingAttributeUtils.EndpointIdValue(method); - - if (string.IsNullOrEmpty(name)) - { - originalServiceName ??= method.DeclaringType.Name; - - string baseName = $"{originalServiceName}.{method.Name}.{AnnotationType.Name}"; - name = baseName; - int count = 1; - - while (ApplicationContext.ContainsService(name)) - { - name = $"{baseName}#{++count}"; - } - } - - return $"{name}.handler"; - } - - protected virtual void SetOutputChannelIfPresent(List annotations, AbstractReplyProducingMessageHandler handler) - { - string outputChannelName = MessagingAttributeUtils.ResolveAttribute(annotations, "OutputChannel"); - - if (!string.IsNullOrEmpty(outputChannelName)) - { - handler.OutputChannelName = outputChannelName; - } - } - - protected virtual object ResolveTargetServiceFromMethodWithServiceAnnotation(MethodInfo method) - { - string id = ResolveTargetServiceName(method); - return ApplicationContext.GetService(id); - } - - protected virtual string ResolveTargetServiceName(MethodInfo method) - { - string id = method.Name; - string name = method.GetCustomAttribute().Name; - - if (string.IsNullOrEmpty(name)) - { - return id; - } - - return name; - } - - protected virtual T ExtractTypeIfPossible(object targetObject) - { - if (targetObject == null) - { - return default; - } - - if (targetObject is T h) - { - return h; - } - - return default; - } - - protected void CheckMessageHandlerAttributes(string handlerServiceName, List annotations) - { - foreach (string property in MessageHandlerProperties) - { - foreach (Attribute annotation in annotations) - { - object value = AttributeUtils.GetValue(annotation, property); - - if (MessagingAttributeUtils.HasValue(value)) - { - throw new InvalidOperationException( - $"The IMessageHandler [{handlerServiceName}] can not be populated because of ambiguity with attribute properties {string.Join(',', MessageHandlerProperties)} which are not allowed when an integration attribute is used with a service definition for a IMessageHandler.\n" + - $"The property causing the ambiguity is: [{property}].\n" + - "Use the appropriate setter on the IMessageHandler directly when configuring an endpoint this way."); - } - } - } - } - - protected abstract IMessageHandler CreateHandler(object service, MethodInfo method, List attributes); - - private IMessageHandler GetHandler(object service, MethodInfo method, List attributes) - { - IMessageHandler handler = CreateHandler(service, method, attributes); - - if (handler is AbstractMessageProducingHandler) - { - string sendTimeout = MessagingAttributeUtils.ResolveAttribute(attributes, "SendTimeout"); - - if (sendTimeout != null) - { - string resolvedValue = ApplicationContext.ResolveEmbeddedValue(sendTimeout); - - if (resolvedValue != null) - { - int value = int.Parse(resolvedValue, CultureInfo.InvariantCulture); - - if (handler is AbstractMessageProducingHandler abstractHandler) - { - abstractHandler.SendTimeout = value; - } - } - } - } - - return handler; - } - - private void GetSourceHandlerFromContext(MethodInfo method, out object sourceHandler, out bool skipEndpointCreation) - { - skipEndpointCreation = false; - sourceHandler = null; - - if (ServiceAnnotationAware() && method.GetCustomAttribute() != null) - { - if (!ApplicationContext.ContainsService(ResolveTargetServiceName(method))) - { - _logger?.LogDebug("Skipping endpoint creation; perhaps due to some '[Conditional]' attribute."); - skipEndpointCreation = true; - } - else - { - sourceHandler = ResolveTargetServiceFromMethodWithServiceAnnotation(method); - } - } - } -} diff --git a/src/Integration/src/Integration/Configuration/IServiceActivatorMethod.cs b/src/Integration/src/Integration/Configuration/IServiceActivatorMethod.cs deleted file mode 100644 index 2882e3ffdb..0000000000 --- a/src/Integration/src/Integration/Configuration/IServiceActivatorMethod.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Integration.Attributes; - -namespace Steeltoe.Integration.Configuration; - -public interface IServiceActivatorMethod -{ - public MethodInfo Method { get; } - - public ServiceActivatorAttribute Attribute { get; } - - public Type ImplementationType { get; } -} diff --git a/src/Integration/src/Integration/Configuration/ServiceActivatorAttributeProcessor.cs b/src/Integration/src/Integration/Configuration/ServiceActivatorAttributeProcessor.cs deleted file mode 100644 index 8de7781e5b..0000000000 --- a/src/Integration/src/Integration/Configuration/ServiceActivatorAttributeProcessor.cs +++ /dev/null @@ -1,182 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Attributes; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Services; -using Steeltoe.Integration.Attributes; -using Steeltoe.Integration.Endpoint; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Configuration; - -public class ServiceActivatorAttributeProcessor : AbstractMethodAttributeProcessor -{ - private readonly List _serviceActivatorMethods; - - public ServiceActivatorAttributeProcessor(IApplicationContext applicationContext, IEnumerable methods, - ILogger logger) - : base(applicationContext, logger) - { - MessageHandlerProperties.AddRange(new List - { - "OutputChannel", - "RequiresReply" - }); - - _serviceActivatorMethods = methods.ToList(); - } - - public void Initialize() - { - if (_serviceActivatorMethods.Count <= 0) - { - return; - } - - foreach (IServiceActivatorMethod method in _serviceActivatorMethods) - { - object service = CreateTargetService(method.ImplementationType); - - if (service == null) - { - continue; - } - - List attributes = method.Method.GetCustomAttributes().ToList(); - string serviceName = GetServiceName(service); - object result = PostProcess(service, serviceName, method.Method, attributes); - - AbstractEndpoint endpoint = GetEndpoint(attributes, result); - - if (endpoint == null) - { - continue; - } - - string endpointName = GenerateServiceName(serviceName, method.Method, typeof(ServiceActivatorAttribute)); - endpoint.ServiceName = endpointName; - ApplicationContext?.Register(endpointName, endpoint); - } - } - - protected override IMessageHandler CreateHandler(object service, MethodInfo method, List attributes) - { - AbstractReplyProducingMessageHandler serviceActivator; - - if (method.GetCustomAttribute() != null) - { - // Service Attribute usage - object target = ResolveTargetServiceFromMethodWithServiceAnnotation(method); - serviceActivator = ExtractTypeIfPossible(target); - - if (serviceActivator == null) - { - if (target is IMessageHandler handler) - { - /* - * Return a reply-producing message handler so that we still get 'produced no reply' messages - * and the super class will inject the advice chain to advise the handler method if needed. - */ - return new ReplyProducingMessageHandlerWrapper(ApplicationContext, handler); - } - - serviceActivator = new ServiceActivatingHandler(ApplicationContext, target, method); - } - else - { - CheckMessageHandlerAttributes(ResolveTargetServiceName(method), attributes); - return (IMessageHandler)target; - } - } - else - { - serviceActivator = new ServiceActivatingHandler(ApplicationContext, service, method); - } - - string requiresReply = MessagingAttributeUtils.ResolveAttribute(attributes, "RequiresReply"); - - if (!string.IsNullOrEmpty(requiresReply)) - { - serviceActivator.RequiresReply = bool.Parse(ApplicationContext.ResolveEmbeddedValue(requiresReply)); - } - - SetOutputChannelIfPresent(attributes, serviceActivator); - return serviceActivator; - } - - protected virtual string GenerateServiceName(string originalServiceName, MethodInfo method, Type attributeType) - { - string name = MessagingAttributeUtils.EndpointIdValue(method); - - if (string.IsNullOrEmpty(name)) - { - name = $"{originalServiceName}.{method.Name}.{attributeType.Name}.{Guid.NewGuid()}"; - } - - return name; - } - - private static string GetServiceName(object service) - { - if (service is IServiceNameAware aware) - { - return aware.ServiceName; - } - - return service.GetType().FullName; - } - - private AbstractEndpoint GetEndpoint(List attributes, object result) - { - var endpoint = result as AbstractEndpoint; - - if (endpoint != null) - { - string autoStartup = MessagingAttributeUtils.ResolveAttribute(attributes, "AutoStartup"); - - if (!string.IsNullOrEmpty(autoStartup)) - { - autoStartup = ApplicationContext?.ResolveEmbeddedValue(autoStartup); - - if (!string.IsNullOrEmpty(autoStartup)) - { - endpoint.IsAutoStartup = bool.Parse(autoStartup); - } - } - - string phase = MessagingAttributeUtils.ResolveAttribute(attributes, "Phase"); - - if (!string.IsNullOrEmpty(phase)) - { - phase = ApplicationContext?.ResolveEmbeddedValue(phase); - - if (!string.IsNullOrEmpty(phase)) - { - endpoint.Phase = int.Parse(phase, CultureInfo.InvariantCulture); - } - } - } - - return endpoint; - } - - private object CreateTargetService(Type implementationType) - { - try - { - return ApplicationContext.GetService(implementationType); - } - catch (Exception e) - { - // Log - throw new InvalidOperationException($"Unable to CreateInstance of type containing StreamListener method, Type: {implementationType}", e); - } - } -} diff --git a/src/Integration/src/Integration/Configuration/ServiceActivatorMethod.cs b/src/Integration/src/Integration/Configuration/ServiceActivatorMethod.cs deleted file mode 100644 index 967e87a5d0..0000000000 --- a/src/Integration/src/Integration/Configuration/ServiceActivatorMethod.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Integration.Attributes; - -namespace Steeltoe.Integration.Configuration; - -public class ServiceActivatorMethod : IServiceActivatorMethod -{ - public MethodInfo Method { get; } - - public ServiceActivatorAttribute Attribute { get; } - - public Type ImplementationType { get; } - - public ServiceActivatorMethod(MethodInfo method, Type targetClass, ServiceActivatorAttribute attribute) - { - Method = method; - ImplementationType = targetClass; - Attribute = attribute; - } -} diff --git a/src/Integration/src/Integration/Dispatcher/AbstractDispatcher.cs b/src/Integration/src/Integration/Dispatcher/AbstractDispatcher.cs deleted file mode 100644 index 90c03ee8f6..0000000000 --- a/src/Integration/src/Integration/Dispatcher/AbstractDispatcher.cs +++ /dev/null @@ -1,194 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Order; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Dispatcher; - -public abstract class AbstractDispatcher : IMessageDispatcher -{ - private readonly object _lock = new(); - private readonly MessageHandlerComparer _comparer = new(); - protected readonly IApplicationContext Context; - protected readonly ILogger InnerLogger; - protected readonly TaskScheduler Executor; - protected readonly TaskFactory Factory; - private IErrorHandler _errorHandler; - private volatile IMessageHandler _theOneHandler; - private IIntegrationServices _integrationServices; - protected List handlers = new(); - - internal List Handlers => new(handlers); - - public virtual int MaxSubscribers { get; set; } = int.MaxValue; - - public virtual int HandlerCount => handlers.Count; - - public virtual ILoadBalancingStrategy LoadBalancingStrategy { get; set; } - - public virtual bool Failover { get; set; } = true; - - public virtual IMessageHandlingDecorator MessageHandlingDecorator { get; set; } - - public virtual IIntegrationServices IntegrationServices - { - get - { - _integrationServices ??= IntegrationServicesUtils.GetIntegrationServices(Context); - return _integrationServices; - } - } - - public virtual IErrorHandler ErrorHandler - { - get - { - if (Factory != null && _errorHandler == null) - { - _errorHandler = new MessagePublishingErrorHandler(Context); - } - - return _errorHandler; - } - set => _errorHandler = value; - } - - protected AbstractDispatcher(IApplicationContext context, TaskScheduler executor, ILogger logger = null) - { - Context = context; - InnerLogger = logger; - Executor = executor; - - if (executor != null) - { - Factory = new TaskFactory(executor); - } - } - - public virtual bool AddHandler(IMessageHandler handler) - { - ArgumentGuard.NotNull(handler); - - lock (_lock) - { - if (handlers.Count == MaxSubscribers) - { - throw new InvalidOperationException("Maximum number of subscribers exceeded."); - } - - var newHandlers = new List(handlers); - - if (newHandlers.Contains(handler)) - { - return false; - } - - newHandlers.Add(handler); - newHandlers.Sort(_comparer); - _theOneHandler = newHandlers.Count == 1 ? handler : null; - - handlers = newHandlers; - } - - return true; - } - - public virtual bool RemoveHandler(IMessageHandler handler) - { - ArgumentGuard.NotNull(handler); - - lock (_lock) - { - var newHandlers = new List(handlers); - bool removed = newHandlers.Remove(handler); - - if (newHandlers.Count == 1) - { - _theOneHandler = newHandlers[0]; - } - else - { - newHandlers.Sort(_comparer); - _theOneHandler = null; - } - - handlers = newHandlers; - - return removed; - } - } - - public override string ToString() - { - return $"{GetType().Name} with handlers: {handlers.Count}"; - } - - public virtual bool Dispatch(IMessage message, CancellationToken cancellationToken = default) - { - return DoDispatch(message, cancellationToken); - } - - protected abstract bool DoDispatch(IMessage message, CancellationToken cancellationToken); - - protected virtual bool TryOptimizedDispatch(IMessage message) - { - IMessageHandler handler = _theOneHandler; - - if (handler != null) - { - try - { - handler.HandleMessage(message); - return true; - } - catch (Exception e) - { - Exception wrapped = IntegrationUtils.WrapInDeliveryExceptionIfNecessary(message, "Dispatcher failed to deliver Message", e); - - if (wrapped != e) - { - throw wrapped; - } - - throw; - } - } - - return false; - } - - private sealed class MessageHandlerComparer : OrderComparer, IComparer - { - public int Compare(IMessageHandler x, IMessageHandler y) - { - var xo = x as IOrdered; - var yo = y as IOrdered; - - if (xo != null && yo != null) - { - return Compare(xo, yo); - } - - if (xo != null) - { - return GetOrder(xo.Order, AbstractOrdered.LowestPrecedence); - } - - if (yo != null) - { - return GetOrder(AbstractOrdered.LowestPrecedence, yo.Order); - } - - return 0; - } - } -} diff --git a/src/Integration/src/Integration/Dispatcher/AggregateMessageDeliveryException.cs b/src/Integration/src/Integration/Dispatcher/AggregateMessageDeliveryException.cs deleted file mode 100644 index ae76237bd8..0000000000 --- a/src/Integration/src/Integration/Dispatcher/AggregateMessageDeliveryException.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Dispatcher; - -public class AggregateMessageDeliveryException : MessageDeliveryException -{ - private readonly List _aggregatedExceptions; - - public List AggregatedExceptions => new(_aggregatedExceptions); - - public override string Message - { - get - { - string baseMessage = base.Message; - var message = new StringBuilder($"{AppendPeriodIfNecessary(baseMessage)} Multiple causes:\n"); - - foreach (Exception exception in _aggregatedExceptions) - { - message.Append($" {exception.Message}\n"); - } - - message.Append("See below for the stacktrace of the first cause."); - return message.ToString(); - } - } - - public AggregateMessageDeliveryException(IMessage failedMessage, string message, List aggregatedExceptions) - : base(failedMessage, message, aggregatedExceptions[0]) - { - _aggregatedExceptions = new List(aggregatedExceptions); - } - - private string AppendPeriodIfNecessary(string baseMessage) - { - if (string.IsNullOrEmpty(baseMessage)) - { - return string.Empty; - } - - if (!baseMessage.EndsWith('.')) - { - return $"{baseMessage}."; - } - - return baseMessage; - } -} diff --git a/src/Integration/src/Integration/Dispatcher/BroadcastingDispatcher.cs b/src/Integration/src/Integration/Dispatcher/BroadcastingDispatcher.cs deleted file mode 100644 index 0e426c8703..0000000000 --- a/src/Integration/src/Integration/Dispatcher/BroadcastingDispatcher.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Dispatcher; - -public class BroadcastingDispatcher : AbstractDispatcher -{ - private readonly bool _requireSubscribers; - - private IMessageBuilderFactory _messageBuilderFactory; - - internal IMessageBuilderFactory MessageBuilderFactory - { - get - { - _messageBuilderFactory ??= IntegrationServices.MessageBuilderFactory; - return _messageBuilderFactory; - } - set => _messageBuilderFactory = value; - } - - internal ILogger Logger => InnerLogger; - - public virtual bool IgnoreFailures { get; set; } - - public virtual bool ApplySequence { get; set; } - - public virtual int MinSubscribers { get; set; } - - public BroadcastingDispatcher(IApplicationContext context, ILogger logger = null) - : this(context, null, false, logger) - { - } - - public BroadcastingDispatcher(IApplicationContext context, TaskScheduler executor, ILogger logger = null) - : this(context, executor, false, logger) - { - } - - public BroadcastingDispatcher(IApplicationContext context, bool requireSubscribers, ILogger logger = null) - : this(context, null, requireSubscribers, logger) - { - } - - public BroadcastingDispatcher(IApplicationContext context, TaskScheduler executor, bool requireSubscribers, ILogger logger = null) - : base(context, executor, logger) - { - _requireSubscribers = requireSubscribers; - } - - protected override bool DoDispatch(IMessage message, CancellationToken cancellationToken) - { - int dispatched = 0; - int sequenceNumber = 1; - List handlers = this.handlers; - - if (_requireSubscribers && handlers.Count == 0) - { - throw new MessageDispatchingException(message, "Dispatcher has no subscribers"); - } - - int sequenceSize = handlers.Count; - IMessage messageToSend = message; - string sequenceId = null; - - if (ApplySequence) - { - sequenceId = message.Headers.Id; - } - - foreach (IMessageHandler handler in handlers) - { - if (ApplySequence) - { - messageToSend = MessageBuilderFactory.FromMessage(message).PushSequenceDetails(sequenceId, sequenceNumber++, sequenceSize).Build(); - - if (message is IMessageDecorator decorator) - { - messageToSend = decorator.DecorateMessage(messageToSend); - } - } - - if (Executor != null) - { - IMessageHandlingRunnable task = CreateMessageHandlingTask(handler, messageToSend); - Factory.StartNew(() => task.Run(), cancellationToken); - dispatched++; - } - else - { - InvokeHandler(handler, messageToSend); - dispatched++; - } - } - - if (dispatched == 0 && MinSubscribers == 0) - { - Logger?.LogDebug(sequenceSize > 0 ? "No subscribers received message, default behavior is ignore" : "No subscribers, default behavior is ignore"); - } - - return dispatched >= MinSubscribers; - } - - protected void InvokeHandler(IMessageHandler handler, IMessage message) - { - try - { - handler.HandleMessage(message); - } - catch (Exception e) - { - if (!IgnoreFailures) - { - if (Factory != null && ErrorHandler != null) - { - ErrorHandler.HandleError(e); - return; - } - - if (e is MessagingException exception && exception.FailedMessage == null) - { - throw new MessagingException(message, "Failed to handle Message", e); - } - - throw; - } - - InnerLogger?.LogWarning(e, "Suppressing Exception since 'ignoreFailures' is set to TRUE."); - } - } - - private IMessageHandlingRunnable CreateMessageHandlingTask(IMessageHandler handler, IMessage message) - { - var messageHandlingRunnable = new MessageHandlingRunnable(this, handler, message); - - if (MessageHandlingDecorator != null) - { - return MessageHandlingDecorator.Decorate(messageHandlingRunnable); - } - - return messageHandlingRunnable; - } - - private sealed class MessageHandlingRunnable : IMessageHandlingRunnable - { - private readonly BroadcastingDispatcher _dispatcher; - - public IMessage Message { get; } - - public IMessageHandler MessageHandler { get; } - - public MessageHandlingRunnable(BroadcastingDispatcher dispatcher, IMessageHandler handler, IMessage message) - { - _dispatcher = dispatcher; - Message = message; - MessageHandler = handler; - } - - public bool Run() - { - _dispatcher.InvokeHandler(MessageHandler, Message); - return true; - } - } -} diff --git a/src/Integration/src/Integration/Dispatcher/RoundRobinLoadBalancingStrategy.cs b/src/Integration/src/Integration/Dispatcher/RoundRobinLoadBalancingStrategy.cs deleted file mode 100644 index 46790a6a63..0000000000 --- a/src/Integration/src/Integration/Dispatcher/RoundRobinLoadBalancingStrategy.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Dispatcher; - -public class RoundRobinLoadBalancingStrategy : ILoadBalancingStrategy -{ - private int _currentHandlerIndex = -1; - -#pragma warning disable S2292 // Trivial properties should be auto-implemented - internal int CurrentHandlerIndex -#pragma warning restore S2292 // Trivial properties should be auto-implemented - { - get => _currentHandlerIndex; - set => _currentHandlerIndex = value; - } - - public int GetNextHandlerStartIndex(IMessage message, List handlers) - { - if (handlers == null) - { - return 0; - } - - int size = handlers.Count; - - if (size > 0) - { - int indexTail = Interlocked.Increment(ref _currentHandlerIndex) % size; - return indexTail < 0 ? indexTail + size : indexTail; - } - - return size; - } -} diff --git a/src/Integration/src/Integration/Dispatcher/UnicastingDispatcher.cs b/src/Integration/src/Integration/Dispatcher/UnicastingDispatcher.cs deleted file mode 100644 index 3ed2ef6ba4..0000000000 --- a/src/Integration/src/Integration/Dispatcher/UnicastingDispatcher.cs +++ /dev/null @@ -1,196 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Dispatcher; - -public class UnicastingDispatcher : AbstractDispatcher -{ - public UnicastingDispatcher(IApplicationContext context, ILogger logger = null) - : base(context, null, logger) - { - } - - public UnicastingDispatcher(IApplicationContext context, TaskScheduler executor, ILogger logger = null) - : base(context, executor, logger) - { - } - - public override bool Dispatch(IMessage message, CancellationToken cancellationToken = default) - { - if (Executor != null) - { - Factory.StartNew(() => - { - IMessageHandlingRunnable task = CreateMessageHandlingTask(message, cancellationToken); - - try - { - task.Run(); - } - catch (Exception e) - { - if (ErrorHandler != null) - { - ErrorHandler.HandleError(e); - } - } - }, CancellationToken.None); - - return true; - } - - return DoDispatch(message, cancellationToken); - } - - protected override bool DoDispatch(IMessage message, CancellationToken cancellationToken) - { - if (TryOptimizedDispatch(message)) - { - return true; - } - - List handlers = this.handlers; - - if (handlers.Count == 0) - { - throw new MessageDispatchingException(message, "Dispatcher has no subscribers"); - } - - MessageHandlerEnumerator handlerIterator = GetHandlerEnumerator(message, handlers); - List exceptions = null; - bool isLast; - bool success = false; - - do - { - IMessageHandler handler = handlerIterator.Current; - isLast = !handlerIterator.MoveNext(); - - try - { - handler.HandleMessage(message); - success = true; // we have a winner. - break; - } - catch (Exception e) - { - Exception runtimeException = IntegrationUtils.WrapInDeliveryExceptionIfNecessary(message, "Dispatcher failed to deliver Message", e); - exceptions ??= new List(); - - exceptions.Add(runtimeException); - - HandleExceptions(exceptions, message, isLast); - } - } - while (!isLast); - - return success; - } - - private MessageHandlerEnumerator GetHandlerEnumerator(IMessage message, List handlers) - { - if (LoadBalancingStrategy != null) - { - int index = LoadBalancingStrategy.GetNextHandlerStartIndex(message, handlers); - return new MessageHandlerEnumerator(index, handlers); - } - - return new MessageHandlerEnumerator(0, handlers); - } - - private void HandleExceptions(List allExceptions, IMessage message, bool isLast) - { - if (isLast || !Failover) - { - if (allExceptions != null && allExceptions.Count == 1) - { - throw allExceptions[0]; - } - - throw new AggregateMessageDeliveryException(message, "All attempts to deliver Message to MessageHandlers failed.", allExceptions); - } - } - - private IMessageHandlingRunnable CreateMessageHandlingTask(IMessage message, CancellationToken cancellationToken) - { - var messageHandlingRunnable = new MessageHandlingRunnable(this, message, cancellationToken); - - if (MessageHandlingDecorator != null) - { - return MessageHandlingDecorator.Decorate(messageHandlingRunnable); - } - - return messageHandlingRunnable; - } - - private sealed class MessageHandlingRunnable : IMessageHandlingRunnable - { - public UnicastingDispatcher Dispatcher { get; } - - public IMessage Message { get; } - - public CancellationToken Token { get; } - - public IMessageHandler MessageHandler { get; } - - public MessageHandlingRunnable(UnicastingDispatcher dispatcher, IMessage message, CancellationToken cancellationToken) - { - Dispatcher = dispatcher; - Message = message; - Token = cancellationToken; - MessageHandler = new MessageHandlerDelegate(this); - } - - public bool Run() - { - Dispatcher.DoDispatch(Message, Token); - return true; - } - - private sealed class MessageHandlerDelegate : IMessageHandler - { - private readonly MessageHandlingRunnable _runnable; - - public string ServiceName { get; set; } - - public MessageHandlerDelegate(MessageHandlingRunnable runnable) - { - _runnable = runnable; - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - _runnable.Dispatcher.DoDispatch(message, _runnable.Token); - } - } - } - - private record struct MessageHandlerEnumerator - { - private readonly int _startIndex; - private readonly List _handlers; - private int _index; - - public IMessageHandler Current => _handlers[_index]; - - public MessageHandlerEnumerator(int startIndex, List handlers) - { - _index = _startIndex = startIndex; - _handlers = handlers; - } - - public bool MoveNext() - { - _index = (_index + 1) % _handlers.Count; - return _index != _startIndex; - } - } -} diff --git a/src/Integration/src/Integration/Endpoint/AbstractEndpoint.cs b/src/Integration/src/Integration/Endpoint/AbstractEndpoint.cs deleted file mode 100644 index 1aa5efdd54..0000000000 --- a/src/Integration/src/Integration/Endpoint/AbstractEndpoint.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Services; -using Steeltoe.Integration.Util; - -namespace Steeltoe.Integration.Endpoint; - -public abstract class AbstractEndpoint : ISmartLifecycle, IServiceNameAware -{ - private readonly object _lifecycleLock = new(); - private IIntegrationServices _integrationServices; - - public IIntegrationServices IntegrationServices - { - get - { - _integrationServices ??= IntegrationServicesUtils.GetIntegrationServices(ApplicationContext); - return _integrationServices; - } - } - - public IApplicationContext ApplicationContext { get; } - - public virtual string ComponentType { get; set; } - - public virtual string ServiceName { get; set; } - - public virtual string ComponentName { get; set; } - - public bool IsAutoStartup { get; set; } = true; - - public bool IsRunning { get; set; } - - public int Phase { get; set; } - - protected AbstractEndpoint(IApplicationContext context) - { - ApplicationContext = context; - ServiceName = $"{GetType().FullName}.{Guid.NewGuid()}"; - } - - public Task StartAsync() - { - bool doTheStart = false; - - lock (_lifecycleLock) - { - if (!IsRunning) - { - doTheStart = true; - IsRunning = true; - } - } - - if (doTheStart) - { - return DoStartAsync(); - } - - return Task.CompletedTask; - } - - public Task StopAsync(Action callback) - { - bool doTheStop = false; - - lock (_lifecycleLock) - { - if (IsRunning) - { - doTheStop = true; - IsRunning = false; - } - } - - if (doTheStop) - { - return DoStopAsync(callback); - } - - callback(); - return Task.CompletedTask; - } - - public Task StopAsync() - { - bool doTheStop = false; - - lock (_lifecycleLock) - { - if (IsRunning) - { - doTheStop = true; - IsRunning = false; - } - } - - if (doTheStop) - { - return DoStopAsync(); - } - - return Task.CompletedTask; - } - - protected virtual async Task DoStopAsync(Action callback) - { - await DoStopAsync(); - callback(); - } - - protected abstract Task DoStopAsync(); - - protected abstract Task DoStartAsync(); -} diff --git a/src/Integration/src/Integration/Endpoint/AbstractMessageSource.cs b/src/Integration/src/Integration/Endpoint/AbstractMessageSource.cs deleted file mode 100644 index 20b4a52104..0000000000 --- a/src/Integration/src/Integration/Endpoint/AbstractMessageSource.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Integration.Expression; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Endpoint; - -public abstract class AbstractMessageSource : AbstractExpressionEvaluator, IMessageSource -{ - public Dictionary HeaderExpressions { get; set; } - - protected AbstractMessageSource(IApplicationContext context) - : base(context) - { - } - - public IMessage Receive() - { - return BuildMessage(DoReceive()); - } - - IMessage IMessageSource.Receive() - { - return BuildMessage(DoReceive()); - } - - protected virtual IMessage BuildMessage(object result) - { - if (result == null) - { - return null; - } - - IDictionary headers = EvaluateHeaders(); - - IMessage message; - - switch (result) - { - case AbstractMessageBuilder amBuilder: - if (headers != null && headers.Count > 0) - { - amBuilder.CopyHeaders(headers); - } - - message = amBuilder.Build(); - break; - case IMessage mResult: - message = mResult; - - if (headers != null && headers.Count > 0) - { - // create a new Message from this one in order to apply headers - message = MessageBuilderFactory.FromMessage(message).CopyHeaders(headers).Build(); - } - - break; - default: - message = MessageBuilderFactory.WithPayload(result).CopyHeaders(headers).Build(); - break; - } - - return (IMessage)message; - } - - protected abstract object DoReceive(); - - private IDictionary EvaluateHeaders() - { - if (HeaderExpressions == null || HeaderExpressions.Count == 0) - { - return null; - } - - return ExpressionEvalDictionary.From(HeaderExpressions).UsingEvaluationContext(EvaluationContext).Build(); - } -} diff --git a/src/Integration/src/Integration/Endpoint/EventDrivenConsumerEndpoint.cs b/src/Integration/src/Integration/Endpoint/EventDrivenConsumerEndpoint.cs deleted file mode 100644 index 6ed26374e6..0000000000 --- a/src/Integration/src/Integration/Endpoint/EventDrivenConsumerEndpoint.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Integration.Router; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Endpoint; - -public class EventDrivenConsumerEndpoint : AbstractEndpoint -{ - private readonly ISubscribableChannel _inputChannel; - - private readonly IMessageHandler _handler; - - public virtual IMessageChannel InputChannel => _inputChannel; - - public virtual IMessageHandler Handler => _handler; - - public virtual IMessageChannel OutputChannel - { - get - { - if (_handler is IMessageProducer producer) - { - return producer.OutputChannel; - } - - if (_handler is IMessageRouter router) - { - return router.DefaultOutputChannel; - } - - return null; - } - } - - public EventDrivenConsumerEndpoint(IApplicationContext context, ISubscribableChannel inputChannel, IMessageHandler handler) - : base(context) - { - ArgumentGuard.NotNull(handler); - ArgumentGuard.NotNull(inputChannel); - - _handler = handler; - _inputChannel = inputChannel; - Phase = int.MaxValue; - } - - protected override Task DoStartAsync() - { - _inputChannel.Subscribe(_handler); - - if (_handler is ILifecycle lifecycle) - { - return lifecycle.StartAsync(); - } - - return Task.CompletedTask; - } - - protected override Task DoStopAsync() - { - _inputChannel.Unsubscribe(_handler); - - if (_handler is ILifecycle lifecycle) - { - return lifecycle.StopAsync(); - } - - return Task.CompletedTask; - } -} diff --git a/src/Integration/src/Integration/Endpoint/MessageProducerSupportEndpoint.cs b/src/Integration/src/Integration/Endpoint/MessageProducerSupportEndpoint.cs deleted file mode 100644 index 81ae46978d..0000000000 --- a/src/Integration/src/Integration/Endpoint/MessageProducerSupportEndpoint.cs +++ /dev/null @@ -1,181 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Endpoint; - -public abstract class MessageProducerSupportEndpoint : AbstractEndpoint, IMessageProducer -{ - private readonly MessagingTemplate _messagingTemplate; - private readonly object _lock = new(); - - private IErrorMessageStrategy _errorMessageStrategy = new DefaultErrorMessageStrategy(); - - private volatile IMessageChannel _outputChannel; - - private volatile string _outputChannelName; - - private volatile IMessageChannel _errorChannel; - - private volatile string _errorChannelName; - - protected virtual MessagingTemplate MessagingTemplate => _messagingTemplate; - - public virtual IMessageChannel OutputChannel - { - get - { - if (_outputChannelName != null) - { - lock (_lock) - { - if (_outputChannelName != null) - { - _outputChannel = IntegrationServices.ChannelResolver.ResolveDestination(_outputChannelName); - } - - _outputChannelName = null; - } - } - - return _outputChannel; - } - set => _outputChannel = value; - } - - public virtual string OutputChannelName - { - get => _outputChannelName; - set - { - ArgumentGuard.NotNullOrEmpty(value); - - _outputChannelName = value; - } - } - - public virtual IMessageChannel ErrorChannel - { - get - { - if (_errorChannelName != null) - { - lock (_lock) - { - if (_errorChannelName != null) - { - _errorChannel = IntegrationServices.ChannelResolver.ResolveDestination(_errorChannelName); - } - - _errorChannelName = null; - } - } - - return _errorChannel; - } - set => _errorChannel = value; - } - - public virtual string ErrorChannelName - { - get => _errorChannelName; - set - { - ArgumentGuard.NotNullOrEmpty(value); - - _errorChannelName = value; - } - } - - public virtual int SendTimeout - { - get => _messagingTemplate.SendTimeout; - set => _messagingTemplate.SendTimeout = value; - } - - public virtual IErrorMessageStrategy ErrorMessageStrategy - { - get => _errorMessageStrategy; - set - { - ArgumentGuard.NotNull(value); - - _errorMessageStrategy = value; - } - } - - protected MessageProducerSupportEndpoint(IApplicationContext context, ILogger logger = null) - : base(context) - { - Phase = int.MaxValue / 2; - _messagingTemplate = new MessagingTemplate(context, logger); - } - - protected internal virtual void SendMessage(IMessage messageArg) - { - IMessage message = messageArg; - - if (message == null) - { - throw new MessagingException("cannot send a null message"); - } - - try - { - if (OutputChannel == null) - { - throw new InvalidOperationException("The 'outputChannel' or `outputChannelName` must be configured"); - } - - _messagingTemplate.Send(OutputChannel, message); - } - catch (Exception e) - { - if (!SendErrorMessageIfNecessary(message, e)) - { - throw; - } - } - } - - protected override Task DoStartAsync() - { - return Task.CompletedTask; - } - - protected override Task DoStopAsync() - { - return Task.CompletedTask; - } - - protected bool SendErrorMessageIfNecessary(IMessage message, Exception exception) - { - IMessageChannel channel = ErrorChannel; - - if (channel != null) - { - _messagingTemplate.Send(channel, BuildErrorMessage(message, exception)); - return true; - } - - return false; - } - - protected ErrorMessage BuildErrorMessage(IMessage message, Exception exception) - { - return _errorMessageStrategy.BuildErrorMessage(exception, GetErrorMessageAttributes(message)); - } - - protected virtual IAttributeAccessor GetErrorMessageAttributes(IMessage message) - { - return ErrorMessageUtils.GetAttributeAccessor(message, null); - } -} diff --git a/src/Integration/src/Integration/ErrorMessagePublisher.cs b/src/Integration/src/Integration/ErrorMessagePublisher.cs deleted file mode 100644 index d93ee3ea88..0000000000 --- a/src/Integration/src/Integration/ErrorMessagePublisher.cs +++ /dev/null @@ -1,166 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration; - -public class ErrorMessagePublisher -{ - private readonly IApplicationContext _context; - protected readonly MessagingTemplate InnerMessagingTemplate; - protected readonly ILogger Logger; - private IIntegrationServices _integrationServices; - private IDestinationResolver _channelResolver; - private IMessageChannel _channel; - private IErrorMessageStrategy _errorMessageStrategy = new DefaultErrorMessageStrategy(); - - protected virtual MessagingTemplate MessagingTemplate => InnerMessagingTemplate; - - public IIntegrationServices IntegrationServices - { - get - { - _integrationServices ??= IntegrationServicesUtils.GetIntegrationServices(_context); - return _integrationServices; - } - } - - public virtual IErrorMessageStrategy ErrorMessageStrategy - { - get => _errorMessageStrategy; - set - { - ArgumentGuard.NotNull(value); - - _errorMessageStrategy = value; - } - } - - public virtual IMessageChannel Channel - { - get - { - PopulateChannel(); - - return _channel; - } - set => _channel = value; - } - - public virtual string ChannelName { get; set; } - - public virtual int SendTimeout - { - get => InnerMessagingTemplate.SendTimeout; - set => InnerMessagingTemplate.SendTimeout = value; - } - - public virtual IDestinationResolver ChannelResolver - { - get - { - _channelResolver ??= IntegrationServices.ChannelResolver; - return _channelResolver; - } - set - { - ArgumentGuard.NotNull(value); - - _channelResolver = value; - } - } - - public ErrorMessagePublisher(IApplicationContext context, ILogger logger = null) - { - _context = context; - Logger = logger; - InnerMessagingTemplate = new MessagingTemplate(context); - } - - public virtual void Publish(MessagingException exception) - { - Publish(null, exception.FailedMessage, exception); - } - - public virtual void Publish(IMessage failedMessage, Exception throwable) - { - Publish(null, failedMessage, throwable); - } - - public virtual void Publish(IMessage inputMessage, MessagingException exception) - { - Publish(inputMessage, exception.FailedMessage, exception); - } - - public virtual void Publish(IMessage inputMessage, IMessage failedMessage, Exception exception) - { - Publish(exception, ErrorMessageUtils.GetAttributeAccessor(inputMessage, failedMessage)); - } - - public virtual void Publish(Exception exception, IAttributeAccessor context) - { - PopulateChannel(); - - Exception payload = DeterminePayload(exception, context); - ErrorMessage errorMessage = _errorMessageStrategy.BuildErrorMessage(payload, context); - - InnerMessagingTemplate.Send(errorMessage); - } - - protected virtual Exception DeterminePayload(Exception exception, IAttributeAccessor context) - { - Exception lastThrowable = exception; - - if (lastThrowable == null) - { - lastThrowable = PayloadWhenNull(context); - } - else if (lastThrowable is not MessagingException) - { - var message = (IMessage)context.GetAttribute(ErrorMessageUtils.FailedMessageContextKey); - - lastThrowable = message == null - ? new MessagingException(lastThrowable.Message, lastThrowable) - : new MessagingException(message, lastThrowable.Message, lastThrowable); - } - - return lastThrowable; - } - - protected virtual Exception PayloadWhenNull(IAttributeAccessor context) - { - var message = (IMessage)context.GetAttribute(ErrorMessageUtils.FailedMessageContextKey); - - return message == null - ? new MessagingException("No root cause exception available") - : new MessagingException(message, "No root cause exception available"); - } - - private void PopulateChannel() - { - if (InnerMessagingTemplate.DefaultDestination == null) - { - if (_channel == null) - { - string recoveryChannelName = ChannelName ?? IntegrationContextUtils.ErrorChannelBeanName; - - if (_channelResolver != null) - { - _channel = _channelResolver.ResolveDestination(recoveryChannelName); - } - } - - InnerMessagingTemplate.DefaultDestination = _channel; - } - } -} diff --git a/src/Integration/src/Integration/ErrorMessageSendingRecoverer.cs b/src/Integration/src/Integration/ErrorMessageSendingRecoverer.cs deleted file mode 100644 index 4ff01f993d..0000000000 --- a/src/Integration/src/Integration/ErrorMessageSendingRecoverer.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Retry; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -public class ErrorMessageSendingRecoverer : ErrorMessagePublisher, IRecoveryCallback -{ - public ErrorMessageSendingRecoverer(IApplicationContext context) - : this(context, null) - { - } - - public ErrorMessageSendingRecoverer(IApplicationContext context, IMessageChannel channel) - : this(context, channel, null) - { - } - - public ErrorMessageSendingRecoverer(IApplicationContext context, IMessageChannel channel, IErrorMessageStrategy errorMessageStrategy) - : base(context) - { - Channel = channel; - ErrorMessageStrategy = errorMessageStrategy ?? new DefaultErrorMessageStrategy(); - } - - public object Recover(IRetryContext context) - { - Publish(context.LastException, context); - return null; - } - - protected override Exception PayloadWhenNull(IAttributeAccessor context) - { - var message = (IMessage)context.GetAttribute(ErrorMessageUtils.FailedMessageContextKey); - - string description = - $"No retry exception available; this can occur, for example, if the RetryPolicy allowed zero attempts to execute the handler; RetryContext: {context}"; - - return message == null ? new RetryExceptionNotAvailableException(description) : new RetryExceptionNotAvailableException(message, description); - } -} diff --git a/src/Integration/src/Integration/Expression/ExpressionEvalDictionary.cs b/src/Integration/src/Integration/Expression/ExpressionEvalDictionary.cs deleted file mode 100644 index 150e78ccbf..0000000000 --- a/src/Integration/src/Integration/Expression/ExpressionEvalDictionary.cs +++ /dev/null @@ -1,283 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using Steeltoe.Common; -using Steeltoe.Common.Expression.Internal; - -namespace Steeltoe.Integration.Expression; - -public class ExpressionEvalDictionary : IDictionary -{ - private readonly IDictionary _original; - private readonly IEvaluationCallback _evaluationCallback; - - public ICollection Keys => _original.Keys; - - public ICollection Values - { - get - { - var list = new List(_original.Count); - ICollection keys = _original.Keys; - - foreach (string key in keys) - { - list.Add(Get(key)); - } - - return list; - } - } - - public int Count => _original.Count; - - public bool IsReadOnly => true; - - public object this[string key] - { - get => Get(key); - set => throw new NotImplementedException(); - } - - private ExpressionEvalDictionary(IDictionary original, IEvaluationCallback evaluationCallback = null) - { - _original = original; - _evaluationCallback = evaluationCallback; - } - - public static ExpressionEvalDictionaryBuilder From(IDictionary expressions) - { - ArgumentGuard.NotNull(expressions); - - return new ExpressionEvalDictionaryBuilder(expressions); - } - - public void Add(string key, object value) - { - throw new InvalidOperationException(); - } - - public void Add(KeyValuePair item) - { - throw new InvalidOperationException(); - } - - public void Clear() - { - throw new InvalidOperationException(); - } - - public bool Contains(KeyValuePair item) - { - throw new InvalidOperationException(); - } - - public bool ContainsKey(string key) - { - return _original.ContainsKey(key); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - throw new InvalidOperationException(); - } - - public IEnumerator> GetEnumerator() - { - var results = new List>(); - - foreach (KeyValuePair entry in _original) - { - object value = Get(entry.Key); - results.Add(new KeyValuePair(entry.Key, value)); - } - - return results.GetEnumerator(); - } - - public bool Remove(string key) - { - throw new InvalidOperationException(); - } - - public bool Remove(KeyValuePair item) - { - throw new InvalidOperationException(); - } - - public bool TryGetValue(string key, [MaybeNullWhen(false)] out object value) - { - if (!ContainsKey(key)) - { - value = null; - return false; - } - - value = Get(key); - return true; - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new InvalidOperationException(); - } - - public object Get(string key) - { - _original.TryGetValue(key, out IExpression expression); - - if (expression != null) - { - return _evaluationCallback.Evaluate(expression); - } - - return null; - } - - public class ComponentsEvaluationCallback : IEvaluationCallback - { - private readonly IEvaluationContext _context; - - private readonly object _root; - - private readonly bool _rootExplicitlySet; - - private readonly Type _returnType; - - public ComponentsEvaluationCallback(IEvaluationContext context, object root, bool rootExplicitlySet, Type returnType) - { - _context = context; - _root = root; - _rootExplicitlySet = rootExplicitlySet; - _returnType = returnType; - } - - public object Evaluate(IExpression expression) - { - if (_context != null) - { - if (_rootExplicitlySet) - { - return expression.GetValue(_context, _root, _returnType); - } - - return expression.GetValue(_context, _returnType); - } - - return expression.GetValue(_root, _returnType); - } - } - - public class SimpleCallback : IEvaluationCallback - { - public object Evaluate(IExpression expression) - { - return expression.GetValue(); - } - } - - public class ExpressionEvalDictionaryBuilder - { - private static readonly IEvaluationCallback SimpleCallback = new SimpleCallback(); - - private readonly IExpressionEvalMapComponentsBuilder _evalMapComponentsBuilder; - - private readonly IExpressionEvalMapFinalBuilder _finalBuilder; - - private IDictionary Expressions { get; } - - private IEvaluationContext EvaluationContext { get; set; } - - private object Root { get; set; } - - private bool RootExplicitlySet { get; set; } - - private Type ReturnType { get; set; } - - private IEvaluationCallback EvaluationCallback { get; set; } - - public ExpressionEvalDictionaryBuilder(IDictionary expressions) - { - Expressions = expressions; - _finalBuilder = new ExpressionEvalMapFinalBuilderImpl(this); - _evalMapComponentsBuilder = new ExpressionEvalMapComponentsBuilderImpl(this); - } - - public IExpressionEvalMapFinalBuilder UsingCallback(IEvaluationCallback callback) - { - EvaluationCallback = callback; - return _finalBuilder; - } - - public IExpressionEvalMapFinalBuilder UsingSimpleCallback() - { - return UsingCallback(SimpleCallback); - } - - public IExpressionEvalMapComponentsBuilder UsingEvaluationContext(IEvaluationContext context) - { - EvaluationContext = context; - return _evalMapComponentsBuilder; - } - - public IExpressionEvalMapComponentsBuilder WithRoot(object root) - { - Root = root; - RootExplicitlySet = true; - return _evalMapComponentsBuilder; - } - - public IExpressionEvalMapComponentsBuilder WithReturnType(Type returnType) - { - ReturnType = returnType; - return _evalMapComponentsBuilder; - } - - private class ExpressionEvalMapFinalBuilderImpl : IExpressionEvalMapFinalBuilder - { - protected ExpressionEvalDictionaryBuilder Builder { get; } - - public ExpressionEvalMapFinalBuilderImpl(ExpressionEvalDictionaryBuilder builder) - { - Builder = builder; - } - - public ExpressionEvalDictionary Build() - { - if (Builder.EvaluationCallback != null) - { - return new ExpressionEvalDictionary(Builder.Expressions, Builder.EvaluationCallback); - } - - return new ExpressionEvalDictionary(Builder.Expressions, - new ComponentsEvaluationCallback(Builder.EvaluationContext, Builder.Root, Builder.RootExplicitlySet, Builder.ReturnType)); - } - } - - private sealed class ExpressionEvalMapComponentsBuilderImpl : ExpressionEvalMapFinalBuilderImpl, IExpressionEvalMapComponentsBuilder - { - public ExpressionEvalMapComponentsBuilderImpl(ExpressionEvalDictionaryBuilder builder) - : base(builder) - { - } - - public IExpressionEvalMapComponentsBuilder UsingEvaluationContext(IEvaluationContext context) - { - return Builder.UsingEvaluationContext(context); - } - - public IExpressionEvalMapComponentsBuilder WithRoot(object root) - { - return Builder.WithRoot(root); - } - - public IExpressionEvalMapComponentsBuilder WithReturnType(Type returnType) - { - return Builder.WithReturnType(returnType); - } - } - } -} diff --git a/src/Integration/src/Integration/Expression/IEvaluationCallback.cs b/src/Integration/src/Integration/Expression/IEvaluationCallback.cs deleted file mode 100644 index 9f8264faa7..0000000000 --- a/src/Integration/src/Integration/Expression/IEvaluationCallback.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; - -namespace Steeltoe.Integration.Expression; - -public interface IEvaluationCallback -{ - object Evaluate(IExpression expression); -} diff --git a/src/Integration/src/Integration/Expression/IExpressionEvalMapComponentsBuilder.cs b/src/Integration/src/Integration/Expression/IExpressionEvalMapComponentsBuilder.cs deleted file mode 100644 index c87f28165e..0000000000 --- a/src/Integration/src/Integration/Expression/IExpressionEvalMapComponentsBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; - -namespace Steeltoe.Integration.Expression; - -public interface IExpressionEvalMapComponentsBuilder : IExpressionEvalMapFinalBuilder -{ - IExpressionEvalMapComponentsBuilder UsingEvaluationContext(IEvaluationContext context); - - IExpressionEvalMapComponentsBuilder WithRoot(object root); - - IExpressionEvalMapComponentsBuilder WithReturnType(Type returnType); -} diff --git a/src/Integration/src/Integration/Expression/IExpressionEvalMapFinalBuilder.cs b/src/Integration/src/Integration/Expression/IExpressionEvalMapFinalBuilder.cs deleted file mode 100644 index 5652f32ee4..0000000000 --- a/src/Integration/src/Integration/Expression/IExpressionEvalMapFinalBuilder.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Integration.Expression; - -public interface IExpressionEvalMapFinalBuilder -{ - ExpressionEvalDictionary Build(); -} diff --git a/src/Integration/src/Integration/Extensions/IntegrationServicesExtensions.cs b/src/Integration/src/Integration/Extensions/IntegrationServicesExtensions.cs deleted file mode 100644 index b1a71b1977..0000000000 --- a/src/Integration/src/Integration/Extensions/IntegrationServicesExtensions.cs +++ /dev/null @@ -1,222 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Attributes; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Configuration; -using Steeltoe.Integration.Endpoint; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Support.Converter; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Extensions; - -public static class IntegrationServicesExtensions -{ - public static IServiceCollection AddErrorChannel(this IServiceCollection services) - { - services.RegisterService(IntegrationContextUtils.ErrorChannelBeanName, typeof(IMessageChannel)); - - services.AddSingleton(p => - { - var context = p.GetService(); - return new PublishSubscribeChannel(context, IntegrationContextUtils.ErrorChannelBeanName); - }); - - services.RegisterService(IntegrationContextUtils.ErrorChannelBeanName, typeof(ISubscribableChannel)); - - services.AddSingleton(p => - { - var context = p.GetService(); - return GetRequiredChannel(context, IntegrationContextUtils.ErrorChannelBeanName); - }); - - return services; - } - - public static IServiceCollection AddNullChannel(this IServiceCollection services) - { - services.RegisterService(IntegrationContextUtils.NullChannelBeanName, typeof(IMessageChannel)); - services.AddSingleton(); - services.RegisterService(IntegrationContextUtils.NullChannelBeanName, typeof(IPollableChannel)); - - services.AddSingleton(p => - { - var context = p.GetService(); - return GetRequiredChannel(context, IntegrationContextUtils.NullChannelBeanName); - }); - - return services; - } - - public static IServiceCollection AddQueueChannel(this IServiceCollection services, string channelName) - { - return services.AddQueueChannel(channelName, null); - } - - public static IServiceCollection AddQueueChannel(this IServiceCollection services, string channelName, Action configure) - { - ArgumentGuard.NotNullOrEmpty(channelName); - - services.RegisterService(channelName, typeof(IMessageChannel)); - - services.AddSingleton(p => - { - var context = p.GetService(); - - var chan = new QueueChannel(context) - { - ServiceName = channelName - }; - - configure?.Invoke(p, chan); - - return chan; - }); - - services.RegisterService(channelName, typeof(IPollableChannel)); - - services.AddSingleton(p => - { - var context = p.GetService(); - return GetRequiredChannel(context, channelName); - }); - - return services; - } - - public static IServiceCollection AddDirectChannel(this IServiceCollection services, string channelName) - { - return services.AddDirectChannel(channelName, null); - } - - public static IServiceCollection AddDirectChannel(this IServiceCollection services, string channelName, Action configure) - { - ArgumentGuard.NotNullOrEmpty(channelName); - - services.RegisterService(channelName, typeof(IMessageChannel)); - - services.AddSingleton(p => - { - var context = p.GetService(); - - var chan = new DirectChannel(context) - { - ServiceName = channelName - }; - - configure?.Invoke(p, chan); - - return chan; - }); - - services.RegisterService(channelName, typeof(ISubscribableChannel)); - - services.AddSingleton(p => - { - var context = p.GetService(); - return GetRequiredChannel(context, channelName); - }); - - return services; - } - - public static IServiceCollection AddLoggingEndpoint(this IServiceCollection services) - { - services.AddSingleton(p => - { - var context = p.GetRequiredService(); - var logger = p.GetRequiredService>(); - var handler = new LoggingHandler(context, LogLevel.Error, logger); - var errorChan = GetRequiredChannel(context, IntegrationContextUtils.ErrorChannelBeanName); - return new EventDrivenConsumerEndpoint(context, errorChan, handler); - }); - - return services; - } - - public static IServiceCollection AddIntegrationServices(this IServiceCollection services) - { - services.AddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - - services.AddNullChannel(); - services.AddErrorChannel(); - services.AddLoggingEndpoint(); - - services.AddSingleton(); - - // SpringIntegrationProperties - return services; - } - - public static IServiceCollection AddServiceActivators(this IServiceCollection services, params Type[] targetClasses) - { - foreach (Type targetClass in targetClasses) - { - services.AddServiceActivators(targetClass); - } - - return services; - } - - public static IServiceCollection AddServiceActivators(this IServiceCollection services, Type targetClass) - { - List targetMethods = AttributeUtils.FindMethodsWithAttribute(targetClass, typeof(ServiceActivatorAttribute), - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - - foreach (MethodInfo method in targetMethods) - { - services.AddServiceActivator(method, targetClass); - } - - if (targetMethods.Count > 0) - { - services.TryAddSingleton(targetClass); - } - - return services; - } - - public static IServiceCollection AddServiceActivators(this IServiceCollection services) - { - return services.AddServiceActivators(typeof(T)); - } - - public static IServiceCollection AddServiceActivator(this IServiceCollection services, MethodInfo method, Type targetClass) - { - var attribute = method.GetCustomAttribute(); - - if (attribute == null) - { - throw new InvalidOperationException($"Method: '{method}' missing ServiceActivatorAttribute"); - } - - services.AddSingleton(new ServiceActivatorMethod(method, targetClass, attribute)); - return services; - } - - private static T GetRequiredChannel(IApplicationContext context, string name) - where T : class - { - if (context.GetServices().FirstOrDefault(chan => chan.ServiceName == name) is not T result) - { - throw new InvalidOperationException($"Unable to resolve channel:{name}"); - } - - return result; - } -} diff --git a/src/Integration/src/Integration/Handler/AbstractMessageHandler.cs b/src/Integration/src/Integration/Handler/AbstractMessageHandler.cs deleted file mode 100644 index e837baa5b7..0000000000 --- a/src/Integration/src/Integration/Handler/AbstractMessageHandler.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Order; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -public abstract class AbstractMessageHandler : IMessageHandler, IOrdered -{ - private IIntegrationServices _integrationServices; - - public IIntegrationServices IntegrationServices - { - get - { - _integrationServices ??= IntegrationServicesUtils.GetIntegrationServices(ApplicationContext); - return _integrationServices; - } - } - - public IApplicationContext ApplicationContext { get; } - - public virtual string ComponentType => "message-handler"; - - public virtual string ServiceName { get; set; } - - public virtual string ComponentName { get; set; } - - public int Order => AbstractOrdered.LowestPrecedence - 1; - - protected AbstractMessageHandler(IApplicationContext context) - { - ApplicationContext = context; - ServiceName = GetType().FullName; - } - - public virtual void HandleMessage(IMessage message) - { - ArgumentGuard.NotNull(message); - - if (message.Payload == null) - { - throw new ArgumentException("Message payload must not be null.", nameof(message)); - } - - try - { - HandleMessageInternal(message); - } - catch (Exception e) - { - Exception wrapped = IntegrationUtils.WrapInHandlingExceptionIfNecessary(message, $"error occurred in message handler [{this}]", e); - - if (wrapped != e) - { - throw wrapped; - } - - throw; - } - } - - public abstract void Initialize(); - - protected abstract void HandleMessageInternal(IMessage message); -} diff --git a/src/Integration/src/Integration/Handler/AbstractMessageProcessor.cs b/src/Integration/src/Integration/Handler/AbstractMessageProcessor.cs deleted file mode 100644 index 3264973c5b..0000000000 --- a/src/Integration/src/Integration/Handler/AbstractMessageProcessor.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -public abstract class AbstractMessageProcessor : AbstractExpressionEvaluator, IMessageProcessor -{ - protected AbstractMessageProcessor(IApplicationContext context) - : base(context) - { - } - - public abstract T ProcessMessage(IMessage message); - - object IMessageProcessor.ProcessMessage(IMessage message) - { - return ProcessMessage(message); - } -} diff --git a/src/Integration/src/Integration/Handler/AbstractMessageProducingHandler.cs b/src/Integration/src/Integration/Handler/AbstractMessageProducingHandler.cs deleted file mode 100644 index cc13ce0b38..0000000000 --- a/src/Integration/src/Integration/Handler/AbstractMessageProducingHandler.cs +++ /dev/null @@ -1,281 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; - -namespace Steeltoe.Integration.Handler; - -public abstract class AbstractMessageProducingHandler : AbstractMessageHandler, IMessageProducer, IHeaderPropagation -{ - private readonly MessagingTemplate _messagingTemplate; - - private string _outputChannelName; - - private IMessageChannel _outputChannel; - - private List _notPropagatedHeaders = new(); - - private bool _noHeadersPropagation; - - private bool _selectiveHeaderPropagation; - - protected virtual bool ShouldCopyRequestHeaders => true; - - public virtual IMessageChannel OutputChannel - { - get - { - if (_outputChannelName != null) - { - _outputChannel = IntegrationServices.ChannelResolver.ResolveDestination(_outputChannelName); - _outputChannelName = null; - } - - return _outputChannel; - } - set => _outputChannel = value; - } - - public virtual string OutputChannelName - { - get => _outputChannelName; - set - { - ArgumentGuard.NotNullOrEmpty(value); - - _outputChannelName = value; - } - } - - public virtual IList NotPropagatedHeaders - { - get => new List(_notPropagatedHeaders); - set => UpdateNotPropagatedHeaders(value, false); - } - - public virtual int SendTimeout - { - get => _messagingTemplate.SendTimeout; - set => _messagingTemplate.SendTimeout = value; - } - - protected AbstractMessageProducingHandler(IApplicationContext context) - : base(context) - { - _messagingTemplate = new MessagingTemplate(context); - } - - public virtual void AddNotPropagatedHeaders(params string[] headers) - { - UpdateNotPropagatedHeaders(headers, true); - } - - protected virtual void UpdateNotPropagatedHeaders(IList headers, bool merge) - { - ArgumentGuard.NotNull(headers); - ArgumentGuard.ElementsNotNullOrEmpty(headers); - - var headerPatterns = new HashSet(); - - if (merge && _notPropagatedHeaders.Count > 0) - { - foreach (string h in _notPropagatedHeaders) - { - headerPatterns.Add(h); - } - } - - if (headers.Count > 0) - { - foreach (string header in headers) - { - headerPatterns.Add(header); - } - - _notPropagatedHeaders = headerPatterns.ToList(); - } - - bool hasAsterisk = headerPatterns.Contains("*"); - - if (hasAsterisk) - { - _notPropagatedHeaders = new List - { - "*" - }; - - _noHeadersPropagation = true; - } - - if (_notPropagatedHeaders.Count > 0) - { - _selectiveHeaderPropagation = true; - } - } - - protected void SendOutputs(object reply, IMessage requestMessage) - { - if (reply == null) - { - return; - } - - if (IsEnumerable(reply)) - { - var multiReply = (IEnumerable)reply; - - if (ShouldSplitOutput(multiReply)) - { - foreach (object r in multiReply) - { - ProduceOutput(r, requestMessage); - } - } - } - - ProduceOutput(reply, requestMessage); - } - - protected virtual void ProduceOutput(object reply, IMessage requestMessage) - { - IMessageHeaders requestHeaders = requestMessage.Headers; - object replyChannel = null; - - if (OutputChannel == null) - { - replyChannel = ObtainReplyChannel(requestMessage.Headers, reply); - } - - DoProduceOutput(requestHeaders, reply, replyChannel); - } - - protected virtual void SendOutput(object output, object replyChannelArg, bool useArgChannel) - { - object replyChannel = replyChannelArg; - IMessageChannel outChannel = OutputChannel; - - if (!useArgChannel && outChannel != null) - { - replyChannel = outChannel; - } - - if (replyChannel == null) - { - throw new DestinationResolutionException("no output-channel or replyChannel header available"); - } - - var outputAsMessage = output as IMessage; - - switch (replyChannel) - { - case IMessageChannel channel: - if (outputAsMessage != null) - { - _messagingTemplate.Send(channel, outputAsMessage); - } - else - { - _messagingTemplate.ConvertAndSend(channel, output); - } - - break; - case string strChannel: - if (outputAsMessage != null) - { - _messagingTemplate.Send(strChannel, outputAsMessage); - } - else - { - _messagingTemplate.ConvertAndSend(strChannel, output); - } - - break; - default: - throw new MessagingException("replyChannel must be a IMessageChannel or String"); - } - } - - protected virtual bool ShouldSplitOutput(IEnumerable reply) - { - foreach (object next in reply) - { - if (next is IMessage) - { - return true; - } - } - - return false; - } - - protected virtual IMessage CreateOutputMessage(object output, IMessageHeaders requestHeaders) - { - IMessageBuilder builder; - - if (output is IMessage outputAsMessage) - { - if (_noHeadersPropagation || !ShouldCopyRequestHeaders) - { - return outputAsMessage; - } - - builder = IntegrationServices.MessageBuilderFactory.FromMessage(outputAsMessage); - } - else - { - builder = IntegrationServices.MessageBuilderFactory.WithPayload(output); - } - - if (!_noHeadersPropagation && ShouldCopyRequestHeaders) - { - builder.FilterAndCopyHeadersIfAbsent(requestHeaders, _selectiveHeaderPropagation ? _notPropagatedHeaders.ToArray() : null); - } - - return builder.Build(); - } - - private bool IsEnumerable(object reply) - { - if (reply is string || reply is Array) - { - return false; - } - - if (reply is IEnumerable) - { - return true; - } - - return false; - } - - private object ObtainReplyChannel(IMessageHeaders requestHeaders, object reply) - { - object replyChannel = requestHeaders.ReplyChannel; - - if (replyChannel == null) - { - if (reply is IMessage replyAsMessage) - { - replyChannel = replyAsMessage.Headers.ReplyChannel; - } - else if (reply is IMessageBuilder replyAsBuilder) - { - replyAsBuilder.Headers.TryGetValue(MessageHeaders.ReplyChannelName, out replyChannel); - } - } - - return replyChannel; - } - - private void DoProduceOutput(IMessageHeaders requestHeaders, object reply, object replyChannel) - { - SendOutput(CreateOutputMessage(reply, requestHeaders), replyChannel, false); - } -} diff --git a/src/Integration/src/Integration/Handler/AbstractReplyProducingMessageHandler.cs b/src/Integration/src/Integration/Handler/AbstractReplyProducingMessageHandler.cs deleted file mode 100644 index cd61d59ba8..0000000000 --- a/src/Integration/src/Integration/Handler/AbstractReplyProducingMessageHandler.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -public abstract class AbstractReplyProducingMessageHandler : AbstractMessageProducingHandler -{ - public bool RequiresReply { get; set; } - - protected AbstractReplyProducingMessageHandler(IApplicationContext context) - : base(context) - { - } - - protected override void HandleMessageInternal(IMessage message) - { - object result = HandleRequestMessage(message); - - if (result != null) - { - SendOutputs(result, message); - } - else if (RequiresReply) - { - throw new ReplyRequiredException(message, $"No reply produced by handler '{GetType().Name}', and its 'requiresReply' property is set to true."); - } - } - - protected abstract object HandleRequestMessage(IMessage requestMessage); -} diff --git a/src/Integration/src/Integration/Handler/BridgeHandler.cs b/src/Integration/src/Integration/Handler/BridgeHandler.cs deleted file mode 100644 index d8309ab84c..0000000000 --- a/src/Integration/src/Integration/Handler/BridgeHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -public class BridgeHandler : AbstractReplyProducingMessageHandler -{ - protected override bool ShouldCopyRequestHeaders => false; - - public override string ComponentType => "bridge"; - - public BridgeHandler(IApplicationContext context) - : base(context) - { - } - - public override void Initialize() - { - // Intentionally left empty. - } - - protected override object HandleRequestMessage(IMessage requestMessage) - { - return requestMessage; - } -} diff --git a/src/Integration/src/Integration/Handler/ExpressionEvaluatingMessageProcessor.cs b/src/Integration/src/Integration/Handler/ExpressionEvaluatingMessageProcessor.cs deleted file mode 100644 index b7f10a188f..0000000000 --- a/src/Integration/src/Integration/Handler/ExpressionEvaluatingMessageProcessor.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -public class ExpressionEvaluatingMessageProcessor : AbstractMessageProcessor -{ - private IExpression Expression { get; } - - private Type ExpectedType { get; } - - public ExpressionEvaluatingMessageProcessor(IApplicationContext context, IExpression expression) - : this(context, expression, typeof(T)) - { - } - - public ExpressionEvaluatingMessageProcessor(IApplicationContext context, IExpression expression, Type expectedType) - : base(context) - { - ArgumentGuard.NotNull(expression); - - Expression = expression; - ExpectedType = expectedType; - } - - public ExpressionEvaluatingMessageProcessor(IApplicationContext context, string expression) - : base(context) - { - try - { - Expression = ExpressionParser.ParseExpression(expression); - ExpectedType = typeof(T); - } - catch (ParseException exception) - { - throw new ArgumentException($"Failed to parse expression '{expression}'.", nameof(expression), exception); - } - } - - public ExpressionEvaluatingMessageProcessor(IApplicationContext context, string expression, Type expectedType) - : base(context) - { - try - { - Expression = ExpressionParser.ParseExpression(expression); - ExpectedType = expectedType; - } - catch (ParseException exception) - { - throw new ArgumentException($"Failed to parse expression '{expression}'.", nameof(expression), exception); - } - } - - public override T ProcessMessage(IMessage message) - { - return (T)EvaluateExpression(Expression, message, ExpectedType); - } - - public override string ToString() - { - return $"ExpressionEvaluatingMessageProcessor for: [{Expression.ExpressionString}]"; - } -} diff --git a/src/Integration/src/Integration/Handler/LoggingHandler.cs b/src/Integration/src/Integration/Handler/LoggingHandler.cs deleted file mode 100644 index 7014f3f1df..0000000000 --- a/src/Integration/src/Integration/Handler/LoggingHandler.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -public class LoggingHandler : AbstractMessageHandler -{ - public LogLevel Level { get; } - - public override string ComponentType => "logging-channel-adapter"; - - public ILogger MessageLogger { get; } - - public LoggingHandler(IApplicationContext context, LogLevel level, ILogger logger) - : base(context) - { - Level = level; - MessageLogger = logger; - } - - public override void Initialize() - { - // Intentionally left empty. - } - - protected override void HandleMessageInternal(IMessage message) - { - switch (Level) - { - case LogLevel.Critical: - if (MessageLogger.IsEnabled(LogLevel.Critical)) - { - MessageLogger.LogCritical(CreateLogMessage(message)); - } - - break; - case LogLevel.Error: - if (MessageLogger.IsEnabled(LogLevel.Error)) - { - MessageLogger.LogError(CreateLogMessage(message)); - } - - break; - case LogLevel.Warning: - if (MessageLogger.IsEnabled(LogLevel.Warning)) - { - MessageLogger.LogWarning(CreateLogMessage(message)); - } - - break; - case LogLevel.Information: - if (MessageLogger.IsEnabled(LogLevel.Information)) - { - MessageLogger.LogInformation(CreateLogMessage(message)); - } - - break; - case LogLevel.Debug: - if (MessageLogger.IsEnabled(LogLevel.Debug)) - { - MessageLogger.LogDebug(CreateLogMessage(message)); - } - - break; - case LogLevel.Trace: - if (MessageLogger.IsEnabled(LogLevel.Trace)) - { - MessageLogger.LogTrace(CreateLogMessage(message)); - } - - break; - } - } - - protected virtual string CreateLogMessage(IMessage message) - { - return message.Payload.ToString(); - } -} diff --git a/src/Integration/src/Integration/Handler/MethodInvokingMessageProcessor.cs b/src/Integration/src/Integration/Handler/MethodInvokingMessageProcessor.cs deleted file mode 100644 index 4ec388655c..0000000000 --- a/src/Integration/src/Integration/Handler/MethodInvokingMessageProcessor.cs +++ /dev/null @@ -1,136 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Handler.Support; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Support.Converter; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Integration.Handler; - -public class MethodInvokingMessageProcessor : AbstractMessageProcessor, ILifecycle -{ - private readonly IInvocableHandlerMethod _invocableHandlerMethod; - private IConversionService _conversionService; - - public virtual IConversionService ConversionService - { - get - { - _conversionService ??= IntegrationServices.ConversionService; - return _conversionService; - } - set => _conversionService = value; - } - - public bool IsRunning { get; private set; } - - public MethodInvokingMessageProcessor(IApplicationContext context, object targetObject, MethodInfo method) - : base(context) - { - IMessageHandlerMethodFactory messageHandlerMethodFactory = ConfigureMessageHandlerFactory(); - _invocableHandlerMethod = messageHandlerMethodFactory.CreateInvocableHandlerMethod(targetObject, method); - } - - public MethodInvokingMessageProcessor(IApplicationContext context, object targetObject, Type attribute) - : base(context) - { - MethodInfo method = FindAnnotatedMethod(targetObject, attribute); - IMessageHandlerMethodFactory messageHandlerMethodFactory = ConfigureMessageHandlerFactory(); - _invocableHandlerMethod = messageHandlerMethodFactory.CreateInvocableHandlerMethod(targetObject, method); - } - - public Task StartAsync() - { - IsRunning = true; - return Task.CompletedTask; - } - - public Task StopAsync() - { - IsRunning = false; - return Task.CompletedTask; - } - - public override T ProcessMessage(IMessage message) - { - try - { - object result = _invocableHandlerMethod.Invoke(message); - - if (result is T) - { - return (T)ConversionService.Convert(result, result.GetType(), typeof(T)); - } - - return (T)result; - } - catch (Exception e) - { - throw new MessageHandlingException(message, e); - } - } - - private static MethodInfo FindAnnotatedMethod(object target, Type attribute) - { - List results = AttributeUtils.FindMethodsWithAttribute( - target.GetType(), attribute, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - - if (results.Count != 1) - { - throw new InvalidOperationException($"Multiple methods with attribute: {attribute} exist on type: {target.GetType()}"); - } - - return results[0]; - } - - private IMessageHandlerMethodFactory ConfigureMessageHandlerFactory() - { - IMessageHandlerMethodFactory factory = ApplicationContext?.GetService() ?? ConfigureLocalMessageHandlerFactory(); - return factory; - } - - private IMessageHandlerMethodFactory ConfigureLocalMessageHandlerFactory() - { - var factory = new DefaultMessageHandlerMethodFactory(ConversionService, ApplicationContext); - - var messageConverter = ApplicationContext?.GetService(IntegrationContextUtils.ArgumentResolverMessageConverterBeanName); - - if (messageConverter != null) - { - factory.MessageConverter = messageConverter; - } - else - { - messageConverter = new ConfigurableCompositeMessageConverter(ConversionService); - } - - var payloadExpressionArgumentResolver = new PayloadExpressionArgumentResolver(ApplicationContext); - var payloadsArgumentResolver = new PayloadsArgumentResolver(ApplicationContext); - var nullResolver = new NullAwarePayloadArgumentResolver(messageConverter); - - var customArgumentResolvers = new List - { - payloadExpressionArgumentResolver, - nullResolver, - payloadsArgumentResolver - }; - - var mapArgumentResolver = new DictionaryArgumentResolver(ApplicationContext); - customArgumentResolvers.Add(mapArgumentResolver); - - factory.CustomArgumentResolvers = customArgumentResolvers; - factory.Initialize(); - return factory; - } -} diff --git a/src/Integration/src/Integration/Handler/ReplyProducingMessageHandlerWrapper.cs b/src/Integration/src/Integration/Handler/ReplyProducingMessageHandlerWrapper.cs deleted file mode 100644 index 1dd5f15107..0000000000 --- a/src/Integration/src/Integration/Handler/ReplyProducingMessageHandlerWrapper.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -public class ReplyProducingMessageHandlerWrapper : AbstractReplyProducingMessageHandler, ILifecycle -{ - private readonly IMessageHandler _target; - - public bool IsRunning => _target is not ILifecycle lifecycle || lifecycle.IsRunning; - - public ReplyProducingMessageHandlerWrapper(IApplicationContext context, IMessageHandler target) - : base(context) - { - ArgumentGuard.NotNull(target); - - _target = target; - } - - public Task StartAsync() - { - if (_target is ILifecycle lifeCycle) - { - return lifeCycle.StartAsync(); - } - - return Task.CompletedTask; - } - - public Task StopAsync() - { - if (_target is ILifecycle lifeCycle) - { - return lifeCycle.StopAsync(); - } - - return Task.CompletedTask; - } - - public override void Initialize() - { - // Intentionally left empty. - } - - protected override object HandleRequestMessage(IMessage requestMessage) - { - _target.HandleMessage(requestMessage); - return null; - } -} diff --git a/src/Integration/src/Integration/Handler/ReplyRequiredException.cs b/src/Integration/src/Integration/Handler/ReplyRequiredException.cs deleted file mode 100644 index fb731011cb..0000000000 --- a/src/Integration/src/Integration/Handler/ReplyRequiredException.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -public class ReplyRequiredException : MessagingException -{ - public ReplyRequiredException(IMessage failedMessage, string message) - : base(failedMessage, message) - { - } - - public ReplyRequiredException(IMessage failedMessage, string message, Exception innerException) - : base(failedMessage, message, innerException) - { - } -} diff --git a/src/Integration/src/Integration/Handler/ServiceActivatingHandler.cs b/src/Integration/src/Integration/Handler/ServiceActivatingHandler.cs deleted file mode 100644 index 240ea2c39b..0000000000 --- a/src/Integration/src/Integration/Handler/ServiceActivatingHandler.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Handler; - -public class ServiceActivatingHandler : AbstractReplyProducingMessageHandler, ILifecycle -{ - private readonly IMessageProcessor _processor; - - public override string ComponentType => "service-activator"; - - public virtual bool IsRunning => _processor is not ILifecycle lifecycle || lifecycle.IsRunning; - - public ServiceActivatingHandler(IApplicationContext context, object instance, MethodInfo method) - : this(context, new MethodInvokingMessageProcessor(context, instance, method)) - { - } - - public ServiceActivatingHandler(IApplicationContext context, IMessageProcessor processor) - : base(context) - { - _processor = processor; - } - - public override void Initialize() - { - // Intentionally left empty. - } - - public virtual Task StartAsync() - { - if (_processor is ILifecycle lifecycle) - { - return lifecycle.StartAsync(); - } - - return Task.CompletedTask; - } - - public virtual Task StopAsync() - { - if (_processor is ILifecycle lifecycle) - { - return lifecycle.StopAsync(); - } - - return Task.CompletedTask; - } - - public override string ToString() - { - return $"ServiceActivator for [{_processor}]{(ComponentName == null ? string.Empty : $" ({ComponentName})")}"; - } - - protected override object HandleRequestMessage(IMessage requestMessage) - { - return _processor.ProcessMessage(requestMessage); - } -} diff --git a/src/Integration/src/Integration/Handler/Support/CollectionArgumentResolver.cs b/src/Integration/src/Integration/Handler/Support/CollectionArgumentResolver.cs deleted file mode 100644 index 77d055658e..0000000000 --- a/src/Integration/src/Integration/Handler/Support/CollectionArgumentResolver.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Integration.Handler.Support; - -public class CollectionArgumentResolver : AbstractExpressionEvaluator, IHandlerMethodArgumentResolver -{ - private readonly bool _canProcessMessageList; - - public CollectionArgumentResolver(IApplicationContext context, bool canProcessMessageList) - : base(context) - { - _canProcessMessageList = canProcessMessageList; - } - - public bool SupportsParameter(ParameterInfo parameter) - { - Type parameterType = parameter.ParameterType; - return typeof(ICollection).IsAssignableFrom(parameterType) || typeof(IEnumerator).IsAssignableFrom(parameterType) || parameterType.IsSZArray; - } - - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - object value = message.Payload; - - if (_canProcessMessageList) - { - if (value is not ICollection messages) - { - throw new InvalidOperationException( - $"This Argument Resolver only supports messages with a payload of ICollection, payload is: {value.GetType()}"); - } - - Type paramType = parameter.ParameterType; - - if (paramType.IsGenericType && typeof(IMessage).IsAssignableFrom(paramType.GetGenericArguments()[0])) - { - value = messages; - } - else - { - var payloadList = new List(); - - foreach (IMessage m in messages) - { - payloadList.Add(m.Payload); - } - - value = payloadList; - } - } - - if (typeof(IEnumerator).IsAssignableFrom(parameter.ParameterType)) - { - if (value is IEnumerator) - { - return value; - } - - return new List - { - value - }.GetEnumerator(); - } - - return EvaluationContext.TypeConverter.ConvertValue(value, value?.GetType(), parameter.ParameterType); - } -} diff --git a/src/Integration/src/Integration/Handler/Support/DictionaryArgumentResolver.cs b/src/Integration/src/Integration/Handler/Support/DictionaryArgumentResolver.cs deleted file mode 100644 index dde992b2dd..0000000000 --- a/src/Integration/src/Integration/Handler/Support/DictionaryArgumentResolver.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Integration.Handler.Support; - -public class DictionaryArgumentResolver : AbstractExpressionEvaluator, IHandlerMethodArgumentResolver -{ - public DictionaryArgumentResolver(IApplicationContext context) - : base(context) - { - } - - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - object payload = message.Payload; - - if (parameter.GetCustomAttribute() == null && payload is IDictionary) - { - return payload; - } - - return message.Headers; - } - - public bool SupportsParameter(ParameterInfo parameter) - { - return parameter.GetCustomAttribute() == null && typeof(IDictionary).IsAssignableFrom(parameter.ParameterType); - } -} diff --git a/src/Integration/src/Integration/Handler/Support/PayloadExpressionArgumentResolver.cs b/src/Integration/src/Integration/Handler/Support/PayloadExpressionArgumentResolver.cs deleted file mode 100644 index 1a1bf6c850..0000000000 --- a/src/Integration/src/Integration/Handler/Support/PayloadExpressionArgumentResolver.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Integration.Handler.Support; - -public class PayloadExpressionArgumentResolver : AbstractExpressionEvaluator, IHandlerMethodArgumentResolver -{ - private readonly Dictionary _expressionCache = new(); - - public PayloadExpressionArgumentResolver(IApplicationContext context) - : base(context) - { - } - - public bool SupportsParameter(ParameterInfo parameter) - { - var ann = parameter.GetCustomAttribute(); - return ann != null && !string.IsNullOrEmpty(ann.Expression); - } - - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - _expressionCache.TryGetValue(parameter, out IExpression expression); - - if (expression == null) - { - var ann = parameter.GetCustomAttribute(); - expression = ExpressionParser.ParseExpression(ann.Expression); - _expressionCache.Add(parameter, expression); - } - - return EvaluateExpression(expression, message.Payload, parameter.ParameterType); - } -} diff --git a/src/Integration/src/Integration/Handler/Support/PayloadsArgumentResolver.cs b/src/Integration/src/Integration/Handler/Support/PayloadsArgumentResolver.cs deleted file mode 100644 index 252a7039a0..0000000000 --- a/src/Integration/src/Integration/Handler/Support/PayloadsArgumentResolver.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Integration.Attributes; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Integration.Handler.Support; - -public class PayloadsArgumentResolver : AbstractExpressionEvaluator, IHandlerMethodArgumentResolver -{ - private readonly Dictionary _expressionCache = new(); - - public PayloadsArgumentResolver(IApplicationContext context) - : base(context) - { - } - - public bool SupportsParameter(ParameterInfo parameter) - { - return parameter.GetCustomAttribute() != null; - } - - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - object payload = message.Payload; - - if (payload is not ICollection messages) - { - throw new ArgumentException("This Argument Resolver supports only messages with payload of type ICollection.", nameof(message)); - } - - if (!_expressionCache.ContainsKey(parameter)) - { - var attribute = parameter.GetCustomAttribute(); - - IExpression value = !string.IsNullOrEmpty(attribute.Expression) ? ExpressionParser.ParseExpression($"![payload.{attribute.Expression}]") : null; - _expressionCache.Add(parameter, value); - } - - _expressionCache.TryGetValue(parameter, out IExpression expression); - - if (expression != null) - { - return EvaluateExpression(expression, messages, parameter.ParameterType); - } - - List payloads = messages.Select(m => m.Payload).ToList(); - return EvaluationContext.TypeConverter.ConvertValue(payloads, payloads.GetType(), parameter.ParameterType); - } -} diff --git a/src/Integration/src/Integration/IntegrationMessageHeaderAccessor.cs b/src/Integration/src/Integration/IntegrationMessageHeaderAccessor.cs deleted file mode 100644 index 526f16db05..0000000000 --- a/src/Integration/src/Integration/IntegrationMessageHeaderAccessor.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Integration.Acks; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration; - -public class IntegrationMessageHeaderAccessor : MessageHeaderAccessor -{ - public const string CorrelationId = "correlationId"; - - public const string ExpirationDate = "expirationDate"; - - public const string Priority = "priority"; - - public const string SequenceNumber = "sequenceNumber"; - - public const string SequenceSize = "sequenceSize"; - - public const string SequenceDetails = "sequenceDetails"; - - public const string RoutingSlip = "routingSlip"; - - public const string DuplicateMessage = "duplicateMessage"; - - public const string CloseableResource = "closeableResource"; - - public const string DeliveryAttempt = "deliveryAttempt"; - - public const string AcknowledgmentCallback = "acknowledgmentCallback"; - - public const string SourceData = "sourceData"; - - private ISet _readOnlyHeaders = new HashSet(); - - public IntegrationMessageHeaderAccessor(IMessage message) - : base(message) - { - } - - public void SetReadOnlyHeaders(IList readOnlyHeaders) - { - ArgumentGuard.NotNull(readOnlyHeaders); - ArgumentGuard.ElementsNotNull(readOnlyHeaders); - - if (readOnlyHeaders.Count > 0) - { - _readOnlyHeaders = new HashSet(readOnlyHeaders); - } - } - - public long? GetExpirationDate() - { - return (long?)GetHeader(ExpirationDate); - } - - public object GetCorrelationId() - { - return GetHeader(CorrelationId); - } - - public int GetSequenceNumber() - { - int? sequenceNumber = (int?)GetHeader(SequenceNumber); - return sequenceNumber ?? 0; - } - - public int GetSequenceSize() - { - int? sequenceSize = (int?)GetHeader(SequenceSize); - return sequenceSize ?? 0; - } - - public int? GetPriority() - { - return (int?)GetHeader(Priority); - } - - public IAcknowledgmentCallback GetAcknowledgmentCallback() - { - return (IAcknowledgmentCallback)GetHeader(AcknowledgmentCallback); - } - - public int? GetDeliveryAttempt() - { - return (int?)GetHeader(DeliveryAttempt); - } - - public T GetHeader(string key) - { - object value = GetHeader(key); - - if (value == null) - { - return default; - } - - if (value is not T typedValue) - { - throw new InvalidOperationException($"Incorrect type specified for header '{key}'. Expected [{typeof(T)}] but actual type is [{value.GetType()}]."); - } - - return typedValue; - } - - public override IDictionary ToDictionary() - { - if (_readOnlyHeaders.Count == 0) - { - return base.ToDictionary(); - } - - IDictionary headers = base.ToDictionary(); - - foreach (string header in _readOnlyHeaders) - { - headers.Remove(header); - } - - return headers; - } - - public new bool IsReadOnly(string headerName) - { - return base.IsReadOnly(headerName) || _readOnlyHeaders.Contains(headerName); - } - - protected override void VerifyType(string headerName, object headerValue) - { - if (headerName != null && headerValue != null) - { - base.VerifyType(headerName, headerValue); - - if (headerName == ExpirationDate) - { - if (!(headerValue is DateTime || headerValue is long)) - { - throw new ArgumentException($"The '{headerName}' header value must be a {typeof(DateTime)} or {typeof(long)}.", nameof(headerValue)); - } - } - else if (headerName == SequenceNumber || headerName == SequenceSize || headerName == Priority) - { - if (headerValue is not int) - { - throw new ArgumentException($"The '{headerName}' header value must be a {typeof(int)}.", nameof(headerValue)); - } - } - else if (headerName == RoutingSlip) - { - if (headerValue is not IDictionary) - { - throw new ArgumentException($"The '{headerName}' header value must be a IDictionary.", nameof(headerValue)); - } - } - else if (headerName == DuplicateMessage && headerValue is not bool) - { - throw new ArgumentException($"The '{headerName}' header value must be a {typeof(bool)}.", nameof(headerValue)); - } - } - } -} diff --git a/src/Integration/src/Integration/IntegrationServices.cs b/src/Integration/src/Integration/IntegrationServices.cs deleted file mode 100644 index 940345245f..0000000000 --- a/src/Integration/src/Integration/IntegrationServices.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Support.Channel; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; - -namespace Steeltoe.Integration; - -public class IntegrationServices : IIntegrationServices -{ - protected IMessageBuilderFactory messageBuilderFactory; - protected IConversionService conversionService; - protected IIdGenerator idGenerator; - protected IDestinationResolver channelResolver; - protected IApplicationContext context; - protected IExpressionParser expressionParser; - - public virtual IMessageBuilderFactory MessageBuilderFactory - { - get - { - messageBuilderFactory ??= context?.GetService() ?? new DefaultMessageBuilderFactory(); - return messageBuilderFactory; - } - set => messageBuilderFactory = value; - } - - public virtual IExpressionParser ExpressionParser - { - get - { - expressionParser ??= context?.GetService() ?? new SpelExpressionParser(); - return expressionParser; - } - set => expressionParser = value; - } - - public virtual IDestinationResolver ChannelResolver - { - get - { - channelResolver ??= context?.GetService>() ?? new DefaultMessageChannelResolver(context); - return channelResolver; - } - set => channelResolver = value; - } - - public virtual IConversionService ConversionService - { - get - { - conversionService ??= context?.GetService() ?? DefaultConversionService.Singleton; - return conversionService; - } - set => conversionService = value; - } - - public virtual IIdGenerator IdGenerator - { - get - { - idGenerator ??= context?.GetService() ?? new DefaultIdGenerator(); - return idGenerator; - } - set => idGenerator = value; - } - - public IntegrationServices(IApplicationContext context) - { - this.context = context; - } -} diff --git a/src/Integration/src/Integration/Mapping/AbstractHeaderMapper.cs b/src/Integration/src/Integration/Mapping/AbstractHeaderMapper.cs deleted file mode 100644 index e9687f0ae6..0000000000 --- a/src/Integration/src/Integration/Mapping/AbstractHeaderMapper.cs +++ /dev/null @@ -1,442 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Mapping; - -public abstract class AbstractHeaderMapper : IRequestReplyHeaderMapper -{ - public const string StandardRequestHeaderNamePattern = "STANDARD_REQUEST_HEADERS"; - public const string StandardReplyHeaderNamePattern = "STANDARD_REPLY_HEADERS"; - public const string NonStandardHeaderNamePattern = "NON_STANDARD_HEADERS"; - - private readonly List _transientHeaderNames = new() - { - MessageHeaders.IdName, - MessageHeaders.TimestampName - }; - - private readonly ILogger _logger; - - public string StandardHeaderPrefix { get; set; } - - public List RequestHeaderNames { get; set; } - - public List ReplyHeaderNames { get; set; } - - public IHeaderMatcher RequestHeaderMatcher { get; set; } - - public IHeaderMatcher ReplyHeaderMatcher { get; set; } - - protected AbstractHeaderMapper(string standardHeaderPrefix, List requestHeaderNames, List replyHeaderNames, ILogger logger) - { - StandardHeaderPrefix = standardHeaderPrefix; - RequestHeaderNames = requestHeaderNames; - ReplyHeaderNames = replyHeaderNames; - _logger = logger; - -#pragma warning disable S1699 // Constructors should only call non-overridable methods - RequestHeaderMatcher = CreateDefaultHeaderMatcher(StandardHeaderPrefix, RequestHeaderNames); - ReplyHeaderMatcher = CreateDefaultHeaderMatcher(StandardHeaderPrefix, ReplyHeaderNames); -#pragma warning restore S1699 // Constructors should only call non-overridable methods - } - - public void SetRequestHeaderNames(params string[] requestHeaderNames) - { - ArgumentGuard.NotNull(requestHeaderNames); - - RequestHeaderMatcher = CreateHeaderMatcher(requestHeaderNames); - } - - public void SetReplyHeaderNames(params string[] replyHeaderNames) - { - ArgumentGuard.NotNull(replyHeaderNames); - - ReplyHeaderMatcher = CreateHeaderMatcher(replyHeaderNames); - } - - public void FromHeadersToRequest(IMessageHeaders headers, T target) - { - FromHeaders(headers, target, RequestHeaderMatcher); - } - - public void FromHeadersToReply(IMessageHeaders headers, T target) - { - FromHeaders(headers, target, ReplyHeaderMatcher); - } - - public IDictionary ToHeadersFromRequest(T source) - { - return ToHeaders(source, RequestHeaderMatcher); - } - - public IDictionary ToHeadersFromReply(T source) - { - return ToHeaders(source, ReplyHeaderMatcher); - } - - protected virtual IHeaderMatcher CreateDefaultHeaderMatcher(string standardHeaderPrefix, List headerNames) - { - return new ContentBasedHeaderMatcher(true, new List(headerNames)); - } - - protected virtual IHeaderMatcher CreateHeaderMatcher(string[] patterns) - { - var matchers = new List(); - - foreach (string pattern in patterns) - { - if (pattern == StandardRequestHeaderNamePattern) - { - matchers.Add(new ContentBasedHeaderMatcher(true, RequestHeaderNames)); - } - else if (pattern == StandardReplyHeaderNamePattern) - { - matchers.Add(new ContentBasedHeaderMatcher(true, ReplyHeaderNames)); - } - else if (pattern == NonStandardHeaderNamePattern) - { - matchers.Add(new PrefixBasedMatcher(false, StandardHeaderPrefix)); - } - else - { - string thePattern = pattern; - bool negate = false; - - if (pattern.StartsWith('!')) - { - thePattern = pattern[1..]; - negate = true; - } - else if (pattern.StartsWith("\\!", StringComparison.Ordinal)) - { - thePattern = pattern[1..]; - } - - if (negate) - { - // negative matchers get priority - matchers.Insert(0, new SinglePatternBasedHeaderMatcher(thePattern, true)); - } - else - { - matchers.Add(new SinglePatternBasedHeaderMatcher(thePattern, false)); - } - } - } - - return new CompositeHeaderMatcher(matchers); - } - - protected virtual TValue GetHeaderIfAvailable(IDictionary headers, string name, Type type) - { - headers.TryGetValue(name, out object value); - - if (value == null) - { - return default; - } - - if (!type.IsInstanceOfType(value)) - { - return default; - } - - return (TValue)value; - } - - protected virtual string CreateTargetPropertyName(string propertyName, bool fromMessageHeaders) - { - return propertyName; - } - - protected virtual List GetTransientHeaderNames() - { - return _transientHeaderNames; - } - - protected abstract IDictionary ExtractStandardHeaders(T source); - - protected abstract IDictionary ExtractUserDefinedHeaders(T source); - - protected abstract void PopulateStandardHeaders(IDictionary headers, T target); - - protected virtual void PopulateStandardHeaders(IDictionary allHeaders, IDictionary subset, T target) - { - PopulateStandardHeaders(subset, target); - } - - protected abstract void PopulateUserDefinedHeader(string headerName, object headerValue, T target); - - private static bool IsMessageChannel(object headerValue) - { - return headerValue is IMessageChannel; - } - - private void FromHeaders(IMessageHeaders headers, T target, IHeaderMatcher headerMatcher) - { - try - { - var subset = new Dictionary(); - - foreach (KeyValuePair entry in (IDictionary)headers) - { - string headerName = entry.Key; - - if (ShouldMapHeader(headerName, headerMatcher)) - { - subset[headerName] = entry.Value; - } - } - - PopulateStandardHeaders(headers, subset, target); - PopulateUserDefinedHeaders(subset, target); - } - catch (Exception ex) - { - _logger?.LogError(ex, ex.Message); - } - } - - private void PopulateUserDefinedHeaders(IDictionary headers, T target) - { - foreach (KeyValuePair entry in headers) - { - string headerName = entry.Key; - object value = entry.Value; - - if (value != null && !IsMessageChannel(value)) - { - try - { - if (!headerName.StartsWith(StandardHeaderPrefix, StringComparison.Ordinal)) - { - string key = CreateTargetPropertyName(headerName, true); - PopulateUserDefinedHeader(key, value, target); - } - } - catch (Exception ex) - { - _logger?.LogError(ex, ex.Message); - } - } - } - } - - private Dictionary ToHeaders(T source, IHeaderMatcher headerMatcher) - { - var headers = new Dictionary(); - IDictionary standardHeaders = ExtractStandardHeaders(source); - CopyHeaders(standardHeaders, headers, headerMatcher); - IDictionary userDefinedHeaders = ExtractUserDefinedHeaders(source); - CopyHeaders(userDefinedHeaders, headers, headerMatcher); - return headers; - } - - private void CopyHeaders(IDictionary source, IDictionary target, IHeaderMatcher headerMatcher) - { - if (source != null) - { - foreach (KeyValuePair entry in source) - { - try - { - string headerName = CreateTargetPropertyName(entry.Key, false); - - if (ShouldMapHeader(headerName, headerMatcher)) - { - target[headerName] = entry.Value; - } - } - catch (Exception) - { - // Log - } - } - } - } - - private bool ShouldMapHeader(string headerName, IHeaderMatcher headerMatcher) - { - return !(string.IsNullOrEmpty(headerName) || GetTransientHeaderNames().Contains(headerName)) && headerMatcher.MatchHeader(headerName); - } - - public interface IHeaderMatcher - { - bool IsNegated { get; } - - bool MatchHeader(string headerName); - } - - protected class ContentBasedHeaderMatcher : IHeaderMatcher - { - private bool Match { get; } - - private List Content { get; } - - public bool IsNegated => false; - - public ContentBasedHeaderMatcher(bool match, List content) - { - ArgumentGuard.NotNull(content); - - Match = match; - Content = content; - } - - public bool MatchHeader(string headerName) - { - bool result = Match == ContainsIgnoreCase(headerName); - return result; - } - - private bool ContainsIgnoreCase(string name) - { - foreach (string headerName in Content) - { - if (headerName.Equals(name, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - } - - protected class PatternBasedHeaderMatcher : IHeaderMatcher - { - private List Patterns { get; } = new(); - - public bool IsNegated => false; - - public PatternBasedHeaderMatcher(List patterns) - { - ArgumentGuard.NotNullOrEmpty(patterns); - - foreach (string pattern in patterns) - { -#pragma warning disable S4040 // Strings should be normalized to uppercase - Patterns.Add(pattern.ToLowerInvariant()); -#pragma warning restore S4040 // Strings should be normalized to uppercase - } - } - - public bool MatchHeader(string headerName) - { -#pragma warning disable S4040 // Strings should be normalized to uppercase - string header = headerName.ToLowerInvariant(); -#pragma warning restore S4040 // Strings should be normalized to uppercase - - foreach (string pattern in Patterns) - { - if (PatternMatchUtils.SimpleMatch(pattern, header)) - { - return true; - } - } - - return false; - } - } - - protected class SinglePatternBasedHeaderMatcher : IHeaderMatcher - { - private string Pattern { get; } - - private bool Negate { get; } - - public bool IsNegated => Negate; - - public SinglePatternBasedHeaderMatcher(string pattern) - : this(pattern, false) - { - } - - public SinglePatternBasedHeaderMatcher(string pattern, bool negate) - { - ArgumentGuard.NotNull(pattern); - -#pragma warning disable S4040 // Strings should be normalized to uppercase - Pattern = pattern.ToLowerInvariant(); -#pragma warning restore S4040 // Strings should be normalized to uppercase - - Negate = negate; - } - - public bool MatchHeader(string headerName) - { -#pragma warning disable S4040 // Strings should be normalized to uppercase - string header = headerName.ToLowerInvariant(); -#pragma warning restore S4040 // Strings should be normalized to uppercase - - if (PatternMatchUtils.SimpleMatch(Pattern, header)) - { - return true; - } - - return false; - } - } - - protected class PrefixBasedMatcher : IHeaderMatcher - { - private bool Match { get; } - - private string Prefix { get; } - - public bool IsNegated { get; } - - public PrefixBasedMatcher(bool match, string prefix) - { - Match = match; - Prefix = prefix; - } - - public bool MatchHeader(string headerName) - { - bool result = Match == headerName.StartsWith(Prefix, StringComparison.Ordinal); - return result; - } - } - - protected class CompositeHeaderMatcher : IHeaderMatcher - { - private List Matchers { get; } - - public bool IsNegated { get; } - - public CompositeHeaderMatcher(List strategies) - { - Matchers = strategies; - } - - public CompositeHeaderMatcher(params IHeaderMatcher[] strategies) - : this(new List(strategies)) - { - } - - public bool MatchHeader(string headerName) - { - foreach (IHeaderMatcher strategy in Matchers) - { - if (strategy.MatchHeader(headerName)) - { - if (strategy.IsNegated) - { - break; - } - - return true; - } - } - - return false; - } - } -} diff --git a/src/Integration/src/Integration/MessageDispatchingException.cs b/src/Integration/src/Integration/MessageDispatchingException.cs deleted file mode 100644 index 8fd1ef99bf..0000000000 --- a/src/Integration/src/Integration/MessageDispatchingException.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -public class MessageDispatchingException : MessageDeliveryException -{ - public MessageDispatchingException(string message) - : base(message) - { - } - - public MessageDispatchingException(IMessage failedMessage) - : base(failedMessage) - { - } - - public MessageDispatchingException(IMessage failedMessage, string message) - : base(failedMessage, message) - { - } - - public MessageDispatchingException(IMessage failedMessage, string message, Exception innerException) - : base(failedMessage, message, innerException) - { - } -} diff --git a/src/Integration/src/Integration/MessageRejectedException.cs b/src/Integration/src/Integration/MessageRejectedException.cs deleted file mode 100644 index 6b19f4c9db..0000000000 --- a/src/Integration/src/Integration/MessageRejectedException.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -public class MessageRejectedException : MessageHandlingException -{ - public MessageRejectedException(IMessage failedMessage, string message) - : base(failedMessage, message) - { - } - - public MessageRejectedException(IMessage failedMessage, string message, Exception innerException) - : base(failedMessage, message, innerException) - { - } -} diff --git a/src/Integration/src/Integration/MessageTimeoutException.cs b/src/Integration/src/Integration/MessageTimeoutException.cs deleted file mode 100644 index e784828550..0000000000 --- a/src/Integration/src/Integration/MessageTimeoutException.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -public class MessageTimeoutException : MessageDeliveryException -{ - public MessageTimeoutException(string message) - : base(message) - { - } - - public MessageTimeoutException(IMessage failedMessage) - : base(failedMessage) - { - } - - public MessageTimeoutException(IMessage failedMessage, string message) - : base(failedMessage, message) - { - } - - public MessageTimeoutException(IMessage failedMessage, string message, Exception innerException) - : base(failedMessage, message, innerException) - { - } -} diff --git a/src/Integration/src/Integration/MessagingTemplate.cs b/src/Integration/src/Integration/MessagingTemplate.cs deleted file mode 100644 index e2d092e608..0000000000 --- a/src/Integration/src/Integration/MessagingTemplate.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; - -namespace Steeltoe.Integration; - -public class MessagingTemplate : MessageChannelTemplate -{ - public IMessageChannel DefaultDestination - { - // Default Receive and Send are kept the same - get => DefaultReceiveDestination; - set => DefaultSendDestination = DefaultReceiveDestination = value; - } - - public override IMessageChannel DefaultReceiveDestination - { - get => base.DefaultReceiveDestination; - set => base.DefaultReceiveDestination = base.DefaultSendDestination = value; - } - - public override IMessageChannel DefaultSendDestination - { - get => base.DefaultSendDestination; - set => base.DefaultSendDestination = DefaultReceiveDestination = value; - } - - public MessagingTemplate(IApplicationContext context, ILogger logger = null) - : base(context, logger) - { - } - - public MessagingTemplate(IApplicationContext context, IMessageChannel defaultChannel, ILogger logger = null) - : base(context, logger) - { - DefaultSendDestination = DefaultReceiveDestination = defaultChannel; - } - - public object ReceiveAndConvert(IMessageChannel destination, int timeout) - { - IMessage message = DoReceive(destination, timeout); - - if (message != null) - { - return DoConvert(message); - } - - return Task.FromResult(null); - } - - public IMessage Receive(IMessageChannel destination, int timeout) - { - return DoReceive(destination, timeout); - } -} diff --git a/src/Integration/src/Integration/Properties/AssemblyInfo.cs b/src/Integration/src/Integration/Properties/AssemblyInfo.cs deleted file mode 100644 index 4abe05b2a8..0000000000 --- a/src/Integration/src/Integration/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Steeltoe.Integration.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Stream.Test")] diff --git a/src/Integration/src/Integration/RetryExceptionNotAvailableException.cs b/src/Integration/src/Integration/RetryExceptionNotAvailableException.cs deleted file mode 100644 index c8f5273fd8..0000000000 --- a/src/Integration/src/Integration/RetryExceptionNotAvailableException.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -public class RetryExceptionNotAvailableException : MessagingException -{ - public RetryExceptionNotAvailableException(IMessage failedMessage, string message) - : base(failedMessage, message) - { - } - - internal RetryExceptionNotAvailableException(string message) - : base(message) - { - } -} diff --git a/src/Integration/src/Integration/StaticMessageHeaderAccessor.cs b/src/Integration/src/Integration/StaticMessageHeaderAccessor.cs deleted file mode 100644 index 32e4987081..0000000000 --- a/src/Integration/src/Integration/StaticMessageHeaderAccessor.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Acks; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration; - -public static class StaticMessageHeaderAccessor -{ - public static Guid? GetId(IMessage message) - { - message.Headers.TryGetValue(MessageHeaders.IdName, out object value); - - if (value == null) - { - return null; - } - - return value is Guid guidValue ? guidValue : Guid.Parse(value.ToString()); - } - - public static long? GetTimestamp(IMessage message) - { - message.Headers.TryGetValue(MessageHeaders.TimestampName, out object value); - - if (value == null) - { - return null; - } - - return value is long longValue ? longValue : long.Parse(value.ToString(), CultureInfo.InvariantCulture); - } - - public static MimeType GetContentType(IMessage message) - { - message.Headers.TryGetValue(MessageHeaders.ContentType, out object value); - - if (value == null) - { - return null; - } - - return value as MimeType ?? MimeType.ToMimeType(value.ToString()); - } - - public static long? GetExpirationDate(IMessage message) - { - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.ExpirationDate, out object value); - - if (value == null) - { - return null; - } - - return value is long longValue ? longValue : long.Parse(value.ToString(), CultureInfo.InvariantCulture); - } - - public static int? GetSequenceNumber(IMessage message) - { - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceNumber, out object value); - return value != null ? int.Parse(value.ToString(), CultureInfo.InvariantCulture) : null; - } - - public static int? GetSequenceSize(IMessage message) - { - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceSize, out object value); - return value != null ? int.Parse(value.ToString(), CultureInfo.InvariantCulture) : null; - } - - public static int? GetPriority(IMessage message) - { - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.Priority, out object value); - return value != null ? int.Parse(value.ToString(), CultureInfo.InvariantCulture) : null; - } - - public static IAcknowledgmentCallback GetAcknowledgmentCallback(IMessage message) - { - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.AcknowledgmentCallback, out object value); - return value as IAcknowledgmentCallback; - } -} diff --git a/src/Integration/src/Integration/Steeltoe.Integration.csproj b/src/Integration/src/Integration/Steeltoe.Integration.csproj deleted file mode 100644 index b077dcafb6..0000000000 --- a/src/Integration/src/Integration/Steeltoe.Integration.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - net8.0;net6.0 - - Steeltoe Integration Base - **PLEASE NOTE** The public API of this package is subject to change and is not recommended for direct use outside of Steeltoe. - - Integration, NET Core, Spring, Spring Cloud - true - - - - - - - - - diff --git a/src/Integration/src/Integration/Support/AbstractMessageBuilder.cs b/src/Integration/src/Integration/Support/AbstractMessageBuilder.cs deleted file mode 100644 index 23712d1378..0000000000 --- a/src/Integration/src/Integration/Support/AbstractMessageBuilder.cs +++ /dev/null @@ -1,226 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -public abstract class AbstractMessageBuilder : IMessageBuilder -{ - protected readonly object InnerPayload; - - protected readonly IMessage OriginalMessage; - - protected readonly IntegrationMessageHeaderAccessor HeaderAccessor; - - protected volatile bool modified; - - protected IList readOnlyHeaders; - - protected abstract List> SequenceDetails { get; } - - protected abstract object CorrelationId { get; } - - protected abstract object SequenceNumber { get; } - - protected abstract object SequenceSize { get; } - - public abstract object Payload { get; } - - public abstract IDictionary Headers { get; } - - protected AbstractMessageBuilder() - { - } - - protected AbstractMessageBuilder(object payload, IMessage originalMessage) - { - ArgumentGuard.NotNull(payload); - - InnerPayload = payload; - OriginalMessage = originalMessage; - HeaderAccessor = new IntegrationMessageHeaderAccessor(originalMessage); - - if (originalMessage != null) - { - modified = !InnerPayload.Equals(originalMessage.Payload); - } - } - - public virtual IMessageBuilder SetExpirationDate(long expirationDate) - { - return SetHeader(IntegrationMessageHeaderAccessor.ExpirationDate, expirationDate); - } - - public virtual IMessageBuilder SetExpirationDate(DateTime? expirationDate) - { - if (expirationDate != null) - { - var datetime = new DateTimeOffset(expirationDate.Value); - return SetHeader(IntegrationMessageHeaderAccessor.ExpirationDate, datetime.ToUnixTimeMilliseconds()); - } - - return SetHeader(IntegrationMessageHeaderAccessor.ExpirationDate, null); - } - - public virtual IMessageBuilder SetCorrelationId(object correlationId) - { - return SetHeader(IntegrationMessageHeaderAccessor.CorrelationId, correlationId); - } - - public virtual IMessageBuilder PushSequenceDetails(object correlationId, int sequenceNumber, int sequenceSize) - { - object incomingCorrelationId = CorrelationId; - List> incomingSequenceDetails = SequenceDetails; - - if (incomingCorrelationId != null) - { - incomingSequenceDetails = incomingSequenceDetails == null ? new List>() : new List>(incomingSequenceDetails); - - incomingSequenceDetails.Add(new List - { - incomingCorrelationId, - SequenceNumber, - SequenceSize - }); - } - - if (incomingSequenceDetails != null) - { - SetHeader(IntegrationMessageHeaderAccessor.SequenceDetails, incomingSequenceDetails); - } - - return SetCorrelationId(correlationId).SetSequenceNumber(sequenceNumber).SetSequenceSize(sequenceSize); - } - - public virtual IMessageBuilder PopSequenceDetails() - { - List> incomingSequenceDetails = SequenceDetails; - - if (incomingSequenceDetails == null) - { - return this; - } - - incomingSequenceDetails = new List>(incomingSequenceDetails); - - List sequenceDetails = incomingSequenceDetails[^1]; - incomingSequenceDetails.RemoveAt(incomingSequenceDetails.Count - 1); - - if (sequenceDetails.Count != 3) - { - throw new InvalidOperationException("Wrong sequence details (not created by MessageBuilder?)"); - } - - SetCorrelationId(sequenceDetails[0]); - int? sequenceNumber = sequenceDetails[1] as int?; - int? sequenceSize = sequenceDetails[2] as int?; - - if (sequenceNumber.HasValue) - { - SetSequenceNumber(sequenceNumber.Value); - } - - if (sequenceSize.HasValue) - { - SetSequenceSize(sequenceSize.Value); - } - - if (incomingSequenceDetails.Count > 0) - { - SetHeader(IntegrationMessageHeaderAccessor.SequenceDetails, incomingSequenceDetails); - } - else - { - RemoveHeader(IntegrationMessageHeaderAccessor.SequenceDetails); - } - - return this; - } - - public virtual IMessageBuilder SetReplyChannel(IMessageChannel replyChannel) - { - return SetHeader(MessageHeaders.ReplyChannelName, replyChannel); - } - - public virtual IMessageBuilder SetReplyChannelName(string replyChannelName) - { - return SetHeader(MessageHeaders.ReplyChannelName, replyChannelName); - } - - public virtual IMessageBuilder SetErrorChannel(IMessageChannel errorChannel) - { - return SetHeader(MessageHeaders.ErrorChannelName, errorChannel); - } - - public virtual IMessageBuilder SetErrorChannelName(string errorChannelName) - { - return SetHeader(MessageHeaders.ErrorChannelName, errorChannelName); - } - - public virtual IMessageBuilder SetSequenceNumber(int sequenceNumber) - { - return SetHeader(IntegrationMessageHeaderAccessor.SequenceNumber, sequenceNumber); - } - - public virtual IMessageBuilder SetSequenceSize(int sequenceSize) - { - return SetHeader(IntegrationMessageHeaderAccessor.SequenceSize, sequenceSize); - } - - public virtual IMessageBuilder SetPriority(int priority) - { - return SetHeader(IntegrationMessageHeaderAccessor.Priority, priority); - } - - public virtual IMessageBuilder FilterAndCopyHeadersIfAbsent(IDictionary headersToCopy, params string[] headerPatternsToFilter) - { - IDictionary headers = new Dictionary(headersToCopy); - - if (headerPatternsToFilter?.Length > 0) - { - foreach (KeyValuePair entry in headersToCopy) - { - if (PatternMatchUtils.SimpleMatch(headerPatternsToFilter, entry.Key)) - { - headers.Remove(entry.Key); - } - } - } - - return CopyHeadersIfAbsent(headers); - } - - public abstract IMessageBuilder SetHeader(string headerName, object headerValue); - - public abstract IMessageBuilder SetHeaderIfAbsent(string headerName, object headerValue); - - public abstract IMessageBuilder RemoveHeaders(params string[] headerPatterns); - - public abstract IMessageBuilder RemoveHeader(string headerName); - - public abstract IMessageBuilder CopyHeaders(IDictionary headersToCopy); - - public abstract IMessageBuilder CopyHeadersIfAbsent(IDictionary headersToCopy); - - public abstract IMessage Build(); - - protected bool ContainsReadOnly(IMessageHeaders headers) - { - if (readOnlyHeaders != null) - { - foreach (string readOnly in readOnlyHeaders) - { - if (headers.ContainsKey(readOnly)) - { - return true; - } - } - } - - return false; - } -} diff --git a/src/Integration/src/Integration/Support/Channel/DefaultMessageChannelResolver.cs b/src/Integration/src/Integration/Support/Channel/DefaultMessageChannelResolver.cs deleted file mode 100644 index 91f87e8578..0000000000 --- a/src/Integration/src/Integration/Support/Channel/DefaultMessageChannelResolver.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; - -namespace Steeltoe.Integration.Support.Channel; - -public class DefaultMessageChannelResolver : IDestinationResolver -{ - private readonly IApplicationContext _context; - private readonly IHeaderChannelRegistry _registry; - - public DefaultMessageChannelResolver(IApplicationContext context, IHeaderChannelRegistry registry = null) - { - ArgumentGuard.NotNull(context); - - _context = context; - _registry = registry; - } - - public virtual IMessageChannel ResolveDestination(string name) - { - var result = _context.GetService(name); - - if (result != null) - { - return result; - } - - if (_registry != null) - { - IMessageChannel channel = _registry.ChannelNameToChannel(name); - - if (channel != null) - { - return channel; - } - } - - throw new DestinationResolutionException( - $"failed to look up MessageChannel with name '{name}' in the Service Container{(_registry == null ? " (and there is no IHeaderChannelRegistry present)." : ".")}"); - } - - object IDestinationResolver.ResolveDestination(string name) - { - return ResolveDestination(name); - } -} diff --git a/src/Integration/src/Integration/Support/Converter/ConfigurableCompositeMessageConverter.cs b/src/Integration/src/Integration/Support/Converter/ConfigurableCompositeMessageConverter.cs deleted file mode 100644 index 2fec5a32b9..0000000000 --- a/src/Integration/src/Integration/Support/Converter/ConfigurableCompositeMessageConverter.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Converter; -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Integration.Support.Converter; - -public class ConfigurableCompositeMessageConverter : CompositeMessageConverter -{ - private readonly bool _registerDefaults; - private IConversionService _conversionService; - - public ConfigurableCompositeMessageConverter(IConversionService conversionService = null) - : base(InitDefaults()) - { - _registerDefaults = true; - _conversionService = conversionService; - AfterPropertiesSet(); - } - - public ConfigurableCompositeMessageConverter(IMessageConverterFactory factory, IConversionService conversionService = null) - : this(factory.AllRegistered, false, conversionService) - { - } - - public ConfigurableCompositeMessageConverter(IEnumerable converters, bool registerDefaults, IConversionService conversionService = null) - : base(registerDefaults ? InitDefaults(converters) : converters.ToList()) - { - _registerDefaults = registerDefaults; - _conversionService = conversionService; - AfterPropertiesSet(); - } - - protected void AfterPropertiesSet() - { - if (_registerDefaults) - { - _conversionService ??= DefaultConversionService.Singleton; - - Converters.Add(new GenericMessageConverter(_conversionService)); - } - } - - private static ICollection InitDefaults(IEnumerable extras = null) - { - List converters = extras != null ? new List(extras) : new List(); - - converters.Add(new NewtonJsonMessageConverter()); - converters.Add(new ByteArrayMessageConverter()); - converters.Add(new ObjectStringMessageConverter()); - converters.Add(new GenericMessageConverter()); - - return converters; - } -} diff --git a/src/Integration/src/Integration/Support/Converter/DefaultDatatypeChannelMessageConverter.cs b/src/Integration/src/Integration/Support/Converter/DefaultDatatypeChannelMessageConverter.cs deleted file mode 100644 index 2e041396fc..0000000000 --- a/src/Integration/src/Integration/Support/Converter/DefaultDatatypeChannelMessageConverter.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Converter; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Integration.Support.Converter; - -public class DefaultDataTypeChannelMessageConverter : IMessageConverter -{ - public const string DefaultServiceName = nameof(DefaultDataTypeChannelMessageConverter); - - private readonly IConversionService _conversionService; - - public string ServiceName { get; set; } = DefaultServiceName; - - public DefaultDataTypeChannelMessageConverter(IConversionService conversionService = null) - { - _conversionService = conversionService ?? DefaultConversionService.Singleton; - } - - public object FromMessage(IMessage message, Type targetType) - { - if (_conversionService.CanConvert(message.Payload.GetType(), targetType)) - { - return _conversionService.Convert(message.Payload, message.Payload.GetType(), targetType); - } - - return null; - } - - public T FromMessage(IMessage message) - { - return (T)FromMessage(message, typeof(T)); - } - - public IMessage ToMessage(object payload, IMessageHeaders headers) - { - throw new NotImplementedException("This converter does not support this method"); - } -} diff --git a/src/Integration/src/Integration/Support/Converter/ObjectStringMessageConverter.cs b/src/Integration/src/Integration/Support/Converter/ObjectStringMessageConverter.cs deleted file mode 100644 index 96ce709449..0000000000 --- a/src/Integration/src/Integration/Support/Converter/ObjectStringMessageConverter.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Integration.Support.Converter; - -public class ObjectStringMessageConverter : StringMessageConverter -{ - protected override object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - object payload = message.Payload; - - if (payload is string || payload is byte[]) - { - return base.ConvertFromInternal(message, targetClass, conversionHint); - } - - return payload.ToString(); - } -} diff --git a/src/Integration/src/Integration/Support/DefaultErrorMessageStrategy.cs b/src/Integration/src/Integration/Support/DefaultErrorMessageStrategy.cs deleted file mode 100644 index 45a78f72f3..0000000000 --- a/src/Integration/src/Integration/Support/DefaultErrorMessageStrategy.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Support; - -public class DefaultErrorMessageStrategy : IErrorMessageStrategy -{ - public ErrorMessage BuildErrorMessage(Exception exception, IAttributeAccessor attributeAccessor) - { - object inputMessage = attributeAccessor?.GetAttribute(ErrorMessageUtils.InputMessageContextKey); - - return inputMessage switch - { - IMessage iMessage => new ErrorMessage(exception, iMessage), - _ => new ErrorMessage(exception) - }; - } -} diff --git a/src/Integration/src/Integration/Support/DefaultMessageBuilderFactory.cs b/src/Integration/src/Integration/Support/DefaultMessageBuilderFactory.cs deleted file mode 100644 index 5fdb5cbce1..0000000000 --- a/src/Integration/src/Integration/Support/DefaultMessageBuilderFactory.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -public class DefaultMessageBuilderFactory : IMessageBuilderFactory -{ - public IList ReadOnlyHeaders { get; set; } = new List(); - - public void AddReadOnlyHeaders(params string[] readOnlyHeaders) - { - foreach (string h in readOnlyHeaders) - { - ReadOnlyHeaders.Add(h); - } - } - - public IMessageBuilder FromMessage(IMessage message) - { - return IntegrationMessageBuilder.FromMessage(message).ReadOnlyHeaders(ReadOnlyHeaders); - } - - public IMessageBuilder FromMessage(IMessage message) - { - return IntegrationMessageBuilder.FromMessage(message).ReadOnlyHeaders(ReadOnlyHeaders); - } - - public IMessageBuilder WithPayload(T payload) - { - return IntegrationMessageBuilder.WithPayload(payload).ReadOnlyHeaders(ReadOnlyHeaders); - } - - public IMessageBuilder WithPayload(object payload) - { - return IntegrationMessageBuilder.WithPayload(payload).ReadOnlyHeaders(ReadOnlyHeaders); - } -} diff --git a/src/Integration/src/Integration/Support/ErrorMessageAttributes.cs b/src/Integration/src/Integration/Support/ErrorMessageAttributes.cs deleted file mode 100644 index 7f36aecec1..0000000000 --- a/src/Integration/src/Integration/Support/ErrorMessageAttributes.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Integration.Support; - -public class ErrorMessageAttributes : AbstractAttributeAccessor -{ -} diff --git a/src/Integration/src/Integration/Support/ErrorMessageUtils.cs b/src/Integration/src/Integration/Support/ErrorMessageUtils.cs deleted file mode 100644 index efbcd063ef..0000000000 --- a/src/Integration/src/Integration/Support/ErrorMessageUtils.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -public static class ErrorMessageUtils -{ - public const string FailedMessageContextKey = $"{MessageHeaders.Internal}message"; - - public const string InputMessageContextKey = $"{MessageHeaders.Internal}inputMessage"; - - public static IAttributeAccessor GetAttributeAccessor(IMessage inputMessage, IMessage failedMessage) - { - AbstractAttributeAccessor attributes = new ErrorMessageAttributes(); - - if (inputMessage != null) - { - attributes.SetAttribute(InputMessageContextKey, inputMessage); - } - - if (failedMessage != null) - { - attributes.SetAttribute(FailedMessageContextKey, failedMessage); - } - - return attributes; - } -} diff --git a/src/Integration/src/Integration/Support/IntegrationMessageBuilder.cs b/src/Integration/src/Integration/Support/IntegrationMessageBuilder.cs deleted file mode 100644 index e246049c89..0000000000 --- a/src/Integration/src/Integration/Support/IntegrationMessageBuilder.cs +++ /dev/null @@ -1,272 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Support; - -public class IntegrationMessageBuilder : IntegrationMessageBuilder, IMessageBuilder -{ - public new T Payload => (T)base.Payload; - - internal IntegrationMessageBuilder(T payload, IMessage originalMessage) - : base(payload, originalMessage) - { - } - - public static IntegrationMessageBuilder FromMessage(IMessage message) - { - ArgumentGuard.NotNull(message); - - return new IntegrationMessageBuilder(message.Payload, message); - } - - public static IntegrationMessageBuilder WithPayload(T payload) - { - return new IntegrationMessageBuilder(payload, null); - } - - public new IMessageBuilder SetHeader(string headerName, object headerValue) - { - base.SetHeader(headerName, headerValue); - return this; - } - - public new IMessageBuilder SetHeaderIfAbsent(string headerName, object headerValue) - { - base.SetHeaderIfAbsent(headerName, headerValue); - return this; - } - - public new IMessageBuilder RemoveHeaders(params string[] headerPatterns) - { - base.RemoveHeaders(headerPatterns); - return this; - } - - public new IMessageBuilder RemoveHeader(string headerName) - { - base.RemoveHeader(headerName); - return this; - } - - public new IMessageBuilder CopyHeaders(IDictionary headersToCopy) - { - base.CopyHeaders(headersToCopy); - return this; - } - - public new IMessageBuilder CopyHeadersIfAbsent(IDictionary headersToCopy) - { - base.CopyHeadersIfAbsent(headersToCopy); - return this; - } - - public new IMessageBuilder ReadOnlyHeaders(IList values) - { - base.ReadOnlyHeaders(values); - return this; - } - - public new IMessage Build() - { - if (!modified && !HeaderAccessor.IsModified && OriginalMessage != null && !ContainsReadOnly(OriginalMessage.Headers)) - { - return (IMessage)OriginalMessage; - } - - if (InnerPayload is Exception exception) - { - return (IMessage)new ErrorMessage(exception, HeaderAccessor.ToDictionary()); - } - - return Message.Create((T)InnerPayload, HeaderAccessor.ToDictionary()); - } - - public new IMessageBuilder SetExpirationDate(long expirationDate) - { - base.SetExpirationDate(expirationDate); - return this; - } - - public new IMessageBuilder SetExpirationDate(DateTime? expirationDate) - { - base.SetExpirationDate(expirationDate); - return this; - } - - public new IMessageBuilder SetCorrelationId(object correlationId) - { - base.SetCorrelationId(correlationId); - return this; - } - - public new IMessageBuilder PushSequenceDetails(object correlationId, int sequenceNumber, int sequenceSize) - { - base.PushSequenceDetails(correlationId, sequenceNumber, sequenceSize); - return this; - } - - public new IMessageBuilder PopSequenceDetails() - { - base.PopSequenceDetails(); - return this; - } - - public new IMessageBuilder SetReplyChannel(IMessageChannel replyChannel) - { - base.SetReplyChannel(replyChannel); - return this; - } - - public new IMessageBuilder SetReplyChannelName(string replyChannelName) - { - base.SetReplyChannelName(replyChannelName); - return this; - } - - public new IMessageBuilder SetErrorChannel(IMessageChannel errorChannel) - { - base.SetErrorChannel(errorChannel); - return this; - } - - public new IMessageBuilder SetErrorChannelName(string errorChannelName) - { - base.SetErrorChannelName(errorChannelName); - return this; - } - - public new IMessageBuilder SetSequenceNumber(int sequenceNumber) - { - base.SetSequenceNumber(sequenceNumber); - return this; - } - - public new IMessageBuilder SetSequenceSize(int sequenceSize) - { - base.SetSequenceSize(sequenceSize); - return this; - } - - public new IMessageBuilder SetPriority(int priority) - { - base.SetPriority(priority); - return this; - } - - public new IMessageBuilder FilterAndCopyHeadersIfAbsent(IDictionary headersToCopy, params string[] headerPatternsToFilter) - { - base.FilterAndCopyHeadersIfAbsent(headersToCopy, headerPatternsToFilter); - return this; - } -} - -public class IntegrationMessageBuilder : AbstractMessageBuilder -{ - protected override List> SequenceDetails => (List>)HeaderAccessor.GetHeader(IntegrationMessageHeaderAccessor.SequenceDetails); - - protected override object CorrelationId => HeaderAccessor.GetCorrelationId(); - - protected override object SequenceNumber => HeaderAccessor.GetSequenceNumber(); - - protected override object SequenceSize => HeaderAccessor.GetSequenceSize(); - - public override object Payload => InnerPayload; - - public override IDictionary Headers => HeaderAccessor.ToDictionary(); - - internal IntegrationMessageBuilder(object payload, IMessage originalMessage) - : base(payload, originalMessage) - { - } - - public static IntegrationMessageBuilder FromMessage(IMessage message) - { - ArgumentGuard.NotNull(message); - - return new IntegrationMessageBuilder(message.Payload, message); - } - - public static IntegrationMessageBuilder WithPayload(object payload) - { - return new IntegrationMessageBuilder(payload, null); - } - - public override IMessageBuilder SetHeader(string headerName, object headerValue) - { - HeaderAccessor.SetHeader(headerName, headerValue); - return this; - } - - public override IMessageBuilder SetHeaderIfAbsent(string headerName, object headerValue) - { - HeaderAccessor.SetHeaderIfAbsent(headerName, headerValue); - return this; - } - - public override IMessageBuilder RemoveHeaders(params string[] headerPatterns) - { - HeaderAccessor.RemoveHeaders(headerPatterns); - return this; - } - - public override IMessageBuilder RemoveHeader(string headerName) - { - if (!HeaderAccessor.IsReadOnly(headerName)) - { - HeaderAccessor.RemoveHeader(headerName); - } - - return this; - } - - public override IMessageBuilder CopyHeaders(IDictionary headersToCopy) - { - HeaderAccessor.CopyHeaders(headersToCopy); - return this; - } - - public override IMessageBuilder CopyHeadersIfAbsent(IDictionary headersToCopy) - { - if (headersToCopy != null) - { - foreach (KeyValuePair entry in headersToCopy) - { - string headerName = entry.Key; - - if (!HeaderAccessor.IsReadOnly(headerName)) - { - HeaderAccessor.SetHeaderIfAbsent(headerName, entry.Value); - } - } - } - - return this; - } - - public IMessageBuilder ReadOnlyHeaders(IList values) - { - readOnlyHeaders = values; - HeaderAccessor.SetReadOnlyHeaders(values); - return this; - } - - public override IMessage Build() - { - if (!modified && !HeaderAccessor.IsModified && OriginalMessage != null && !ContainsReadOnly(OriginalMessage.Headers)) - { - return OriginalMessage; - } - - if (InnerPayload is Exception exception) - { - return new ErrorMessage(exception, HeaderAccessor.ToDictionary()); - } - - return Message.Create(InnerPayload, HeaderAccessor.ToDictionary(), InnerPayload.GetType()); - } -} diff --git a/src/Integration/src/Integration/Support/IntegrationUtils.cs b/src/Integration/src/Integration/Support/IntegrationUtils.cs deleted file mode 100644 index c84622b6cb..0000000000 --- a/src/Integration/src/Integration/Support/IntegrationUtils.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -public static class IntegrationUtils -{ - public const string IntegrationConversionServiceBeanName = "integrationConversionService"; - - public const string IntegrationMessageBuilderFactoryBeanName = "messageBuilderFactory"; - - public static Exception WrapInDeliveryExceptionIfNecessary(IMessage message, string text, Exception e) - { - if (e is not MessagingException me) - { - return new MessageDeliveryException(message, text, e); - } - - if (me.FailedMessage == null) - { - return new MessageDeliveryException(message, text, e); - } - - return me; - } - - public static Exception WrapInHandlingExceptionIfNecessary(IMessage message, string text, Exception e) - { - if (e is not MessagingException me) - { - return new MessageHandlingException(message, text, e); - } - - if (me.FailedMessage == null) - { - return new MessageHandlingException(message, text, e); - } - - return me; - } -} diff --git a/src/Integration/src/Integration/Support/MessagingWrapperException.cs b/src/Integration/src/Integration/Support/MessagingWrapperException.cs deleted file mode 100644 index 7a2f83e76e..0000000000 --- a/src/Integration/src/Integration/Support/MessagingWrapperException.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -public class MessagingWrapperException : MessagingException -{ - public MessagingWrapperException(IMessage failedMessage, MessagingException innerException) - : base(failedMessage, innerException) - { - } -} diff --git a/src/Integration/src/Integration/Support/MutableIntegrationMessageBuilder.cs b/src/Integration/src/Integration/Support/MutableIntegrationMessageBuilder.cs deleted file mode 100644 index ecf5b0e447..0000000000 --- a/src/Integration/src/Integration/Support/MutableIntegrationMessageBuilder.cs +++ /dev/null @@ -1,372 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -public class MutableIntegrationMessageBuilder : AbstractMessageBuilder -{ - protected MutableMessage mutableMessage; - - protected IDictionary headers; - - protected override List> SequenceDetails - { - get - { - if (headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceDetails, out object result)) - { - return (List>)result; - } - - return null; - } - } - - protected override object CorrelationId - { - get - { - if (headers.TryGetValue(IntegrationMessageHeaderAccessor.CorrelationId, out object result)) - { - return result; - } - - return null; - } - } - - protected override object SequenceNumber - { - get - { - if (headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceNumber, out object result)) - { - return result; - } - - return null; - } - } - - protected override object SequenceSize - { - get - { - if (headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceSize, out object result)) - { - return result; - } - - return null; - } - } - - public override object Payload => mutableMessage.Payload; - - public override IDictionary Headers => headers; - - protected MutableIntegrationMessageBuilder() - { - } - - private MutableIntegrationMessageBuilder(IMessage message) - { - ArgumentGuard.NotNull(message); - - mutableMessage = message as MutableMessage ?? new MutableMessage(message.Payload, message.Headers); - - headers = mutableMessage.RawHeaders; - } - - public static MutableIntegrationMessageBuilder WithPayload(object payload) - { - return WithPayload(payload, true); - } - - public static MutableIntegrationMessageBuilder WithPayload(object payload, bool generateHeaders) - { - MutableMessage message = generateHeaders - ? new MutableMessage(payload) - : new MutableMessage(payload, new MutableMessageHeaders(null, MessageHeaders.IdValueNone, -1L)); - - return FromMessage(message); - } - - public static MutableIntegrationMessageBuilder FromMessage(IMessage message) - { - ArgumentGuard.NotNull(message); - - return new MutableIntegrationMessageBuilder(message); - } - - public override IMessageBuilder SetHeader(string headerName, object headerValue) - { - ArgumentGuard.NotNull(headerName); - - if (headerValue == null) - { - RemoveHeader(headerName); - } - else - { - headers[headerName] = headerValue; - } - - return this; - } - - public override IMessageBuilder SetHeaderIfAbsent(string headerName, object headerValue) - { - if (!headers.ContainsKey(headerName)) - { - headers.Add(headerName, headerValue); - } - - return this; - } - - public override IMessageBuilder RemoveHeaders(params string[] headerPatterns) - { - var headersToRemove = new List(); - - foreach (string pattern in headerPatterns) - { - if (!string.IsNullOrEmpty(pattern)) - { - if (pattern.Contains('*')) - { - headersToRemove.AddRange(GetMatchingHeaderNames(pattern, headers)); - } - else - { - headersToRemove.Add(pattern); - } - } - } - - foreach (string headerToRemove in headersToRemove) - { - RemoveHeader(headerToRemove); - } - - return this; - } - - public override IMessageBuilder RemoveHeader(string headerName) - { - if (!string.IsNullOrEmpty(headerName)) - { - headers.Remove(headerName); - } - - return this; - } - - public override IMessageBuilder CopyHeaders(IDictionary headersToCopy) - { - if (headersToCopy != null) - { - foreach (KeyValuePair header in headersToCopy) - { - headers.Add(header); - } - } - - return this; - } - - public override IMessageBuilder CopyHeadersIfAbsent(IDictionary headersToCopy) - { - if (headersToCopy != null) - { - foreach (KeyValuePair entry in headersToCopy) - { - SetHeaderIfAbsent(entry.Key, entry.Value); - } - } - - return this; - } - - public override IMessage Build() - { - return mutableMessage; - } - - protected List GetMatchingHeaderNames(string pattern, IDictionary headers) - { - var matchingHeaderNames = new List(); - - if (headers != null) - { - foreach (KeyValuePair header in headers) - { - if (PatternMatchUtils.SimpleMatch(pattern, header.Key)) - { - matchingHeaderNames.Add(header.Key); - } - } - } - - return matchingHeaderNames; - } -} - -public class MutableIntegrationMessageBuilder : MutableIntegrationMessageBuilder, IMessageBuilder -{ - public new T Payload => (T)mutableMessage.Payload; - - private MutableIntegrationMessageBuilder(IMessage message) - { - ArgumentGuard.NotNull(message); - - mutableMessage = message as MutableMessage ?? new MutableMessage(message.Payload, message.Headers); - - headers = mutableMessage.RawHeaders; - } - - public static MutableIntegrationMessageBuilder WithPayload(T payload) - { - return WithPayload(payload, true); - } - - public static MutableIntegrationMessageBuilder WithPayload(T payload, bool generateHeaders) - { - MutableMessage message = generateHeaders - ? new MutableMessage(payload) - : new MutableMessage(payload, new MutableMessageHeaders(null, MessageHeaders.IdValueNone, -1L)); - - return FromMessage(message); - } - - public static MutableIntegrationMessageBuilder FromMessage(IMessage message) - { - ArgumentGuard.NotNull(message); - - return new MutableIntegrationMessageBuilder(message); - } - - public new IMessageBuilder SetHeader(string headerName, object headerValue) - { - base.SetHeader(headerName, headerValue); - return this; - } - - public new IMessageBuilder SetHeaderIfAbsent(string headerName, object headerValue) - { - base.SetHeaderIfAbsent(headerName, headerValue); - return this; - } - - public new IMessageBuilder RemoveHeaders(params string[] headerPatterns) - { - base.RemoveHeaders(headerPatterns); - return this; - } - - public new IMessageBuilder RemoveHeader(string headerName) - { - base.RemoveHeader(headerName); - return this; - } - - public new IMessageBuilder CopyHeaders(IDictionary headersToCopy) - { - base.CopyHeaders(headersToCopy); - return this; - } - - public new IMessageBuilder CopyHeadersIfAbsent(IDictionary headersToCopy) - { - base.CopyHeadersIfAbsent(headersToCopy); - return this; - } - - public new IMessage Build() - { - return (IMessage)mutableMessage; - } - - public new IMessageBuilder SetExpirationDate(long expirationDate) - { - base.SetExpirationDate(expirationDate); - return this; - } - - public new IMessageBuilder SetExpirationDate(DateTime? expirationDate) - { - base.SetExpirationDate(expirationDate); - return this; - } - - public new IMessageBuilder SetCorrelationId(object correlationId) - { - base.SetCorrelationId(correlationId); - return this; - } - - public new IMessageBuilder PushSequenceDetails(object correlationId, int sequenceNumber, int sequenceSize) - { - base.PushSequenceDetails(correlationId, sequenceNumber, sequenceSize); - return this; - } - - public new IMessageBuilder PopSequenceDetails() - { - base.PopSequenceDetails(); - return this; - } - - public new IMessageBuilder SetReplyChannel(IMessageChannel replyChannel) - { - base.SetReplyChannel(replyChannel); - return this; - } - - public new IMessageBuilder SetReplyChannelName(string replyChannelName) - { - base.SetReplyChannelName(replyChannelName); - return this; - } - - public new IMessageBuilder SetErrorChannel(IMessageChannel errorChannel) - { - base.SetErrorChannel(errorChannel); - return this; - } - - public new IMessageBuilder SetErrorChannelName(string errorChannelName) - { - base.SetErrorChannelName(errorChannelName); - return this; - } - - public new IMessageBuilder SetSequenceNumber(int sequenceNumber) - { - base.SetSequenceNumber(sequenceNumber); - return this; - } - - public new IMessageBuilder SetSequenceSize(int sequenceSize) - { - base.SetSequenceSize(sequenceSize); - return this; - } - - public new IMessageBuilder SetPriority(int priority) - { - base.SetPriority(priority); - return this; - } - - public new IMessageBuilder FilterAndCopyHeadersIfAbsent(IDictionary headersToCopy, params string[] headerPatternsToFilter) - { - base.FilterAndCopyHeadersIfAbsent(headersToCopy, headerPatternsToFilter); - return this; - } -} diff --git a/src/Integration/src/Integration/Support/MutableIntegrationMessageBuilderFactory.cs b/src/Integration/src/Integration/Support/MutableIntegrationMessageBuilderFactory.cs deleted file mode 100644 index d22f81307d..0000000000 --- a/src/Integration/src/Integration/Support/MutableIntegrationMessageBuilderFactory.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -public class MutableIntegrationMessageBuilderFactory : IMessageBuilderFactory -{ - public IMessageBuilder FromMessage(IMessage message) - { - return MutableIntegrationMessageBuilder.FromMessage(message); - } - - public IMessageBuilder FromMessage(IMessage message) - { - return MutableIntegrationMessageBuilder.FromMessage(message); - } - - public IMessageBuilder WithPayload(T payload) - { - return MutableIntegrationMessageBuilder.WithPayload(payload); - } - - public IMessageBuilder WithPayload(object payload) - { - return MutableIntegrationMessageBuilder.WithPayload(payload); - } -} diff --git a/src/Integration/src/Integration/Support/MutableMessage.cs b/src/Integration/src/Integration/Support/MutableMessage.cs deleted file mode 100644 index a4e6417b07..0000000000 --- a/src/Integration/src/Integration/Support/MutableMessage.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -public class MutableMessage : IMessage -{ - protected readonly object InnerPayload; - - protected readonly MutableMessageHeaders InnerHeaders; - - protected internal IDictionary RawHeaders => InnerHeaders.RawHeaders; - - public IMessageHeaders Headers => InnerHeaders; - - public object Payload => InnerPayload; - - public MutableMessage(object payload) - : this(payload, (Dictionary)null) - { - } - - public MutableMessage(object payload, IDictionary headers) - : this(payload, new MutableMessageHeaders(headers)) - { - } - - public MutableMessage(object payload, MutableMessageHeaders headers) - { - ArgumentGuard.NotNull(payload); - ArgumentGuard.NotNull(headers); - - InnerPayload = payload; - InnerHeaders = headers; - } - - public override string ToString() - { - var sb = new StringBuilder(GetType().Name); - sb.Append(" [payload="); - - if (InnerPayload is byte[] v) - { - sb.Append("byte[").Append(v.Length).Append(']'); - } - else - { - sb.Append(InnerPayload); - } - - sb.Append(", headers=").Append(InnerHeaders).Append(']'); - return sb.ToString(); - } - - public override int GetHashCode() - { - return HashCode.Combine(InnerHeaders, InnerPayload); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not MutableMessage other) - { - return false; - } - - string thisId = InnerHeaders.Id; - string otherId = other.InnerHeaders.Id; - - return thisId == otherId && InnerHeaders.Equals(other.InnerHeaders) && InnerPayload.Equals(other.InnerPayload); - } -} - -public class MutableMessage : MutableMessage, IMessage -{ - public new T Payload => (T)InnerPayload; - - public MutableMessage(T payload) - : this(payload, (Dictionary)null) - { - } - - public MutableMessage(T payload, IDictionary headers) - : this(payload, new MutableMessageHeaders(headers)) - { - } - - public MutableMessage(T payload, MutableMessageHeaders headers) - : base(payload, headers) - { - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not MutableMessage other) - { - return false; - } - - string thisId = InnerHeaders.Id; - string otherId = other.InnerHeaders.Id; - - return thisId == otherId && InnerHeaders.Equals(other.InnerHeaders) && InnerPayload.Equals(other.InnerPayload); - } -} diff --git a/src/Integration/src/Integration/Support/MutableMessageHeaders.cs b/src/Integration/src/Integration/Support/MutableMessageHeaders.cs deleted file mode 100644 index a61e33854d..0000000000 --- a/src/Integration/src/Integration/Support/MutableMessageHeaders.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Support; - -public class MutableMessageHeaders : MessageHeaders -{ - public override object this[string key] - { - get => Headers[key]; - set => Headers[key] = value; - } - - public MutableMessageHeaders(IDictionary headers) - : base(headers, ExtractId(headers), ExtractTimestamp(headers)) - { - } - - public MutableMessageHeaders(IDictionary headers, string id, long? timestamp) - : base(headers, id, timestamp) - { - } - - public override void Add(string key, object value) - { - Headers.Add(key, value); - } - - public override void Add(KeyValuePair item) - { - Headers.Add(item); - } - - public virtual void AddRange(IDictionary map) - { - foreach (KeyValuePair entry in map) - { - Headers.Add(entry); - } - } - - public override void Clear() - { - Headers.Clear(); - } - - public override bool Remove(KeyValuePair item) - { - return Headers.Remove(item); - } - - public override bool Remove(string key) - { - return Headers.Remove(key); - } - - private static string ExtractId(IDictionary headers) - { - if (headers != null && headers.ContainsKey(IdName)) - { - object id = headers[IdName]; - - switch (id) - { - case string idAsString: - return idAsString; - case byte[] v: - return new Guid(v).ToString(); - case Guid guid: - return guid.ToString(); - } - } - - return null; - } - - private static long? ExtractTimestamp(IDictionary headers) - { - if (headers != null && headers.ContainsKey(TimestampName)) - { - object timestamp = headers[TimestampName]; - return timestamp is string strTimestamp ? long.Parse(strTimestamp, CultureInfo.InvariantCulture) : (long)timestamp; - } - - return null; - } -} diff --git a/src/Integration/src/Integration/Support/NullAwarePayloadArgumentResolver.cs b/src/Integration/src/Integration/Support/NullAwarePayloadArgumentResolver.cs deleted file mode 100644 index 1e18f45567..0000000000 --- a/src/Integration/src/Integration/Support/NullAwarePayloadArgumentResolver.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes.Support; - -namespace Steeltoe.Integration.Support; - -public class NullAwarePayloadArgumentResolver : PayloadMethodArgumentResolver -{ - public NullAwarePayloadArgumentResolver(IMessageConverter messageConverter) - : base(messageConverter, false) - { - } - - public NullAwarePayloadArgumentResolver(IMessageConverter messageConverter, bool useDefaultResolution) - : base(messageConverter, useDefaultResolution) - { - } - - protected override bool IsEmptyPayload(object payload) - { - return base.IsEmptyPayload(payload) || payload.GetType().Name == "KafkaNull"; - } -} diff --git a/src/Integration/src/Integration/Transformer/AbstractTransformer.cs b/src/Integration/src/Integration/Transformer/AbstractTransformer.cs deleted file mode 100644 index 22dda95658..0000000000 --- a/src/Integration/src/Integration/Transformer/AbstractTransformer.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Transformer; - -public abstract class AbstractTransformer : ITransformer -{ - private IMessageBuilderFactory _messageBuilderFactory; - - public IApplicationContext ApplicationContext { get; } - - public virtual IMessageBuilderFactory MessageBuilderFactory - { - get - { - _messageBuilderFactory ??= ApplicationContext.GetService() ?? new DefaultMessageBuilderFactory(); - return _messageBuilderFactory; - } - set => _messageBuilderFactory = value; - } - - protected AbstractTransformer(IApplicationContext context) - { - ApplicationContext = context; - } - - public IMessage Transform(IMessage message) - { - try - { - object result = DoTransform(message); - - if (result == null) - { - return null; - } - - return result as IMessage ?? MessageBuilderFactory.WithPayload(result).CopyHeaders(message.Headers).Build(); - } - catch (MessageTransformationException) - { - throw; - } - catch (Exception e) - { - throw new MessageTransformationException(message, "failed to transform message", e); - } - } - - protected abstract object DoTransform(IMessage message); -} diff --git a/src/Integration/src/Integration/Transformer/JsonToObjectTransformer.cs b/src/Integration/src/Integration/Transformer/JsonToObjectTransformer.cs deleted file mode 100644 index 6cc8588153..0000000000 --- a/src/Integration/src/Integration/Transformer/JsonToObjectTransformer.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Integration.Transformer; - -public class JsonToObjectTransformer : AbstractTransformer -{ - private readonly DefaultTypeMapper _defaultTypeMapper = new(); - - public Type TargetType { get; set; } - - public JsonSerializerSettings Settings { get; set; } - - public Encoding DefaultCharset { get; set; } - - public JsonToObjectTransformer(IApplicationContext context, Type targetType = null) - : base(context) - { - var contractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy - { - ProcessDictionaryKeys = false - } - }; - - Settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = contractResolver - }; - - TargetType = targetType; - _defaultTypeMapper.DefaultType = null; - DefaultCharset = EncodingUtils.Utf8; - } - - protected override object DoTransform(IMessage message) - { - IMessageHeaders headers = message.Headers; - bool removeHeaders = false; - Type targetClass = ObtainResolvableTypeFromHeadersIfAny(headers); - - if (targetClass == null) - { - targetClass = TargetType; - } - else - { - removeHeaders = true; - } - - object payload = message.Payload; - object result; - - switch (payload) - { - case string sPayload: - { - result = JsonConvert.DeserializeObject(sPayload, targetClass, Settings); - break; - } - case byte[] bPayload: - { - string contentAsString = DefaultCharset.GetString(bPayload); - result = JsonConvert.DeserializeObject(contentAsString, targetClass, Settings); - break; - } - default: - { - throw new MessageConversionException($"Failed to convert message content, message missing byte[] or string: {payload.GetType()}"); - } - } - - if (removeHeaders) - { - return MessageBuilderFactory.WithPayload(result).CopyHeaders(headers) - .RemoveHeaders(MessageHeaders.TypeId, MessageHeaders.ContentTypeId, MessageHeaders.KeyTypeId).Build(); - } - - return result; - } - - private Type ObtainResolvableTypeFromHeadersIfAny(IMessageHeaders headers) - { - return _defaultTypeMapper.ToType(headers); - } -} diff --git a/src/Integration/src/Integration/Transformer/MessageTransformationException.cs b/src/Integration/src/Integration/Transformer/MessageTransformationException.cs deleted file mode 100644 index aa2b98029d..0000000000 --- a/src/Integration/src/Integration/Transformer/MessageTransformationException.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Transformer; - -public class MessageTransformationException : MessagingException -{ - public MessageTransformationException(string message) - : base(message) - { - } - - public MessageTransformationException(string message, Exception innerException) - : base(message, innerException) - { - } - - public MessageTransformationException(IMessage failedMessage, string message) - : base(failedMessage, message) - { - } - - public MessageTransformationException(IMessage failedMessage, string message, Exception innerException) - : base(failedMessage, message, innerException) - { - } -} diff --git a/src/Integration/src/Integration/Transformer/MessageTransformingHandler.cs b/src/Integration/src/Integration/Transformer/MessageTransformingHandler.cs deleted file mode 100644 index 431191a1f0..0000000000 --- a/src/Integration/src/Integration/Transformer/MessageTransformingHandler.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Integration.Handler; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Transformer; - -public class MessageTransformingHandler : AbstractReplyProducingMessageHandler, ILifecycle -{ - protected override bool ShouldCopyRequestHeaders => false; - - public ITransformer Transformer { get; } - - public bool IsRunning - { - get - { - if (Transformer is ILifecycle asLifecycle) - { - return asLifecycle.IsRunning; - } - - return false; - } - } - - public MessageTransformingHandler(IApplicationContext context, ITransformer transformer) - : base(context) - { - ArgumentGuard.NotNull(transformer); - - Transformer = transformer; - RequiresReply = true; - } - - public override void Initialize() - { - // Intentionally left empty. - } - - public Task StartAsync() - { - if (Transformer is ILifecycle asLifecycle) - { - return asLifecycle.StartAsync(); - } - - return Task.CompletedTask; - } - - public Task StopAsync() - { - if (Transformer is ILifecycle asLifecycle) - { - return asLifecycle.StopAsync(); - } - - return Task.CompletedTask; - } - - protected override object HandleRequestMessage(IMessage requestMessage) - { - try - { - return Transformer.Transform(requestMessage); - } - catch (Exception e) - { - if (e is MessageTransformationException) - { - throw; - } - - throw new MessageTransformationException(requestMessage, $"Failed to transform Message in {this}", e); - } - } -} diff --git a/src/Integration/src/Integration/Transformer/ObjectToJsonTransformer.cs b/src/Integration/src/Integration/Transformer/ObjectToJsonTransformer.cs deleted file mode 100644 index 3aa220ebe4..0000000000 --- a/src/Integration/src/Integration/Transformer/ObjectToJsonTransformer.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Transformer; - -public class ObjectToJsonTransformer : AbstractTransformer -{ - private readonly DefaultTypeMapper _defaultTypeMapper = new(); - - public Type ResultType { get; set; } - - public JsonSerializerSettings Settings { get; set; } - - public string ContentType { get; set; } - - public Encoding DefaultCharset { get; set; } - - public ObjectToJsonTransformer(IApplicationContext context, Type resultType = null) - : base(context) - { - var contractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy - { - ProcessDictionaryKeys = false - } - }; - - Settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = contractResolver - }; - - ResultType = resultType ?? typeof(string); - DefaultCharset = EncodingUtils.Utf8; - ContentType = MessageHeaders.ContentTypeJson; - } - - protected override object DoTransform(IMessage message) - { - object payload = BuildJsonPayload(message.Payload); - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(message); - string contentType = accessor.ContentType; - - if (string.IsNullOrEmpty(contentType)) - { - accessor.ContentType = ContentType; - } - - IMessageHeaders headers = accessor.MessageHeaders; - _defaultTypeMapper.FromType(message.Payload.GetType(), headers); - - if (ResultType == typeof(string)) - { - return MessageBuilderFactory.WithPayload((string)payload).CopyHeaders(headers).Build(); - } - - return MessageBuilderFactory.WithPayload((byte[])payload).CopyHeaders(headers).Build(); - } - - private object BuildJsonPayload(object payload) - { - string jsonString = JsonConvert.SerializeObject(payload, Settings); - - if (ResultType == typeof(string)) - { - return jsonString; - } - - if (ResultType == typeof(byte[])) - { - return DefaultCharset.GetBytes(jsonString); - } - - throw new InvalidOperationException($"Unsupported result type: {ResultType}"); - } -} diff --git a/src/Integration/src/Integration/Util/AbstractExpressionEvaluator.cs b/src/Integration/src/Integration/Util/AbstractExpressionEvaluator.cs deleted file mode 100644 index 72269c8f91..0000000000 --- a/src/Integration/src/Integration/Util/AbstractExpressionEvaluator.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Util; - -public abstract class AbstractExpressionEvaluator -{ - private IEvaluationContext _evaluationContext; - - private IMessageBuilderFactory _messageBuilderFactory; - - private IIntegrationServices _integrationServices; - - public static IExpressionParser ExpressionParser { get; } = new SpelExpressionParser(); - - public IEvaluationContext EvaluationContext - { - get - { - _evaluationContext ??= CreateEvaluationContext(); - return _evaluationContext; - } - set => _evaluationContext = value; - } - - public IIntegrationServices IntegrationServices - { - get - { - _integrationServices ??= IntegrationServicesUtils.GetIntegrationServices(ApplicationContext); - return _integrationServices; - } - } - - public IMessageBuilderFactory MessageBuilderFactory - { - get - { - _messageBuilderFactory ??= CreateMessageBuilderFactory(); - return _messageBuilderFactory; - } - set => _messageBuilderFactory = value; - } - - public ITypeConverter TypeConverter { get; set; } = new ServiceFactoryTypeConverter(); - - public IApplicationContext ApplicationContext { get; } - - protected AbstractExpressionEvaluator(IApplicationContext context) - { - ApplicationContext = context; - } - - protected virtual IMessageBuilderFactory CreateMessageBuilderFactory() - { - return IntegrationServices.MessageBuilderFactory; - } - - protected virtual IEvaluationContext CreateEvaluationContext(bool contextRequired = true) - { - if (_evaluationContext == null) - { - if (ApplicationContext == null && !contextRequired) - { - _evaluationContext = new StandardEvaluationContext(); - } - else - { - _evaluationContext = new StandardEvaluationContext(ApplicationContext); - } - - ((StandardEvaluationContext)_evaluationContext).TypeConverter = TypeConverter; - - if (ApplicationContext != null) - { - var conversionService = ApplicationContext.GetService(IntegrationUtils.IntegrationConversionServiceBeanName); - - if (conversionService != null) - { - TypeConverter.ConversionService = conversionService; - } - } - } - - return _evaluationContext; - } - - protected T EvaluateExpression(IExpression expression, IMessage message) - { - return (T)EvaluateExpression(expression, message, typeof(T)); - } - - protected object EvaluateExpression(IExpression expression, IMessage message, Type expectedType) - { - try - { - return EvaluateExpression(expression, (object)message, expectedType); - } - catch (Exception ex) - { - Exception cause = null; - - if (ex is EvaluationException) - { - cause = ex.InnerException; - } - - Exception wrapped = - IntegrationServicesUtils.WrapInHandlingExceptionIfNecessary(message, $"Expression evaluation failed: {expression.ExpressionString}", - cause ?? ex); - - if (wrapped != ex) - { - throw wrapped; - } - - throw; - } - } - - protected object EvaluateExpression(string expression, object input) - { - return EvaluateExpression(expression, input, null); - } - - protected object EvaluateExpression(string expression, object input, Type expectedType) - { - return ExpressionParser.ParseExpression(expression).GetValue(EvaluationContext, input, expectedType); - } - - protected object EvaluateExpression(IExpression expression) - { - return expression.GetValue(EvaluationContext); - } - - protected object EvaluateExpression(IExpression expression, object input, Type expectedType) - { - return expression.GetValue(EvaluationContext, input, expectedType); - } - - protected T EvaluateExpression(IExpression expression, object input) - { - return (T)EvaluateExpression(expression, input, typeof(T)); - } -} diff --git a/src/Integration/src/Integration/Util/AnnotatedMethodFilter.cs b/src/Integration/src/Integration/Util/AnnotatedMethodFilter.cs deleted file mode 100644 index f120442e07..0000000000 --- a/src/Integration/src/Integration/Util/AnnotatedMethodFilter.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Expression.Internal; - -namespace Steeltoe.Integration.Util; - -public class AnnotatedMethodFilter : IMethodFilter -{ - private readonly Type _annotationType; - - private readonly string _methodName; - - private readonly bool _requiresReply; - - public AnnotatedMethodFilter(Type annotationType, string methodName, bool requiresReply) - { - _annotationType = annotationType; - _methodName = methodName; - _requiresReply = requiresReply; - } - - public List Filter(List methods) - { - var annotatedCandidates = new List(); - var fallbackCandidates = new List(); - - foreach (MethodInfo method in methods) - { - if (_requiresReply && (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task))) - { - continue; - } - - if (!string.IsNullOrEmpty(_methodName) && _methodName != method.Name) - { - continue; - } - - if (_annotationType != null && method.GetCustomAttribute(_annotationType) != null) - { - annotatedCandidates.Add(method); - } - else - { - fallbackCandidates.Add(method); - } - } - - if (annotatedCandidates.Count > 0) - { - return annotatedCandidates; - } - - return fallbackCandidates; - } -} diff --git a/src/Integration/src/Integration/Util/BeanFactoryTypeConverter.cs b/src/Integration/src/Integration/Util/BeanFactoryTypeConverter.cs deleted file mode 100644 index ced8b62fab..0000000000 --- a/src/Integration/src/Integration/Util/BeanFactoryTypeConverter.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Expression.Internal; - -namespace Steeltoe.Integration.Util; - -public class BeanFactoryTypeConverter : ITypeConverter -{ - public IConversionService ConversionService { get; set; } - - public BeanFactoryTypeConverter() - { - ConversionService = DefaultConversionService.Singleton; - } - - public BeanFactoryTypeConverter(IConversionService conversionService) - { - ArgumentGuard.NotNull(conversionService); - - ConversionService = conversionService; - } - - public bool CanConvert(Type sourceType, Type targetType) - { - return ConversionService.CanConvert(sourceType, targetType); - } - - public object ConvertValue(object value, Type sourceType, Type targetType) - { - if (CanConvert(sourceType, targetType)) - { - return ConversionService.Convert(value, sourceType, targetType); - } - - throw new NotImplementedException("Cannot be converted by default service"); - } -} diff --git a/src/Integration/src/Integration/Util/ErrorHandlingMessageHandlingRunnable.cs b/src/Integration/src/Integration/Util/ErrorHandlingMessageHandlingRunnable.cs deleted file mode 100644 index f16c8f9657..0000000000 --- a/src/Integration/src/Integration/Util/ErrorHandlingMessageHandlingRunnable.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.Util; - -public class ErrorHandlingMessageHandlingRunnable : IMessageHandlingRunnable -{ - private readonly IMessageHandlingRunnable _runnable; - private readonly IErrorHandler _errorHandler; - - public IMessage Message => _runnable.Message; - - public IMessageHandler MessageHandler => _runnable.MessageHandler; - - public ErrorHandlingMessageHandlingRunnable(IMessageHandlingRunnable runnable, IErrorHandler errorHandler) - { - ArgumentGuard.NotNull(runnable); - ArgumentGuard.NotNull(errorHandler); - - _runnable = runnable; - _errorHandler = errorHandler; - } - - public bool Run() - { - try - { - return _runnable.Run(); - } - catch (Exception e) - { - _errorHandler.HandleError(e); - return false; - } - } -} - -// IMessageHandlingRunnable diff --git a/src/Integration/src/Integration/Util/FixedMethodFilter.cs b/src/Integration/src/Integration/Util/FixedMethodFilter.cs deleted file mode 100644 index 67b1ca8670..0000000000 --- a/src/Integration/src/Integration/Util/FixedMethodFilter.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common; -using Steeltoe.Common.Expression.Internal; - -namespace Steeltoe.Integration.Util; - -public class FixedMethodFilter : IMethodFilter -{ - private readonly MethodInfo _method; - - public FixedMethodFilter(MethodInfo method) - { - ArgumentGuard.NotNull(method); - - _method = method; - } - - public List Filter(List methods) - { - if (methods != null && methods.Contains(_method)) - { - return new List - { - _method - }; - } - - return new List(); - } -} diff --git a/src/Integration/src/Integration/Util/IntegrationContextUtils.cs b/src/Integration/src/Integration/Util/IntegrationContextUtils.cs deleted file mode 100644 index ba513ccf2c..0000000000 --- a/src/Integration/src/Integration/Util/IntegrationContextUtils.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Util; - -public static class IntegrationContextUtils -{ - public const string NullChannelBeanName = "nullChannel"; - - public const string ErrorChannelBeanName = "errorChannel"; - - public const string IntegrationEvaluationContextBeanName = "integrationEvaluationContext"; - - public const string IntegrationSimpleEvaluationContextBeanName = "integrationSimpleEvaluationContext"; - - public const string MessageHandlerFactoryBeanName = "integrationMessageHandlerMethodFactory"; - - public const string ArgumentResolverMessageConverterBeanName = "integrationArgumentResolverMessageConverter"; - - public static IMessageChannel GetErrorChannel(IApplicationContext context) - { - return context.GetService(ErrorChannelBeanName); - } - - public static IMessageChannel GetNullChannel(IApplicationContext context) - { - return context.GetService(NullChannelBeanName); - } - - public static IEvaluationContext GetEvaluationContext(IApplicationContext context) - { - return context.GetService(IntegrationEvaluationContextBeanName); - } - - public static IEvaluationContext GetSimpleEvaluationContext(IApplicationContext context) - { - return context.GetService(IntegrationSimpleEvaluationContextBeanName); - } - - public static IExpressionParser GetExpressionParser(IApplicationContext context) - { - IExpressionParser result = context.GetService() ?? new SpelExpressionParser(); - - return result; - } -} diff --git a/src/Integration/src/Integration/Util/IntegrationServicesUtils.cs b/src/Integration/src/Integration/Util/IntegrationServicesUtils.cs deleted file mode 100644 index b08834e0b9..0000000000 --- a/src/Integration/src/Integration/Util/IntegrationServicesUtils.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Util; - -public static class IntegrationServicesUtils -{ - public static T ExtractTypeIfPossible(object targetObject) - { - return targetObject switch - { - T t => t, - _ => default - }; - } - - public static Exception WrapInHandlingExceptionIfNecessary(IMessage message, string text, Exception ex) - { - return ex is not MessagingException exception || exception.FailedMessage == null ? new MessageHandlingException(message, text, ex) : ex; - } - - public static IIntegrationServices GetIntegrationServices(IApplicationContext context) - { - return context?.GetService() ?? new IntegrationServices(context); - } -} diff --git a/src/Integration/src/Integration/Util/MessagingAttributeUtils.cs b/src/Integration/src/Integration/Util/MessagingAttributeUtils.cs deleted file mode 100644 index 4443192447..0000000000 --- a/src/Integration/src/Integration/Util/MessagingAttributeUtils.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Attributes; - -namespace Steeltoe.Integration.Util; - -public static class MessagingAttributeUtils -{ - public static bool HasValue(object value) - { - if (value == null) - { - return false; - } - - if (value is string stringValue && string.IsNullOrEmpty(stringValue)) - { - return false; - } - - if (value.GetType().IsArray && ((Array)value).Length == 0) - { - return false; - } - - return true; - } - - public static T ResolveAttribute(List attributes, string name) - { - foreach (Attribute attribute in attributes) - { - object value = AttributeUtils.GetValue(attribute, name); - - if (value != null && value.GetType() == typeof(T) && HasValue(value)) - { - return (T)value; - } - } - - return default; - } - - public static string EndpointIdValue(MethodInfo method) - { - var endpointId = method.GetCustomAttribute(); - return endpointId?.Id; - } -} diff --git a/src/Integration/src/Integration/Util/ServiceFactoryTypeConverter.cs b/src/Integration/src/Integration/Util/ServiceFactoryTypeConverter.cs deleted file mode 100644 index 5c08be2bc2..0000000000 --- a/src/Integration/src/Integration/Util/ServiceFactoryTypeConverter.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Converter; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.Util; - -public class ServiceFactoryTypeConverter : ITypeConverter -{ - public IConversionService ConversionService { get; set; } - - public ServiceFactoryTypeConverter() - { - ConversionService = DefaultConversionService.Singleton; - } - - public ServiceFactoryTypeConverter(IConversionService conversionService) - { - ConversionService = conversionService; - } - - public bool CanConvert(Type sourceType, Type targetType) - { - if (ConversionService.CanConvert(sourceType, targetType)) - { - return true; - } - - return false; - } - - public object ConvertValue(object value, Type sourceType, Type targetType) - { - if (targetType == typeof(void) && value == null) - { - return null; - } - - if (sourceType != null) - { - bool isMessageHeaders = sourceType == typeof(IMessageHeaders) && targetType == typeof(IMessageHeaders); - - if (isMessageHeaders || (targetType.IsAssignableFrom(sourceType) && sourceType.IsArray && sourceType.IsPrimitive)) - { - return value; - } - } - - return ConversionService.Convert(value, sourceType, targetType); - } -} diff --git a/src/Integration/src/RabbitMQ/Inbound/RabbitInboundChannelAdapter.cs b/src/Integration/src/RabbitMQ/Inbound/RabbitInboundChannelAdapter.cs deleted file mode 100644 index cc69f15e04..0000000000 --- a/src/Integration/src/RabbitMQ/Inbound/RabbitInboundChannelAdapter.cs +++ /dev/null @@ -1,237 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using RabbitMQ.Client; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Retry; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Endpoint; -using Steeltoe.Integration.RabbitMQ.Support; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.Support; -using RabbitConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter; - -namespace Steeltoe.Integration.RabbitMQ.Inbound; - -public class RabbitInboundChannelAdapter : MessageProducerSupportEndpoint -{ - private static readonly AsyncLocal AttributesHolder = new(); - - private AbstractMessageListenerContainer MessageListenerContainer { get; } - - public ISmartMessageConverter MessageConverter { get; set; } = new RabbitConverter.SimpleMessageConverter(); - - public RetryTemplate RetryTemplate { get; set; } - - public IRecoveryCallback RecoveryCallback { get; set; } - - public IBatchingStrategy BatchingStrategy { get; set; } = new SimpleBatchingStrategy(0, 0, 0L); - - public bool BindSourceMessage { get; set; } - - public RabbitInboundChannelAdapter(IApplicationContext context, AbstractMessageListenerContainer listenerContainer, ILogger logger = null) - : base(context, logger) - { - ArgumentGuard.NotNull(listenerContainer); - - if (listenerContainer.MessageListener != null) - { - throw new ArgumentException( - $"The {nameof(listenerContainer)} provided to a RabbitMQ inbound Channel Adapter must not have a " + - $"{nameof(listenerContainer.MessageListener)} configured since the adapter configures its own listener implementation.", - nameof(listenerContainer)); - } - - listenerContainer.IsAutoStartup = true; - MessageListenerContainer = listenerContainer; - ErrorMessageStrategy = new RabbitMessageHeaderErrorMessageStrategy(); - var messageListener = new Listener(this, logger); - MessageListenerContainer.MessageListener = messageListener; - MessageListenerContainer.Initialize(); - } - - protected override Task DoStartAsync() - { - return MessageListenerContainer.StartAsync(); - } - - protected override Task DoStopAsync() - { - return MessageListenerContainer.StopAsync(); - } - - protected override IAttributeAccessor GetErrorMessageAttributes(IMessage message) - { - IAttributeAccessor attributes = AttributesHolder.Value; - - if (attributes == null) - { - return base.GetErrorMessageAttributes(message); - } - - return attributes; - } - - private void SetAttributesIfNecessary(IMessage original, IMessage endMessage) - { - bool needHolder = ErrorChannel != null && RetryTemplate == null; - bool needAttributes = needHolder || RetryTemplate != null; - - if (needHolder) - { - AttributesHolder.Value = ErrorMessageUtils.GetAttributeAccessor(null, null); - } - - if (needAttributes) - { - IAttributeAccessor attributes = RetryTemplate != null ? RetrySynchronizationManager.GetContext() : AttributesHolder.Value; - - if (attributes != null) - { - attributes.SetAttribute(ErrorMessageUtils.InputMessageContextKey, endMessage); - attributes.SetAttribute(RabbitMessageHeaderErrorMessageStrategy.AmqpRawMessage, original); - } - } - } - - protected class Listener : IChannelAwareMessageListener - { - private readonly RabbitInboundChannelAdapter _adapter; - private readonly ILogger _logger; - - private bool IsManualAck => _adapter.MessageListenerContainer.AcknowledgeMode == AcknowledgeMode.Manual; - - public AcknowledgeMode ContainerAckMode { get; set; } - - public Listener(RabbitInboundChannelAdapter adapter, ILogger logger) - { - _adapter = adapter; - _logger = logger; - } - - public void OnMessage(IMessage message, IModel channel) - { - bool retryDisabled = _adapter.RetryTemplate == null; - - try - { - if (retryDisabled) - { - CreateAndSend(message, channel); - } - else - { - IMessage toSend = CreateMessage(message, channel); - - _adapter.RetryTemplate.Execute(context => - { - try - { - _logger?.LogTrace($"RabbitInboundChannelAdapter::OnMessage Context: {context}"); - var deliveryAttempts = message.Headers.Get(IntegrationMessageHeaderAccessor.DeliveryAttempt); - deliveryAttempts?.IncrementAndGet(); - _adapter.SetAttributesIfNecessary(message, toSend); - _adapter.SendMessage(message); - } - catch (Exception ex) - { - _logger?.LogError(ex, ex.Message, context); - throw; - } - }, _adapter.RecoveryCallback); - } - } - catch (MessageConversionException e) - { - if (_adapter.ErrorChannel != null) - { - _adapter.SetAttributesIfNecessary(message, null); - ErrorMessage errorMessage = _adapter.BuildErrorMessage(null, EndpointUtils.CreateErrorMessagePayload(message, channel, IsManualAck, e)); - _adapter.MessagingTemplate.Send(_adapter.ErrorChannel, errorMessage); - } - else - { - throw; - } - } - finally - { - if (retryDisabled) - { - AttributesHolder.Value = null; - } - } - } - - public void OnMessage(IMessage message) - { - throw new InvalidOperationException("Should never be called for a ChannelAwareMessageListener"); - } - - public void OnMessageBatch(List messages, IModel channel) - { - throw new NotSupportedException("This listener does not support message batches"); - } - - public void OnMessageBatch(List messages) - { - throw new NotSupportedException("This listener does not support message batches"); - } - - private void CreateAndSend(IMessage message, IModel channel) - { - IMessage toSend = CreateMessage(message, channel); - _adapter.SetAttributesIfNecessary(message, toSend); - _adapter.SendMessage(toSend); - } - - private IMessage CreateMessage(IMessage message, IModel channel) - { - object payload; - - if (_adapter.BatchingStrategy.CanDebatch(message.Headers)) - { - var payloads = new List(); - _adapter.BatchingStrategy.DeBatch(message, fragment => payloads.Add(_adapter.MessageConverter.FromMessage(fragment, null))); - payload = payloads; - } - else - { - payload = _adapter.MessageConverter.FromMessage(message, null); - } - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - - if (IsManualAck) - { - accessor.SetHeader(RabbitMessageHeaders.DeliveryTag, message.Headers.DeliveryTag()); - accessor.SetHeader(RabbitMessageHeaders.Channel, channel); - } - - if (_adapter.RetryTemplate != null) - { - accessor.SetHeader(IntegrationMessageHeaderAccessor.DeliveryAttempt, new AtomicInteger()); - } - - if (_adapter.BindSourceMessage) - { - accessor.SetHeader(IntegrationMessageHeaderAccessor.SourceData, message); - } - - IMessage messagingMessage = _adapter.IntegrationServices.MessageBuilderFactory.WithPayload(payload).CopyHeaders(accessor.MessageHeaders).Build(); - - return messagingMessage; - } - } -} diff --git a/src/Integration/src/RabbitMQ/Inbound/RabbitMessageSource.cs b/src/Integration/src/RabbitMQ/Inbound/RabbitMessageSource.cs deleted file mode 100644 index a2ad4412b4..0000000000 --- a/src/Integration/src/RabbitMQ/Inbound/RabbitMessageSource.cs +++ /dev/null @@ -1,195 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Acks; -using Steeltoe.Integration.Endpoint; -using Steeltoe.Integration.RabbitMQ.Support; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Support; -using RabbitConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Integration.RabbitMQ.Inbound; - -public class RabbitMessageSource : AbstractMessageSource -{ - public IConnectionFactory ConnectionFactory { get; } - - public RabbitAckCallbackFactory AckCallbackFactory { get; } - - public string QueueName { get; } - - public bool Transacted { get; set; } - - public IMessageHeadersConverter MessageHeaderConverter { get; set; } = new DefaultMessageHeadersConverter(); - - public ISmartMessageConverter MessageConverter { get; set; } = new RabbitConverter.SimpleMessageConverter(); - - public bool RawMessageHeader { get; set; } - - public IBatchingStrategy BatchingStrategy { get; set; } = new SimpleBatchingStrategy(0, 0, 0L); - - public RabbitMessageSource(IApplicationContext context, IConnectionFactory connectionFactory, string queueName) - : this(context, connectionFactory, new RabbitAckCallbackFactory(), queueName) - { - } - - public RabbitMessageSource(IApplicationContext context, IConnectionFactory connectionFactory, RabbitAckCallbackFactory ackCallbackFactory, string queueName) - : base(context) - { - ArgumentGuard.NotNull(connectionFactory); - ArgumentGuard.NotNull(ackCallbackFactory); - ArgumentGuard.NotNull(queueName); - - ConnectionFactory = connectionFactory; - AckCallbackFactory = ackCallbackFactory; - QueueName = queueName; - } - - protected override object DoReceive() - { - IConnection connection = ConnectionFactory.CreateConnection(); - RC.IModel channel = connection.CreateChannel(Transacted); - - try - { - RC.BasicGetResult resp = channel.BasicGet(QueueName, false); - - if (resp == null) - { - RabbitUtils.CloseChannel(channel); - RabbitUtils.CloseConnection(connection); - return null; - } - - IAcknowledgmentCallback callback = AckCallbackFactory.CreateCallback(new RabbitAckInfo(connection, channel, Transacted, resp)); - var envelope = new Envelope(resp.DeliveryTag, resp.Redelivered, resp.Exchange, resp.RoutingKey); - IMessageHeaders messageProperties = MessageHeaderConverter.ToMessageHeaders(resp.BasicProperties, envelope, EncodingUtils.Utf8); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(messageProperties); - accessor.ConsumerQueue = QueueName; - - IMessage message = Message.Create(resp.Body, accessor.MessageHeaders); - - object payload; - - if (BatchingStrategy.CanDebatch(message.Headers)) - { - var payloads = new List(); - BatchingStrategy.DeBatch(message, fragment => payloads.Add(MessageConverter.FromMessage(fragment, null))); - payload = payloads; - } - else - { - payload = MessageConverter.FromMessage(message, null); - } - - IMessageBuilder builder = MessageBuilderFactory.WithPayload(payload).CopyHeaders(accessor.MessageHeaders) - .SetHeader(IntegrationMessageHeaderAccessor.AcknowledgmentCallback, callback); - - if (RawMessageHeader) - { - builder.SetHeader(RabbitMessageHeaderErrorMessageStrategy.AmqpRawMessage, message); - builder.SetHeader(IntegrationMessageHeaderAccessor.SourceData, message); - } - - return builder; - } - catch (Exception e) - { - RabbitUtils.CloseChannel(channel); - RabbitUtils.CloseConnection(connection); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - public class RabbitAckCallbackFactory : IAcknowledgmentCallbackFactory - { - public IAcknowledgmentCallback CreateCallback(RabbitAckInfo info) - { - return new RabbitAckCallback(info); - } - } - - public class RabbitAckInfo - { - public IConnection Connection { get; } - - public RC.IModel Channel { get; } - - public bool Transacted { get; } - - public RC.BasicGetResult Response { get; } - - public RabbitAckInfo(IConnection connection, RC.IModel channel, bool transacted, RC.BasicGetResult getResponse) - { - Connection = connection; - Channel = channel; - Transacted = transacted; - Response = getResponse; - } - - public override string ToString() - { - return $"RabbitAckInfo [connection={Connection}, channel={Channel}, transacted={Transacted}, getResponse={Response}]"; - } - } - - public class RabbitAckCallback : IAcknowledgmentCallback - { - public RabbitAckInfo AckInfo { get; } - - public bool IsAcknowledged { get; set; } - - public bool IsAutoAck { get; set; } = true; - - public RabbitAckCallback(RabbitAckInfo ackInfo) - { - AckInfo = ackInfo; - } - - public void Acknowledge(Status status) - { - try - { - ulong deliveryTag = AckInfo.Response.DeliveryTag; - - switch (status) - { - case Status.Accept: - AckInfo.Channel.BasicAck(deliveryTag, false); - break; - case Status.Reject: - AckInfo.Channel.BasicReject(deliveryTag, false); - break; - case Status.Requeue: - AckInfo.Channel.BasicReject(deliveryTag, true); - break; - } - - if (AckInfo.Transacted) - { - AckInfo.Channel.TxCommit(); - } - } - catch (Exception e) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - finally - { - RabbitUtils.CloseChannel(AckInfo.Channel); - RabbitUtils.CloseConnection(AckInfo.Connection); - IsAcknowledged = true; - } - } - } -} diff --git a/src/Integration/src/RabbitMQ/Outbound/AbstractRabbitOutboundEndpoint.cs b/src/Integration/src/RabbitMQ/Outbound/AbstractRabbitOutboundEndpoint.cs deleted file mode 100644 index 1649b8fd3e..0000000000 --- a/src/Integration/src/RabbitMQ/Outbound/AbstractRabbitOutboundEndpoint.cs +++ /dev/null @@ -1,525 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.RabbitMQ.Support; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Support; - -namespace Steeltoe.Integration.RabbitMQ.Outbound; - -public abstract class AbstractRabbitOutboundEndpoint : AbstractReplyProducingMessageHandler, ILifecycle -{ - private readonly object _lock = new(); - private readonly string _noId = Guid.Empty.ToString(); - private readonly ILogger _logger; - - private Task ConfirmChecker { get; set; } - - private CancellationTokenSource TokenSource { get; set; } - - public string ExchangeName { get; set; } - - public string RoutingKey { get; set; } - - public IExpression ExchangeNameExpression { get; set; } - - public IExpression RoutingKeyExpression { get; set; } - - public ExpressionEvaluatingMessageProcessor RoutingKeyGenerator { get; set; } - - public ExpressionEvaluatingMessageProcessor ExchangeNameGenerator { get; set; } - - public IRabbitHeaderMapper HeaderMapper { get; set; } - - public IExpression ConfirmCorrelationExpression { get; set; } - - public ExpressionEvaluatingMessageProcessor CorrelationDataGenerator { get; set; } - - public IMessageChannel ConfirmAckChannel { get; set; } - - public string ConfirmAckChannelName { get; set; } - - public IMessageChannel ConfirmNackChannel { get; set; } - - public string ConfirmNackChannelName { get; set; } - - public IMessageChannel ReturnChannel { get; set; } - - public MessageDeliveryMode DefaultDeliveryMode { get; set; } - - public bool LazyConnect { get; set; } = true; - - public IConnectionFactory ConnectionFactory { get; set; } - - public IExpression DelayExpression { get; set; } - - public ExpressionEvaluatingMessageProcessor DelayGenerator { get; set; } - - public bool HeadersMappedLast { get; set; } - - public IErrorMessageStrategy ErrorMessageStrategy { get; set; } = new DefaultErrorMessageStrategy(); - - public TimeSpan? ConfirmTimeout { get; set; } - - public bool Running { get; set; } - - public bool IsRunning => Running; - - protected AbstractRabbitOutboundEndpoint(IApplicationContext context, ILogger logger) - : base(context) - { - _logger = logger; - HeaderMapper = DefaultRabbitHeaderMapper.GetOutboundMapper(_logger); - } - - public void SetExchangeNameExpressionString(string exchangeNameExpression) - { - ArgumentGuard.NotNullOrEmpty(exchangeNameExpression); - - ExchangeNameExpression = IntegrationServices.ExpressionParser.ParseExpression(exchangeNameExpression); - } - - public void SetRoutingKeyExpressionString(string routingKeyExpression) - { - ArgumentGuard.NotNullOrEmpty(routingKeyExpression); - - RoutingKeyExpression = IntegrationServices.ExpressionParser.ParseExpression(routingKeyExpression); - } - - public void SetConfirmCorrelationExpressionString(string confirmCorrelationExpression) - { - ArgumentGuard.NotNullOrEmpty(confirmCorrelationExpression); - - ConfirmCorrelationExpression = IntegrationServices.ExpressionParser.ParseExpression(confirmCorrelationExpression); - } - - public void SetDelay(int delay) - { - DelayExpression = new ValueExpression(delay); - } - - public void SetDelayExpressionString(string delayExpression) - { - DelayExpression = delayExpression == null ? null : IntegrationServices.ExpressionParser.ParseExpression(delayExpression); - } - - public void SetConfirmTimeout(int confirmTimeout) - { - ConfirmTimeout = TimeSpan.FromMilliseconds(confirmTimeout); - } - - public void Start() - { - lock (_lock) - { - if (!Running) - { - if (!LazyConnect && ConnectionFactory != null) - { - try - { - IConnection connection = ConnectionFactory.CreateConnection(); - - if (connection != null) - { - connection.Close(); - } - } - catch (Exception e) - { - _logger?.LogError(e, "Failed to eagerly establish the connection."); - } - } - - DoStart(); - - if (ConfirmTimeout != null && GetResolvedConfirmNackChannel() != null && GetRabbitTemplate() != null) - { - TokenSource = new CancellationTokenSource(); - ConfirmChecker = Task.Run(() => CheckUnconfirmed(TokenSource.Token)); - } - - Running = true; - } - } - } - - public void Stop() - { - lock (_lock) - { - if (Running) - { - DoStop(); - } - - Running = false; - - if (TokenSource != null) - { - TokenSource.Cancel(); - TokenSource = null; - ConfirmChecker = null; - } - } - } - - public override void Initialize() - { - ConfigureExchangeNameGenerator(ApplicationContext); - ConfigureRoutingKeyGenerator(ApplicationContext); - ConfigureCorrelationDataGenerator(); - ConfigureDelayGenerator(ApplicationContext); - - EndpointInit(); - } - - public Task StartAsync() - { - return Task.Run(Start); - } - - public Task StopAsync() - { - Stop(); - return Task.CompletedTask; - } - - protected virtual void DoStart() - { - } - - protected virtual void DoStop() - { - } - - protected virtual IMessageChannel GetResolvedConfirmAckChannel() - { - if (ConfirmAckChannel == null && ConfirmAckChannelName != null) - { - ConfirmAckChannel = IntegrationServices.ChannelResolver.ResolveDestination(ConfirmAckChannelName); - } - - return ConfirmAckChannel; - } - - protected virtual IMessageChannel GetResolvedConfirmNackChannel() - { - if (ConfirmNackChannel == null && ConfirmNackChannelName != null) - { - ConfirmNackChannel = IntegrationServices.ChannelResolver.ResolveDestination(ConfirmNackChannelName); - } - - return ConfirmNackChannel; - } - - protected virtual void EndpointInit() - { - } - - protected abstract RabbitTemplate GetRabbitTemplate(); - - protected virtual CorrelationData GenerateCorrelationData(IMessage requestMessage) - { - CorrelationData correlationData = null; - - string messageId = requestMessage.Headers.Id ?? _noId; - - if (CorrelationDataGenerator != null) - { - object userData = CorrelationDataGenerator.ProcessMessage(requestMessage); - - if (userData != null) - { - correlationData = new CorrelationDataWrapper(messageId, userData, requestMessage); - } - else - { - _logger?.LogDebug("'confirmCorrelationExpression' resolved to 'null'; no publisher confirm will be sent to the ack or nack channel"); - } - } - - if (correlationData == null) - { - object correlation = requestMessage.Headers[RabbitMessageHeaders.PublishConfirmCorrelation]; - - if (correlation is CorrelationData cdata) - { - correlationData = cdata; - } - - if (correlationData != null) - { - correlationData = new CorrelationDataWrapper(messageId, correlationData, requestMessage); - } - } - - return correlationData; - } - - protected virtual string GenerateExchangeName(IMessage requestMessage) - { - string exchange = ExchangeName; - - if (ExchangeNameGenerator != null) - { - exchange = ExchangeNameGenerator.ProcessMessage(requestMessage); - } - - return exchange; - } - - protected virtual string GenerateRoutingKey(IMessage requestMessage) - { - string key = RoutingKey; - - if (RoutingKeyGenerator != null) - { - key = RoutingKeyGenerator.ProcessMessage(requestMessage); - } - - return key; - } - - protected virtual void AddDelayProperty(IMessage message) - { - if (DelayGenerator != null) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.Delay = DelayGenerator.ProcessMessage(message); - } - } - - protected virtual IMessageBuilder BuildReply(IMessageConverter converter, IMessage amqpReplyMessage) - { - object replyObject = converter.FromMessage(amqpReplyMessage, null); - IMessageBuilder builder = PrepareMessageBuilder(replyObject); - - builder.CopyHeadersIfAbsent(amqpReplyMessage.Headers); - return builder; - } - - protected virtual IMessage BuildReturnedMessage(IMessage message, int replyCode, string replyText, string exchange, string returnedRoutingKey, - IMessageConverter converter) - { - object returnedObject = converter.FromMessage(message, null); - IMessageBuilder builder = PrepareMessageBuilder(returnedObject); - - if (ErrorMessageStrategy == null) - { - builder.CopyHeadersIfAbsent(message.Headers).SetHeader(RabbitMessageHeaders.ReturnReplyCode, replyCode) - .SetHeader(RabbitMessageHeaders.ReturnReplyText, replyText).SetHeader(RabbitMessageHeaders.ReturnExchange, exchange) - .SetHeader(RabbitMessageHeaders.ReturnRoutingKey, returnedRoutingKey); - } - - IMessage returnedMessage = builder.Build(); - - if (ErrorMessageStrategy != null) - { - returnedMessage = ErrorMessageStrategy.BuildErrorMessage( - new ReturnedRabbitMessageException(returnedMessage, replyCode, replyText, exchange, returnedRoutingKey), null); - } - - return returnedMessage; - } - - protected void HandleConfirm(CorrelationData correlationData, bool ack, string cause) - { - var wrapper = (CorrelationDataWrapper)correlationData; - - if (correlationData == null) - { - _logger.LogDebug("No correlation data provided for ack: {ack} cause:{cause}", ack, cause); - return; - } - - object userCorrelationData = wrapper.UserData; - IMessage confirmMessage; - confirmMessage = BuildConfirmMessage(ack, cause, wrapper, userCorrelationData); - - if (ack && GetResolvedConfirmAckChannel() != null) - { - SendOutput(confirmMessage, GetResolvedConfirmAckChannel(), true); - } - else if (!ack && GetResolvedConfirmNackChannel() != null) - { - SendOutput(confirmMessage, GetResolvedConfirmNackChannel(), true); - } - else - { - _logger.LogInformation("Nowhere to send publisher confirm {confirm} for {user}", ack ? "ack" : "nack", userCorrelationData); - } - } - - private IMessage BuildConfirmMessage(bool ack, string cause, CorrelationDataWrapper wrapper, object userCorrelationData) - { - if (ErrorMessageStrategy == null || ack) - { - var headers = new Dictionary - { - { RabbitMessageHeaders.PublishConfirm, ack } - }; - - if (!ack && !string.IsNullOrEmpty(cause)) - { - headers.Add(RabbitMessageHeaders.PublishConfirmNackCause, cause); - } - - return PrepareMessageBuilder(userCorrelationData).CopyHeaders(headers).Build(); - } - - return ErrorMessageStrategy.BuildErrorMessage(new NackedRabbitMessageException(wrapper.Message, wrapper.UserData, cause), null); - } - - private IMessageBuilder PrepareMessageBuilder(object replyObject) - { - IMessageBuilderFactory factory = IntegrationServices.MessageBuilderFactory; - return replyObject is IMessage message ? factory.FromMessage(message) : factory.WithPayload(replyObject); - } - - private void CheckUnconfirmed(CancellationToken cancellationToken) - { - double delay = ConfirmTimeout.Value.TotalMilliseconds / 2L; - - while (!cancellationToken.IsCancellationRequested) - { - RabbitTemplate rabbitTemplate = GetRabbitTemplate(); - - if (rabbitTemplate != null) - { - ICollection unconfirmed = rabbitTemplate.GetUnconfirmed((long)ConfirmTimeout.Value.TotalMilliseconds); - - if (unconfirmed != null) - { - foreach (CorrelationData cd in unconfirmed) - { - HandleConfirm(cd, false, "Confirm timed out"); - } - } - } - - Thread.Sleep((int)delay); - } - } - - private void ConfigureExchangeNameGenerator(IApplicationContext context) - { - if (ExchangeNameExpression != null && ExchangeName != null) - { - throw new InvalidOperationException("Either a exchangeName or an exchangeNameExpression can be provided, but not both"); - } - - if (ExchangeNameExpression != null) - { - ExchangeNameGenerator = new ExpressionEvaluatingMessageProcessor(context, ExchangeNameExpression); - } - } - - private void ConfigureRoutingKeyGenerator(IApplicationContext context) - { - if (RoutingKeyExpression != null && RoutingKey != null) - { - throw new InvalidOperationException("Either a routingKey or an routingKeyExpression can be provided, but not both"); - } - - if (RoutingKeyExpression != null) - { - RoutingKeyGenerator = new ExpressionEvaluatingMessageProcessor(context, RoutingKeyExpression); - } - } - - private void ConfigureCorrelationDataGenerator() - { - if (ConfirmCorrelationExpression != null) - { - CorrelationDataGenerator = new ExpressionEvaluatingMessageProcessor(ApplicationContext, ConfirmCorrelationExpression); - } - else - { - var nullChannel = IntegrationServicesUtils.ExtractTypeIfPossible(ConfirmAckChannel); - - if (!((ConfirmAckChannel == null || nullChannel != null) && ConfirmAckChannelName == null)) - { - throw new InvalidOperationException("A 'confirmCorrelationExpression' is required when specifying a 'confirmAckChannel'"); - } - - nullChannel = IntegrationServicesUtils.ExtractTypeIfPossible(ConfirmNackChannel); - - if (!((ConfirmNackChannel == null || nullChannel != null) && ConfirmNackChannelName == null)) - { - throw new InvalidOperationException("A 'confirmCorrelationExpression' is required when specifying a 'confirmNackChannel'"); - } - } - } - - private void ConfigureDelayGenerator(IApplicationContext context) - { - if (DelayExpression != null) - { - DelayGenerator = new ExpressionEvaluatingMessageProcessor(context, DelayExpression); - } - } - - protected class CorrelationDataWrapper : CorrelationData - { - public object UserData { get; } - - public IMessage Message { get; } - - public override TaskCompletionSource FutureSource - { - get - { - if (UserData is CorrelationData data) - { - return data.FutureSource; - } - - return base.FutureSource; - } - } - - public override IMessage ReturnedMessage - { - get - { - if (UserData is CorrelationData data) - { - return data.ReturnedMessage; - } - - return base.ReturnedMessage; - } - set - { - if (UserData is CorrelationData data) - { - data.ReturnedMessage = value; - } - - base.ReturnedMessage = value; - } - } - - public CorrelationDataWrapper(string id, object userData, IMessage message) - : base(id) - { - UserData = userData; - Message = message; - } - } -} diff --git a/src/Integration/src/RabbitMQ/Outbound/RabbitOutboundEndpoint.cs b/src/Integration/src/RabbitMQ/Outbound/RabbitOutboundEndpoint.cs deleted file mode 100644 index c48b7c8111..0000000000 --- a/src/Integration/src/RabbitMQ/Outbound/RabbitOutboundEndpoint.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.RabbitMQ.Support; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using static Steeltoe.Messaging.RabbitMQ.Core.RabbitTemplate; - -namespace Steeltoe.Integration.RabbitMQ.Outbound; - -public class RabbitOutboundEndpoint : AbstractRabbitOutboundEndpoint, IConfirmCallback, IReturnCallback -{ - public RabbitTemplate Template { get; } - - public bool ExpectReply { get; set; } - - public bool ShouldWaitForConfirm { get; set; } - - public TimeSpan WaitForConfirmTimeout { get; set; } - - public RabbitOutboundEndpoint(IApplicationContext context, RabbitTemplate rabbitTemplate, ILogger logger) - : base(context, logger) - { - ArgumentGuard.NotNull(rabbitTemplate); - - Template = rabbitTemplate; - ConnectionFactory = Template.ConnectionFactory; - } - - public new void Initialize() - { - base.Initialize(); - } - - public void Confirm(CorrelationData correlationData, bool ack, string cause) - { - HandleConfirm(correlationData, ack, cause); - } - - public void ReturnedMessage(IMessage message, int replyCode, string replyText, string exchange, string routingKey) - { - // no need for null check; we asserted we have a RabbitTemplate in doInit() - IMessageConverter converter = Template.MessageConverter; - IMessage returned = BuildReturnedMessage(message, replyCode, replyText, exchange, routingKey, converter); - ReturnChannel.Send(returned); - } - - protected override RabbitTemplate GetRabbitTemplate() - { - return Template; - } - - protected override void EndpointInit() - { - if (ConfirmCorrelationExpression != null) - { - Template.ConfirmCallback = this; - } - - if (ReturnChannel != null) - { - Template.ReturnCallback = this; - } - - TimeSpan? confirmTimeout = ConfirmTimeout; - - if (confirmTimeout != null) - { - WaitForConfirmTimeout = confirmTimeout.Value; - } - } - - protected override void DoStop() - { - Template.StopAsync().GetAwaiter().GetResult(); - } - - protected override object HandleRequestMessage(IMessage requestMessage) - { - CorrelationData correlationData = GenerateCorrelationData(requestMessage); - string exchangeName = GenerateExchangeName(requestMessage); - string routingKey = GenerateRoutingKey(requestMessage); - - if (ExpectReply) - { - return SendAndReceive(exchangeName, routingKey, requestMessage, correlationData); - } - - Send(exchangeName, routingKey, requestMessage, correlationData); - - if (ShouldWaitForConfirm && correlationData != null) - { - WaitForConfirm(requestMessage, correlationData); - } - - return null; - } - - private void WaitForConfirm(IMessage requestMessage, CorrelationData correlationData) - { - try - { - if (!correlationData.Future.Wait(WaitForConfirmTimeout)) - { - throw new MessageTimeoutException(requestMessage, $"{this}: Timed out awaiting publisher confirm"); - } - - CorrelationData.Confirm confirm = correlationData.Future.Result; - - if (!confirm.Ack) - { - throw new RabbitException($"Negative publisher confirm received: {confirm}"); - } - - if (correlationData.ReturnedMessage != null) - { - throw new RabbitException("Message was returned by the broker"); - } - } - catch (Exception e) - { - throw new RabbitException("Failed to get publisher confirm", e); - } - } - - private void Send(string exchangeName, string routingKey, IMessage requestMessage, CorrelationData correlationData) - { - IMessageConverter converter = Template.MessageConverter; - - IMessage message = MappingUtils.MapMessage(requestMessage, converter, HeaderMapper, DefaultDeliveryMode, HeadersMappedLast); - AddDelayProperty(message); - Template.Send(exchangeName, routingKey, message, correlationData); - } - - private IMessageBuilder SendAndReceive(string exchangeName, string routingKey, IMessage requestMessage, CorrelationData correlationData) - { - IMessageConverter converter = Template.MessageConverter; - - IMessage message = MappingUtils.MapMessage(requestMessage, converter, HeaderMapper, DefaultDeliveryMode, HeadersMappedLast); - AddDelayProperty(message); - - IMessage amqpReplyMessage = Template.SendAndReceive(exchangeName, routingKey, message, correlationData); - - if (amqpReplyMessage == null) - { - return null; - } - - return BuildReply(converter, amqpReplyMessage); - } -} diff --git a/src/Integration/src/RabbitMQ/Steeltoe.Integration.RabbitMQ.csproj b/src/Integration/src/RabbitMQ/Steeltoe.Integration.RabbitMQ.csproj deleted file mode 100644 index 69a4818b17..0000000000 --- a/src/Integration/src/RabbitMQ/Steeltoe.Integration.RabbitMQ.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - net8.0;net6.0 - - Steeltoe Integration RabbitMQ - **PLEASE NOTE** The public API of this package is subject to change and is not recommended for direct use outside of Steeltoe. - - Integration, ASPNET Core, Spring, Spring Cloud, RabbitMQ - true - - - - - - - - - - diff --git a/src/Integration/src/RabbitMQ/Support/DefaultRabbitHeaderMapper.cs b/src/Integration/src/RabbitMQ/Support/DefaultRabbitHeaderMapper.cs deleted file mode 100644 index 6704914dec..0000000000 --- a/src/Integration/src/RabbitMQ/Support/DefaultRabbitHeaderMapper.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Integration.Mapping; -using Steeltoe.Messaging; -using Steeltoe.Messaging.RabbitMQ; - -namespace Steeltoe.Integration.RabbitMQ.Support; - -public class DefaultRabbitHeaderMapper : AbstractHeaderMapper, IRabbitHeaderMapper -{ - private static readonly List StandardHeaderNames = new(); - - public static string[] InboundRequestHeaders { get; } = - { - "*" - }; - - public static string[] InboundReplyHeaders { get; } = SafeOutboundHeaders; - - public static string[] SafeOutboundHeaders { get; } = - { - "!x-*", - "*" - }; - - public static string[] OutboundRequestHeaders { get; } = SafeOutboundHeaders; - - public static string[] OutboundReplyHeaders { get; } = - { - "*" - }; - - static DefaultRabbitHeaderMapper() - { - StandardHeaderNames.Add(RabbitMessageHeaders.AppId); - StandardHeaderNames.Add(RabbitMessageHeaders.ClusterId); - StandardHeaderNames.Add(RabbitMessageHeaders.ContentEncoding); - StandardHeaderNames.Add(RabbitMessageHeaders.ContentLength); - StandardHeaderNames.Add(RabbitMessageHeaders.ContentType); - StandardHeaderNames.Add(RabbitMessageHeaders.CorrelationId); - - StandardHeaderNames.Add(RabbitMessageHeaders.Delay); - StandardHeaderNames.Add(RabbitMessageHeaders.DeliveryMode); - StandardHeaderNames.Add(RabbitMessageHeaders.DeliveryTag); - StandardHeaderNames.Add(RabbitMessageHeaders.Expiration); - StandardHeaderNames.Add(RabbitMessageHeaders.MessageCount); - StandardHeaderNames.Add(RabbitMessageHeaders.MessageId); - StandardHeaderNames.Add(RabbitMessageHeaders.ReceivedDelay); - StandardHeaderNames.Add(RabbitMessageHeaders.ReceivedDeliveryMode); - StandardHeaderNames.Add(RabbitMessageHeaders.ReceivedExchange); - StandardHeaderNames.Add(RabbitMessageHeaders.ReceivedRoutingKey); - StandardHeaderNames.Add(RabbitMessageHeaders.Redelivered); - StandardHeaderNames.Add(RabbitMessageHeaders.ReplyTo); - StandardHeaderNames.Add(RabbitMessageHeaders.Timestamp); - StandardHeaderNames.Add(RabbitMessageHeaders.Type); - StandardHeaderNames.Add(RabbitMessageHeaders.UserId); - StandardHeaderNames.Add(MessageHeaders.TypeId); - StandardHeaderNames.Add(MessageHeaders.ContentTypeId); - StandardHeaderNames.Add(MessageHeaders.KeyTypeId); - StandardHeaderNames.Add(RabbitMessageHeaders.SpringReplyCorrelation); - StandardHeaderNames.Add(RabbitMessageHeaders.SpringReplyToStack); - } - - protected DefaultRabbitHeaderMapper(string[] requestHeaderNames, string[] replyHeaderNames, ILogger logger) - : base(RabbitMessageHeaders.Prefix, StandardHeaderNames, StandardHeaderNames, logger) - { - if (requestHeaderNames != null) - { - SetRequestHeaderNames(requestHeaderNames); - } - - if (replyHeaderNames != null) - { - SetReplyHeaderNames(replyHeaderNames); - } - } - - protected override IDictionary ExtractStandardHeaders(IMessageHeaders source) - { - return new Dictionary(); - } - - protected override IDictionary ExtractUserDefinedHeaders(IMessageHeaders source) - { - throw new NotImplementedException(); - } - - protected override void PopulateStandardHeaders(IDictionary headers, IMessageHeaders target) - { - headers.Where(header => StandardHeaderNames.Contains(header.Key) && !string.IsNullOrEmpty(header.Value?.ToString())).ToList() - .ForEach(header => target.Add(header.Key, header.Value)); - } - - protected override void PopulateUserDefinedHeader(string headerName, object headerValue, IMessageHeaders target) - { - if (!target.ContainsKey(headerName) && headerName != RabbitMessageHeaders.ContentType && !headerName.StartsWith("json", StringComparison.Ordinal)) - { - target.Add(headerName, headerValue); - } - } - - public static DefaultRabbitHeaderMapper GetInboundMapper(ILogger logger) - { - return new DefaultRabbitHeaderMapper(InboundRequestHeaders, InboundReplyHeaders, logger); - } - - public static DefaultRabbitHeaderMapper GetOutboundMapper(ILogger logger) - { - return new DefaultRabbitHeaderMapper(OutboundRequestHeaders, OutboundReplyHeaders, logger); - } -} diff --git a/src/Integration/src/RabbitMQ/Support/EndpointUtils.cs b/src/Integration/src/RabbitMQ/Support/EndpointUtils.cs deleted file mode 100644 index 91810302bc..0000000000 --- a/src/Integration/src/RabbitMQ/Support/EndpointUtils.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client; -using Steeltoe.Messaging; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; - -namespace Steeltoe.Integration.RabbitMQ.Support; - -public static class EndpointUtils -{ - private const string ErrorMessage = "Message conversion failed"; - - public static ListenerExecutionFailedException CreateErrorMessagePayload(IMessage message, IModel channel, bool isManualAck, Exception e) - { - if (isManualAck) - { - return new ManualAckListenerExecutionFailedException(ErrorMessage, e, message, channel, message.Headers.DeliveryTag().Value); - } - - return new ListenerExecutionFailedException(ErrorMessage, e, message); - } -} diff --git a/src/Integration/src/RabbitMQ/Support/IRabbitHeaderMapper.cs b/src/Integration/src/RabbitMQ/Support/IRabbitHeaderMapper.cs deleted file mode 100644 index bc762808be..0000000000 --- a/src/Integration/src/RabbitMQ/Support/IRabbitHeaderMapper.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Integration.Mapping; -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.RabbitMQ.Support; - -public interface IRabbitHeaderMapper : IRequestReplyHeaderMapper -{ -} diff --git a/src/Integration/src/RabbitMQ/Support/ManualAckListenerExecutionFailedException.cs b/src/Integration/src/RabbitMQ/Support/ManualAckListenerExecutionFailedException.cs deleted file mode 100644 index ba2c19f2a9..0000000000 --- a/src/Integration/src/RabbitMQ/Support/ManualAckListenerExecutionFailedException.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client; -using Steeltoe.Messaging; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; - -namespace Steeltoe.Integration.RabbitMQ.Support; - -public class ManualAckListenerExecutionFailedException : ListenerExecutionFailedException -{ - public IModel Channel { get; } - public ulong DeliveryTag { get; } - - public ManualAckListenerExecutionFailedException(string message, Exception innerException, IMessage failedMessage, IModel channel, ulong deliveryTag) - : base(message, innerException, failedMessage) - { - Channel = channel; - DeliveryTag = deliveryTag; - } -} diff --git a/src/Integration/src/RabbitMQ/Support/MappingUtils.cs b/src/Integration/src/RabbitMQ/Support/MappingUtils.cs deleted file mode 100644 index fc86b50b1d..0000000000 --- a/src/Integration/src/RabbitMQ/Support/MappingUtils.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; - -namespace Steeltoe.Integration.RabbitMQ.Support; - -public static class MappingUtils -{ - /// - /// Map an o.s.m.Message to an o.s.a.core.Message. When using a , AmqpHeaders#CONTENT_TYPE and - /// MessageHeaders#CONTENT_TYPE will be used for the selection, with the AMQP header taking precedence. - /// - /// - /// The request message. - /// - /// - /// The message converter to use. - /// - /// - /// The header mapper to use. - /// - /// - /// The default delivery mode. - /// - /// - /// true if headers are mapped after conversion. - /// - /// - /// The mapped message. - /// - public static IMessage MapMessage(IMessage requestMessage, IMessageConverter converter, IRabbitHeaderMapper headerMapper, - MessageDeliveryMode defaultDeliveryMode, bool headersMappedLast) - { - return DoMapMessage(requestMessage, converter, headerMapper, headersMappedLast, false); - } - - private static IMessage DoMapMessage(IMessage message, IMessageConverter converter, IRabbitHeaderMapper headerMapper, bool headersMappedLast, bool reply) - { - var targetHeaders = new MessageHeaders(); - IMessage amqpMessage; - - if (!headersMappedLast) - { - MapHeaders(message.Headers, targetHeaders, headerMapper, reply); - } - - amqpMessage = converter.ToMessage(message.Payload, targetHeaders); - - if (headersMappedLast) - { - MapHeaders(message.Headers, targetHeaders, headerMapper, reply); - } - - return amqpMessage; - } - - private static void MapHeaders(IMessageHeaders messageHeaders, IMessageHeaders targetHeaders, IRabbitHeaderMapper headerMapper, bool reply) - { - if (reply) - { - headerMapper.FromHeadersToReply(messageHeaders, targetHeaders); - } - else - { - headerMapper.FromHeadersToRequest(messageHeaders, targetHeaders); - } - } -} diff --git a/src/Integration/src/RabbitMQ/Support/NackedRabbitMessageException.cs b/src/Integration/src/RabbitMQ/Support/NackedRabbitMessageException.cs deleted file mode 100644 index 72091d1e63..0000000000 --- a/src/Integration/src/RabbitMQ/Support/NackedRabbitMessageException.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.RabbitMQ.Support; - -public class NackedRabbitMessageException : MessagingException -{ - public object CorrelationData { get; } - public string NackReason { get; } - - public NackedRabbitMessageException(IMessage failedMessage, object correlationData, string nackReason) - : base(failedMessage) - { - CorrelationData = correlationData; - NackReason = nackReason; - } -} diff --git a/src/Integration/src/RabbitMQ/Support/RabbitMessageHeaderErrorMessageStrategy.cs b/src/Integration/src/RabbitMQ/Support/RabbitMessageHeaderErrorMessageStrategy.cs deleted file mode 100644 index 1cded6246f..0000000000 --- a/src/Integration/src/RabbitMQ/Support/RabbitMessageHeaderErrorMessageStrategy.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Integration.RabbitMQ.Support; - -public class RabbitMessageHeaderErrorMessageStrategy : IErrorMessageStrategy -{ - public const string AmqpRawMessage = $"{MessageHeaders.Internal}raw_message"; - - public ErrorMessage BuildErrorMessage(Exception exception, IAttributeAccessor attributeAccessor) - { - object inputMessage = attributeAccessor?.GetAttribute(ErrorMessageUtils.InputMessageContextKey); - var headers = new Dictionary(); - - if (attributeAccessor != null) - { - headers[AmqpRawMessage] = attributeAccessor.GetAttribute(AmqpRawMessage); - headers[IntegrationMessageHeaderAccessor.SourceData] = attributeAccessor.GetAttribute(AmqpRawMessage); - } - - return inputMessage is IMessage message ? new ErrorMessage(exception, headers, message) : new ErrorMessage(exception, headers); - } -} diff --git a/src/Integration/src/RabbitMQ/Support/ReturnedRabbitMessageException.cs b/src/Integration/src/RabbitMQ/Support/ReturnedRabbitMessageException.cs deleted file mode 100644 index 46a57cb6bb..0000000000 --- a/src/Integration/src/RabbitMQ/Support/ReturnedRabbitMessageException.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Integration.RabbitMQ.Support; - -public class ReturnedRabbitMessageException : MessagingException -{ - public int ReplyCode { get; } - public string ReplyText { get; } - public string Exchange { get; } - public string RoutingKey { get; } - - public ReturnedRabbitMessageException(IMessage failedMessage, int replyCode, string replyText, string exchange, string routingKey) - : base(failedMessage) - { - ReplyCode = replyCode; - ReplyText = replyText; - Exchange = exchange; - RoutingKey = routingKey; - } - - public override string ToString() - { - return $"{base.ToString()}, replyCode={ReplyCode}, replyText={ReplyText}, exchange={Exchange}, routingKey={RoutingKey}]"; - } -} diff --git a/src/Integration/test/Benchmark/Benchmark.csproj b/src/Integration/test/Benchmark/Benchmark.csproj deleted file mode 100644 index e6fea5206e..0000000000 --- a/src/Integration/test/Benchmark/Benchmark.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - Exe - net8.0;net6.0 - false - - - - - - - - - - - - - diff --git a/src/Integration/test/Benchmark/BenchmarkDotNet.Artifacts/results/Program-report-github.md b/src/Integration/test/Benchmark/BenchmarkDotNet.Artifacts/results/Program-report-github.md deleted file mode 100644 index 629d622f2e..0000000000 --- a/src/Integration/test/Benchmark/BenchmarkDotNet.Artifacts/results/Program-report-github.md +++ /dev/null @@ -1,18 +0,0 @@ -``` ini - -BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041 -Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores -.NET Core SDK=3.1.101 - [Host] : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT - DefaultJob : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT - - -``` -| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | -|------------------------------------------------------ |---------:|--------:|--------:|------:|------:|------:|----------:| -| DirectChannel_Send_10_000_000_SingleHandler | 229.1 ms | 2.47 ms | 2.31 ms | - | - | - | 5.97 KB | -| DirectChannel_SendAsync_10_000_000_SingleHandler | 233.5 ms | 1.50 ms | 1.33 ms | - | - | - | 4.73 KB | -| DirectChannel_Send_10_000_000_TwoHandlers | 472.9 ms | 5.28 ms | 4.41 ms | - | - | - | 6.6 KB | -| DirectChannel_Send_10_000_000_FourHandlers | 469.4 ms | 2.77 ms | 2.45 ms | - | - | - | 5.29 KB | -| PublishSubscribeChannel_Send_10_000_000_SingleHandler | 387.0 ms | 2.89 ms | 2.56 ms | - | - | - | 6.13 KB | -| PublishSubscribeChannel_Send_10_000_000_TwoHandlers | 468.5 ms | 3.60 ms | 3.37 ms | - | - | - | 6.32 KB | diff --git a/src/Integration/test/Benchmark/BenchmarkDotNet.Artifacts/results/Program-report.csv b/src/Integration/test/Benchmark/BenchmarkDotNet.Artifacts/results/Program-report.csv deleted file mode 100644 index ddcc0940de..0000000000 --- a/src/Integration/test/Benchmark/BenchmarkDotNet.Artifacts/results/Program-report.csv +++ /dev/null @@ -1,7 +0,0 @@ -Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,Gen 0,Gen 1,Gen 2,Allocated -DirectChannel_Send_10_000_000_SingleHandler,Default,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,229.1 ms,2.47 ms,2.31 ms,0.0000,0.0000,0.0000,5.97 KB -DirectChannel_SendAsync_10_000_000_SingleHandler,Default,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,233.5 ms,1.50 ms,1.33 ms,0.0000,0.0000,0.0000,4.73 KB -DirectChannel_Send_10_000_000_TwoHandlers,Default,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,472.9 ms,5.28 ms,4.41 ms,0.0000,0.0000,0.0000,6.6 KB -DirectChannel_Send_10_000_000_FourHandlers,Default,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,469.4 ms,2.77 ms,2.45 ms,0.0000,0.0000,0.0000,5.29 KB -PublishSubscribeChannel_Send_10_000_000_SingleHandler,Default,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,387.0 ms,2.89 ms,2.56 ms,0.0000,0.0000,0.0000,6.13 KB -PublishSubscribeChannel_Send_10_000_000_TwoHandlers,Default,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,468.5 ms,3.60 ms,3.37 ms,0.0000,0.0000,0.0000,6.32 KB diff --git a/src/Integration/test/Benchmark/BenchmarkDotNet.Artifacts/results/Program-report.html b/src/Integration/test/Benchmark/BenchmarkDotNet.Artifacts/results/Program-report.html deleted file mode 100644 index 595f6aeeb4..0000000000 --- a/src/Integration/test/Benchmark/BenchmarkDotNet.Artifacts/results/Program-report.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - Program-20200119-173654 - - - - -

-BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041
-Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
-.NET Core SDK=3.1.101
-  [Host]     : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT
-  DefaultJob : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodMeanErrorStdDevGen 0Gen 1Gen 2Allocated
DirectChannel_Send_10_000_000_SingleHandler229.1 ms2.47 ms2.31 ms---5.97 KB
DirectChannel_SendAsync_10_000_000_SingleHandler233.5 ms1.50 ms1.33 ms---4.73 KB
DirectChannel_Send_10_000_000_TwoHandlers472.9 ms5.28 ms4.41 ms---6.6 KB
DirectChannel_Send_10_000_000_FourHandlers469.4 ms2.77 ms2.45 ms---5.29 KB
PublishSubscribeChannel_Send_10_000_000_SingleHandler387.0 ms2.89 ms2.56 ms---6.13 KB
PublishSubscribeChannel_Send_10_000_000_TwoHandlers468.5 ms3.60 ms3.37 ms---6.32 KB
- - diff --git a/src/Integration/test/Benchmark/Program.cs b/src/Integration/test/Benchmark/Program.cs deleted file mode 100644 index f2e4cb48d7..0000000000 --- a/src/Integration/test/Benchmark/Program.cs +++ /dev/null @@ -1,220 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; - -namespace Benchmark; - -[MemoryDiagnoser] -public sealed class Program -{ - // | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | - // |------------------------------------------------------ |---------:|--------:|--------:|------:|------:|------:|----------:| - // | DirectChannel_Send_10_000_000_SingleHandler | 229.1 ms | 2.47 ms | 2.31 ms | - | - | - | 5.97 KB | - // | DirectChannel_SendAsync_10_000_000_SingleHandler | 233.5 ms | 1.50 ms | 1.33 ms | - | - | - | 4.73 KB | - // | DirectChannel_Send_10_000_000_TwoHandlers | 472.9 ms | 5.28 ms | 4.41 ms | - | - | - | 6.6 KB | - // | DirectChannel_Send_10_000_000_FourHandlers | 469.4 ms | 2.77 ms | 2.45 ms | - | - | - | 5.29 KB | - // | PublishSubscribeChannel_Send_10_000_000_SingleHandler | 387.0 ms | 2.89 ms | 2.56 ms | - | - | - | 6.13 KB | - // | PublishSubscribeChannel_Send_10_000_000_TwoHandlers | 468.5 ms | 3.60 ms | 3.37 ms | - | - | - | 6.32 KB | - - private static void Main() - { - BenchmarkRunner.Run(); - } - - [Benchmark] - public void DirectChannel_Send_10_000_000_SingleHandler() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var directChannel = new DirectChannel(provider.GetService()); - var handler = new CounterHandler(); - - directChannel.Subscribe(handler); - IMessage message = Message.Create("test"); - directChannel.Send(message); - - for (int i = 0; i < 10_000_000; i++) - { - directChannel.Send(message); - } - - if (handler.Count != 10_000_000 + 1) - { - throw new InvalidOperationException("Handler count invalid"); - } - } - - [Benchmark] - public async Task DirectChannel_SendAsync_10_000_000_SingleHandlerAsync() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var directChannel = new DirectChannel(provider.GetService()); - var handler = new CounterHandler(); - - directChannel.Subscribe(handler); - IMessage message = Message.Create("test"); - await directChannel.SendAsync(message); - - for (int i = 0; i < 10_000_000; i++) - { - await directChannel.SendAsync(message); - } - - if (handler.Count != 10_000_000 + 1) - { - throw new InvalidOperationException("Handler count invalid"); - } - } - - [Benchmark] - public void DirectChannel_Send_10_000_000_TwoHandlers() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var channel = new DirectChannel(provider.GetService()); - var count1 = new CounterHandler(); - var count2 = new CounterHandler(); - channel.Subscribe(count1); - channel.Subscribe(count2); - IMessage message = Message.Create("test"); - - for (int i = 0; i < 10_000_000; i++) - { - channel.Send(message); - } - - if (count1.Count != 5_000_000) - { - throw new InvalidOperationException("Handler count1 invalid"); - } - - if (count2.Count != 5_000_000) - { - throw new InvalidOperationException("Handler count2 invalid"); - } - } - - [Benchmark] - public void DirectChannel_Send_10_000_000_FourHandlers() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var channel = new DirectChannel(provider.GetService()); - var count1 = new CounterHandler(); - var count2 = new CounterHandler(); - var count3 = new CounterHandler(); - var count4 = new CounterHandler(); - channel.Subscribe(count1); - channel.Subscribe(count2); - channel.Subscribe(count3); - channel.Subscribe(count4); - IMessage message = Message.Create("test"); - - for (int i = 0; i < 10_000_000; i++) - { - channel.Send(message); - } - - if (count1.Count != 10_000_000 / 4) - { - throw new InvalidOperationException("Handler count1 invalid"); - } - - if (count2.Count != 10_000_000 / 4) - { - throw new InvalidOperationException("Handler count2 invalid"); - } - - if (count3.Count != 10_000_000 / 4) - { - throw new InvalidOperationException("Handler count3 invalid"); - } - - if (count4.Count != 10_000_000 / 4) - { - throw new InvalidOperationException("Handler count4 invalid"); - } - } - - [Benchmark] - public void PublishSubscribeChannel_Send_10_000_000_SingleHandler() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var channel = new PublishSubscribeChannel(provider.GetService()); - var handler = new CounterHandler(); - - channel.Subscribe(handler); - IMessage message = Message.Create("test"); - channel.Send(message); - - for (int i = 0; i < 10_000_000; i++) - { - channel.Send(message); - } - - if (handler.Count != 10_000_000 + 1) - { - throw new InvalidOperationException("Handler count invalid"); - } - } - - [Benchmark] - public void PublishSubscribeChannel_Send_10_000_000_TwoHandlers() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var channel = new PublishSubscribeChannel(provider.GetService()); - var handler1 = new CounterHandler(); - var handler2 = new CounterHandler(); - channel.Subscribe(handler1); - channel.Subscribe(handler2); - IMessage message = Message.Create("test"); - - for (int i = 0; i < 10_000_000; i++) - { - channel.Send(message); - } - - if (handler1.Count != 10_000_000) - { - throw new InvalidOperationException("Handler count1 invalid"); - } - - if (handler2.Count != 10_000_000) - { - throw new InvalidOperationException("Handler count2 invalid"); - } - } - - private sealed class CounterHandler : IMessageHandler - { - public int Count { get; private set; } - - public string ServiceName { get; set; } = nameof(CounterHandler); - - public void HandleMessage(IMessage message) - { - Count++; - } - } -} diff --git a/src/Integration/test/Integration.Test/Channel/DatatypeChannelTest.cs b/src/Integration/test/Integration.Test/Channel/DatatypeChannelTest.cs deleted file mode 100644 index 84721623f8..0000000000 --- a/src/Integration/test/Integration.Test/Channel/DatatypeChannelTest.cs +++ /dev/null @@ -1,217 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Support.Converter; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Integration.Test.Channel; - -public sealed class DataTypeChannelTest -{ - [Fact] - public void SupportedType() - { - IMessageChannel channel = CreateChannel(typeof(string)); - Assert.True(channel.Send(Message.Create("test"))); - } - - [Fact] - public void UnsupportedTypeAndNoConversionService() - { - IMessageChannel channel = CreateChannel(typeof(int)); - Assert.Throws(() => channel.Send(Message.Create("123"))); - } - - [Fact] - public void UnsupportedTypeButConversionServiceSupports() - { - QueueChannel channel = CreateChannel(typeof(int)); - IConversionService conversionService = new DefaultConversionService(); - var converter = new DefaultDataTypeChannelMessageConverter(conversionService); - channel.MessageConverter = converter; - Assert.True(channel.Send(Message.Create("123"))); - } - - [Fact] - public void UnsupportedTypeAndConversionServiceDoesNotSupport() - { - QueueChannel channel = CreateChannel(typeof(int)); - IConversionService conversionService = new DefaultConversionService(); - var converter = new DefaultDataTypeChannelMessageConverter(conversionService); - channel.MessageConverter = converter; - Assert.Throws(() => channel.Send(Message.Create(true))); - } - - [Fact] - public void UnsupportedTypeButCustomConversionServiceSupports() - { - QueueChannel channel = CreateChannel(typeof(int)); - GenericConversionService conversionService = new DefaultConversionService(); - conversionService.AddConverter(new BoolToIntConverter()); - var converter = new DefaultDataTypeChannelMessageConverter(conversionService); - channel.MessageConverter = converter; - Assert.True(channel.Send(Message.Create(true))); - Assert.Equal(1, channel.Receive().Payload); - } - - [Fact] - public void ConversionServiceUsedByDefault() - { - var converter = new BoolToIntConverter(); - var convService = new GenericConversionService(); - convService.AddConverter(converter); - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(convService); - services.AddSingleton(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - - var channel = new QueueChannel(provider.GetService(), "testChannel") - { - DataTypes = new List - { - typeof(int), - typeof(DateTime) - } - }; - - Assert.True(channel.Send(Message.Create(true))); - Assert.Equal(1, channel.Receive().Payload); - } - - [Fact] - public void MultipleTypes() - { - IMessageChannel channel = CreateChannel(typeof(string), typeof(int)); - Assert.True(channel.Send(Message.Create("test1"))); - Assert.True(channel.Send(Message.Create(2))); - Exception exception = null; - - try - { - channel.Send(Message.Create(default)); - } - catch (MessageDeliveryException e) - { - exception = e; - } - - Assert.NotNull(exception); - } - - [Fact] - public void SubclassOfAcceptedType() - { - IMessageChannel channel = CreateChannel(typeof(Exception)); - Assert.True(channel.Send(new ErrorMessage(new MessagingException("test")))); - } - - [Fact] - public void SuperclassOfAcceptedTypeNotAccepted() - { - IMessageChannel channel = CreateChannel(typeof(InvalidOperationException)); - Assert.Throws(() => channel.Send(new ErrorMessage(new Exception("test")))); - } - - [Fact] - public void GenericConverters() - { - QueueChannel channel = CreateChannel(typeof(Foo)); - var conversionService = new DefaultConversionService(); - conversionService.AddConverter(new StringToBarConverter()); - conversionService.AddConverter(new IntegerToBazConverter()); - var converter = new DefaultDataTypeChannelMessageConverter(conversionService); - channel.MessageConverter = converter; - Assert.True(channel.Send(Message.Create("foo"))); - IMessage outMessage = channel.Receive(0); - Assert.IsType(outMessage.Payload); - Assert.True(channel.Send(Message.Create(42))); - outMessage = channel.Receive(0); - Assert.IsType(outMessage.Payload); - } - - private static QueueChannel CreateChannel(params Type[] dataTypes) - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - - var channel = new QueueChannel(provider.GetService(), "testChannel") - { - DataTypes = new List(dataTypes) - }; - - return channel; - } - - private sealed class BoolToIntConverter : IGenericConverter - { - public ISet<(Type SourceType, Type TargetType)> ConvertibleTypes { get; } = new HashSet<(Type, Type)> - { - (typeof(bool), typeof(int)) - }; - - public object Convert(object source, Type sourceType, Type targetType) - { - bool asBool = (bool)source; - return asBool ? 1 : 0; - } - } - - private abstract class Foo - { - } - - private sealed class Bar : Foo - { - } - - private sealed class Baz : Foo - { - } - - private sealed class StringToBarConverter : IGenericConverter - { - public ISet<(Type SourceType, Type TargetType)> ConvertibleTypes { get; } = new HashSet<(Type, Type)> - { - (typeof(string), typeof(Foo)), - (typeof(string), typeof(Bar)) - }; - - public object Convert(object source, Type sourceType, Type targetType) - { - return new Bar(); - } - } - - private sealed class IntegerToBazConverter : IGenericConverter - { - public ISet<(Type SourceType, Type TargetType)> ConvertibleTypes { get; } = new HashSet<(Type, Type)> - { - (typeof(int), typeof(Foo)), - (typeof(int), typeof(Baz)) - }; - - public object Convert(object source, Type sourceType, Type targetType) - { - return new Baz(); - } - } -} diff --git a/src/Integration/test/Integration.Test/Channel/DirectChannelTest.cs b/src/Integration/test/Integration.Test/Channel/DirectChannelTest.cs deleted file mode 100644 index 4a24f6ef19..0000000000 --- a/src/Integration/test/Integration.Test/Channel/DirectChannelTest.cs +++ /dev/null @@ -1,210 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Channel; - -public sealed class DirectChannelTest -{ - private readonly IServiceProvider _provider; - - public DirectChannelTest() - { - var services = new ServiceCollection(); - services.AddSingleton(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - } - - [Fact] - public void TestSend() - { - var target = new ThreadNameExtractingTestTarget(); - var channel = new DirectChannel(_provider.GetService()); - channel.Subscribe(target); - IMessage message = Message.Create("test"); - int? currentId = Task.CurrentId; - int curThreadId = Thread.CurrentThread.ManagedThreadId; - Assert.True(channel.Send(message)); - Assert.Equal(currentId, target.TaskId); - Assert.Equal(curThreadId, target.ThreadId); - } - - [Fact] - public async Task TestSendAsync() - { - var target = new ThreadNameExtractingTestTarget(); - var channel = new DirectChannel(_provider.GetService()); - channel.Subscribe(target); - IMessage message = Message.Create("test"); - Assert.True(await channel.SendAsync(message)); - } - - [Fact] - public void TestSendOneHandler_10_000_000() - { - /* - * INT-3308 - used to run 12 million/sec - * 1. optimize for single handler 20 million/sec - * 2. Don't iterate over empty data types 23 million/sec - * 3. Don't iterate over empty interceptors 31 million/sec - * 4. Move single handler optimization to dispatcher 34 million/sec - * - * 29 million per second with increment counter in the handler - */ - var channel = new DirectChannel(_provider.GetService()); - - var handler = new CounterHandler(); - channel.Subscribe(handler); - IMessage message = Message.Create("test"); - Assert.True(channel.Send(message)); - - for (int i = 0; i < 10_000_000; i++) - { - channel.Send(message); - } - - Assert.Equal(10_000_001, handler.Count); - } - - [Fact] - public async Task TestSendAsyncOneHandler_10_000_000() - { - var channel = new DirectChannel(_provider.GetService()); - - var handler = new CounterHandler(); - channel.Subscribe(handler); - IMessage message = Message.Create("test"); - Assert.True(await channel.SendAsync(message)); - - for (int i = 0; i < 10_000_000; i++) - { - await channel.SendAsync(message); - } - - Assert.Equal(10_000_001, handler.Count); - } - - [Fact] - public void TestSendTwoHandlers_10_000_000() - { - /* - * INT-3308 - used to run 6.4 million/sec - * 1. Skip empty iterators as above 7.2 million/sec - * 2. optimize for single handler 6.7 million/sec (small overhead added) - * 3. remove LB rwlock from UnicastingDispatcher 7.2 million/sec - * 4. Move single handler optimization to dispatcher 7.3 million/sec - */ - var channel = new DirectChannel(_provider.GetService()); - var count1 = new CounterHandler(); - var count2 = new CounterHandler(); - channel.Subscribe(count1); - channel.Subscribe(count2); - IMessage message = Message.Create("test"); - - for (int i = 0; i < 10_000_000; i++) - { - channel.Send(message); - } - - Assert.Equal(5_000_000, count1.Count); - Assert.Equal(5_000_000, count2.Count); - } - - [Fact] - public void TestSendFourHandlers_10_000_000() - { - var channel = new DirectChannel(_provider.GetService()); - var count1 = new CounterHandler(); - var count2 = new CounterHandler(); - var count3 = new CounterHandler(); - var count4 = new CounterHandler(); - channel.Subscribe(count1); - channel.Subscribe(count2); - channel.Subscribe(count3); - channel.Subscribe(count4); - IMessage message = Message.Create("test"); - - for (int i = 0; i < 10_000_000; i++) - { - channel.Send(message); - } - - Assert.Equal(10_000_000 / 4, count1.Count); - Assert.Equal(10_000_000 / 4, count2.Count); - Assert.Equal(10_000_000 / 4, count3.Count); - Assert.Equal(10_000_000 / 4, count4.Count); - } - - [Fact] - public void TestSendInSeparateThread() - { - var latch = new CountdownEvent(1); - var channel = new DirectChannel(_provider.GetService()); - var target = new ThreadNameExtractingTestTarget(latch); - channel.Subscribe(target); - IMessage message = Message.Create("test"); - - var thread = new Thread(() => channel.Send(message)) - { - Name = "test-thread" - }; - - thread.Start(); - latch.Wait(1000); - Assert.Equal("test-thread", target.ThreadName); - } - - internal sealed class CounterHandler : IMessageHandler - { - public int Count { get; private set; } - - public string ServiceName { get; set; } = nameof(CounterHandler); - - public void HandleMessage(IMessage message) - { - Count++; - } - } - - internal sealed class ThreadNameExtractingTestTarget : IMessageHandler - { - public CountdownEvent Latch { get; } - public int? TaskId { get; private set; } - public int ThreadId { get; private set; } - public string ThreadName { get; private set; } - - public string ServiceName { get; set; } = nameof(ThreadNameExtractingTestTarget); - - public ThreadNameExtractingTestTarget() - : this(null) - { - } - - public ThreadNameExtractingTestTarget(CountdownEvent latch) - { - Latch = latch; - } - - public void HandleMessage(IMessage message) - { - TaskId = Task.CurrentId; - ThreadId = Thread.CurrentThread.ManagedThreadId; - ThreadName = Thread.CurrentThread.Name; - - if (Latch != null) - { - Latch.Signal(); - } - } - } -} diff --git a/src/Integration/test/Integration.Test/Channel/DirectChannelWriterTest.cs b/src/Integration/test/Integration.Test/Channel/DirectChannelWriterTest.cs deleted file mode 100644 index ebd05182b4..0000000000 --- a/src/Integration/test/Integration.Test/Channel/DirectChannelWriterTest.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Xunit; -using static Steeltoe.Integration.Test.Channel.DirectChannelTest; - -namespace Steeltoe.Integration.Test.Channel; - -public sealed class DirectChannelWriterTest -{ - private readonly ServiceCollection _services; - - public DirectChannelWriterTest() - { - _services = new ServiceCollection(); - _services.AddSingleton(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - _services.AddSingleton(configurationRoot); - _services.AddSingleton(); - } - - [Fact] - public async Task TestWriteAsync() - { - ServiceProvider provider = _services.BuildServiceProvider(true); - var target = new ThreadNameExtractingTestTarget(); - var channel = new DirectChannel(provider.GetService()); - channel.Subscribe(target); - IMessage message = Message.Create("test"); - int? currentId = Task.CurrentId; - int curThreadId = Thread.CurrentThread.ManagedThreadId; - await channel.Writer.WriteAsync(message); - Assert.Equal(currentId, target.TaskId); - Assert.Equal(curThreadId, target.ThreadId); - } - - [Fact] - public void TestTryWrite() - { - ServiceProvider provider = _services.BuildServiceProvider(true); - var target = new ThreadNameExtractingTestTarget(); - var channel = new DirectChannel(provider.GetService()); - channel.Subscribe(target); - IMessage message = Message.Create("test"); - int? currentId = Task.CurrentId; - int curThreadId = Thread.CurrentThread.ManagedThreadId; - Assert.True(channel.Writer.TryWrite(message)); - Assert.Equal(currentId, target.TaskId); - Assert.Equal(curThreadId, target.ThreadId); - } - - [Fact] - public async Task TestWaitToWriteAsync() - { - ServiceProvider provider = _services.BuildServiceProvider(true); - var target = new ThreadNameExtractingTestTarget(); - var channel = new DirectChannel(provider.GetService()); - channel.Subscribe(target); - Assert.True(await channel.Writer.WaitToWriteAsync()); - channel.Unsubscribe(target); - Assert.False(await channel.Writer.WaitToWriteAsync()); - } - - [Fact] - public void TestTryComplete() - { - ServiceProvider provider = _services.BuildServiceProvider(true); - var channel = new DirectChannel(provider.GetService()); - Assert.False(channel.Writer.TryComplete()); - } -} diff --git a/src/Integration/test/Integration.Test/Channel/DispatcherHasNoSubscribersTest.cs b/src/Integration/test/Integration.Test/Channel/DispatcherHasNoSubscribersTest.cs deleted file mode 100644 index 7f3688c17e..0000000000 --- a/src/Integration/test/Integration.Test/Channel/DispatcherHasNoSubscribersTest.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Handler; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Channel; - -public sealed class DispatcherHasNoSubscribersTest -{ - [Fact] - public void OneChannel() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var noSubscribersChannel = new DirectChannel(provider.GetService()); - - try - { - noSubscribersChannel.Send(Message.Create("Hello, world!")); - throw new Exception("Exception expected"); - } - catch (MessagingException e) - { - Assert.Contains("Dispatcher has no subscribers", e.Message, StringComparison.Ordinal); - } - } - - [Fact] - public void BridgedChannel() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var noSubscribersChannel = new DirectChannel(provider.GetService()); - var subscribedChannel = new DirectChannel(provider.GetService()); - - var bridgeHandler = new BridgeHandler(provider.GetService()) - { - OutputChannel = noSubscribersChannel - }; - - subscribedChannel.Subscribe(bridgeHandler); - - try - { - subscribedChannel.Send(Message.Create("Hello, world!")); - throw new Exception("Exception expected"); - } - catch (MessagingException e) - { - Assert.Contains("Dispatcher has no subscribers", e.Message, StringComparison.Ordinal); - } - } -} diff --git a/src/Integration/test/Integration.Test/Channel/DispatchingChannelErrorHandlingTest.cs b/src/Integration/test/Integration.Test/Channel/DispatchingChannelErrorHandlingTest.cs deleted file mode 100644 index 6c999f0362..0000000000 --- a/src/Integration/test/Integration.Test/Channel/DispatchingChannelErrorHandlingTest.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Xunit; - -namespace Steeltoe.Integration.Test.Channel; - -public sealed class DispatchingChannelErrorHandlingTest -{ - private readonly CountdownEvent _latch = new(1); - - private readonly IServiceCollection _services; - - public DispatchingChannelErrorHandlingTest() - { - _services = new ServiceCollection(); - _services.AddSingleton(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - _services.AddSingleton(configurationRoot); - _services.AddSingleton(); - } - - [Fact] - public void HandlerThrowsExceptionPublishSubscribeWithoutScheduler() - { - ServiceProvider provider = _services.BuildServiceProvider(true); - var channel = new PublishSubscribeChannel(provider.GetService()); - var handler = new ThrowingHandler(); - channel.Subscribe(handler); - IMessage message = IntegrationMessageBuilder.WithPayload("test").Build(); - Assert.Throws(() => channel.Send(message)); - } - - [Fact] - public void HandlerThrowsExceptionPublishSubscribeWithExecutor() - { - _services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - _services.AddSingleton(); - _services.AddSingleton(p => new DirectChannel(p.GetService(), "errorChannel")); - ServiceProvider provider = _services.BuildServiceProvider(true); - - var defaultErrorChannel = provider.GetService() as DirectChannel; - var channel = new PublishSubscribeChannel(provider.GetService(), TaskScheduler.Default); - var resultHandler = new ResultHandler(_latch); - var throwingHandler = new ThrowMessageExceptionHandler(); - channel.Subscribe(throwingHandler); - defaultErrorChannel.Subscribe(resultHandler); - IMessage message = IntegrationMessageBuilder.WithPayload("test").Build(); - channel.Send(message); - Assert.True(_latch.Wait(10000)); - IMessage errorMessage = resultHandler.LastMessage; - Assert.IsType(errorMessage.Payload); - var exceptionPayload = (MessagingException)errorMessage.Payload; - Assert.IsType(exceptionPayload.InnerException); - Assert.Same(message, exceptionPayload.FailedMessage); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, resultHandler.LastThread.ManagedThreadId); - } - - [Fact] - public void HandlerThrowsExceptionExecutorChannel() - { - _services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - _services.AddSingleton(); - _services.AddSingleton(p => new DirectChannel(p.GetService(), "errorChannel")); - ServiceProvider provider = _services.BuildServiceProvider(true); - - var defaultErrorChannel = provider.GetService() as DirectChannel; - var channel = new TaskSchedulerChannel(provider.GetService(), TaskScheduler.Default); - var resultHandler = new ResultHandler(_latch); - var throwingHandler = new ThrowMessageExceptionHandler(); - channel.Subscribe(throwingHandler); - defaultErrorChannel.Subscribe(resultHandler); - IMessage message = IntegrationMessageBuilder.WithPayload("test").Build(); - channel.Send(message); - Assert.True(_latch.Wait(10000)); - IMessage errorMessage = resultHandler.LastMessage; - Assert.IsType(errorMessage.Payload); - var exceptionPayload = (MessagingException)errorMessage.Payload; - Assert.IsType(exceptionPayload.InnerException); - Assert.Same(message, exceptionPayload.FailedMessage); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, resultHandler.LastThread.ManagedThreadId); - } - - private sealed class ThrowMessageExceptionHandler : IMessageHandler - { - public Exception ExceptionToThrow { get; } = new NotSupportedException("intentional test failure"); - - public string ServiceName { get; set; } = nameof(ThrowMessageExceptionHandler); - - public void HandleMessage(IMessage message) - { - throw new MessagingException(message, ExceptionToThrow); - } - } - - private sealed class ThrowingHandler : IMessageHandler - { - public Exception ExceptionToThrow { get; } = new NotSupportedException("intentional test failure"); - - public string ServiceName { get; set; } = nameof(ThrowingHandler); - - public void HandleMessage(IMessage message) - { - throw ExceptionToThrow; - } - } - - private sealed class ResultHandler : IMessageHandler - { - private readonly CountdownEvent _latch; - private volatile IMessage _lastMessage; - private volatile Thread _lastThread; - - public string ServiceName { get; set; } = nameof(ResultHandler); - public IMessage LastMessage => _lastMessage; - public Thread LastThread => _lastThread; - - public ResultHandler(CountdownEvent latch) - { - _latch = latch; - } - - public void HandleMessage(IMessage message) - { - _lastMessage = message; - _lastThread = Thread.CurrentThread; - _latch.Signal(); - } - } - - public sealed class TestTimedOutException : Exception - { - public TestTimedOutException() - : base("timed out while waiting for latch") - { - } - } -} diff --git a/src/Integration/test/Integration.Test/Channel/Interceptor/ChannelInterceptorTest.cs b/src/Integration/test/Integration.Test/Channel/Interceptor/ChannelInterceptorTest.cs deleted file mode 100644 index b1b76dd4d4..0000000000 --- a/src/Integration/test/Integration.Test/Channel/Interceptor/ChannelInterceptorTest.cs +++ /dev/null @@ -1,374 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Support; -using Xunit; -using AbstractMessageChannel = Steeltoe.Integration.Channel.AbstractMessageChannel; - -namespace Steeltoe.Integration.Test.Channel.Interceptor; - -public sealed class ChannelInterceptorTest -{ - private readonly QueueChannel _channel; - private readonly IServiceProvider _provider; - - public ChannelInterceptorTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - _channel = new QueueChannel(_provider.GetService()); - } - - [Fact] - public void TestPreSendInterceptorReturnsMessage() - { - var interceptor = new PreSendReturnsMessageInterceptor(); - _channel.AddInterceptor(interceptor); - _channel.Send(Message.Create("test")); - IMessage result = _channel.Receive(0); - Assert.NotNull(result); - Assert.Equal("test", result.Payload); - Assert.Equal(1, result.Headers[nameof(PreSendReturnsMessageInterceptor)]); - Assert.True(interceptor.AfterCompletionInvoked); - } - - [Fact] - public void TestPreSendInterceptorReturnsNull() - { - var interceptor = new PreSendReturnsNullInterceptor(); - _channel.AddInterceptor(interceptor); - IMessage message = Message.Create("test"); - _channel.Send(message); - Assert.Equal(1, interceptor.Counter); - - Assert.True(_channel.RemoveInterceptor(interceptor)); - - _channel.Send(Message.Create("TEST")); - Assert.Equal(1, interceptor.Counter); - - IMessage result = _channel.Receive(0); - Assert.NotNull(result); - Assert.Equal("TEST", result.Payload); - } - - [Fact] - public void TestPostSendInterceptorWithSentMessage() - { - var interceptor = new TestPostSendInterceptorWithSentMessageInterceptor(); - _channel.AddInterceptor(interceptor); - _channel.Send(Message.Create("test")); - Assert.True(interceptor.Invoked); - } - - [Fact] - public void TestPostSendInterceptorWithUnsentMessage() - { - var singleItemChannel = new QueueChannel(_provider.GetService(), 1); - var interceptor = new TestPostSendInterceptorWithUnsentMessageInterceptor(); - singleItemChannel.AddInterceptor(interceptor); - Assert.Equal(0, interceptor.InvokedCounter); - Assert.Equal(0, interceptor.SentCounter); - singleItemChannel.Send(Message.Create("test1")); - Assert.Equal(1, interceptor.InvokedCounter); - Assert.Equal(1, interceptor.SentCounter); - singleItemChannel.Send(Message.Create("test2"), 0); - Assert.Equal(2, interceptor.InvokedCounter); - Assert.Equal(1, interceptor.SentCounter); - Assert.NotNull(singleItemChannel.RemoveInterceptor(0)); - singleItemChannel.Send(Message.Create("test2"), 0); - Assert.Equal(2, interceptor.InvokedCounter); - Assert.Equal(1, interceptor.SentCounter); - } - - [Fact] - public void AfterCompletionWithSendException() - { - AbstractMessageChannel testChannel = new AfterCompletionWithSendExceptionChannel(_provider.GetService()); - - var interceptor1 = new AfterCompletionTestInterceptor(); - var interceptor2 = new AfterCompletionTestInterceptor(); - testChannel.AddInterceptor(interceptor1); - testChannel.AddInterceptor(interceptor2); - - try - { - testChannel.Send(IntegrationMessageBuilder.WithPayload("test").Build()); - } - catch (Exception ex) - { - Assert.Equal("Simulated exception", ex.InnerException.Message); - } - - Assert.True(interceptor1.AfterCompletionInvoked); - Assert.True(interceptor2.AfterCompletionInvoked); - } - - [Fact] - public void AfterCompletionWithPreSendException() - { - var interceptor1 = new AfterCompletionTestInterceptor(); - - var interceptor2 = new AfterCompletionTestInterceptor - { - ExceptionToRaise = new Exception("Simulated exception") - }; - - _channel.AddInterceptor(interceptor1); - _channel.AddInterceptor(interceptor2); - - try - { - _channel.Send(IntegrationMessageBuilder.WithPayload("test").Build()); - } - catch (Exception ex) - { - Assert.Equal("Simulated exception", ex.InnerException.Message); - } - - Assert.True(interceptor1.AfterCompletionInvoked); - Assert.False(interceptor2.AfterCompletionInvoked); - } - - [Fact] - public void TestPreReceiveInterceptorReturnsTrue() - { - var interceptor = new PreReceiveReturnsTrueInterceptor(); - _channel.AddInterceptor(interceptor); - IMessage message = Message.Create("test"); - _channel.Send(message); - IMessage result = _channel.Receive(0); - Assert.Equal(1, interceptor.Counter); - Assert.NotNull(result); - Assert.True(interceptor.AfterCompletionInvoked); - } - - [Fact] - public void TestPreReceiveInterceptorReturnsFalse() - { - var interceptor = new PreReceiveReturnsFalseInterceptor(); - _channel.AddInterceptor(interceptor); - IMessage message = Message.Create("test"); - _channel.Send(message); - IMessage result = _channel.Receive(0); - Assert.Equal(1, interceptor.Counter); - Assert.Null(result); - } - - [Fact] - public void TestPostReceiveInterceptor() - { - var interceptor = new TestPostReceiveInterceptorInterceptor(); - _channel.AddInterceptor(interceptor); - - _channel.Receive(0); - Assert.Equal(0, interceptor.Counter); - _channel.Send(Message.Create("test")); - IMessage result = _channel.Receive(0); - Assert.NotNull(result); - Assert.Equal(1, interceptor.Counter); - } - - [Fact] - public void AfterCompletionWithReceiveException() - { - var interceptor1 = new PreReceiveReturnsTrueInterceptor(); - - var interceptor2 = new PreReceiveReturnsTrueInterceptor - { - ExceptionToRaise = new Exception("Simulated exception") - }; - - _channel.AddInterceptor(interceptor1); - _channel.AddInterceptor(interceptor2); - - try - { - _channel.Receive(0); - } - catch (Exception ex) - { - Assert.Equal("Simulated exception", ex.Message); - } - - Assert.True(interceptor1.AfterCompletionInvoked); - Assert.False(interceptor2.AfterCompletionInvoked); - } - - public sealed class TestPostReceiveInterceptorInterceptor : AbstractChannelInterceptor - { - private int _counter; - - public int Counter => _counter; - - public override IMessage PostReceive(IMessage message, IMessageChannel channel) - { - Assert.NotNull(channel); - Interlocked.Increment(ref _counter); - return message; - } - } - - public sealed class AfterCompletionWithSendExceptionChannel : AbstractMessageChannel - { - internal AfterCompletionWithSendExceptionChannel(IApplicationContext context, ILogger logger = null) - : base(context, logger) - { - } - - protected override bool DoSendInternal(IMessage message, CancellationToken cancellationToken) - { - throw new Exception("Simulated exception"); - } - } - - public sealed class TestPostSendInterceptorWithUnsentMessageInterceptor : AbstractChannelInterceptor - { - private int _invokedCounter; - private int _sentCounter; - - public int InvokedCounter => _invokedCounter; - public int SentCounter => _sentCounter; - - public override void PostSend(IMessage message, IMessageChannel channel, bool sent) - { - Assert.NotNull(message); - Assert.NotNull(channel); - - if (sent) - { - Interlocked.Increment(ref _sentCounter); - } - - Interlocked.Increment(ref _invokedCounter); - } - } - - private sealed class TestPostSendInterceptorWithSentMessageInterceptor : AbstractChannelInterceptor - { - public bool Invoked { get; private set; } - - public override void PostSend(IMessage message, IMessageChannel channel, bool sent) - { - Assert.NotNull(message); - Assert.NotNull(channel); - Assert.True(sent); - Invoked = true; - } - } - - private sealed class PreSendReturnsMessageInterceptor : AbstractChannelInterceptor - { - private int _counter; - private volatile bool _afterCompletionInvoked; - - public bool AfterCompletionInvoked => _afterCompletionInvoked; - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - Assert.NotNull(message); - int value = Interlocked.Increment(ref _counter); - return IntegrationMessageBuilder.FromMessage(message).SetHeader(GetType().Name, value).Build(); - } - - public override void AfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception exception) - { - _afterCompletionInvoked = true; - } - } - - private sealed class PreSendReturnsNullInterceptor : AbstractChannelInterceptor - { - private int _counter; - - public int Counter => _counter; - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - Assert.NotNull(message); - Interlocked.Increment(ref _counter); - return null; - } - } - - private sealed class AfterCompletionTestInterceptor : AbstractChannelInterceptor - { - private int _counter; - private volatile bool _afterCompletionInvoked; - - public Exception ExceptionToRaise { get; set; } - public bool AfterCompletionInvoked => _afterCompletionInvoked; - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - Assert.NotNull(message); - Interlocked.Increment(ref _counter); - - if (ExceptionToRaise != null) - { - throw ExceptionToRaise; - } - - return message; - } - - public override void AfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception exception) - { - _afterCompletionInvoked = true; - } - } - - private sealed class PreReceiveReturnsTrueInterceptor : AbstractChannelInterceptor - { - private int _counter; - private volatile bool _afterCompletionInvoked; - - public Exception ExceptionToRaise { get; set; } - public int Counter => _counter; - public bool AfterCompletionInvoked => _afterCompletionInvoked; - - public override bool PreReceive(IMessageChannel channel) - { - Interlocked.Increment(ref _counter); - - if (ExceptionToRaise != null) - { - throw ExceptionToRaise; - } - - return true; - } - - public override void AfterReceiveCompletion(IMessage message, IMessageChannel channel, Exception exception) - { - _afterCompletionInvoked = true; - } - } - - private sealed class PreReceiveReturnsFalseInterceptor : AbstractChannelInterceptor - { - private int _counter; - - public int Counter => _counter; - - public override bool PreReceive(IMessageChannel channel) - { - Interlocked.Increment(ref _counter); - return false; - } - } -} diff --git a/src/Integration/test/Integration.Test/Channel/MixedDispatcherConfigurationScenarioTest.cs b/src/Integration/test/Integration.Test/Channel/MixedDispatcherConfigurationScenarioTest.cs deleted file mode 100644 index a546ba3696..0000000000 --- a/src/Integration/test/Integration.Test/Channel/MixedDispatcherConfigurationScenarioTest.cs +++ /dev/null @@ -1,486 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Xunit; - -namespace Steeltoe.Integration.Test.Channel; - -public sealed class MixedDispatcherConfigurationScenarioTest -{ - private const int TotalExecutions = 40; - private readonly IMessage _message = Message.Create("test"); - private readonly CountdownEvent _allDone; - private readonly CountdownEvent _start; - private readonly Mock _handlerA; - private readonly Mock _handlerB; - private readonly Mock _handlerC; - private readonly Mock> _exceptionRegistry; - private readonly IServiceProvider _provider; - private int _failed; - - public MixedDispatcherConfigurationScenarioTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - - _handlerA = new Mock(); - _handlerB = new Mock(); - _handlerC = new Mock(); - _allDone = new CountdownEvent(TotalExecutions); - _start = new CountdownEvent(1); - _failed = 0; - _exceptionRegistry = new Mock>(); - } - - [Fact] - public void NoFailoverNoLoadBalancing() - { - var channel = new DirectChannel(_provider.GetService(), null, "noLoadBalancerNoFailover") - { - Failover = false - }; - - _handlerA.Setup(h => h.HandleMessage(_message)).Throws(new MessageRejectedException(_message, null)); - IMessageDispatcher dispatcher = channel.Dispatcher; - dispatcher.AddHandler(_handlerA.Object); - dispatcher.AddHandler(_handlerB.Object); - - try - { - channel.Send(_message); - } - catch (Exception) - { - // Intentionally left empty. - } - - try - { - channel.Send(_message); - } - catch (Exception) - { - // Intentionally left empty. - } - - _handlerA.Verify(h => h.HandleMessage(_message), Times.Exactly(2)); - _handlerB.Verify(h => h.HandleMessage(_message), Times.Exactly(0)); - } - - [Fact] - public void NoFailoverNoLoadBalancingConcurrent() - { - var channel = new DirectChannel(_provider.GetService(), null, "noLoadBalancerNoFailover") - { - Failover = false - }; - - _handlerA.Setup(h => h.HandleMessage(_message)).Throws(new MessageRejectedException(_message, null)); - IMessageDispatcher dispatcher = channel.Dispatcher; - dispatcher.AddHandler(_handlerA.Object); - dispatcher.AddHandler(_handlerB.Object); - - void MessageSenderTask() - { - _start.Wait(); - - bool sent = false; - - try - { - sent = channel.Send(_message); - } - catch (Exception e2) - { - _exceptionRegistry.Object.Add(e2); - } - - if (!sent) - { - _failed = 1; - } - - _allDone.Signal(); - } - - for (int i = 0; i < TotalExecutions; i++) - { - Task.Run(MessageSenderTask); - } - - _start.Signal(); - Assert.True(_allDone.Wait(10000)); - - Assert.Equal(1, _failed); - _handlerA.Verify(h => h.HandleMessage(_message), Times.Exactly(TotalExecutions)); - _handlerB.Verify(h => h.HandleMessage(_message), Times.Exactly(0)); - - _exceptionRegistry.Verify(list => list.Add(It.IsAny()), Times.Exactly(TotalExecutions)); - } - - [Fact] - public void NoFailoverNoLoadBalancingWithExecutorConcurrent() - { - var channel = new TaskSchedulerChannel(_provider.GetService(), TaskScheduler.Default, null, "noLoadBalancerNoFailoverExecutor") - { - Failover = false - }; - - _handlerA.Setup(h => h.HandleMessage(_message)).Callback(() => - { - var e = new Exception(); - _failed = 1; - _exceptionRegistry.Object.Add(e); - _allDone.Signal(); - throw e; - }); - - IMessageDispatcher dispatcher = channel.Dispatcher; - dispatcher.AddHandler(_handlerA.Object); - dispatcher.AddHandler(_handlerB.Object); - - void MessageSenderTask() - { - _start.Wait(); - - channel.Send(_message); - } - - for (int i = 0; i < TotalExecutions; i++) - { - Task.Run(MessageSenderTask); - } - - _start.Signal(); - Assert.True(_allDone.Wait(10000)); - - Assert.Equal(1, _failed); - _handlerA.Verify(h => h.HandleMessage(_message), Times.Exactly(TotalExecutions)); - _handlerB.Verify(h => h.HandleMessage(_message), Times.Exactly(0)); - - _exceptionRegistry.Verify(list => list.Add(It.IsAny()), Times.Exactly(TotalExecutions)); - } - - [Fact] - public void NoFailoverLoadBalancing() - { - var channel = new DirectChannel(_provider.GetService(), "loadBalancerNoFailover") - { - Failover = false - }; - - _handlerA.Setup(h => h.HandleMessage(_message)).Throws(new MessageRejectedException(_message, null)); - IMessageDispatcher dispatcher = channel.Dispatcher; - dispatcher.AddHandler(_handlerA.Object); - dispatcher.AddHandler(_handlerB.Object); - dispatcher.AddHandler(_handlerC.Object); - - try - { - channel.Send(_message); - } - catch (Exception) - { - // Intentionally left empty. - } - - _handlerA.Verify(h => h.HandleMessage(_message), Times.Exactly(1)); - _handlerB.Verify(h => h.HandleMessage(_message), Times.Exactly(0)); - _handlerC.Verify(h => h.HandleMessage(_message), Times.Exactly(0)); - - try - { - channel.Send(_message); - } - catch (Exception) - { - // Intentionally left empty. - } - - _handlerA.Verify(h => h.HandleMessage(_message), Times.Exactly(1)); - _handlerB.Verify(h => h.HandleMessage(_message), Times.Exactly(1)); - _handlerC.Verify(h => h.HandleMessage(_message), Times.Exactly(0)); - - try - { - channel.Send(_message); - } - catch (Exception) - { - // Intentionally left empty. - } - - _handlerA.Verify(h => h.HandleMessage(_message), Times.Exactly(1)); - _handlerB.Verify(h => h.HandleMessage(_message), Times.Exactly(1)); - _handlerC.Verify(h => h.HandleMessage(_message), Times.Exactly(1)); - } - - [Fact] - public void NoFailoverLoadBalancingConcurrent() - { - var channel = new DirectChannel(_provider.GetService(), "loadBalancerNoFailover") - { - Failover = false - }; - - _handlerA.Setup(h => h.HandleMessage(_message)).Throws(new MessageRejectedException(_message, null)); - IMessageDispatcher dispatcher = channel.Dispatcher; - dispatcher.AddHandler(_handlerA.Object); - dispatcher.AddHandler(_handlerB.Object); - dispatcher.AddHandler(_handlerC.Object); - - var start1 = new CountdownEvent(1); - var allDone1 = new CountdownEvent(TotalExecutions); - IMessage message2 = _message; - int failed1 = 0; - - void MessageSenderTask() - { - start1.Wait(); - - bool sent = false; - - try - { - sent = channel.Send(message2); - } - catch (Exception e2) - { - _exceptionRegistry.Object.Add(e2); - } - - if (!sent) - { - failed1 = 1; - } - - allDone1.Signal(); - } - - for (int i = 0; i < TotalExecutions; i++) - { - Task.Run(MessageSenderTask); - } - - start1.Signal(); - Assert.True(allDone1.Wait(10000)); - - Assert.Equal(1, failed1); - _handlerA.Verify(h => h.HandleMessage(message2), Times.Exactly(14)); - _handlerB.Verify(h => h.HandleMessage(message2), Times.Exactly(13)); - _handlerC.Verify(h => h.HandleMessage(message2), Times.Exactly(13)); - _exceptionRegistry.Verify(list => list.Add(It.IsAny()), Times.Exactly(14)); - } - - [Fact] - public void NoFailoverLoadBalancingWithExecutorConcurrent() - { - var channel = new TaskSchedulerChannel(_provider.GetService(), TaskScheduler.Default) - { - Failover = false - }; - - IMessageDispatcher dispatcher = channel.Dispatcher; - dispatcher.AddHandler(_handlerA.Object); - dispatcher.AddHandler(_handlerB.Object); - dispatcher.AddHandler(_handlerC.Object); - - var start1 = new CountdownEvent(1); - var allDone1 = new CountdownEvent(TotalExecutions); - IMessage message2 = _message; - int failed1 = 0; - - _handlerA.Setup(h => h.HandleMessage(_message)).Callback(() => - { - failed1 = 1; - var e = new Exception(); - _exceptionRegistry.Object.Add(e); - allDone1.Signal(); - throw e; - }); - - _handlerB.Setup(h => h.HandleMessage(_message)).Callback(() => - { - allDone1.Signal(); - }); - - _handlerC.Setup(h => h.HandleMessage(_message)).Callback(() => - { - allDone1.Signal(); - }); - - void MessageSenderTask() - { - start1.Wait(); - - channel.Send(_message); - } - - for (int i = 0; i < TotalExecutions; i++) - { - Task.Run(MessageSenderTask); - } - - start1.Signal(); - Assert.True(allDone1.Wait(10000)); - Assert.Equal(1, failed1); - _handlerA.Verify(h => h.HandleMessage(message2), Times.Exactly(14)); - _handlerB.Verify(h => h.HandleMessage(message2), Times.Exactly(13)); - _handlerC.Verify(h => h.HandleMessage(message2), Times.Exactly(13)); - _exceptionRegistry.Verify(list => list.Add(It.IsAny()), Times.Exactly(14)); - } - - [Fact] - public void FailoverNoLoadBalancing() - { - var channel = new DirectChannel(_provider.GetService(), null, "loadBalancerNoFailover") - { - Failover = true - }; - - _handlerA.Setup(h => h.HandleMessage(_message)).Throws(new MessageRejectedException(_message, null)); - IMessageDispatcher dispatcher = channel.Dispatcher; - dispatcher.AddHandler(_handlerA.Object); - dispatcher.AddHandler(_handlerB.Object); - - try - { - channel.Send(_message); - } - catch (Exception) - { - // Intentionally left empty. - } - - _handlerA.Verify(h => h.HandleMessage(_message), Times.Exactly(1)); - _handlerB.Verify(h => h.HandleMessage(_message), Times.Exactly(1)); - - try - { - channel.Send(_message); - } - catch (Exception) - { - // Intentionally left empty. - } - - _handlerA.Verify(h => h.HandleMessage(_message), Times.Exactly(2)); - _handlerB.Verify(h => h.HandleMessage(_message), Times.Exactly(2)); - } - - [Fact] - public void FailoverNoLoadBalancingConcurrent() - { - var channel = new DirectChannel(_provider.GetService(), null, "noLoadBalancerFailover") - { - Failover = true - }; - - _handlerA.Setup(h => h.HandleMessage(_message)).Throws(new MessageRejectedException(_message, null)); - IMessageDispatcher dispatcher = channel.Dispatcher; - dispatcher.AddHandler(_handlerA.Object); - dispatcher.AddHandler(_handlerB.Object); - dispatcher.AddHandler(_handlerC.Object); - - var start1 = new CountdownEvent(1); - var allDone1 = new CountdownEvent(TotalExecutions); - IMessage message2 = _message; - int failed1 = 0; - - void MessageSenderTask() - { - start1.Wait(); - - bool sent = false; - - try - { - sent = channel.Send(message2); - } - catch (Exception e2) - { - _exceptionRegistry.Object.Add(e2); - } - - if (!sent) - { - failed1 = 1; - } - - allDone1.Signal(); - } - - for (int i = 0; i < TotalExecutions; i++) - { - Task.Run(MessageSenderTask); - } - - start1.Signal(); - Assert.True(allDone1.Wait(10000)); - Assert.Equal(0, failed1); - _handlerA.Verify(h => h.HandleMessage(message2), Times.Exactly(TotalExecutions)); - _handlerB.Verify(h => h.HandleMessage(message2), Times.Exactly(TotalExecutions)); - _handlerC.Verify(h => h.HandleMessage(message2), Times.Never()); - _exceptionRegistry.Verify(list => list.Add(It.IsAny()), Times.Never()); - } - - [Fact] - public void FailoverNoLoadBalancingWithExecutorConcurrent() - { - var channel = new TaskSchedulerChannel(_provider.GetService(), TaskScheduler.Default, null, null) - { - Failover = true - }; - - IMessageDispatcher dispatcher = channel.Dispatcher; - dispatcher.AddHandler(_handlerA.Object); - dispatcher.AddHandler(_handlerB.Object); - dispatcher.AddHandler(_handlerC.Object); - - _handlerA.Setup(h => h.HandleMessage(_message)).Callback(() => - { - _failed = 1; - var e = new Exception(); - throw e; - }); - - _handlerB.Setup(h => h.HandleMessage(_message)).Callback(() => - { - _allDone.Signal(); - }); - - void MessageSenderTask() - { - _start.Wait(); - - channel.Send(_message); - } - - for (int i = 0; i < TotalExecutions; i++) - { - Task.Run(MessageSenderTask); - } - - _start.Signal(); - Assert.True(_allDone.Wait(10000)); - _handlerA.Verify(h => h.HandleMessage(_message), Times.Exactly(TotalExecutions)); - _handlerB.Verify(h => h.HandleMessage(_message), Times.Exactly(TotalExecutions)); - _handlerC.Verify(h => h.HandleMessage(_message), Times.Never()); - } -} diff --git a/src/Integration/test/Integration.Test/Channel/PublishSubscribeChannelTest.cs b/src/Integration/test/Integration.Test/Channel/PublishSubscribeChannelTest.cs deleted file mode 100644 index 5676475451..0000000000 --- a/src/Integration/test/Integration.Test/Channel/PublishSubscribeChannelTest.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Channel; - -public sealed class PublishSubscribeChannelTest -{ - [Fact] - public void TestSend() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var handler = new CounterHandler(); - var channel = new PublishSubscribeChannel(provider.GetService()); - channel.Subscribe(handler); - IMessage message = Message.Create("test"); - Assert.True(channel.Send(message)); - Assert.Equal(1, handler.Count); - } - - [Fact] - public async Task TestSendAsync() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var handler = new CounterHandler(); - var channel = new PublishSubscribeChannel(provider.GetService()); - channel.Subscribe(handler); - IMessage message = Message.Create("test"); - Assert.True(await channel.SendAsync(message)); - Assert.Equal(1, handler.Count); - } - - [Fact] - public void TestSendOneHandler_10_000_000() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var handler = new CounterHandler(); - var channel = new PublishSubscribeChannel(provider.GetService()); - channel.Subscribe(handler); - IMessage message = Message.Create("test"); - Assert.True(channel.Send(message)); - - for (int i = 0; i < 10_000_000; i++) - { - channel.Send(message); - } - - Assert.Equal(10_000_001, handler.Count); - } - - [Fact] - public async Task TestSendAsyncOneHandler_10_000_000() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var handler = new CounterHandler(); - var channel = new PublishSubscribeChannel(provider.GetService()); - channel.Subscribe(handler); - IMessage message = Message.Create("test"); - Assert.True(await channel.SendAsync(message)); - - for (int i = 0; i < 10_000_000; i++) - { - await channel.SendAsync(message); - } - - Assert.Equal(10_000_001, handler.Count); - } - - [Fact] - public async Task TestSendAsyncTwoHandler_10_000_000() - { - var services = new ServiceCollection(); - services.AddSingleton(); - ServiceProvider provider = services.BuildServiceProvider(true); - var handler1 = new CounterHandler(); - var handler2 = new CounterHandler(); - var channel = new PublishSubscribeChannel(provider.GetService()); - channel.Subscribe(handler1); - channel.Subscribe(handler2); - IMessage message = Message.Create("test"); - - for (int i = 0; i < 10_000_000; i++) - { - await channel.SendAsync(message); - } - - Assert.Equal(10_000_000, handler1.Count); - Assert.Equal(10_000_000, handler2.Count); - } - - private sealed class CounterHandler : IMessageHandler - { - public int Count { get; private set; } - - public string ServiceName { get; set; } = nameof(CounterHandler); - - public void HandleMessage(IMessage message) - { - Count++; - } - } -} diff --git a/src/Integration/test/Integration.Test/Channel/QueueChannelTest.cs b/src/Integration/test/Integration.Test/Channel/QueueChannelTest.cs deleted file mode 100644 index b476ac00e7..0000000000 --- a/src/Integration/test/Integration.Test/Channel/QueueChannelTest.cs +++ /dev/null @@ -1,236 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -#nullable enable - -using System.Threading.Channels; -using FluentAssertions; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Channel; - -public sealed class QueueChannelTest -{ - private static readonly TimeSpan DefaultTestTimeout = TimeSpan.FromSeconds(5); - private static readonly Action SendTestMessage = channel => channel.Send(Message.Create("testing")); - - private static readonly Func ReceiveTestMessage = (channel, timeout) => - timeout == null ? channel.Receive() : channel.Receive(timeout.Value); - - [Fact] - public void TestSimpleSendAndReceive() - { - var channel = new QueueChannel(); - - AssertMessageExchange(() => ReceiveTestMessage(channel, null) != null, () => SendTestMessage(channel)); - } - - [Fact] - public void TestSimpleSendAndReceiveWithNonBlockingQueue() - { - var boundedChannel = System.Threading.Channels.Channel.CreateBounded(new BoundedChannelOptions(int.MaxValue) - { - FullMode = BoundedChannelFullMode.DropWrite - }); - - var channel = new QueueChannel(null, boundedChannel); - - AssertMessageExchange(() => ReceiveTestMessage(channel, null) != null, () => SendTestMessage(channel)); - } - - [Fact] - public void TestSimpleSendAndReceiveWithNonBlockingQueueWithTimeout() - { - var boundedChannel = System.Threading.Channels.Channel.CreateBounded(new BoundedChannelOptions(int.MaxValue) - { - FullMode = BoundedChannelFullMode.DropWrite - }); - - var channel = new QueueChannel(null, boundedChannel); - const int receiveTimeout = 500; - - AssertMessageExchange(() => ReceiveTestMessage(channel, receiveTimeout) != null, () => SendTestMessage(channel)); - } - - [Fact] - public void TestSimpleSendAndReceiveWithTimeout() - { - var channel = new QueueChannel(); - const int receiveTimeout = 500; - - AssertMessageExchange(() => ReceiveTestMessage(channel, receiveTimeout) != null, () => SendTestMessage(channel)); - } - - [Fact] - public void TestImmediateReceive() - { - var channel = new QueueChannel(); - const int receiveTimeout = 0; - - AssertMessageExchange(() => ReceiveTestMessage(channel, receiveTimeout) == null, null); - SendTestMessage(channel); - AssertMessageExchange(() => ReceiveTestMessage(channel, receiveTimeout) != null, null); - } - - [Fact] - public async Task TestBlockingReceiveAsyncWithNoTimeout() - { - var channel = new QueueChannel(); - var cancellationTokenSource = new CancellationTokenSource(); - - await AssertMessageExchangeAsync(async () => await channel.ReceiveAsync(cancellationTokenSource.Token) == null, cancellationTokenSource.Cancel); - } - - [Fact] - public void TestBlockingReceiveWithTimeout() - { - var channel = new QueueChannel(); - const int receiveTimeout = 500; - - AssertMessageExchange(() => ReceiveTestMessage(channel, receiveTimeout) == null, null); - } - - [Fact] - public async Task TestBlockingReceiveAsyncWithTimeout() - { - var channel = new QueueChannel(); - const int receiveTimeout = 500; - - var cancellationTokenSource = new CancellationTokenSource(); - cancellationTokenSource.CancelAfter(receiveTimeout); - - await AssertMessageExchangeAsync(async () => await channel.ReceiveAsync(cancellationTokenSource.Token) == null, cancellationTokenSource.Cancel); - } - - [Fact] - public void TestImmediateSend() - { - var channel = new QueueChannel(null, 3); - const int receiveTimeout = 500; - const int receiveTimeoutZero = 0; - - channel.Send(Message.Create("test-1")).Should().BeTrue(); - channel.Send(Message.Create("test-2"), receiveTimeout).Should().BeTrue(); - channel.Send(Message.Create("test-3"), receiveTimeoutZero).Should().BeTrue(); - channel.Send(Message.Create("test-4"), receiveTimeoutZero).Should().BeFalse(); - } - - [Fact] - public async Task TestBlockingSendAsyncWithNoTimeout() - { - var channel = new QueueChannel(null, 1); - - (await channel.SendAsync(Message.Create("test-1"))).Should().BeTrue(); - - var cancellationTokenSource = new CancellationTokenSource(); - - await AssertMessageExchangeAsync(async () => !await channel.SendAsync(Message.Create("test-2"), cancellationTokenSource.Token), - cancellationTokenSource.Cancel); - } - - [Fact] - public void TestBlockingSendWithTimeout() - { - var channel = new QueueChannel(null, 1); - - channel.Send(Message.Create("test-1")).Should().BeTrue(); - - const int sendTimeout = 500; - AssertMessageExchange(() => !channel.Send(Message.Create("test-2"), sendTimeout), null); - } - - [Fact] - public async Task TestBlockingSendAsyncWithTimeout() - { - var channel = new QueueChannel(null, 1); - - (await channel.SendAsync(Message.Create("test-1"))).Should().BeTrue(); - - var cancellationTokenSource = new CancellationTokenSource(); - const int sendTimeout = 500; - - await AssertMessageExchangeAsync(async () => - { - cancellationTokenSource.CancelAfter(sendTimeout); - return !await channel.SendAsync(Message.Create("test-2"), cancellationTokenSource.Token); - }, null); - } - - [Fact] - public void TestClear() - { - var channel = new QueueChannel(null, 2); - - IMessage message1 = Message.Create("test1"); - IMessage message2 = Message.Create("test2"); - IMessage message3 = Message.Create("test3"); - - channel.Send(message1).Should().BeTrue(); - channel.Send(message2).Should().BeTrue(); - channel.Send(message3, 0).Should().BeFalse(); - - channel.QueueSize.Should().Be(2); - channel.RemainingCapacity.Should().Be(2 - 2); - - IList clearedMessages = channel.Clear(); - clearedMessages.Should().HaveCount(2); - - channel.QueueSize.Should().Be(0); - channel.RemainingCapacity.Should().Be(2); - - channel.Send(message3).Should().BeTrue(); - } - - [Fact] - public void TestClearEmptyChannel() - { - var channel = new QueueChannel(); - - IList clearedMessages = channel.Clear(); - - clearedMessages.Should().BeEmpty(); - } - - [Fact] - public void TestPurge() - { - var channel = new QueueChannel(); - - Func> action = () => channel.Purge(null); - action.Should().ThrowExactly(); - } - - private static void AssertMessageExchange(Func backgroundOperation, Action? foregroundOperation) - { - Task backgroundTask = Task.Run(() => - { - bool succeeded = backgroundOperation(); - succeeded.Should().BeTrue("background operation should succeed"); - }); - - foregroundOperation?.Invoke(); - - bool succeeded = backgroundTask.Wait(DefaultTestTimeout); - - if (!succeeded) - { - throw new TimeoutException($"Background operation timed out unexpectedly after {DefaultTestTimeout}."); - } - } - - private static async Task AssertMessageExchangeAsync(Func> backgroundAsyncOperation, Action? foregroundOperation) - { - Task backgroundTask = Task.Run(async () => - { - bool succeeded = await backgroundAsyncOperation(); - succeeded.Should().BeTrue("async background operation should succeed"); - }); - - foregroundOperation?.Invoke(); - - await backgroundTask.WaitAsync(DefaultTestTimeout); - } -} diff --git a/src/Integration/test/Integration.Test/Channel/TaskSchedulerChannelTest.cs b/src/Integration/test/Integration.Test/Channel/TaskSchedulerChannelTest.cs deleted file mode 100644 index 0b4e32160a..0000000000 --- a/src/Integration/test/Integration.Test/Channel/TaskSchedulerChannelTest.cs +++ /dev/null @@ -1,265 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Integration.Test.Channel; - -public sealed class TaskSchedulerChannelTest -{ - private readonly IServiceProvider _provider; - - public TaskSchedulerChannelTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - } - - [Fact] - public void VerifyDifferentThread() - { - var channel = new TaskSchedulerChannel(_provider.GetService(), TaskScheduler.Default); - var latch = new CountdownEvent(1); - var handler = new TestHandler(latch); - channel.Subscribe(handler); - channel.Send(Message.Create("test")); - Assert.True(latch.Wait(1000)); - Assert.NotNull(handler.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler.Thread.ManagedThreadId); - } - - [Fact] - public void RoundRobinLoadBalancing() - { - const int numberOfMessages = 12; - var channel = new TaskSchedulerChannel(_provider.GetService(), TaskScheduler.Default); - var latch = new CountdownEvent(numberOfMessages); - var handler1 = new TestHandler(latch); - var handler2 = new TestHandler(latch); - var handler3 = new TestHandler(latch); - channel.Subscribe(handler1); - channel.Subscribe(handler2); - channel.Subscribe(handler3); - - for (int i = 0; i < numberOfMessages; i++) - { - channel.Send(Message.Create($"test-{i}")); - } - - Assert.True(latch.Wait(3000)); - Assert.NotNull(handler1.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler1.Thread.ManagedThreadId); - Assert.NotNull(handler2.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler2.Thread.ManagedThreadId); - Assert.NotNull(handler3.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler3.Thread.ManagedThreadId); - - Assert.Equal(4, handler1.Count); - Assert.Equal(4, handler2.Count); - Assert.Equal(4, handler3.Count); - } - - [Fact] - public void VerifyFailoverWithLoadBalancing() - { - const int numberOfMessages = 12; - var channel = new TaskSchedulerChannel(_provider.GetService(), TaskScheduler.Default); - var latch = new CountdownEvent(numberOfMessages); - var handler1 = new TestHandler(latch); - var handler2 = new TestHandler(latch); - var handler3 = new TestHandler(latch); - channel.Subscribe(handler1); - channel.Subscribe(handler2); - channel.Subscribe(handler3); - handler2.ShouldFail = true; - - for (int i = 0; i < numberOfMessages; i++) - { - channel.Send(Message.Create($"test-{i}")); - } - - Assert.True(latch.Wait(3000)); - Assert.NotNull(handler1.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler1.Thread.ManagedThreadId); - Assert.NotNull(handler2.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler2.Thread.ManagedThreadId); - Assert.NotNull(handler3.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler3.Thread.ManagedThreadId); - - Assert.Equal(4, handler1.Count); - Assert.Equal(0, handler2.Count); - Assert.Equal(8, handler3.Count); - } - - [Fact] - public void VerifyFailoverWithoutLoadBalancing() - { - const int numberOfMessages = 12; - var channel = new TaskSchedulerChannel(_provider.GetService(), TaskScheduler.Default); - var latch = new CountdownEvent(numberOfMessages); - var handler1 = new TestHandler(latch); - var handler2 = new TestHandler(latch); - var handler3 = new TestHandler(latch); - channel.Subscribe(handler1); - channel.Subscribe(handler2); - channel.Subscribe(handler3); - handler1.ShouldFail = true; - - for (int i = 0; i < numberOfMessages; i++) - { - channel.Send(Message.Create($"test-{i}")); - } - - Assert.True(latch.Wait(3000)); - Assert.NotNull(handler1.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler1.Thread.ManagedThreadId); - Assert.NotNull(handler2.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler2.Thread.ManagedThreadId); - Assert.NotNull(handler3.Thread); - Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, handler3.Thread.ManagedThreadId); - - Assert.Equal(0, handler1.Count); - Assert.Equal(8, handler2.Count); - Assert.Equal(4, handler3.Count); - } - - [Fact] - public void InterceptorWithModifiedMessage() - { - var channel = new TaskSchedulerChannel(_provider.GetService(), TaskScheduler.Default); - - var mockHandler = new Mock(); - var mockExpected = new Mock(); - var latch = new CountdownEvent(2); - - var interceptor = new BeforeHandleInterceptor(latch) - { - MessageToReturn = mockExpected.Object - }; - - channel.AddInterceptor(interceptor); - channel.Subscribe(mockHandler.Object); - channel.Send(Message.Create("foo")); - Assert.True(latch.Wait(10000)); - mockHandler.Verify(h => h.HandleMessage(mockExpected.Object)); - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.AfterHandledInvoked); - } - - [Fact] - public void InterceptorWithException() - { - var channel = new TaskSchedulerChannel(_provider.GetService(), TaskScheduler.Default); - IMessage message = Message.Create("foo"); - var mockHandler = new Mock(); - - var expected = new InvalidOperationException("Fake exception"); - var latch = new CountdownEvent(2); - mockHandler.Setup(h => h.HandleMessage(message)).Throws(expected); - var interceptor = new BeforeHandleInterceptor(latch); - channel.AddInterceptor(interceptor); - channel.Subscribe(mockHandler.Object); - - try - { - channel.Send(message); - } - catch (MessageDeliveryException actual) - { - Assert.Same(expected, actual.InnerException); - } - - Assert.True(latch.Wait(3000)); - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.AfterHandledInvoked); - } - - private sealed class TestHandler : IMessageHandler - { - private volatile Thread _thread; - private volatile bool _shouldFail; - private int _count; - - public CountdownEvent Latch { get; } - - public string ServiceName { get; set; } = nameof(TestHandler); - - public Thread Thread => _thread; - - public bool ShouldFail - { - get => _shouldFail; - set => _shouldFail = value; - } - - public int Count => _count; - - public TestHandler(CountdownEvent latch) - { - Latch = latch; - } - - public void HandleMessage(IMessage message) - { - _thread = Thread.CurrentThread; - - if (ShouldFail) - { - throw new Exception("intentional test failure"); - } - - Interlocked.Increment(ref _count); - Latch.Signal(); - } - } - - private sealed class BeforeHandleInterceptor : AbstractTaskSchedulerChannelInterceptor - { - private readonly CountdownEvent _latch; - private volatile bool _afterHandledInvoked; - - public int Counter { get; private set; } - public IMessage MessageToReturn { get; set; } - - public bool AfterHandledInvoked => _afterHandledInvoked; - - public BeforeHandleInterceptor() - { - } - - public BeforeHandleInterceptor(CountdownEvent latch) - { - _latch = latch; - } - - public override IMessage BeforeHandled(IMessage message, IMessageChannel channel, IMessageHandler handler) - { - Assert.NotNull(message); - Counter++; - _latch?.Signal(); - return MessageToReturn ?? message; - } - - public override void AfterMessageHandled(IMessage message, IMessageChannel channel, IMessageHandler handler, Exception exception) - { - _afterHandledInvoked = true; - _latch?.Signal(); - } - } -} diff --git a/src/Integration/test/Integration.Test/Dispatcher/BroadcastingDispatcherTest.cs b/src/Integration/test/Integration.Test/Dispatcher/BroadcastingDispatcherTest.cs deleted file mode 100644 index ed4285b3e1..0000000000 --- a/src/Integration/test/Integration.Test/Dispatcher/BroadcastingDispatcherTest.cs +++ /dev/null @@ -1,399 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Dispatcher; - -public sealed class BroadcastingDispatcherTest -{ - private readonly Mock _messageMock = new(); - - private readonly Mock _targetMock1 = new(); - - private readonly Mock _targetMock2 = new(); - - private readonly Mock _targetMock3 = new(); - - private readonly IServiceProvider _provider; - - public BroadcastingDispatcherTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - } - - [Fact] - public void SingleTargetWithoutTaskExecutor() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.Dispatch(_messageMock.Object); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void SingleTargetWithTaskExecutor() - { - var latch = new CountdownEvent(1); - _targetMock1.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - var dispatcher = new BroadcastingDispatcher(_provider.GetService(), TaskScheduler.Default); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.Dispatch(_messageMock.Object); - Assert.True(latch.Wait(3000)); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void MultipleTargetsWithoutTaskExecutor() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock2.Object); - dispatcher.AddHandler(_targetMock3.Object); - dispatcher.Dispatch(_messageMock.Object); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object)); - _targetMock2.Verify(h => h.HandleMessage(_messageMock.Object)); - _targetMock3.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void MultipleTargetsWithTaskExecutor() - { - var latch = new CountdownEvent(3); - _targetMock1.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - _targetMock2.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - _targetMock3.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - var dispatcher = new BroadcastingDispatcher(_provider.GetService(), TaskScheduler.Default); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock2.Object); - dispatcher.AddHandler(_targetMock3.Object); - dispatcher.Dispatch(_messageMock.Object); - Assert.True(latch.Wait(3000)); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object)); - _targetMock2.Verify(h => h.HandleMessage(_messageMock.Object)); - _targetMock3.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void MultipleTargetsPartialFailureFirst() - { - var latch = new CountdownEvent(2); - _targetMock1.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - _targetMock2.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - _targetMock3.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - var dispatcher = new BroadcastingDispatcher(_provider.GetService(), new PartialFailingTaskScheduler(false, true, true)); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock2.Object); - dispatcher.AddHandler(_targetMock3.Object); - dispatcher.Dispatch(_messageMock.Object); - Assert.True(latch.Wait(3000)); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object), Times.Never()); - _targetMock2.Verify(h => h.HandleMessage(_messageMock.Object)); - _targetMock3.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void MultipleTargetsPartialFailureMiddle() - { - var latch = new CountdownEvent(2); - _targetMock1.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - _targetMock2.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - _targetMock3.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - var dispatcher = new BroadcastingDispatcher(_provider.GetService(), new PartialFailingTaskScheduler(true, false, true)); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock2.Object); - dispatcher.AddHandler(_targetMock3.Object); - dispatcher.Dispatch(_messageMock.Object); - Assert.True(latch.Wait(3000)); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object)); - _targetMock2.Verify(h => h.HandleMessage(_messageMock.Object), Times.Never()); - _targetMock3.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void MultipleTargetsPartialFailureLast() - { - var latch = new CountdownEvent(2); - _targetMock1.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - _targetMock2.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - _targetMock3.Setup(h => h.HandleMessage(_messageMock.Object)).Callback(() => latch.Signal()); - var dispatcher = new BroadcastingDispatcher(_provider.GetService(), new PartialFailingTaskScheduler(true, true, false)); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock2.Object); - dispatcher.AddHandler(_targetMock3.Object); - dispatcher.Dispatch(_messageMock.Object); - Assert.True(latch.Wait(3000)); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object)); - _targetMock2.Verify(h => h.HandleMessage(_messageMock.Object)); - _targetMock3.Verify(h => h.HandleMessage(_messageMock.Object), Times.Never()); - } - - [Fact] - public void MultipleTargetsAllFail() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService(), new PartialFailingTaskScheduler(false, false, false)); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock2.Object); - dispatcher.AddHandler(_targetMock3.Object); - dispatcher.Dispatch(_messageMock.Object); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object), Times.Never()); - _targetMock2.Verify(h => h.HandleMessage(_messageMock.Object), Times.Never()); - _targetMock3.Verify(h => h.HandleMessage(_messageMock.Object), Times.Never()); - } - - [Fact] - public void NoDuplicateSubscription() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.Dispatch(_messageMock.Object); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void RemoveConsumerBeforeSend() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock2.Object); - dispatcher.AddHandler(_targetMock3.Object); - dispatcher.RemoveHandler(_targetMock2.Object); - dispatcher.Dispatch(_messageMock.Object); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object)); - _targetMock2.Verify(h => h.HandleMessage(_messageMock.Object), Times.Never()); - _targetMock3.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void RemoveConsumerBetweenSends() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()); - dispatcher.AddHandler(_targetMock1.Object); - dispatcher.AddHandler(_targetMock2.Object); - dispatcher.AddHandler(_targetMock3.Object); - dispatcher.Dispatch(_messageMock.Object); - dispatcher.RemoveHandler(_targetMock2.Object); - dispatcher.Dispatch(_messageMock.Object); - _targetMock1.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(2)); - _targetMock2.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(1)); - _targetMock3.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(2)); - } - - [Fact] - public void ApplySequenceDisabledByDefault() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()); - var messages = new ConcurrentQueue(); - var target1 = new MessageStoringTestEndpoint(messages); - var target2 = new MessageStoringTestEndpoint(messages); - dispatcher.AddHandler(target1); - dispatcher.AddHandler(target2); - dispatcher.Dispatch(Message.Create("test")); - Assert.Equal(2, messages.Count); - - Assert.True(messages.TryDequeue(out IMessage message)); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceNumber, out object value); - Assert.Null(value); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceSize, out value); - Assert.Null(value); - - Assert.True(messages.TryDequeue(out message)); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceNumber, out value); - Assert.Null(value); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceSize, out value); - Assert.Null(value); - } - - [Fact] - public void ApplySequenceEnabled() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()) - { - ApplySequence = true - }; - - var messages = new ConcurrentQueue(); - var target1 = new MessageStoringTestEndpoint(messages); - var target2 = new MessageStoringTestEndpoint(messages); - var target3 = new MessageStoringTestEndpoint(messages); - dispatcher.AddHandler(target1); - dispatcher.AddHandler(target2); - dispatcher.AddHandler(target3); - IMessage inputMessage = Message.Create("test"); - string originalId = inputMessage.Headers.Id; - dispatcher.Dispatch(inputMessage); - Assert.Equal(3, messages.Count); - - Assert.True(messages.TryDequeue(out IMessage message)); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceNumber, out object value); - Assert.Equal(1, value); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceSize, out value); - Assert.Equal(3, value); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.CorrelationId, out value); - Assert.Equal(originalId, value); - - Assert.True(messages.TryDequeue(out message)); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceNumber, out value); - Assert.Equal(2, value); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceSize, out value); - Assert.Equal(3, value); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.CorrelationId, out value); - Assert.Equal(originalId, value); - - Assert.True(messages.TryDequeue(out message)); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceNumber, out value); - Assert.Equal(3, value); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.SequenceSize, out value); - Assert.Equal(3, value); - message.Headers.TryGetValue(IntegrationMessageHeaderAccessor.CorrelationId, out value); - Assert.Equal(originalId, value); - } - - [Fact] - public void TestExceptionEnhancement() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()); - dispatcher.AddHandler(_targetMock1.Object); - _targetMock1.Setup(h => h.HandleMessage(_messageMock.Object)).Throws(new MessagingException("Mock Exception")); - - try - { - dispatcher.Dispatch(_messageMock.Object); - throw new Exception("Expected Exception"); - } - catch (MessagingException e) - { - Assert.Equal(_messageMock.Object, e.FailedMessage); - } - } - - [Fact] - public void TestNoExceptionEnhancement() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()); - dispatcher.AddHandler(_targetMock1.Object); - _targetMock1.Object.HandleMessage(_messageMock.Object); - IMessage doNotReplaceThisMessage = IntegrationMessageBuilder.WithPayload("x").Build(); - _targetMock1.Setup(h => h.HandleMessage(_messageMock.Object)).Throws(new MessagingException(doNotReplaceThisMessage, "Mock Exception")); - - try - { - dispatcher.Dispatch(_messageMock.Object); - throw new Exception("Expected Exception"); - } - catch (MessagingException e) - { - Assert.Equal(doNotReplaceThisMessage, e.FailedMessage); - } - } - - [Fact] - public void TestNoHandler() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService()); - Assert.True(dispatcher.Dispatch(_messageMock.Object)); - } - - [Fact] - public void TestNoHandlerWithExecutor() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService(), TaskScheduler.Default); - Assert.True(dispatcher.Dispatch(_messageMock.Object)); - } - - [Fact] - public void TestNoHandlerWithRequiredSubscriber() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService(), true); - - try - { - dispatcher.Dispatch(_messageMock.Object); - throw new Exception("Expected Exception"); - } - catch (MessageDispatchingException exception) - { - Assert.Equal(_messageMock.Object, exception.FailedMessage); - } - } - - [Fact] - public void TestNoHandlerWithExecutorWithRequiredSubscriber() - { - var dispatcher = new BroadcastingDispatcher(_provider.GetService(), TaskScheduler.Default, true); - - try - { - dispatcher.Dispatch(_messageMock.Object); - throw new Exception("Expected Exception"); - } - catch (MessageDispatchingException exception) - { - Assert.Equal(_messageMock.Object, exception.FailedMessage); - } - } - - private sealed class MessageStoringTestEndpoint : IMessageHandler - { - public ConcurrentQueue MessageList { get; } - - public string ServiceName { get; set; } = nameof(MessageStoringTestEndpoint); - - public MessageStoringTestEndpoint(ConcurrentQueue messageList) - { - MessageList = messageList; - } - - public void HandleMessage(IMessage message) - { - MessageList.Enqueue(message); - } - } - - private sealed class PartialFailingTaskScheduler : TaskScheduler - { - private readonly bool[] _failures; - private int _count = -1; - - public PartialFailingTaskScheduler(params bool[] failures) - { - _failures = failures; - } - - protected override IEnumerable GetScheduledTasks() - { - return new List(); - } - - protected override void QueueTask(Task task) - { - int val = Interlocked.Increment(ref _count); - - if (val < _failures.Length && _failures[val]) - { - TryExecuteTask(task); - } - } - - protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) - { - return false; - } - } -} diff --git a/src/Integration/test/Integration.Test/Dispatcher/FailOverDispatcherTest.cs b/src/Integration/test/Integration.Test/Dispatcher/FailOverDispatcherTest.cs deleted file mode 100644 index 6f098eb91a..0000000000 --- a/src/Integration/test/Integration.Test/Dispatcher/FailOverDispatcherTest.cs +++ /dev/null @@ -1,265 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Dispatcher; - -public sealed class FailOverDispatcherTest -{ - private readonly IServiceProvider _provider; - - public FailOverDispatcherTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - } - - [Fact] - public void SingleMessage() - { - var dispatcher = new UnicastingDispatcher(_provider.GetService()); - var latch = new CountdownEvent(1); - var processor = new LatchedProcessor(latch); - dispatcher.AddHandler(CreateConsumer(processor)); - dispatcher.Dispatch(Message.Create("test")); - Assert.True(latch.Wait(500)); - } - - [Fact] - public void PointToPoint() - { - var dispatcher = new UnicastingDispatcher(_provider.GetService()); - var latch = new CountdownEvent(1); - var processor1 = new LatchedProcessor(latch); - var processor2 = new LatchedProcessor(latch); - dispatcher.AddHandler(CreateConsumer(processor1)); - dispatcher.AddHandler(CreateConsumer(processor2)); - dispatcher.Dispatch(Message.Create("test")); - Assert.True(latch.Wait(3000)); - Assert.Equal(1, processor1.Counter + processor2.Counter); - } - - [Fact] - public void NoDuplicateSubscriptions() - { - var dispatcher = new UnicastingDispatcher(_provider.GetService()); - var target = new CountingTestEndpoint(false); - dispatcher.AddHandler(target); - dispatcher.AddHandler(target); - - try - { - dispatcher.Dispatch(Message.Create("test")); - } - catch (Exception) - { - // ignore - } - - Assert.Equal(1, target.Counter); - } - - [Fact] - public void RemoveConsumerBeforeSend() - { - var dispatcher = new UnicastingDispatcher(_provider.GetService()); - var target1 = new CountingTestEndpoint(false); - var target2 = new CountingTestEndpoint(false); - var target3 = new CountingTestEndpoint(false); - dispatcher.AddHandler(target1); - dispatcher.AddHandler(target2); - dispatcher.AddHandler(target3); - dispatcher.RemoveHandler(target2); - - try - { - dispatcher.Dispatch(Message.Create("test")); - } - catch (Exception) - { - // ignore - } - - Assert.Equal(2, target1.Counter + target2.Counter + target3.Counter); - } - - [Fact] - public void RemoveConsumerBetweenSends() - { - var dispatcher = new UnicastingDispatcher(_provider.GetService()); - var target1 = new CountingTestEndpoint(false); - var target2 = new CountingTestEndpoint(false); - var target3 = new CountingTestEndpoint(false); - dispatcher.AddHandler(target1); - dispatcher.AddHandler(target2); - dispatcher.AddHandler(target3); - - try - { - dispatcher.Dispatch(Message.Create("test1")); - } - catch (Exception) - { - // ignore - } - - Assert.Equal(3, target1.Counter + target2.Counter + target3.Counter); - dispatcher.RemoveHandler(target2); - - try - { - dispatcher.Dispatch(Message.Create("test2")); - } - catch (Exception) - { - // ignore - } - - Assert.Equal(5, target1.Counter + target2.Counter + target3.Counter); - dispatcher.RemoveHandler(target1); - - try - { - dispatcher.Dispatch(Message.Create("test3")); - } - catch (Exception) - { - // ignore - } - - Assert.Equal(6, target1.Counter + target2.Counter + target3.Counter); - } - - [Fact] - public void RemoveConsumerLastTargetCausesDeliveryException() - { - var dispatcher = new UnicastingDispatcher(_provider.GetService()); - var target1 = new CountingTestEndpoint(false); - dispatcher.AddHandler(target1); - - try - { - dispatcher.Dispatch(Message.Create("test1")); - } - catch (Exception) - { - // ignore - } - - Assert.Equal(1, target1.Counter); - dispatcher.RemoveHandler(target1); - Assert.Throws(() => dispatcher.Dispatch(Message.Create("test2"))); - } - - [Fact] - public void FirstHandlerReturnsTrue() - { - var dispatcher = new UnicastingDispatcher(_provider.GetService()); - var target1 = new CountingTestEndpoint(true); - var target2 = new CountingTestEndpoint(false); - var target3 = new CountingTestEndpoint(false); - dispatcher.AddHandler(target1); - dispatcher.AddHandler(target2); - dispatcher.AddHandler(target3); - Assert.True(dispatcher.Dispatch(Message.Create("test"))); - Assert.Equal(1, target1.Counter + target2.Counter + target3.Counter); - } - - [Fact] - public void MiddleHandlerReturnsTrue() - { - var dispatcher = new UnicastingDispatcher(_provider.GetService()); - var target1 = new CountingTestEndpoint(false); - var target2 = new CountingTestEndpoint(true); - var target3 = new CountingTestEndpoint(false); - dispatcher.AddHandler(target1); - dispatcher.AddHandler(target2); - dispatcher.AddHandler(target3); - Assert.True(dispatcher.Dispatch(Message.Create("test"))); - Assert.Equal(2, target1.Counter + target2.Counter + target3.Counter); - } - - [Fact] - public void AllHandlersReturnFalse() - { - var dispatcher = new UnicastingDispatcher(_provider.GetService()); - var target1 = new CountingTestEndpoint(false); - var target2 = new CountingTestEndpoint(false); - var target3 = new CountingTestEndpoint(false); - dispatcher.AddHandler(target1); - dispatcher.AddHandler(target2); - dispatcher.AddHandler(target3); - - try - { - Assert.False(dispatcher.Dispatch(Message.Create("test"))); - } - catch (AggregateMessageDeliveryException) - { - // Intentionally left empty. - } - - Assert.Equal(3, target1.Counter + target2.Counter + target3.Counter); - } - - private ServiceActivatingHandler CreateConsumer(IMessageProcessor processor) - { - var handler = new ServiceActivatingHandler(_provider.GetService(), processor); - return handler; - } - - public sealed class CountingTestEndpoint : IMessageHandler - { - public int Counter { get; private set; } - public bool ShouldAccept { get; } - - public string ServiceName { get; set; } = nameof(CountingTestEndpoint); - - public CountingTestEndpoint(bool shouldAccept) - { - ShouldAccept = shouldAccept; - } - - public void HandleMessage(IMessage message) - { - Counter++; - - if (!ShouldAccept) - { - throw new MessageRejectedException(message, "intentional test failure"); - } - } - } - - public sealed class LatchedProcessor : IMessageProcessor - { - public int Counter { get; private set; } - public CountdownEvent Latch { get; } - - public LatchedProcessor(CountdownEvent latch) - { - Latch = latch; - } - - public object ProcessMessage(IMessage message) - { - Latch.Signal(); - Counter++; - return null; - } - } -} diff --git a/src/Integration/test/Integration.Test/Dispatcher/RoundRobinDispatcherConcurrentTest.cs b/src/Integration/test/Integration.Test/Dispatcher/RoundRobinDispatcherConcurrentTest.cs deleted file mode 100644 index 60ade3c6dd..0000000000 --- a/src/Integration/test/Integration.Test/Dispatcher/RoundRobinDispatcherConcurrentTest.cs +++ /dev/null @@ -1,158 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Dispatcher; - -public sealed class RoundRobinDispatcherConcurrentTest -{ - private const int TotalExecutions = 40; - - private readonly UnicastingDispatcher _dispatcher; - - private readonly Mock _messageMock = new(); - - private readonly Mock _handlerMock1 = new(); - - private readonly Mock _handlerMock2 = new(); - - private readonly Mock _handlerMock3 = new(); - - private readonly Mock _handlerMock4 = new(); - - private readonly IServiceProvider _provider; - - public RoundRobinDispatcherConcurrentTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - - _dispatcher = new UnicastingDispatcher(_provider.GetService()) - { - LoadBalancingStrategy = new RoundRobinLoadBalancingStrategy() - }; - } - - [Fact] - public void NoHandlerExhaustion() - { - _dispatcher.AddHandler(_handlerMock1.Object); - _dispatcher.AddHandler(_handlerMock2.Object); - _dispatcher.AddHandler(_handlerMock3.Object); - _dispatcher.AddHandler(_handlerMock4.Object); - - var start = new CountdownEvent(1); - var allDone = new CountdownEvent(TotalExecutions); - IMessage message = _messageMock.Object; - bool failed = false; - - void MessageSenderTask() - { - start.Wait(); - - if (!_dispatcher.Dispatch(message)) - { - failed = true; - } - - allDone.Signal(); - } - - for (int i = 0; i < TotalExecutions; i++) - { - Task.Run(MessageSenderTask); - } - - start.Signal(); - Assert.True(allDone.Wait(10000)); - Assert.False(failed); - _handlerMock1.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(TotalExecutions / 4)); - _handlerMock2.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(TotalExecutions / 4)); - _handlerMock3.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(TotalExecutions / 4)); - _handlerMock4.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(TotalExecutions / 4)); - } - - [Fact] - public void UnlockOnFailure() - { - // dispatcher has no subscribers (shouldn't lead to deadlock) - var start = new CountdownEvent(1); - var allDone = new CountdownEvent(TotalExecutions); - IMessage message = _messageMock.Object; - - void MessageSenderTask() - { - start.Wait(); - - try - { - _dispatcher.Dispatch(message); - throw new Exception("this shouldn't happen"); - } - catch (MessagingException) - { - // expected - } - - allDone.Signal(); - } - - for (int i = 0; i < TotalExecutions; i++) - { - Task.Run(MessageSenderTask); - } - - start.Signal(); - Assert.True(allDone.Wait(10000)); - } - - [Fact] - public void NoHandlerSkipUnderConcurrentFailureWithFailover() - { - _dispatcher.AddHandler(_handlerMock1.Object); - _dispatcher.AddHandler(_handlerMock2.Object); - _handlerMock1.Setup(h => h.HandleMessage(_messageMock.Object)).Throws(new MessageRejectedException(_messageMock.Object, null)); - var start = new CountdownEvent(1); - var allDone = new CountdownEvent(TotalExecutions); - IMessage message = _messageMock.Object; - bool failed = false; - - void MessageSenderTask() - { - start.Wait(); - - if (!_dispatcher.Dispatch(message)) - { - failed = true; - } - else - { - allDone.Signal(); - } - } - - for (int i = 0; i < TotalExecutions; i++) - { - Task.Run(MessageSenderTask); - } - - start.Signal(); - Assert.True(allDone.Wait(10000)); - Assert.False(failed); - _handlerMock1.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(TotalExecutions / 2)); - _handlerMock2.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(TotalExecutions)); - } -} diff --git a/src/Integration/test/Integration.Test/Dispatcher/RoundRobinDispatcherTest.cs b/src/Integration/test/Integration.Test/Dispatcher/RoundRobinDispatcherTest.cs deleted file mode 100644 index 449ae5cfc7..0000000000 --- a/src/Integration/test/Integration.Test/Dispatcher/RoundRobinDispatcherTest.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Dispatcher; - -public sealed class RoundRobinDispatcherTest -{ - private readonly UnicastingDispatcher _dispatcher; - - private readonly Mock _messageMock = new(); - - private readonly Mock _handlerMock = new(); - - private readonly Mock _differentHandlerMock = new(); - - private readonly IServiceProvider _provider; - - public RoundRobinDispatcherTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - - _dispatcher = new UnicastingDispatcher(_provider.GetService()) - { - LoadBalancingStrategy = new RoundRobinLoadBalancingStrategy() - }; - } - - [Fact] - public void DispatchMessageWithSingleHandler() - { - _dispatcher.AddHandler(_handlerMock.Object); - _dispatcher.Dispatch(_messageMock.Object); - _handlerMock.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void DifferentHandlerInvokedOnSecondMessage() - { - _dispatcher.AddHandler(_handlerMock.Object); - _dispatcher.AddHandler(_differentHandlerMock.Object); - _dispatcher.Dispatch(_messageMock.Object); - _dispatcher.Dispatch(_messageMock.Object); - _handlerMock.Verify(h => h.HandleMessage(_messageMock.Object)); - _differentHandlerMock.Verify(h => h.HandleMessage(_messageMock.Object)); - } - - [Fact] - public void MultipleCyclesThroughHandlers() - { - _dispatcher.AddHandler(_handlerMock.Object); - _dispatcher.AddHandler(_differentHandlerMock.Object); - - for (int i = 0; i < 7; i++) - { - _dispatcher.Dispatch(_messageMock.Object); - } - - _handlerMock.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(4)); - _differentHandlerMock.Verify(h => h.HandleMessage(_messageMock.Object), Times.Exactly(3)); - } - - [Fact] - public void CurrentHandlerIndexOverFlow() - { - _dispatcher.AddHandler(_handlerMock.Object); - _dispatcher.AddHandler(_differentHandlerMock.Object); - var balancer = _dispatcher.LoadBalancingStrategy as RoundRobinLoadBalancingStrategy; - balancer.CurrentHandlerIndex = int.MaxValue - 5; - - for (int i = 0; i < 40; i++) - { - _dispatcher.Dispatch(_messageMock.Object); - } - - _handlerMock.Verify(h => h.HandleMessage(_messageMock.Object), Times.AtLeast(18)); - _differentHandlerMock.Verify(h => h.HandleMessage(_messageMock.Object), Times.AtLeast(18)); - } - - [Fact] - public void TestExceptionEnhancement() - { - _dispatcher.AddHandler(_handlerMock.Object); - _handlerMock.Setup(h => h.HandleMessage(_messageMock.Object)).Throws(new MessagingException("Mock Exception")); - var ex = Assert.Throws(() => _dispatcher.Dispatch(_messageMock.Object)); - Assert.Equal(_messageMock.Object, ex.FailedMessage); - } - - [Fact] - public void TestNoExceptionEnhancement() - { - _dispatcher.AddHandler(_handlerMock.Object); - IMessage doNotReplaceThisMessage = IntegrationMessageBuilder.WithPayload("x").Build(); - _handlerMock.Setup(h => h.HandleMessage(_messageMock.Object)).Throws(new MessagingException(doNotReplaceThisMessage, "Mock Exception")); - var ex = Assert.Throws(() => _dispatcher.Dispatch(_messageMock.Object)); - Assert.Equal("Mock Exception", ex.Message); - Assert.Equal(doNotReplaceThisMessage, ex.FailedMessage); - } - - [Fact] - public void TestFailOver() - { - var testException = new Exception("intentional"); - _handlerMock.Setup(h => h.HandleMessage(_messageMock.Object)).Throws(testException); - - _dispatcher.AddHandler(_handlerMock.Object); - _dispatcher.AddHandler(_differentHandlerMock.Object); - - _dispatcher.Dispatch(_messageMock.Object); - _handlerMock.Verify(h => h.HandleMessage(_messageMock.Object)); - _differentHandlerMock.Verify(h => h.HandleMessage(_messageMock.Object)); - } -} diff --git a/src/Integration/test/Integration.Test/Endpoint/CorrelationIdTest.cs b/src/Integration/test/Integration.Test/Endpoint/CorrelationIdTest.cs deleted file mode 100644 index 065fcbd9df..0000000000 --- a/src/Integration/test/Integration.Test/Endpoint/CorrelationIdTest.cs +++ /dev/null @@ -1,136 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Endpoint; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Xunit; - -namespace Steeltoe.Integration.Test.Endpoint; - -public sealed class CorrelationIdTest -{ - private readonly IServiceProvider _provider; - - public CorrelationIdTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(p => new DirectChannel(p.GetService(), "errorChannel")); - _provider = services.BuildServiceProvider(true); - } - - [Fact] - public async Task TestCorrelationIdPassedIfAvailable() - { - object correlationId = "123-ABC"; - IMessage message = IntegrationMessageBuilder.WithPayload("test").SetCorrelationId(correlationId).Build(); - var inputChannel = new DirectChannel(_provider.GetService()); - var outputChannel = new QueueChannel(_provider.GetService(), 1); - - var serviceActivator = new ServiceActivatingHandler(_provider.GetService(), new TestBeanUpperCase()) - { - OutputChannel = outputChannel - }; - - var endpoint = new EventDrivenConsumerEndpoint(_provider.GetService(), inputChannel, serviceActivator); - await endpoint.StartAsync(); - Assert.True(inputChannel.Send(message)); - IMessage reply = outputChannel.Receive(0); - var accessor = new IntegrationMessageHeaderAccessor(reply); - Assert.Equal(correlationId, accessor.GetCorrelationId()); - } - - [Fact] - public async Task TestCorrelationIdCopiedFromMessageCorrelationIdIfAvailable() - { - object correlationId = "correlationId"; - IMessage message = IntegrationMessageBuilder.WithPayload("test").SetCorrelationId(correlationId).Build(); - var inputChannel = new DirectChannel(_provider.GetService()); - var outputChannel = new QueueChannel(_provider.GetService(), 1); - - var serviceActivator = new ServiceActivatingHandler(_provider.GetService(), new TestBeanUpperCase()) - { - OutputChannel = outputChannel - }; - - var endpoint = new EventDrivenConsumerEndpoint(_provider.GetService(), inputChannel, serviceActivator); - await endpoint.StartAsync(); - Assert.True(inputChannel.Send(message)); - IMessage reply = outputChannel.Receive(0); - var accessor1 = new IntegrationMessageHeaderAccessor(reply); - var accessor2 = new IntegrationMessageHeaderAccessor(message); - Assert.Equal(accessor2.GetCorrelationId(), accessor1.GetCorrelationId()); - } - - [Fact] - public async Task TestCorrelationNotPassedFromRequestHeaderIfAlreadySetByHandler() - { - object correlationId = "123-ABC"; - IMessage message = IntegrationMessageBuilder.WithPayload("test").SetCorrelationId(correlationId).Build(); - var inputChannel = new DirectChannel(_provider.GetService()); - var outputChannel = new QueueChannel(_provider.GetService(), 1); - - var serviceActivator = new ServiceActivatingHandler(_provider.GetService(), new TestBeanCreateMessage()) - { - OutputChannel = outputChannel - }; - - var endpoint = new EventDrivenConsumerEndpoint(_provider.GetService(), inputChannel, serviceActivator); - await endpoint.StartAsync(); - Assert.True(inputChannel.Send(message)); - IMessage reply = outputChannel.Receive(0); - var accessor = new IntegrationMessageHeaderAccessor(reply); - Assert.Equal("456-XYZ", accessor.GetCorrelationId()); - } - - [Fact] - public async Task TestCorrelationNotCopiedFromRequestMessageIdIfAlreadySetByHandler() - { - IMessage message = Message.Create("test"); - var inputChannel = new DirectChannel(_provider.GetService()); - var outputChannel = new QueueChannel(_provider.GetService(), 1); - - var serviceActivator = new ServiceActivatingHandler(_provider.GetService(), new TestBeanCreateMessage()) - { - OutputChannel = outputChannel - }; - - var endpoint = new EventDrivenConsumerEndpoint(_provider.GetService(), inputChannel, serviceActivator); - await endpoint.StartAsync(); - Assert.True(inputChannel.Send(message)); - IMessage reply = outputChannel.Receive(0); - var accessor = new IntegrationMessageHeaderAccessor(reply); - Assert.Equal("456-XYZ", accessor.GetCorrelationId()); - } - - private sealed class TestBeanUpperCase : IMessageProcessor - { - public object ProcessMessage(IMessage message) - { - string str = message.Payload as string; - return str.ToUpperInvariant(); - } - } - - private sealed class TestBeanCreateMessage : IMessageProcessor - { - public object ProcessMessage(IMessage message) - { - string str = message.Payload as string; - return IntegrationMessageBuilder.WithPayload(str).SetCorrelationId("456-XYZ").Build(); - } - } -} diff --git a/src/Integration/test/Integration.Test/Endpoint/MessageProducerSupportEndpointTest.cs b/src/Integration/test/Integration.Test/Endpoint/MessageProducerSupportEndpointTest.cs deleted file mode 100644 index 177ca1b3be..0000000000 --- a/src/Integration/test/Integration.Test/Endpoint/MessageProducerSupportEndpointTest.cs +++ /dev/null @@ -1,192 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Endpoint; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Integration.Test.Endpoint; - -public sealed class MessageProducerSupportEndpointTest -{ - private readonly ServiceCollection _services; - - public MessageProducerSupportEndpointTest() - { - _services = new ServiceCollection(); - _services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - _services.AddSingleton(); - _services.AddSingleton(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - _services.AddSingleton(configurationRoot); - _services.AddSingleton(); - } - - [Fact] - public async Task ValidateExceptionIfNoErrorChannel() - { - ServiceProvider provider = _services.BuildServiceProvider(true); - var outChannel = new DirectChannel(provider.GetService()); - var handler = new ExceptionHandler(); - outChannel.Subscribe(handler); - - var mps = new TestMessageProducerSupportEndpoint(provider.GetService()) - { - OutputChannel = outChannel - }; - - await mps.StartAsync(); - Assert.Throws(() => mps.SendMessage(Message.Create("hello"))); - } - - [Fact] - public async Task ValidateExceptionIfSendToErrorChannelFails() - { - ServiceProvider provider = _services.BuildServiceProvider(true); - var outChannel = new DirectChannel(provider.GetService()); - var handler = new ExceptionHandler(); - outChannel.Subscribe(handler); - var errorChannel = new PublishSubscribeChannel(provider.GetService()); - errorChannel.Subscribe(handler); - - var mps = new TestMessageProducerSupportEndpoint(provider.GetService()) - { - OutputChannel = outChannel, - ErrorChannel = errorChannel - }; - - await mps.StartAsync(); - Assert.Throws(() => mps.SendMessage(Message.Create("hello"))); - } - - [Fact] - public async Task ValidateSuccessfulErrorFlowDoesNotThrowErrors() - { - ServiceProvider provider = _services.BuildServiceProvider(true); - var outChannel = new DirectChannel(provider.GetService()); - var handler = new ExceptionHandler(); - outChannel.Subscribe(handler); - var errorChannel = new PublishSubscribeChannel(provider.GetService()); - var errorService = new SuccessfulErrorService(); - var errorHandler = new ServiceActivatingHandler(provider.GetService(), errorService); - errorChannel.Subscribe(errorHandler); - - var mps = new TestMessageProducerSupportEndpoint(provider.GetService()) - { - OutputChannel = outChannel, - ErrorChannel = errorChannel - }; - - await mps.StartAsync(); - IMessage message = Message.Create("hello"); - mps.SendMessage(message); - Assert.IsType(errorService.LastMessage); - Assert.IsType(errorService.LastMessage.Payload); - var exception = (MessageDeliveryException)errorService.LastMessage.Payload; - Assert.Equal(message, exception.FailedMessage); - } - - [Fact] - public async Task TestWithChannelName() - { - _services.AddSingleton(p => new DirectChannel(p.GetService(), "foo")); - ServiceProvider provider = _services.BuildServiceProvider(true); - - var mps = new TestMessageProducerSupportEndpoint(provider.GetService()) - { - OutputChannelName = "foo" - }; - - await mps.StartAsync(); - Assert.NotNull(mps.OutputChannel); - Assert.Equal("foo", mps.OutputChannel.ServiceName); - } - - [Fact] - public async Task CustomDoStop() - { - ServiceProvider provider = _services.BuildServiceProvider(true); - var endpoint = new CustomEndpoint(provider.GetService()); - Assert.Equal(0, endpoint.Count); - Assert.False(endpoint.IsRunning); - await endpoint.StartAsync(); - Assert.True(endpoint.IsRunning); - - await endpoint.StopAsync(() => - { - // Do nothing - }); - - Assert.Equal(1, endpoint.Count); - Assert.False(endpoint.IsRunning); - } - - private sealed class TestMessageProducerSupportEndpoint : MessageProducerSupportEndpoint - { - public TestMessageProducerSupportEndpoint(IApplicationContext context) - : base(context) - { - } - } - - private sealed class ExceptionHandler : IMessageHandler - { - public string ServiceName { get; set; } = nameof(ExceptionHandler); - - public void HandleMessage(IMessage message) - { - throw new Exception("problems"); - } - } - - private sealed class SuccessfulErrorService : IMessageProcessor - { - private volatile IMessage _lastMessage; - - public IMessage LastMessage => _lastMessage; - - public object ProcessMessage(IMessage message) - { - _lastMessage = message; - return null; - } - } - - private sealed class CustomEndpoint : AbstractEndpoint - { - public int Count { get; set; } - public bool Stopped { get; set; } - - public CustomEndpoint(IApplicationContext context) - : base(context) - { - } - - protected override Task DoStartAsync() - { - Stopped = false; - return Task.CompletedTask; - } - - protected override Task DoStopAsync() - { - Stopped = true; - return Task.CompletedTask; - } - - protected override Task DoStopAsync(Action callback) - { - Count++; - return base.DoStopAsync(callback); - } - } -} diff --git a/src/Integration/test/Integration.Test/Handler/AbstractReplyProducingMessageHandlerTest.cs b/src/Integration/test/Integration.Test/Handler/AbstractReplyProducingMessageHandlerTest.cs deleted file mode 100644 index 3bf930182c..0000000000 --- a/src/Integration/test/Integration.Test/Handler/AbstractReplyProducingMessageHandlerTest.cs +++ /dev/null @@ -1,161 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Xunit; - -namespace Steeltoe.Integration.Test.Handler; - -public sealed class AbstractReplyProducingMessageHandlerTest -{ - private readonly Mock _mockChannel; - private readonly TestAbstractReplyProducingMessageHandler _handler; - private readonly IMessage _message; - private readonly IServiceProvider _provider; - - public AbstractReplyProducingMessageHandlerTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - _handler = new TestAbstractReplyProducingMessageHandler(_provider.GetService()); - _mockChannel = new Mock(); - _message = IntegrationMessageBuilder.WithPayload("test").Build(); - } - - [Fact] - public void ErrorMessageShouldContainChannelName() - { - _handler.OutputChannel = _mockChannel.Object; - _mockChannel.Setup(c => c.Send(_message)).Returns(false); - _mockChannel.Setup(c => c.ToString()).Returns("testChannel"); - - try - { - _handler.HandleMessage(_message); - throw new Exception("Expected a MessagingException"); - } - catch (MessagingException e) - { - Assert.Contains("AbstractReplyProducingMessageHandler", e.Message, StringComparison.Ordinal); - } - } - - [Fact] - public void TestNotPropagate() - { - _handler.ReturnValue = Message.Create("world", new Dictionary - { - { "bar", "RAB" } - }); - - Assert.Empty(_handler.NotPropagatedHeaders); - - _handler.NotPropagatedHeaders = new List - { - "f*", - "*r" - }; - - _handler.OutputChannel = _mockChannel.Object; - IMessage captor = null; - _mockChannel.Setup(c => c.Send(It.IsAny(), It.IsAny())).Returns(true).Callback((m, _) => captor = m); - _mockChannel.Setup(c => c.ToString()).Returns("testChannel"); - - _handler.HandleMessage(IntegrationMessageBuilder.WithPayload("hello").SetHeader("foo", "FOO").SetHeader("bar", "BAR").SetHeader("baz", "BAZ").Build()); - - Assert.NotNull(captor); - Assert.Null(captor.Headers.Get("foo")); - Assert.Equal("RAB", captor.Headers.Get("bar")); - Assert.Equal("BAZ", captor.Headers.Get("baz")); - } - - [Fact] - public void TestNotPropagateAddWhenNonExist() - { - _handler.ReturnValue = Message.Create("world", new Dictionary - { - { "bar", "RAB" } - }); - - Assert.Empty(_handler.NotPropagatedHeaders); - _handler.AddNotPropagatedHeaders("boom"); - _handler.OutputChannel = _mockChannel.Object; - IMessage captor = null; - _mockChannel.Setup(c => c.Send(It.IsAny(), It.IsAny())).Returns(true).Callback((m, _) => captor = m); - _mockChannel.Setup(c => c.ToString()).Returns("testChannel"); - - _handler.HandleMessage(IntegrationMessageBuilder.WithPayload("hello").SetHeader("boom", "FOO").SetHeader("bar", "BAR").SetHeader("baz", "BAZ").Build()); - - Assert.NotNull(captor); - Assert.Null(captor.Headers.Get("boom")); - Assert.Equal("RAB", captor.Headers.Get("bar")); - Assert.Equal("BAZ", captor.Headers.Get("baz")); - } - - [Fact] - public void TestNotPropagateAdd() - { - _handler.ReturnValue = Message.Create("world", new Dictionary - { - { "bar", "RAB" } - }); - - Assert.Empty(_handler.NotPropagatedHeaders); - - _handler.NotPropagatedHeaders = new List - { - "foo" - }; - - _handler.AddNotPropagatedHeaders("b*r"); - _handler.OutputChannel = _mockChannel.Object; - IMessage captor = null; - _mockChannel.Setup(c => c.Send(It.IsAny(), It.IsAny())).Returns(true).Callback((m, _) => captor = m); - _mockChannel.Setup(c => c.ToString()).Returns("testChannel"); - - _handler.HandleMessage(IntegrationMessageBuilder.WithPayload("hello").SetHeader("foo", "FOO").SetHeader("bar", "BAR").SetHeader("baz", "BAZ").Build()); - - Assert.NotNull(captor); - Assert.Null(captor.Headers.Get("foo")); - Assert.Equal("RAB", captor.Headers.Get("bar")); - Assert.Equal("BAZ", captor.Headers.Get("baz")); - } - - private sealed class TestAbstractReplyProducingMessageHandler : AbstractReplyProducingMessageHandler - { - public object ReturnValue { get; set; } - - public TestAbstractReplyProducingMessageHandler(IApplicationContext context) - : base(context) - { - } - - public override void Initialize() - { - } - - protected override object HandleRequestMessage(IMessage requestMessage) - { - if (ReturnValue != null) - { - return ReturnValue; - } - - throw new NotImplementedException(); - } - } -} diff --git a/src/Integration/test/Integration.Test/Handler/BridgeHandlerTest.cs b/src/Integration/test/Integration.Test/Handler/BridgeHandlerTest.cs deleted file mode 100644 index 13fe709149..0000000000 --- a/src/Integration/test/Integration.Test/Handler/BridgeHandlerTest.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Xunit; - -namespace Steeltoe.Integration.Test.Handler; - -public sealed class BridgeHandlerTest -{ - private readonly BridgeHandler _handler; - private readonly IServiceProvider _provider; - - public BridgeHandlerTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - _handler = new BridgeHandler(_provider.GetService()); - } - - [Fact] - public void SimpleBridge() - { - var outputChannel = new QueueChannel(_provider.GetService()); - _handler.OutputChannel = outputChannel; - IMessage request = Message.Create("test"); - _handler.HandleMessage(request); - IMessage reply = outputChannel.Receive(0); - Assert.NotNull(reply); - Assert.Equal(request.Payload, reply.Payload); - Assert.Equal(request.Headers, reply.Headers); - } - - [Fact] - public void MissingOutputChannelVerifiedAtRuntime() - { - IMessage request = Message.Create("test"); - var ex = Assert.Throws(() => _handler.HandleMessage(request)); - Assert.IsType(ex.InnerException); - } - - [Fact] - public void MissingOutputChannelAllowedForReplyChannelMessages() - { - var replyChannel = new QueueChannel(_provider.GetService()); - IMessage request = IntegrationMessageBuilder.WithPayload("tst").SetReplyChannel(replyChannel).Build(); - _handler.HandleMessage(request); - IMessage reply = replyChannel.Receive(); - Assert.NotNull(reply); - Assert.Equal(request.Payload, reply.Payload); - Assert.Equal(request.Headers, reply.Headers); - } -} diff --git a/src/Integration/test/Integration.Test/Handler/CollectionAndArrayTest.cs b/src/Integration/test/Integration.Test/Handler/CollectionAndArrayTest.cs deleted file mode 100644 index 7238beb075..0000000000 --- a/src/Integration/test/Integration.Test/Handler/CollectionAndArrayTest.cs +++ /dev/null @@ -1,118 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Xunit; - -namespace Steeltoe.Integration.Test.Handler; - -public sealed class CollectionAndArrayTest -{ - private readonly TestAbstractReplyProducingMessageHandler _handler; - private readonly IServiceProvider _provider; - - public CollectionAndArrayTest() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddSingleton(); - services.AddSingleton, DefaultMessageChannelDestinationResolver>(); - services.AddSingleton(); - services.AddSingleton(); - _provider = services.BuildServiceProvider(true); - _handler = new TestAbstractReplyProducingMessageHandler(_provider.GetService()); - } - - [Fact] - public void ListWithRequestReplyHandler() - { - _handler.ReturnValue = new List - { - "foo", - "bar" - }; - - var channel = new QueueChannel(_provider.GetService()); - IMessage message = IntegrationMessageBuilder.WithPayload("test").SetReplyChannel(channel).Build(); - _handler.HandleMessage(message); - IMessage reply1 = channel.Receive(0); - IMessage reply2 = channel.Receive(0); - Assert.NotNull(reply1); - Assert.Null(reply2); - Assert.IsType>(reply1.Payload); - Assert.Equal(2, ((List)reply1.Payload).Count); - } - - [Fact] - public void SetWithRequestReplyHandler() - { - _handler.ReturnValue = new HashSet(new[] - { - "foo", - "bar" - }); - - var channel = new QueueChannel(_provider.GetService()); - IMessage message = IntegrationMessageBuilder.WithPayload("test").SetReplyChannel(channel).Build(); - _handler.HandleMessage(message); - IMessage reply1 = channel.Receive(0); - IMessage reply2 = channel.Receive(0); - Assert.NotNull(reply1); - Assert.Null(reply2); - Assert.IsType>(reply1.Payload); - Assert.Equal(2, ((HashSet)reply1.Payload).Count); - } - - [Fact] - public void ArrayWithRequestReplyHandler() - { - _handler.ReturnValue = new[] - { - "foo", - "bar" - }; - - var channel = new QueueChannel(_provider.GetService()); - IMessage message = IntegrationMessageBuilder.WithPayload("test").SetReplyChannel(channel).Build(); - _handler.HandleMessage(message); - IMessage reply1 = channel.Receive(0); - IMessage reply2 = channel.Receive(0); - Assert.NotNull(reply1); - Assert.Null(reply2); - Assert.IsType(reply1.Payload); - Assert.Equal(2, ((string[])reply1.Payload).Length); - } - - private sealed class TestAbstractReplyProducingMessageHandler : AbstractReplyProducingMessageHandler - { - public object ReturnValue { get; set; } - - public TestAbstractReplyProducingMessageHandler(IApplicationContext context) - : base(context) - { - } - - public override void Initialize() - { - } - - protected override object HandleRequestMessage(IMessage requestMessage) - { - if (ReturnValue != null) - { - return ReturnValue; - } - - throw new NotImplementedException(); - } - } -} diff --git a/src/Integration/test/Integration.Test/Handler/MethodInvokingMessageProcessorAnnotationTest.cs b/src/Integration/test/Integration.Test/Handler/MethodInvokingMessageProcessorAnnotationTest.cs deleted file mode 100644 index 2a7d9b7e35..0000000000 --- a/src/Integration/test/Integration.Test/Handler/MethodInvokingMessageProcessorAnnotationTest.cs +++ /dev/null @@ -1,406 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Integration.Handler; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Integration.Test.Handler; - -public sealed class MethodInvokingMessageProcessorAnnotationTest -{ - private readonly TestService _testService = new(); - private readonly Employee _employee = new("oleg", "zhurakousky"); - - [Fact] - public void OptionalHeader() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(OptionalHeader)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - int? result = processor.ProcessMessage(Message.Create("foo")); - Assert.Null(result); - } - - [Fact] - public void RequiredHeaderNotProvided() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.RequiredHeader)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - Assert.Throws(() => processor.ProcessMessage(Message.Create("foo"))); - } - - [Fact] - public void RequiredHeaderNotProvidedOnSecondMessage() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.RequiredHeader)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - IMessage messageWithHeader = MessageBuilder.WithPayload("foo").SetHeader("num", 123).Build(); - IMessage messageWithoutHeader = Message.Create("foo"); - - processor.ProcessMessage(messageWithHeader); - Assert.Throws(() => processor.ProcessMessage(messageWithoutHeader)); - } - - [Fact] - public void FromMessageWithRequiredHeaderProvided() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.RequiredHeader)); - IApplicationContext context = GetDefaultContext(); - IMessage message = MessageBuilder.WithPayload("foo").SetHeader("num", 123).Build(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - int result = processor.ProcessMessage(message); - Assert.Equal(123, result); - } - - [Fact] - public void FromMessageWithOptionalAndRequiredHeaderAndOnlyOptionalHeaderProvided() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.OptionalAndRequiredHeader)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - IMessage message = MessageBuilder.WithPayload("foo").SetHeader("prop", "bar").Build(); - Assert.Throws(() => processor.ProcessMessage(message)); - } - - [Fact] - public void FromMessageWithOptionalAndRequiredHeaderAndOnlyRequiredHeaderProvided() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.OptionalAndRequiredHeader)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - IMessage message = MessageBuilder.WithPayload("foo").SetHeader("num", 123).Build(); - string result = processor.ProcessMessage(message); - Assert.Equal("null123", result); - } - - [Fact] - public void FromMessageWithOptionalAndRequiredHeaderAndBothHeadersProvided() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.OptionalAndRequiredHeader)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - IMessage message = MessageBuilder.WithPayload("foo").SetHeader("num", 123).SetHeader("prop", "bar").Build(); - string result = processor.ProcessMessage(message); - Assert.Equal("bar123", result); - } - - [Fact] - public void FromMessageWithMapAndObjectMethod() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.MapHeadersAndPayload)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - IMessage message = MessageBuilder.WithPayload("test").SetHeader("prop1", "foo").SetHeader("prop2", "bar").Build(); - var result = (IDictionary)processor.ProcessMessage(message); - Assert.Equal(5, result.Count); - Assert.True(result.ContainsKey(MessageHeaders.IdName)); - Assert.True(result.ContainsKey(MessageHeaders.TimestampName)); - Assert.Equal("foo", result["prop1"]); - Assert.Equal("bar", result["prop2"]); - Assert.Equal("test", result["payload"]); - } - - [Fact] - public void FromMessageWithMapMethodAndHeadersAnnotation() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.MapHeaders)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - IMessage message = MessageBuilder.WithPayload("test").SetHeader("attrib1", 123).SetHeader("attrib2", 456).Build(); - var result = (IDictionary)processor.ProcessMessage(message); - Assert.Equal(123, result["attrib1"]); - Assert.Equal(456, result["attrib2"]); - } - - [Fact] - public void FromMessageWithMapMethodAndMapPayload() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.MapPayload)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - - var payload = new Dictionary - { - { "attrib1", 88 }, - { "attrib2", 99 } - }; - - IMessage message = MessageBuilder.WithPayload(payload).SetHeader("attrib1", 123).SetHeader("attrib2", 456).Build(); - var result = (IDictionary)processor.ProcessMessage(message); - Assert.Equal(2, result.Count); - Assert.Equal(88, result["attrib1"]); - Assert.Equal(99, result["attrib2"]); - } - - [Fact] - public void HeaderAnnotationWithExpression() - { - IMessage message = GetMessage(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.HeaderAnnotationWithExpression)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - object result = processor.ProcessMessage(message); - Assert.Equal("monday", result); - } - - [Fact] - public void IrrelevantAnnotation() - { - IMessage message = MessageBuilder.WithPayload("foo").Build(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.IrrelevantAnnotation)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - - object result = processor.ProcessMessage(message); - Assert.Equal("foo", result); - } - - [Fact] - public void MultipleAnnotatedArgs() - { - IMessage message = GetMessage(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.MultipleAnnotatedArguments)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - - object[] parameters = (object[])processor.ProcessMessage(message); - Assert.NotNull(parameters); - Assert.Equal(5, parameters.Length); - Assert.Equal("monday", parameters[0]); - Assert.Equal("September", parameters[1]); - Assert.Equal(parameters[2], _employee); - Assert.Equal("oleg", parameters[3]); - Assert.True(parameters[4] is IDictionary); - } - - [Fact] - public void FromMessageToPayload() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.MapOnly)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - - IMessage message = MessageBuilder.WithPayload(_employee).SetHeader("number", "jkl").Build(); - var result = processor.ProcessMessage(message) as IDictionary; - Assert.NotNull(result); - Assert.Equal("jkl", result["number"]); - } - - [Fact] - public void FromMessageToPayloadArg() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.PayloadAnnotationFirstName)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - - IMessage message = MessageBuilder.WithPayload(_employee).SetHeader("number", "jkl").Build(); - string result = processor.ProcessMessage(message) as string; - Assert.NotNull(result); - Assert.Equal("oleg", result); - } - - [Fact] - public void FromMessageToPayloadArgs() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.PayloadAnnotationFullName)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - - IMessage message = MessageBuilder.WithPayload(_employee).SetHeader("number", "jkl").Build(); - object result = processor.ProcessMessage(message); - Assert.Equal("oleg zhurakousky", result); - } - - [Fact] - public void FromMessageToPayloadArgsHeaderArgs() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.PayloadArgAndHeaderArg)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - IMessage message = MessageBuilder.WithPayload(_employee).SetHeader("day", "monday").Build(); - object result = processor.ProcessMessage(message); - Assert.Equal("olegmonday", result); - } - - [Fact] - public void FromMessageInvalidMethodWithMultipleMappingAnnotations() - { - MethodInfo method = typeof(MultipleMappingAnnotationTestBean).GetMethod(nameof(MultipleMappingAnnotationTestBean.Test)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - IMessage message = MessageBuilder.WithPayload("payload").SetHeader("foo", "bar").Build(); - Assert.Throws(() => processor.ProcessMessage(message)); - } - - [Fact] - public void FromMessageToHyphenatedHeaderName() - { - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.HeaderNameWithHyphen)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, _testService, method); - IMessage message = MessageBuilder.WithPayload("payload").SetHeader("foo-bar", "abc").Build(); - object result = processor.ProcessMessage(message); - Assert.Equal("ABC", result); - } - - private IApplicationContext GetDefaultContext() - { - var serviceCollection = new ServiceCollection(); - var configBuilder = new ConfigurationBuilder(); - var context = new GenericApplicationContext(serviceCollection.BuildServiceProvider(true), configBuilder.Build()); - context.ServiceExpressionResolver = new StandardServiceExpressionResolver(); - return context; - } - - private IMessage GetMessage() - { - AbstractMessageBuilder builder = MessageBuilder.WithPayload(_employee); - builder.SetHeader("day", "monday"); - builder.SetHeader("month", "September"); - return builder.Build(); - } - - public sealed class MultipleMappingAnnotationTestBean - { - public void Test([Payload("payload")] [Header("foo")] string s) - { - } - } - - public sealed class TestService - { - public ISet Ids { get; } = new HashSet(); - - public IDictionary MapOnly(IDictionary map) - { - return map; - } - - public string PayloadAnnotationFirstName([Payload("FirstName")] string name) - { - return name; - } - - public string PayloadAnnotationFullName([Payload("FirstName")] string first, [Payload("LastName")] string last) - { - return $"{first} {last}"; - } - - public string PayloadArgAndHeaderArg([Payload("FirstName")] string name, [Header] string day) - { - return name + day; - } - - public int? OptionalHeader([Header(Required = false)] int? num) - { - return num; - } - - public int RequiredHeader([Header(Name = "num", Required = true)] int num) - { - return num; - } - - public string HeadersWithExpressions([Header("emp.FirstName")] string firstName, [Header("emp.LastName.ToUpperInvariant()")] string lastName) - { - return $"{lastName}, {firstName}"; - } - - public string OptionalAndRequiredHeader([Header(Required = false)] string prop, [Header(Name = "num", Required = true)] int num) - { - return (prop ?? "null") + num; - } - - public IDictionary MapPayload(IDictionary map) - { - return map; - } - - public IDictionary MapHeaders([Headers] IDictionary map) - { - return map; - } - - public object MapHeadersAndPayload(IDictionary headers, object payload) - { - var map = new Dictionary(); - - foreach (KeyValuePair kvp in headers) - { - map.Add(kvp.Key, kvp.Value); - } - - map.Add("payload", payload); - return map; - } - - public int IntegerMethod(int i) - { - return i; - } - - public string HeaderAnnotationWithExpression([Header("day")] string value) - { - return value; - } - - public object[] MultipleAnnotatedArguments([Header("day")] string argA, [Header("month")] string argB, [Payload] Employee payloadArg, - [Payload("FirstName")] string value, [Headers] IDictionary headers) - { - return new object[] - { - argA, - argB, - payloadArg, - value, - headers - }; - } - - public string IrrelevantAnnotation([Bogus] string value) - { - return value; - } - - public string HeaderNameWithHyphen([Header("foo-bar")] string foobar) - { - return foobar.ToUpperInvariant(); - } - - public string HeaderId(string payload, [Header("id")] string id) - { - Ids.Add(id); - return "foo"; - } - } - - [AttributeUsage(AttributeTargets.Parameter)] - public sealed class BogusAttribute : Attribute - { - } - - public sealed class Employee - { - public string FirstName { get; } - - public string LastName { get; } - - public Employee(string firstName, string lastName) - { - FirstName = firstName; - LastName = lastName; - } - } -} diff --git a/src/Integration/test/Integration.Test/Handler/MethodInvokingMessageProcessorTest.cs b/src/Integration/test/Integration.Test/Handler/MethodInvokingMessageProcessorTest.cs deleted file mode 100644 index cefc759ee7..0000000000 --- a/src/Integration/test/Integration.Test/Handler/MethodInvokingMessageProcessorTest.cs +++ /dev/null @@ -1,631 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Integration.Attributes; -using Steeltoe.Integration.Configuration; -using Steeltoe.Integration.Extensions; -using Steeltoe.Integration.Handler; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Integration.Test.Handler; - -public sealed class MethodInvokingMessageProcessorTest -{ - [Fact] - public async Task TestMessageHandlerMethodFactoryOverride() - { - IServiceCollection serviceCollection = GetDefaultContainer(); - - serviceCollection.AddServiceActivators(); - var f = new DefaultMessageHandlerMethodFactory(); - - f.SetArgumentResolvers(new List - { - new TestHandlerMethodArgumentResolver() - }); - - serviceCollection.AddSingleton(f); - - ServiceProvider container = serviceCollection.BuildServiceProvider(true); - ILifecycleProcessor lifeCycleProcessor = await StartAsync(container); - - var appContext = container.GetService(); - var channel = appContext.GetService("foo"); - channel.Send(MessageBuilder.WithPayload("Bob Smith").Build()); - var outChan = appContext.GetService("out"); - Assert.Equal("Person: Bob Smith", outChan.Receive().Payload); - - await lifeCycleProcessor.StopAsync(); - } - - [Fact] - public async Task TestHandlerInheritanceMethodImplInSuper() - { - IServiceCollection serviceCollection = GetDefaultContainer(); - - serviceCollection.AddServiceActivators(); - ServiceProvider container = serviceCollection.BuildServiceProvider(true); - ILifecycleProcessor lifeCycleProcessor = await StartAsync(container); - - var appContext = container.GetService(); - var channel = appContext.GetService("in"); - channel.Send(Message.Create(string.Empty)); - var outChan = appContext.GetService("out"); - IMessage received = outChan.Receive(); - Assert.Equal("A1", received.Headers.Get("A1")); - - await lifeCycleProcessor.StopAsync(); - } - - [Fact] - public async Task TestHandlerInheritanceMethodImplInSubClass() - { - IServiceCollection serviceCollection = GetDefaultContainer(); - - serviceCollection.AddServiceActivators(); - ServiceProvider container = serviceCollection.BuildServiceProvider(true); - ILifecycleProcessor lifeCycleProcessor = await StartAsync(container); - - var appContext = container.GetService(); - var channel = appContext.GetService("in"); - channel.Send(Message.Create(string.Empty)); - var outChan = appContext.GetService("out"); - IMessage received = outChan.Receive(); - Assert.Equal("C2", received.Headers.Get("C2")); - - await lifeCycleProcessor.StopAsync(); - } - - [Fact] - public async Task TestHandlerInheritanceMethodImplInSubClassAndSuper() - { - IServiceCollection serviceCollection = GetDefaultContainer(); - - serviceCollection.AddServiceActivators(); - ServiceProvider container = serviceCollection.BuildServiceProvider(true); - ILifecycleProcessor lifeCycleProcessor = await StartAsync(container); - - var appContext = container.GetService(); - var channel = appContext.GetService("in"); - channel.Send(Message.Create(string.Empty)); - var outChan = appContext.GetService("out"); - IMessage received = outChan.Receive(); - Assert.Equal("C3", received.Headers.Get("C3")); - - await lifeCycleProcessor.StopAsync(); - } - - [Fact] - public void PayloadAsMethodParameterAndObjectAsReturnValue() - { - var testService = new TestService(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.AcceptPayloadAndReturnObject)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - object result = processor.ProcessMessage(Message.Create("testing")); - Assert.Equal("testing-1", result); - } - - [Fact] - public void TestPayloadCoercedToString() - { - var testService = new TestService(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.AcceptPayloadAndReturnObject)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - object result = processor.ProcessMessage(Message.Create(123_456_789)); - Assert.Equal("123456789-1", result); - } - - [Fact] - public void PayloadAsMethodParameterAndMessageAsReturnValue() - { - var testService = new TestService(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.AcceptPayloadAndReturnMessage)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - var result = processor.ProcessMessage(Message.Create("testing")) as IMessage; - Assert.NotNull(result); - Assert.Equal("testing-2", result.Payload); - } - - [Fact] - public void MessageAsMethodParameterAndObjectAsReturnValue() - { - var testService = new TestService(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.AcceptMessageAndReturnObject)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - object result = processor.ProcessMessage(Message.Create("testing")); - Assert.NotNull(result); - Assert.Equal("testing-3", result); - } - - [Fact] - public void MessageAsMethodParameterAndMessageAsReturnValue() - { - var testService = new TestService(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.AcceptMessageAndReturnMessage)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - var result = processor.ProcessMessage(Message.Create("testing")) as IMessage; - Assert.NotNull(result); - Assert.Equal("testing-4", result.Payload); - } - - [Fact] - public void MessageSubclassAsMethodParameterAndMessageAsReturnValue() - { - var testService = new TestService(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.AcceptMessageSubclassAndReturnMessage)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - var result = processor.ProcessMessage(Message.Create("testing")) as IMessage; - Assert.NotNull(result); - Assert.Equal("testing-5", result.Payload); - } - - [Fact] - public void MessageSubclassAsMethodParameterAndMessageSubclassAsReturnValue() - { - var testService = new TestService(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.AcceptMessageSubclassAndReturnMessageSubclass)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - var result = processor.ProcessMessage(Message.Create("testing")) as IMessage; - Assert.NotNull(result); - Assert.Equal("testing-6", result.Payload); - } - - [Fact] - public void PayloadAndHeaderAnnotationMethodParametersAndObjectAsReturnValue() - { - var testService = new TestService(); - MethodInfo method = typeof(TestService).GetMethod(nameof(TestService.AcceptPayloadAndHeaderAndReturnObject)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - IMessage request = MessageBuilder.WithPayload("testing").SetHeader("number", 123).Build(); - object result = processor.ProcessMessage(request); - Assert.NotNull(result); - Assert.Equal("testing-123", result); - } - - [Fact] - public void MessageOnlyWithAnnotatedMethod() - { - var testService = new AnnotatedTestService(); - MethodInfo method = typeof(AnnotatedTestService).GetMethod(nameof(AnnotatedTestService.MessageOnly)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - object result = processor.ProcessMessage(Message.Create("foo")); - Assert.Equal("foo", result); - } - - [Fact] - public void PayloadWithAnnotatedMethod() - { - var testService = new AnnotatedTestService(); - MethodInfo method = typeof(AnnotatedTestService).GetMethod(nameof(AnnotatedTestService.IntegerMethod)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - object result = processor.ProcessMessage(Message.Create(123)); - Assert.Equal(123, result); - } - - [Fact] - public void ConvertedPayloadWithAnnotatedMethod() - { - var testService = new AnnotatedTestService(); - MethodInfo method = typeof(AnnotatedTestService).GetMethod(nameof(AnnotatedTestService.IntegerMethod)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - object result = processor.ProcessMessage(Message.Create("456")); - Assert.Equal(456, result); - } - - [Fact] - public void ConversionFailureWithAnnotatedMethod() - { - var testService = new AnnotatedTestService(); - MethodInfo method = typeof(AnnotatedTestService).GetMethod(nameof(AnnotatedTestService.IntegerMethod)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - var ex = Assert.Throws(() => processor.ProcessMessage(Message.Create("foo"))); - Assert.IsType(ex.InnerException); - } - - [Fact] - public void FilterSelectsAnnotationMethodsOnly() - { - var service = new OverloadedMethodService(); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, service, typeof(ServiceActivatorAttribute)); - processor.ProcessMessage(MessageBuilder.WithPayload(123).Build()); - Assert.NotNull(service.LastArg); - Assert.IsType(service.LastArg); - Assert.Equal("123", service.LastArg); - } - - [Fact] - public void TestProcessMessageRuntimeException() - { - var testService = new TestErrorService(); - MethodInfo method = typeof(TestErrorService).GetMethod(nameof(TestErrorService.Error)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - var ex = Assert.Throws(() => processor.ProcessMessage(Message.Create("foo"))); - Assert.IsType(ex.InnerException); - } - - [Fact] - public void TestProcessMessageCheckedException() - { - var testService = new TestErrorService(); - MethodInfo method = typeof(TestErrorService).GetMethod(nameof(TestErrorService.Checked)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - var ex = Assert.Throws(() => processor.ProcessMessage(Message.Create("foo"))); - Assert.IsType(ex.InnerException); - } - - [Fact] - public void MessageAndHeaderWithAnnotatedMethod() - { - var testService = new AnnotatedTestService(); - MethodInfo method = typeof(AnnotatedTestService).GetMethod(nameof(AnnotatedTestService.MessageAndHeader)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - IMessage request = MessageBuilder.WithPayload("foo").SetHeader("number", 42).Build(); - object result = processor.ProcessMessage(request); - Assert.NotNull(result); - Assert.Equal("foo-42", result); - } - - [Fact] - public void MultipleHeadersWithAnnotatedMethod() - { - var testService = new AnnotatedTestService(); - MethodInfo method = typeof(AnnotatedTestService).GetMethod(nameof(AnnotatedTestService.TwoHeaders)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - IMessage request = MessageBuilder.WithPayload("foo").SetHeader("number", 42).SetHeader("prop", "bar").Build(); - object result = processor.ProcessMessage(request); - Assert.NotNull(result); - Assert.Equal("bar-42", result); - } - - [Fact] - public void OptionalAndRequiredWithAnnotatedMethod() - { - var testService = new AnnotatedTestService(); - MethodInfo method = typeof(AnnotatedTestService).GetMethod(nameof(AnnotatedTestService.OptionalAndRequiredHeader)); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, testService, method); - IMessage message = MessageBuilder.WithPayload("foo").SetHeader("num", 42).Build(); - object result = processor.ProcessMessage(message); - Assert.Equal("null42", result); - message = MessageBuilder.WithPayload("foo").SetHeader("prop", "bar").SetHeader("num", 42).Build(); - result = processor.ProcessMessage(message); - Assert.Equal("bar42", result); - message = MessageBuilder.WithPayload("foo").SetHeader("prop", "bar").Build(); - var ex = Assert.Throws(() => processor.ProcessMessage(message)); - Assert.Contains("num", ex.InnerException.Message, StringComparison.Ordinal); - } - - [Fact] - public void TestPrivateMethod() - { - var service = new Foo(); - IApplicationContext context = GetDefaultContext(); - var processor = new MethodInvokingMessageProcessor(context, service, typeof(ServiceActivatorAttribute)); - Assert.Equal("FOO", processor.ProcessMessage(Message.Create("foo"))); - Assert.Equal("BAR", processor.ProcessMessage(Message.Create("bar"))); - } - - private IApplicationContext GetDefaultContext() - { - var serviceCollection = new ServiceCollection(); - var configBuilder = new ConfigurationBuilder(); - - var context = new GenericApplicationContext(serviceCollection.BuildServiceProvider(true), configBuilder.Build()) - { - ServiceExpressionResolver = new StandardServiceExpressionResolver() - }; - - return context; - } - - private async Task StartAsync(ServiceProvider container) - { - var saProcessor = container.GetRequiredService(); - saProcessor.Initialize(); - - var lifeCycleProcessor = container.GetRequiredService(); - await lifeCycleProcessor.StartAsync(); - return lifeCycleProcessor; - } - - private IServiceCollection GetDefaultContainer() - { - var serviceCollection = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - serviceCollection.AddSingleton(configurationRoot); - serviceCollection.AddLogging(); - - serviceCollection.AddGenericApplicationContext((_, context) => - { - context.ServiceExpressionResolver = new StandardServiceExpressionResolver(); - }); - - serviceCollection.AddIntegrationServices(); - serviceCollection.AddQueueChannel("out"); - - return serviceCollection; - } - - public sealed class Foo - { - [ServiceActivator] -#pragma warning disable IDE0051 // Remove unused private members - private string Service(string payload) -#pragma warning restore IDE0051 // Remove unused private members - { - return payload.ToUpperInvariant(); - } - } - - public sealed class TestErrorService - { - public string Error(string input) - { - throw new InvalidOperationException("Expected test exception"); - } - - public string Checked(string input) - { - throw new CheckedException("Expected test exception"); - } - } - - public sealed class CheckedException : Exception - { - public CheckedException(string message) - : base(message) - { - } - } - - public sealed class OverloadedMethodService - { - private volatile object _lastArg; - - public object LastArg => _lastArg; - - public void Foo(bool b) - { - _lastArg = b; - } - - [ServiceActivator] - public string Foo(string s) - { - _lastArg = s; - return s; - } - } - - public sealed class AnnotatedTestService - { - public string MessageOnly(IMessage message) - { - return (string)message.Payload; - } - - public string MessageAndHeader(IMessage message, [Header("number")] int num) - { - return $"{message.Payload}-{num}"; - } - - public string TwoHeaders([Header] string prop, [Header("number")] int num) - { - return $"{prop}-{num}"; - } - - public int OptionalHeader([Header(Required = false)] int num) - { - return num; - } - - public int RequiredHeader([Header("num")] int num) - { - return num; - } - - public string OptionalAndRequiredHeader([Header(Required = false)] string prop, [Header("num")] int num) - { - return (prop ?? "null") + num; - } - - public string OptionalAndRequiredDottedHeader([Header(Name = "dot1.foo", Required = false)] string prop, [Header(Name = "dot2.baz")] int num, - [Header("'dotted.literal'")] string dotted) - { - return prop + num + dotted; - } - - public IDictionary MapMethod(IDictionary map) - { - return map; - } - - public int IntegerMethod(int i) - { - return i; - } - } - - public sealed class TestService - { - public string AcceptPayloadAndReturnObject(string s) - { - return $"{s}-1"; - } - - public IMessage AcceptPayloadAndReturnMessage(string s) - { - return Message.Create($"{s}-2"); - } - - public string AcceptMessageAndReturnObject(IMessage m) - { - return $"{m.Payload}-3"; - } - - public IMessage AcceptMessageAndReturnMessage(IMessage m) - { - return Message.Create($"{m.Payload}-4"); - } - - public IMessage AcceptMessageSubclassAndReturnMessage(IMessage m) - { - return Message.Create($"{m.Payload}-5"); - } - - public IMessage AcceptMessageSubclassAndReturnMessageSubclass(IMessage m) - { - return Message.Create($"{m.Payload}-6"); - } - - public string AcceptPayloadAndHeaderAndReturnObject(string s, [Header("number")] int n) - { - return $"{s}-{n}"; - } - - public void TestVoidReturningMethods(string s) - { - // do nothing - } - - public int TestVoidReturningMethods(int i) - { - return i; - } - } - - public class A1 - { - [ServiceActivator(InputChannel = "in", OutputChannel = "out")] - public IMessage MyMethod(IMessage msg) - { - return MessageBuilder.FromMessage(msg).SetHeader("A1", "A1").Build(); - } - } - - public class B1 : A1 - { - } - - public sealed class C1 : B1 - { - } - - public class A2 - { - [ServiceActivator(InputChannel = "in", OutputChannel = "out")] - public virtual IMessage MyMethod(IMessage msg) - { - return MessageBuilder.FromMessage(msg).SetHeader("A2", "A2").Build(); - } - } - - public class B2 : A2 - { - [ServiceActivator(InputChannel = "in", OutputChannel = "out")] - public override IMessage MyMethod(IMessage msg) - { - return MessageBuilder.FromMessage(msg).SetHeader("B2", "B2").Build(); - } - } - - public sealed class C2 : B2 - { - [ServiceActivator(InputChannel = "in", OutputChannel = "out")] - public override IMessage MyMethod(IMessage msg) - { - return MessageBuilder.FromMessage(msg).SetHeader("C2", "C2").Build(); - } - } - - public class A3 - { - [ServiceActivator(InputChannel = "in", OutputChannel = "out")] - public virtual IMessage MyMethod(IMessage msg) - { - return MessageBuilder.FromMessage(msg).SetHeader("A3", "A3").Build(); - } - } - - public class B3 : A3 - { - } - - public sealed class C3 : B3 - { - [ServiceActivator(InputChannel = "in", OutputChannel = "out")] - public override IMessage MyMethod(IMessage msg) - { - return MessageBuilder.FromMessage(msg).SetHeader("C3", "C3").Build(); - } - } - - public sealed class MyConfiguration - { - [ServiceActivator(InputChannel = "foo", OutputChannel = "out")] - public string Foo(Person person) - { - return person.ToString(); - } - } - - public sealed class TestHandlerMethodArgumentResolver : IHandlerMethodArgumentResolver - { - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - string[] names = ((string)message.Payload).Split(' '); - return new Person(names[0], names[1]); - } - - public bool SupportsParameter(ParameterInfo parameter) - { - return true; - } - } - - public sealed class Person - { - public string Name { get; } - - public Person(string firstName, string lastName) - { - Name = $"{firstName} {lastName}"; - } - - public override string ToString() - { - return $"Person: {Name}"; - } - } -} diff --git a/src/Integration/test/Integration.Test/Steeltoe.Integration.Test.csproj b/src/Integration/test/Integration.Test/Steeltoe.Integration.Test.csproj deleted file mode 100644 index a7635739fd..0000000000 --- a/src/Integration/test/Integration.Test/Steeltoe.Integration.Test.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net8.0;net6.0 - - - - - - - - - - - - diff --git a/src/Integration/test/Integration.Test/Support/MessageBuilderTest.cs b/src/Integration/test/Integration.Test/Support/MessageBuilderTest.cs deleted file mode 100644 index 99d7d8eb94..0000000000 --- a/src/Integration/test/Integration.Test/Support/MessageBuilderTest.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Support; - -public sealed class MessageBuilderTest -{ - [Fact] - public void TestReadOnlyHeaders() - { - var factory = new DefaultMessageBuilderFactory(); - IMessage message = factory.WithPayload("bar").SetHeader("foo", "baz").SetHeader("qux", "fiz").Build(); - Assert.Equal("baz", message.Headers.Get("foo")); - Assert.Equal("fiz", message.Headers.Get("qux")); - - factory.ReadOnlyHeaders = new List - { - "foo" - }; - - message = factory.FromMessage(message).Build(); - Assert.Null(message.Headers.Get("foo")); - Assert.Equal("fiz", message.Headers.Get("qux")); - factory.AddReadOnlyHeaders("qux"); - message = factory.FromMessage(message).Build(); - Assert.Null(message.Headers.Get("foo")); - Assert.Null(message.Headers.Get("qux")); - } -} diff --git a/src/Integration/test/Integration.Test/Support/MutableMessageBuilderTest.cs b/src/Integration/test/Integration.Test/Support/MutableMessageBuilderTest.cs deleted file mode 100644 index 790b06ae6b..0000000000 --- a/src/Integration/test/Integration.Test/Support/MutableMessageBuilderTest.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Support; - -public sealed class MutableMessageBuilderTest -{ - [Fact] - public void Mutable() - { - MutableIntegrationMessageBuilder builder = MutableIntegrationMessageBuilder.WithPayload("test"); - IMessage message1 = builder.SetHeader("foo", "bar").Build(); - IMessage message2 = MutableIntegrationMessageBuilder.FromMessage(message1).SetHeader("another", 1).Build(); - Assert.Equal("bar", message2.Headers["foo"]); - Assert.Equal(message1.Headers.Id, message2.Headers.Id); - Assert.True(message2 == message1); - } - - [Fact] - public void MutableFromImmutable() - { - IMessage message1 = IntegrationMessageBuilder.WithPayload("test").SetHeader("foo", "bar").Build(); - IMessage message2 = MutableIntegrationMessageBuilder.FromMessage(message1).SetHeader("another", 1).Build(); - Assert.Equal("bar", message2.Headers["foo"]); - Assert.Equal(message1.Headers.Id, message2.Headers.Id); - Assert.NotEqual(message1, message2); - Assert.False(message2 == message1); - } - - [Fact] - public void MutableFromImmutableMutate() - { - IMessage message1 = IntegrationMessageBuilder.WithPayload("test").SetHeader("foo", "bar").Build(); - IMessage message2 = new MutableIntegrationMessageBuilderFactory().FromMessage(message1).SetHeader("another", 1).Build(); - Assert.Equal("bar", message2.Headers["foo"]); - Assert.Equal(message1.Headers.Id, message2.Headers.Id); - Assert.NotEqual(message1, message2); - Assert.False(message2 == message1); - } - - [Fact] - public void TestPushAndPopSequenceDetailsMutable() - { - IMessage message1 = MutableIntegrationMessageBuilder.WithPayload(1).PushSequenceDetails("foo", 1, 2).Build(); - Assert.False(message1.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - IMessage message2 = MutableIntegrationMessageBuilder.FromMessage(message1).PushSequenceDetails("bar", 1, 1).Build(); - Assert.True(message2.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - IMessage message3 = MutableIntegrationMessageBuilder.FromMessage(message2).PopSequenceDetails().Build(); - Assert.False(message3.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - } - - [Fact] - public void TestPushAndPopSequenceDetailsWhenNoCorrelationIdMutable() - { - IMessage message1 = MutableIntegrationMessageBuilder.WithPayload(1).Build(); - Assert.False(message1.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - IMessage message2 = MutableIntegrationMessageBuilder.FromMessage(message1).PushSequenceDetails("bar", 1, 1).Build(); - Assert.False(message2.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - IMessage message3 = MutableIntegrationMessageBuilder.FromMessage(message2).PopSequenceDetails().Build(); - Assert.False(message3.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - } - - [Fact] - public void TestPopSequenceDetailsWhenNotPoppedMutable() - { - IMessage message1 = MutableIntegrationMessageBuilder.WithPayload(1).Build(); - Assert.False(message1.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - IMessage message2 = MutableIntegrationMessageBuilder.FromMessage(message1).PopSequenceDetails().Build(); - Assert.False(message2.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - } - - [Fact] - public void TestPushAndPopSequenceDetailsWhenNoSequenceMutable() - { - IMessage message1 = MutableIntegrationMessageBuilder.WithPayload(1).SetCorrelationId("foo").Build(); - Assert.False(message1.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - IMessage message2 = MutableIntegrationMessageBuilder.FromMessage(message1).PushSequenceDetails("bar", 1, 1).Build(); - Assert.True(message2.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - IMessage message3 = MutableIntegrationMessageBuilder.FromMessage(message2).PopSequenceDetails().Build(); - Assert.False(message3.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - } - - [Fact] - public void TestNoIdAndTimestampHeaders() - { - IMessage message = MutableIntegrationMessageBuilder.WithPayload("foo", false).PushSequenceDetails("bar", 1, 1).Build(); - Assert.True(message.Headers.ContainsKey(IntegrationMessageHeaderAccessor.CorrelationId)); - Assert.True(message.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceNumber)); - Assert.True(message.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceSize)); - Assert.False(message.Headers.ContainsKey(IntegrationMessageHeaderAccessor.SequenceDetails)); - Assert.False(message.Headers.ContainsKey(MessageHeaders.IdName)); - Assert.False(message.Headers.ContainsKey(MessageHeaders.TimestampName)); - } -} diff --git a/src/Integration/test/Integration.Test/Support/MutableMessageTest.cs b/src/Integration/test/Integration.Test/Support/MutableMessageTest.cs deleted file mode 100644 index a21d8fcb7e..0000000000 --- a/src/Integration/test/Integration.Test/Support/MutableMessageTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Integration.Test.Support; - -public sealed class MutableMessageTest -{ - [Fact] - public void TestMessageIdTimestampRemains() - { - var uuid = Guid.NewGuid(); - long timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - object payload = new(); - - var headerDictionary = new Dictionary - { - { MessageHeaders.IdName, uuid }, - { MessageHeaders.TimestampName, timestamp } - }; - - var mutableMessage = new MutableMessage(payload, headerDictionary); - var headers = mutableMessage.Headers as MutableMessageHeaders; - - Assert.Equal(uuid.ToString(), headers.RawHeaders[MessageHeaders.IdName]); - Assert.Equal(timestamp, headers.RawHeaders[MessageHeaders.TimestampName]); - } - - [Fact] - public void TestMessageHeaderIsSettable() - { - object payload = new(); - var headerDictionary = new Dictionary(); - var additional = new Dictionary(); - - var mutableMessage = new MutableMessage(payload, headerDictionary); - var headers = mutableMessage.Headers as MutableMessageHeaders; - - // Should not throw an UnsupportedOperationException - headers.Add("foo", "bar"); - headers.Add("eep", "bar"); - headers.Remove("eep"); - headers.AddRange(additional); - - Assert.Equal("bar", headers.RawHeaders["foo"]); - } - - [Fact] - public void TestMessageHeaderIsSerializable() - { - object payload = new(); - - var uuid = Guid.NewGuid(); - long timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - // UUID as string; timestamp as string - var headerDictionaryStrings = new Dictionary - { - { MessageHeaders.IdName, uuid.ToString() }, - { MessageHeaders.TimestampName, timestamp.ToString(CultureInfo.InvariantCulture) } - }; - - var mutableMessageStrings = new MutableMessage(payload, headerDictionaryStrings); - Assert.Equal(uuid.ToString(), mutableMessageStrings.Headers.Id); - Assert.Equal(timestamp, mutableMessageStrings.Headers.Timestamp); - - // UUID as byte[]; timestamp as Long - var headerDictionaryByte = new Dictionary(); - byte[] uuidAsBytes = uuid.ToByteArray(); - - headerDictionaryByte.Add(MessageHeaders.IdName, uuidAsBytes); - headerDictionaryByte.Add(MessageHeaders.TimestampName, timestamp); - var mutableMessageBytes = new MutableMessage(payload, headerDictionaryByte); - Assert.Equal(uuid.ToString(), mutableMessageBytes.Headers.Id); - Assert.Equal(timestamp, mutableMessageBytes.Headers.Timestamp); - } -} diff --git a/src/Integration/test/RabbitMQ.Test/Inbound/InboundEndpointTest.cs b/src/Integration/test/RabbitMQ.Test/Inbound/InboundEndpointTest.cs deleted file mode 100644 index cbcb31a552..0000000000 --- a/src/Integration/test/RabbitMQ.Test/Inbound/InboundEndpointTest.cs +++ /dev/null @@ -1,297 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.RabbitMQ.Inbound; -using Steeltoe.Integration.RabbitMQ.Support; -using Steeltoe.Integration.Transformer; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Steeltoe.Messaging.Support; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Integration.RabbitMQ.Test.Inbound; - -public sealed class InboundEndpointTest -{ - [Fact] - public void TestInt2809JavaTypePropertiesToRabbit() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - ServiceProvider services = new ServiceCollection().BuildServiceProvider(true); - var context = new GenericApplicationContext(services, configurationRoot); - - var channel = new Mock(); - channel.Setup(c => c.IsOpen).Returns(true); - var connection = new Mock(); - connection.Setup(c => c.IsOpen).Returns(true); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - var connectionFactory = new Mock(); - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - var container = new DirectMessageListenerContainer(); - container.ConnectionFactory = connectionFactory.Object; - container.AcknowledgeMode = AcknowledgeMode.Manual; - - var adapter = new RabbitInboundChannelAdapter(context, container) - { - MessageConverter = new JsonMessageConverter() - }; - - var queueChannel = new QueueChannel(context); - adapter.OutputChannel = queueChannel; - adapter.BindSourceMessage = true; - object payload = new Foo("bar1"); - var objectToJsonTransformer = new ObjectToJsonTransformer(context, typeof(byte[])); - IMessage jsonMessage = objectToJsonTransformer.Transform(Message.Create(payload)); - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(jsonMessage); - accessor.DeliveryTag = 123ul; - var listener = container.MessageListener as IChannelAwareMessageListener; - var rabbitChannel = new Mock(); - listener.OnMessage(jsonMessage, rabbitChannel.Object); - IMessage result = queueChannel.Receive(1000); - Assert.Equal(payload, result.Payload); - Assert.Same(rabbitChannel.Object, result.Headers.Get(RabbitMessageHeaders.Channel)); - Assert.Equal(123ul, result.Headers.DeliveryTag()); - var sourceData = result.Headers.Get(IntegrationMessageHeaderAccessor.SourceData); - Assert.Same(jsonMessage, sourceData); - } - - [Fact] - public void TestInt2809JavaTypePropertiesFromAmqp() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - ServiceProvider services = new ServiceCollection().BuildServiceProvider(true); - var context = new GenericApplicationContext(services, configurationRoot); - - var channel = new Mock(); - channel.Setup(c => c.IsOpen).Returns(true); - var connection = new Mock(); - connection.Setup(c => c.IsOpen).Returns(true); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - var connectionFactory = new Mock(); - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - var container = new DirectMessageListenerContainer(); - container.ConnectionFactory = connectionFactory.Object; - var adapter = new RabbitInboundChannelAdapter(context, container); - var queueChannel = new QueueChannel(context); - adapter.OutputChannel = queueChannel; - object payload = new Foo("bar1"); - var headers = new MessageHeaders(); - IMessage amqpMessage = new JsonMessageConverter().ToMessage(payload, headers); - var listener = container.MessageListener as IChannelAwareMessageListener; - listener.OnMessage(amqpMessage, null); - IMessage receive = queueChannel.Receive(1000); - IMessage result = new JsonToObjectTransformer(context).Transform(receive); - Assert.NotNull(result); - Assert.Equal(payload, result.Payload); - var sourceData = result.Headers.Get(IntegrationMessageHeaderAccessor.SourceData); - Assert.Null(sourceData); - } - - [Fact] - public void TestAdapterConversionError() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - ServiceProvider services = new ServiceCollection().BuildServiceProvider(true); - var context = new GenericApplicationContext(services, configurationRoot); - - var channel = new Mock(); - channel.Setup(c => c.IsOpen).Returns(true); - var connection = new Mock(); - connection.Setup(c => c.IsOpen).Returns(true); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - var connectionFactory = new Mock(); - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - var container = new DirectMessageListenerContainer(); - container.ConnectionFactory = connectionFactory.Object; - - var adapter = new RabbitInboundChannelAdapter(context, container); - var outputChannel = new QueueChannel(context); - adapter.OutputChannel = outputChannel; - var errorChannel = new QueueChannel(context); - adapter.ErrorChannel = errorChannel; - adapter.MessageConverter = new ThrowingMessageConverter(); - - var accessor = new RabbitHeaderAccessor - { - DeliveryTag = 123ul - }; - - IMessageHeaders headers = accessor.MessageHeaders; - IMessage message = Message.Create(string.Empty, headers); - var listener = container.MessageListener as IChannelAwareMessageListener; - listener.OnMessage(message, null); - Assert.Null(outputChannel.Receive(0)); - IMessage received = errorChannel.Receive(0); - Assert.NotNull(received.Headers.Get(RabbitMessageHeaderErrorMessageStrategy.AmqpRawMessage)); - Assert.IsType(received.Payload); - - container.AcknowledgeMode = AcknowledgeMode.Manual; - var channel2 = new Mock(); - listener.OnMessage(message, channel2.Object); - Assert.Null(outputChannel.Receive(0)); - received = errorChannel.Receive(0); - Assert.NotNull(received.Headers.Get(RabbitMessageHeaderErrorMessageStrategy.AmqpRawMessage)); - Assert.IsType(received.Payload); - var ex = (ManualAckListenerExecutionFailedException)received.Payload; - Assert.Same(channel2.Object, ex.Channel); - Assert.Equal(123ul, ex.DeliveryTag); - } - - [Fact] - public void TestRetryWithinOnMessageAdapter() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - ServiceProvider services = new ServiceCollection().BuildServiceProvider(true); - var context = new GenericApplicationContext(services, configurationRoot); - - var container = new DirectMessageListenerContainer(); - - var adapter = new RabbitInboundChannelAdapter(context, container) - { - OutputChannel = new DirectChannel(context), - RetryTemplate = new PollyRetryTemplate(3, 1, 1, 1) - }; - - var errors = new QueueChannel(context); - - var recoveryCallback = new ErrorMessageSendingRecoverer(context, errors) - { - ErrorMessageStrategy = new RabbitMessageHeaderErrorMessageStrategy() - }; - - adapter.RecoveryCallback = recoveryCallback; - var listener = container.MessageListener as IChannelAwareMessageListener; - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("foo")).CopyHeaders(new MessageHeaders()).Build(); - listener.OnMessage(message, null); - IMessage errorMessage = errors.Receive(0); - Assert.NotNull(errorMessage); - var payload = errorMessage.Payload as MessagingException; - Assert.NotNull(payload); - Assert.Contains("Dispatcher has no", payload.Message, StringComparison.Ordinal); - var deliveryAttempts = payload.FailedMessage.Headers.Get(IntegrationMessageHeaderAccessor.DeliveryAttempt); - Assert.NotNull(deliveryAttempts); - Assert.Equal(3, deliveryAttempts.Value); - var amqpMessage = errorMessage.Headers.Get(RabbitMessageHeaderErrorMessageStrategy.AmqpRawMessage); - Assert.NotNull(amqpMessage); - Assert.Null(errors.Receive(0)); - } - - [Fact] - public void TestBatchedAdapter() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - ServiceProvider services = new ServiceCollection().BuildServiceProvider(true); - var context = new GenericApplicationContext(services, configurationRoot); - - var container = new DirectMessageListenerContainer(); - var adapter = new RabbitInboundChannelAdapter(context, container); - var outChannel = new QueueChannel(context); - adapter.OutputChannel = outChannel; - var listener = container.MessageListener as IChannelAwareMessageListener; - var bs = new SimpleBatchingStrategy(2, 10_000, 10_000L); - - var accessor = new MessageHeaderAccessor - { - ContentType = "text/plain" - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("test1"), accessor.MessageHeaders); - bs.AddToBatch("foo", "bar", message); - message = Message.Create(Encoding.UTF8.GetBytes("test2"), accessor.MessageHeaders); - MessageBatch? batched = bs.AddToBatch("foo", "bar", message); - Assert.NotNull(batched); - listener.OnMessage(batched.Value.Message, null); - IMessage received = outChannel.Receive(); - Assert.NotNull(received); - var asList = received.Payload as List; - Assert.NotNull(asList); - Assert.Equal(2, asList.Count); - Assert.Contains("test1", asList); - Assert.Contains("test2", asList); - } - - public sealed class ThrowingMessageConverter : ISmartMessageConverter - { - public string ServiceName { get; set; } - - public object FromMessage(IMessage message, Type targetType) - { - throw new MessageConversionException("intended"); - } - - public T FromMessage(IMessage message) - { - throw new MessageConversionException("intended"); - } - - public object FromMessage(IMessage message, Type targetType, object conversionHint) - { - throw new MessageConversionException("intended"); - } - - public T FromMessage(IMessage message, object conversionHint) - { - throw new MessageConversionException("intended"); - } - - public IMessage ToMessage(object payload, IMessageHeaders headers) - { - throw new MessageConversionException("intended"); - } - - public IMessage ToMessage(object payload, IMessageHeaders headers, object conversionHint) - { - throw new MessageConversionException("intended"); - } - } - - public sealed class Foo - { - public string Bar { get; } - - public Foo(string bar) - { - Bar = bar; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not Foo other) - { - return false; - } - - return Bar == other.Bar; - } - - public override int GetHashCode() - { - return Bar?.GetHashCode() ?? 0; - } - } -} diff --git a/src/Integration/test/RabbitMQ.Test/Inbound/RabbitMessageSourceTest.cs b/src/Integration/test/RabbitMQ.Test/Inbound/RabbitMessageSourceTest.cs deleted file mode 100644 index 15547abe33..0000000000 --- a/src/Integration/test/RabbitMQ.Test/Inbound/RabbitMessageSourceTest.cs +++ /dev/null @@ -1,160 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Acks; -using Steeltoe.Integration.RabbitMQ.Inbound; -using Steeltoe.Integration.RabbitMQ.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.RabbitMQ; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; -using R = RabbitMQ.Client; - -namespace Steeltoe.Integration.RabbitMQ.Test.Inbound; - -public sealed class RabbitMessageSourceTest -{ - [Fact] - public void TestAck() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - ServiceProvider services = new ServiceCollection().BuildServiceProvider(true); - var context = new GenericApplicationContext(services, configurationRoot); - var channel = new Mock(); - channel.Setup(c => c.IsOpen).Returns(true); - var props = new MockRabbitBasicProperties(); - var getResponse = new R.BasicGetResult(123Ul, false, "ex", "rk", 0, props, Encoding.UTF8.GetBytes("foo")); - channel.Setup(c => c.BasicGet("foo", false)).Returns(getResponse); - var connection = new Mock(); - connection.Setup(c => c.IsOpen).Returns(true); - connection.Setup(c => c.CreateModel()).Returns(channel.Object); - var connectionFactory = new Mock(); - connectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(connection.Object); - - var ccf = new CachingConnectionFactory(connectionFactory.Object); - - var source = new RabbitMessageSource(context, ccf, "foo") - { - RawMessageHeader = true - }; - - IMessage received = source.Receive(); - var rawMessage = received.Headers.Get(RabbitMessageHeaderErrorMessageStrategy.AmqpRawMessage); - var sourceData = received.Headers.Get(IntegrationMessageHeaderAccessor.SourceData); - Assert.NotNull(rawMessage); - Assert.Same(rawMessage, sourceData); - Assert.Equal("foo", received.Headers.Get(RabbitMessageHeaders.ConsumerQueue)); - - // make sure channel is not cached - IConnection conn = ccf.CreateConnection(); - R.IModel notCached = conn.CreateChannel(); - connection.Verify(c => c.CreateModel(), Times.Exactly(2)); - var callback = received.Headers.Get(IntegrationMessageHeaderAccessor.AcknowledgmentCallback); - callback.Acknowledge(Status.Accept); - channel.Verify(c => c.BasicAck(123ul, false)); - R.IModel cached = conn.CreateChannel(); // should have been "closed" - connection.Verify(c => c.CreateModel(), Times.Exactly(2)); - notCached.Close(); - cached.Close(); - ccf.Destroy(); - channel.Verify(c => c.Close(), Times.Exactly(2)); - connection.Verify(c => c.Close(30000)); - } - - [Fact] - public void TestNAck() - { - TestNackOrRequeue(false); - } - - [Fact] - public void TestRequeue() - { - TestNackOrRequeue(false); - } - - [Fact] - public void TestBatch() - { - var bs = new SimpleBatchingStrategy(2, 10_000, 10_000L); - - var headers = new RabbitHeaderAccessor - { - ContentType = "test/plain" - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("test1"), headers.MessageHeaders); - bs.AddToBatch("foo", "bar", message); - message = Message.Create(Encoding.UTF8.GetBytes("test2"), headers.MessageHeaders); - MessageBatch? batched = bs.AddToBatch("foo", "bar", message); - Assert.True(batched.HasValue); - IMessage batchMessage = batched.Value.Message; - IMessageHeaders batchHeaders = batchMessage.Headers; - var headerConverter = new DefaultMessageHeadersConverter(); - var props = new MockRabbitBasicProperties(); - headerConverter.FromMessageHeaders(batchHeaders, props, Encoding.UTF8); - props.ContentType = "text/plain"; - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - ServiceProvider services = new ServiceCollection().BuildServiceProvider(true); - var context = new GenericApplicationContext(services, configurationRoot); - var channel = new Mock(); - channel.Setup(c => c.IsOpen).Returns(true); - - var getResponse = new R.BasicGetResult(123Ul, false, "ex", "rk", 0, props, (byte[])batchMessage.Payload); - channel.Setup(c => c.BasicGet("foo", false)).Returns(getResponse); - var connection = new Mock(); - connection.Setup(c => c.IsOpen).Returns(true); - connection.Setup(c => c.CreateModel()).Returns(channel.Object); - var connectionFactory = new Mock(); - connectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(connection.Object); - - var ccf = new CachingConnectionFactory(connectionFactory.Object); - var source = new RabbitMessageSource(context, ccf, "foo"); - IMessage received = source.Receive(); - Assert.NotNull(received); - var asList = received.Payload as List; - Assert.NotNull(asList); - Assert.Contains("test1", asList); - Assert.Contains("test2", asList); - } - - private void TestNackOrRequeue(bool requeue) - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - ServiceProvider services = new ServiceCollection().BuildServiceProvider(true); - var context = new GenericApplicationContext(services, configurationRoot); - - var channel = new Mock(); - channel.Setup(c => c.IsOpen).Returns(true); - var props = new MockRabbitBasicProperties(); - var getResponse = new R.BasicGetResult(123Ul, false, "ex", "rk", 0, props, Encoding.UTF8.GetBytes("bar")); - channel.Setup(c => c.BasicGet("foo", false)).Returns(getResponse); - var connection = new Mock(); - connection.Setup(c => c.IsOpen).Returns(true); - connection.Setup(c => c.CreateModel()).Returns(channel.Object); - var connectionFactory = new Mock(); - connectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(connection.Object); - - var ccf = new CachingConnectionFactory(connectionFactory.Object); - var source = new RabbitMessageSource(context, ccf, "foo"); - IMessage received = source.Receive(); - connection.Verify(c => c.CreateModel()); - var callback = received.Headers.Get(IntegrationMessageHeaderAccessor.AcknowledgmentCallback); - callback.Acknowledge(requeue ? Status.Requeue : Status.Reject); - - channel.Verify(c => c.BasicReject(123ul, requeue)); - connection.Verify(c => c.CreateModel()); - ccf.Destroy(); - channel.Verify(c => c.Close()); - connection.Verify(c => c.Close(30000)); - } -} diff --git a/src/Integration/test/RabbitMQ.Test/MockRabbitBasicProperties.cs b/src/Integration/test/RabbitMQ.Test/MockRabbitBasicProperties.cs deleted file mode 100644 index 5babdf50f4..0000000000 --- a/src/Integration/test/RabbitMQ.Test/MockRabbitBasicProperties.cs +++ /dev/null @@ -1,176 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client; - -namespace Steeltoe.Integration.RabbitMQ.Test; - -public sealed class MockRabbitBasicProperties : IBasicProperties -{ - string IContentHeader.ProtocolClassName => string.Empty; - - int IContentHeader.ProtocolClassId => 0; - - public string AppId { get; set; } - - public string ClusterId { get; set; } - - public string ContentEncoding { get; set; } - - public string ContentType { get; set; } - - public string CorrelationId { get; set; } - - public byte DeliveryMode { get; set; } = 0xff; - - public string Expiration { get; set; } - - public IDictionary Headers { get; set; } = new Dictionary(); - - public string MessageId { get; set; } - - public bool Persistent { get; set; } - - public byte Priority { get; set; } = 0xff; - - public string ReplyTo { get; set; } - - public PublicationAddress ReplyToAddress { get; set; } - - public AmqpTimestamp Timestamp { get; set; } - - public string Type { get; set; } - - public string UserId { get; set; } - - public void ClearAppId() - { - } - - public void ClearClusterId() - { - } - - public void ClearContentEncoding() - { - } - - public void ClearContentType() - { - } - - public void ClearCorrelationId() - { - } - - public void ClearDeliveryMode() - { - } - - public void ClearExpiration() - { - } - - public void ClearHeaders() - { - } - - public void ClearMessageId() - { - } - - public void ClearPriority() - { - } - - public void ClearReplyTo() - { - } - - public void ClearTimestamp() - { - } - - public void ClearType() - { - } - - public void ClearUserId() - { - } - - public bool IsAppIdPresent() - { - return AppId != null; - } - - public bool IsClusterIdPresent() - { - return ClusterId != null; - } - - public bool IsContentEncodingPresent() - { - return ContentEncoding != null; - } - - public bool IsContentTypePresent() - { - return ContentType != null; - } - - public bool IsCorrelationIdPresent() - { - return CorrelationId != null; - } - - public bool IsDeliveryModePresent() - { - return DeliveryMode != 0xff; - } - - public bool IsExpirationPresent() - { - return Expiration != null; - } - - public bool IsHeadersPresent() - { - return Headers != null; - } - - public bool IsMessageIdPresent() - { - return MessageId != null; - } - - public bool IsPriorityPresent() - { - return Priority != 0xff; - } - - public bool IsReplyToPresent() - { - return ReplyTo != null; - } - - public bool IsTimestampPresent() - { - return !default(AmqpTimestamp).Equals(Timestamp); - } - - public bool IsTypePresent() - { - return Type != null; - } - - public bool IsUserIdPresent() - { - return UserId != null; - } - - public void SetPersistent(bool persistent) - { - } -} diff --git a/src/Integration/test/RabbitMQ.Test/Outbound/OutboundEndpointTest.cs b/src/Integration/test/RabbitMQ.Test/Outbound/OutboundEndpointTest.cs deleted file mode 100644 index 302f06e337..0000000000 --- a/src/Integration/test/RabbitMQ.Test/Outbound/OutboundEndpointTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.RabbitMQ.Outbound; -using Steeltoe.Messaging; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Xunit; - -namespace Steeltoe.Integration.RabbitMQ.Test.Outbound; - -public sealed class OutboundEndpointTest -{ - [Fact] - public void TestDelay() - { - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - ServiceProvider services = new ServiceCollection().BuildServiceProvider(true); - var context = new GenericApplicationContext(services, configurationRoot); - - var connectionFactory = new Mock(); - - var rabbitTemplate = new TestRabbitTemplate - { - ConnectionFactory = connectionFactory.Object - }; - - var endpoint = new RabbitOutboundEndpoint(context, rabbitTemplate, null) - { - ExchangeName = "foo", - RoutingKey = "bar" - }; - - endpoint.SetDelayExpressionString("42"); - endpoint.Initialize(); - - endpoint.HandleMessage(Message.Create("foo")); - Assert.NotNull(rabbitTemplate.SendMessage); - Assert.Equal("foo", rabbitTemplate.ExchangeName); - Assert.Equal("bar", rabbitTemplate.RoutingKey); - Assert.Equal(42, rabbitTemplate.SendMessage.Headers.Delay().Value); - - endpoint.ExpectReply = true; - endpoint.OutputChannel = new NullChannel(); - endpoint.HandleMessage(Message.Create("foo")); - Assert.NotNull(rabbitTemplate.SendAndReceiveMessage); - Assert.Equal("foo", rabbitTemplate.ExchangeName); - Assert.Equal("bar", rabbitTemplate.RoutingKey); - Assert.Equal(42, rabbitTemplate.SendAndReceiveMessage.Headers.Delay().Value); - - endpoint.SetDelay(23); - endpoint.RoutingKey = "baz"; - endpoint.Initialize(); - endpoint.HandleMessage(Message.Create("foo")); - Assert.NotNull(rabbitTemplate.SendAndReceiveMessage); - Assert.Equal("foo", rabbitTemplate.ExchangeName); - Assert.Equal("baz", rabbitTemplate.RoutingKey); - Assert.Equal(23, rabbitTemplate.SendAndReceiveMessage.Headers.Delay().Value); - } - - public sealed class TestRabbitTemplate : RabbitTemplate - { - public IMessage SendMessage { get; set; } - - public IMessage SendAndReceiveMessage { get; set; } - - public string ExchangeName { get; set; } - - public override void Send(string exchange, string routingKey, IMessage message, CorrelationData correlationData) - { - ExchangeName = exchange; - RoutingKey = routingKey; - SendMessage = message; - } - - public override IMessage SendAndReceive(string exchange, string routingKey, IMessage message, CorrelationData correlationData) - { - SendAndReceiveMessage = message; - ExchangeName = exchange; - RoutingKey = routingKey; - return Message.Create(Encoding.UTF8.GetBytes("foo")); - } - } -} diff --git a/src/Integration/test/RabbitMQ.Test/Steeltoe.Integration.RabbitMQ.Test.csproj b/src/Integration/test/RabbitMQ.Test/Steeltoe.Integration.RabbitMQ.Test.csproj deleted file mode 100644 index 3a383214c5..0000000000 --- a/src/Integration/test/RabbitMQ.Test/Steeltoe.Integration.RabbitMQ.Test.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net8.0;net6.0 - - - - - - - - - - - - diff --git a/src/Management/src/Endpoint/DbMigrations/DbMigrationsEndpointHandler.cs b/src/Management/src/Endpoint/DbMigrations/DbMigrationsEndpointHandler.cs index dea29907c9..49ff0fcc53 100755 --- a/src/Management/src/Endpoint/DbMigrations/DbMigrationsEndpointHandler.cs +++ b/src/Management/src/Endpoint/DbMigrations/DbMigrationsEndpointHandler.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Steeltoe.Common; -using Steeltoe.Common.Util; namespace Steeltoe.Management.Endpoint.DbMigrations; @@ -79,17 +78,35 @@ public async Task> InvokeAsync(object try { - descriptor.PendingMigrations.AddRange(_scanner.GetPendingMigrations(dbContext)); - descriptor.AppliedMigrations.AddRange(_scanner.GetAppliedMigrations(dbContext)); + AddRange(descriptor.PendingMigrations, _scanner.GetPendingMigrations(dbContext)); + AddRange(descriptor.AppliedMigrations, _scanner.GetAppliedMigrations(dbContext)); } catch (DbException exception) when (exception.Message.Contains("exist", StringComparison.Ordinal)) { _logger.LogWarning(exception, "Encountered exception loading migrations: {exception}", exception.Message); - descriptor.PendingMigrations.AddRange(_scanner.GetMigrations(dbContext)); + AddRange(descriptor.PendingMigrations, _scanner.GetMigrations(dbContext)); } } } return result; } + + private static void AddRange(IList source, IEnumerable items) + { + ArgumentGuard.NotNull(source); + ArgumentGuard.NotNull(items); + + if (source is List list) + { + list.AddRange(items); + } + else + { + foreach (T item in items) + { + source.Add(item); + } + } + } } diff --git a/src/Management/src/Endpoint/Metrics/Observer/ClrRuntimeSource.cs b/src/Management/src/Endpoint/Metrics/Observer/ClrRuntimeSource.cs index b8f31403d4..79d5cab838 100644 --- a/src/Management/src/Endpoint/Metrics/Observer/ClrRuntimeSource.cs +++ b/src/Management/src/Endpoint/Metrics/Observer/ClrRuntimeSource.cs @@ -34,10 +34,7 @@ public record struct HeapMetrics(long TotalMemory, IList CollectionCounts) } public record struct ThreadMetrics( - long AvailableThreadPoolWorkers, - long AvailableThreadCompletionPort, - long MaxThreadPoolWorkers, - long MaxThreadCompletionPort) + long AvailableThreadPoolWorkers, long AvailableThreadCompletionPort, long MaxThreadPoolWorkers, long MaxThreadCompletionPort) { public readonly long AvailableThreadPoolWorkers = AvailableThreadPoolWorkers; public readonly long AvailableThreadCompletionPort = AvailableThreadCompletionPort; diff --git a/src/Management/src/Endpoint/Properties/AssemblyInfo.cs b/src/Management/src/Endpoint/Properties/AssemblyInfo.cs index 708833e400..5d050f7e07 100644 --- a/src/Management/src/Endpoint/Properties/AssemblyInfo.cs +++ b/src/Management/src/Endpoint/Properties/AssemblyInfo.cs @@ -10,3 +10,4 @@ [assembly: InternalsVisibleTo("Steeltoe.Management.Prometheus")] [assembly: InternalsVisibleTo("Steeltoe.Management.Wavefront")] [assembly: InternalsVisibleTo("Steeltoe.Management.Tracing")] +[assembly: InternalsVisibleTo("Steeltoe.Management.Wavefront.Test")] diff --git a/src/Management/test/OpenTelemetry.Test/Steeltoe.OpenTelemetry.Test.csproj b/src/Management/test/OpenTelemetry.Test/Steeltoe.OpenTelemetry.Test.csproj deleted file mode 100644 index abf582289e..0000000000 --- a/src/Management/test/OpenTelemetry.Test/Steeltoe.OpenTelemetry.Test.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - net6.0 - Unit test project for OpenTelemetry - - - - - - - - - - - - diff --git a/src/Management/test/OpenTelemetry.Test/xunit.runner.json b/src/Management/test/OpenTelemetry.Test/xunit.runner.json deleted file mode 100644 index a2f869986e..0000000000 --- a/src/Management/test/OpenTelemetry.Test/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "maxParallelThreads": 1, - "parallelizeTestCollections": false -} diff --git a/src/Management/test/Prometheus.Test/PrometheusTests.cs b/src/Management/test/Prometheus.Test/PrometheusTests.cs index d1732de1aa..4f79b345f8 100644 --- a/src/Management/test/Prometheus.Test/PrometheusTests.cs +++ b/src/Management/test/Prometheus.Test/PrometheusTests.cs @@ -2,31 +2,24 @@ // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. -using Microsoft.Extensions.Configuration; +using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Xunit; namespace Steeltoe.Management.Prometheus.Test; -public class PrometheusTests -{ - - [Fact] - public void AddPrometheusActuator_ThrowsOnNulls() - { - const IServiceCollection services = null; - - var ex = Assert.Throws(services.AddPrometheusActuator); - Assert.Contains(nameof(services), ex.Message, StringComparison.Ordinal); - } +public sealed class PrometheusTests +{ [Fact] - public void AddPrometheusActuator_SetupsRequiredServices() + public void AddPrometheusActuator_SetsUpRequiredServices() { var services = new ServiceCollection(); services.AddPrometheusActuator(); - var provider = services.BuildServiceProvider(); - var options = provider.GetService>(); - Assert.NotNull(options); + + using ServiceProvider provider = services.BuildServiceProvider(); + var optionsMonitor = provider.GetRequiredService>(); + + optionsMonitor.CurrentValue.Path.Should().Be("prometheus"); } } diff --git a/src/Management/test/Prometheus.Test/Steeltoe.Management.Prometheus.Test.csproj b/src/Management/test/Prometheus.Test/Steeltoe.Management.Prometheus.Test.csproj index cbb89d7b40..98963064d9 100644 --- a/src/Management/test/Prometheus.Test/Steeltoe.Management.Prometheus.Test.csproj +++ b/src/Management/test/Prometheus.Test/Steeltoe.Management.Prometheus.Test.csproj @@ -1,8 +1,7 @@ - - net6.0 - enable + net8.0;net6.0 + enable @@ -10,5 +9,4 @@ - diff --git a/src/Integration/test/Integration.Test/xunit.runner.json b/src/Management/test/Prometheus.Test/xunit.runner.json similarity index 100% rename from src/Integration/test/Integration.Test/xunit.runner.json rename to src/Management/test/Prometheus.Test/xunit.runner.json diff --git a/src/Management/test/Wavefront.Test/Steeltoe.Management.Wavefront.Test.csproj b/src/Management/test/Wavefront.Test/Steeltoe.Management.Wavefront.Test.csproj index 60412425af..9507353d6b 100644 --- a/src/Management/test/Wavefront.Test/Steeltoe.Management.Wavefront.Test.csproj +++ b/src/Management/test/Wavefront.Test/Steeltoe.Management.Wavefront.Test.csproj @@ -1,8 +1,7 @@ - - net6.0 - enable + net8.0;net6.0 + enable @@ -10,5 +9,4 @@ - diff --git a/src/Management/test/Wavefront.Test/WavefrontMetricsTests.cs b/src/Management/test/Wavefront.Test/WavefrontMetricsTests.cs index 2eaa44265d..dd4ea1dac4 100644 --- a/src/Management/test/Wavefront.Test/WavefrontMetricsTests.cs +++ b/src/Management/test/Wavefront.Test/WavefrontMetricsTests.cs @@ -3,30 +3,29 @@ // See the LICENSE file in the project root for more information. using System.Reflection; -using System.Runtime.CompilerServices; +using FluentAssertions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; +using Steeltoe.Common.TestResources; using Steeltoe.Management.Diagnostics; using Steeltoe.Management.Endpoint.Diagnostics; -using Steeltoe.Management.Endpoint.Metrics; using Steeltoe.Management.Wavefront.Exporters; using Xunit; namespace Steeltoe.Management.Wavefront.Test; -public class WavefrontMetricsTests -{ - +public sealed class WavefrontMetricsTests +{ [Fact] - public async Task AddWavefrontExporter() + public async Task AddWavefront_WebApplicationBuilder() { - var settings = new Dictionary + var appSettings = new Dictionary { { "management:metrics:export:wavefront:apiToken", "test" }, { "management:metrics:export:wavefront:uri", "http://test.io" }, @@ -34,87 +33,85 @@ public async Task AddWavefrontExporter() }; WebApplicationBuilder builder = WebApplication.CreateBuilder(); - builder.Configuration.AddInMemoryCollection(settings); + builder.WebHost.UseDefaultServiceProvider(options => options.ValidateScopes = true); + builder.Configuration.AddInMemoryCollection(appSettings); + builder.Services.AddWavefrontMetrics(); builder.WebHost.UseTestServer(); - using WebApplication host = builder.AddWavefrontMetrics().Build(); - + await using WebApplication host = builder.Build(); await host.StartAsync(); + AssertExporterIsAdded(host.Services); - await host.StopAsync(); } [Fact] public async Task AddWavefront_IWebHostBuilder() { - var wfSettings = new Dictionary + var appSettings = new Dictionary { { "management:metrics:export:wavefront:uri", "https://wavefront.vmware.com" }, { "management:metrics:export:wavefront:apiToken", "testToken" } }; - IWebHostBuilder hostBuilder = new WebHostBuilder().Configure(_ => - { - }).ConfigureAppConfiguration(builder => builder.AddInMemoryCollection(wfSettings)); - using IWebHost host = hostBuilder.UseTestServer() - .AddWavefrontMetrics().Build(); + IWebHostBuilder hostBuilder = new WebHostBuilder(); + hostBuilder.UseDefaultServiceProvider(options => options.ValidateScopes = true); + hostBuilder.Configure(HostingHelpers.EmptyAction); + hostBuilder.ConfigureAppConfiguration(builder => builder.AddInMemoryCollection(appSettings)); + hostBuilder.ConfigureServices(services => services.AddWavefrontMetrics()); + using IWebHost host = hostBuilder.UseTestServer().Build(); await host.StartAsync(); IEnumerable diagnosticsManagers = host.Services.GetServices(); - Assert.Single(diagnosticsManagers); - IEnumerable diagnosticServices = host.Services.GetServices().OfType(); - Assert.Single(diagnosticServices); - IEnumerable options = host.Services.GetServices(); - Assert.Single(options); - - AssertExporterIsAdded(host.Services); - await host.StopAsync(); - } - - private static void AssertExporterIsAdded(IServiceProvider services) - { - var meterProvider = services.GetService(); - Assert.NotNull(meterProvider); - object reader = GetProperty(meterProvider, "Reader"); - Assert.NotNull(reader); - Assert.IsType(reader); + diagnosticsManagers.Should().HaveCount(1); + IEnumerable diagnosticServices = host.Services.GetServices().OfType(); + diagnosticServices.Should().HaveCount(1); - object exporter = GetProperty(reader, "Exporter"); - Assert.NotNull(exporter); - Assert.IsType(exporter); - } + var optionsMonitor = host.Services.GetRequiredService>(); + optionsMonitor.CurrentValue.ApiToken.Should().Be("testToken"); - private static object GetProperty(object meterProvider, string propName) - { - return meterProvider.GetType().GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Instance) - .GetValue(meterProvider); + AssertExporterIsAdded(host.Services); } [Fact] public void AddWavefront_ProxyConfigIsValid() { - var wfSettings = new Dictionary + var appSettings = new Dictionary { { "management:metrics:export:wavefront:uri", "proxy://wavefront.vmware.com" }, { "management:metrics:export:wavefront:apiToken", string.Empty } // Should not throw }; - IWebHostBuilder hostBuilder = new WebHostBuilder().Configure(_ => - { - }).ConfigureAppConfiguration(builder => builder.AddInMemoryCollection(wfSettings)); - - IWebHost host = hostBuilder.AddWavefrontMetrics().Build(); - Assert.NotNull(host); + IWebHostBuilder hostBuilder = new WebHostBuilder(); + hostBuilder.Configure(HostingHelpers.EmptyAction); + hostBuilder.ConfigureAppConfiguration(builder => builder.AddInMemoryCollection(appSettings)); + hostBuilder.ConfigureServices(services => services.AddWavefrontMetrics()); + using IWebHost host = hostBuilder.Build(); + host.Should().NotBeNull(); } + private static void AssertExporterIsAdded(IServiceProvider services) + { + var meterProvider = services.GetService(); + meterProvider.Should().NotBeNull(); - [Fact] - public void AddWavefront_ThrowsWhenNull() + object? reader = GetProperty(meterProvider!, "Reader"); + reader.Should().NotBeNull(); + reader.Should().BeOfType(); + + object? exporter = GetProperty(reader!, "Exporter"); + exporter.Should().NotBeNull(); + exporter.Should().BeOfType(); + } + + private static object? GetProperty(object instance, string propertyName) { - var ex = Assert.Throws(() => WavefrontExtensions.AddWavefrontMetrics((IServiceCollection)null)); - Assert.Contains("services", ex.Message, StringComparison.Ordinal); + Type type = instance.GetType(); + PropertyInfo? property = type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance); + property.Should().NotBeNull(); + + return property!.GetValue(instance); } } diff --git a/src/Integration/test/RabbitMQ.Test/xunit.runner.json b/src/Management/test/Wavefront.Test/xunit.runner.json similarity index 100% rename from src/Integration/test/RabbitMQ.Test/xunit.runner.json rename to src/Management/test/Wavefront.Test/xunit.runner.json diff --git a/src/Messaging/src/Abstractions/Converter/IContentTypeResolver.cs b/src/Messaging/src/Abstractions/Converter/IContentTypeResolver.cs deleted file mode 100644 index bc4aff8576..0000000000 --- a/src/Messaging/src/Abstractions/Converter/IContentTypeResolver.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Converter; - -/// -/// Resolve the content type for a message. -/// -public interface IContentTypeResolver -{ - /// - /// Determine the MimeType of a message from the given message headers. - /// - /// - /// the headers to use. - /// - /// - /// the resolved MimeType. - /// - MimeType Resolve(IMessageHeaders headers); -} diff --git a/src/Messaging/src/Abstractions/Converter/IMessageConverter.cs b/src/Messaging/src/Abstractions/Converter/IMessageConverter.cs deleted file mode 100644 index a0e87d5d51..0000000000 --- a/src/Messaging/src/Abstractions/Converter/IMessageConverter.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.Converter; - -/// -/// A converter to turn the payload of a message from serialized form to a typed object and vice versa. -/// -public interface IMessageConverter : IServiceNameAware -{ - /// - /// Convert the payload of a message to a typed object. - /// - /// - /// the input message. - /// - /// - /// the target type for the conversion. - /// - /// - /// the result of the conversion. - /// - object FromMessage(IMessage message, Type targetType); - - /// - /// Convert the payload of a message to a typed object. - /// - /// - /// the target type for the conversion. - /// - /// - /// the input message. - /// - /// - /// the result of the conversion. - /// - T FromMessage(IMessage message); - - /// - /// Create a message whose payload is the result of converting the given payload object to serialized form. - /// - /// - /// the object to convert. - /// - /// - /// optional headers for the message. - /// - /// - /// the new message or null if converter does not support the payload type. - /// - IMessage ToMessage(object payload, IMessageHeaders headers); -} diff --git a/src/Messaging/src/Abstractions/Converter/IMessageConverterFactory.cs b/src/Messaging/src/Abstractions/Converter/IMessageConverterFactory.cs deleted file mode 100644 index 59eacac0be..0000000000 --- a/src/Messaging/src/Abstractions/Converter/IMessageConverterFactory.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Converter; - -/// -/// An implementation provides a factory for obtaining message converters. -/// -public interface IMessageConverterFactory -{ - /// - /// Gets a single composite message converter for all registered converters. - /// - ISmartMessageConverter MessageConverterForAllRegistered { get; } - - /// - /// Gets all the message converters provided by this factory. - /// - IList AllRegistered { get; } - - /// - /// Obtain a message converter for the given MimeType. - /// - /// - /// the MimeType to obtain a converter for. - /// - /// - /// a message converter or null if no converter exists. - /// - IMessageConverter GetMessageConverterForType(MimeType mimeType); -} diff --git a/src/Messaging/src/Abstractions/Converter/ISmartMessageConverter.cs b/src/Messaging/src/Abstractions/Converter/ISmartMessageConverter.cs deleted file mode 100644 index afdb7dcf4a..0000000000 --- a/src/Messaging/src/Abstractions/Converter/ISmartMessageConverter.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Converter; - -/// -/// An extended message converter supporting conversion hints. -/// -public interface ISmartMessageConverter : IMessageConverter -{ - /// - /// Convert the payload of a message to a typed object. - /// - /// - /// the input message. - /// - /// - /// the target type of the conversion. - /// - /// - /// an extra object passed to the converter which may used for handling the conversion. - /// - /// - /// the result of the conversion. - /// - object FromMessage(IMessage message, Type targetType, object conversionHint); - - /// - /// Convert the payload of a message to a typed object. - /// - /// - /// the target type for the conversion. - /// - /// - /// the input message. - /// - /// - /// an extra object passed to the converter which may used for handling the conversion. - /// - /// - /// the result of the conversion. - /// - T FromMessage(IMessage message, object conversionHint); - - /// - /// Create a message whose payload is the result of converting the given payload object to serialized form. - /// - /// - /// the object to convert. - /// - /// - /// optional headers for the message. - /// - /// - /// an extra object passed to the converter which may used for handling the conversion. - /// - /// - /// the new message or null if converter does not support the payload type. - /// - IMessage ToMessage(object payload, IMessageHeaders headers, object conversionHint); -} diff --git a/src/Messaging/src/Abstractions/Converter/ITypeMapper.cs b/src/Messaging/src/Abstractions/Converter/ITypeMapper.cs deleted file mode 100644 index 3c6c250e6c..0000000000 --- a/src/Messaging/src/Abstractions/Converter/ITypeMapper.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Converter; - -public interface ITypeMapper -{ - TypePrecedence Precedence { get; set; } - - Type DefaultType { get; set; } - - void FromType(Type type, IMessageHeaders headers); - - Type ToType(IMessageHeaders headers); - - Type GetInferredType(IMessageHeaders headers); -} diff --git a/src/Messaging/src/Abstractions/Converter/TypePrecedence.cs b/src/Messaging/src/Abstractions/Converter/TypePrecedence.cs deleted file mode 100644 index 07017f2185..0000000000 --- a/src/Messaging/src/Abstractions/Converter/TypePrecedence.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Converter; - -public enum TypePrecedence -{ - Inferred, - TypeId -} diff --git a/src/Messaging/src/Abstractions/Core/IDestinationResolver.cs b/src/Messaging/src/Abstractions/Core/IDestinationResolver.cs deleted file mode 100644 index 3c6f2e3581..0000000000 --- a/src/Messaging/src/Abstractions/Core/IDestinationResolver.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -/// -/// Strategy for resolving a string name to a destination. -/// -public interface IDestinationResolver -{ - /// - /// Resolve the name to a destination. - /// - /// - /// the name to resolve. - /// - /// - /// the destination if it exists. - /// - object ResolveDestination(string name); -} - -/// -/// A typed strategy for resolving a string name to a destination. -/// -/// -/// the type of destinations this resolver returns. -/// -public interface IDestinationResolver : IDestinationResolver -{ - /// - /// Resolve the name to a destination. - /// - /// - /// the name to resolve. - /// - /// - /// the destination if it exists. - /// - new T ResolveDestination(string name); -} diff --git a/src/Messaging/src/Abstractions/Core/IDestinationResolvingMessageReceivingOperations.cs b/src/Messaging/src/Abstractions/Core/IDestinationResolvingMessageReceivingOperations.cs deleted file mode 100644 index 96ddc0594a..0000000000 --- a/src/Messaging/src/Abstractions/Core/IDestinationResolvingMessageReceivingOperations.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -/// -/// Provides operations for receiving messages from a destination specified as a resolvable string. -/// -/// -/// the destination type. -/// -public interface IDestinationResolvingMessageReceivingOperations : IMessageReceivingOperations -{ - /// - /// Resolve the given destination and receive a message from it. - /// - /// - /// the destination name to resolve. - /// - /// - /// a token used to cancel the operation. - /// - /// - /// a task to signal completion. - /// - Task ReceiveAsync(string destinationName, CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination, receive a message from it, convert the payload to the specified target type. - /// - /// - /// the target type. - /// - /// - /// the destination name to resolve. - /// - /// - /// a token used to cancel the operation. - /// - /// - /// a task to signal completion. - /// - Task ReceiveAndConvertAsync(string destinationName, CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination and receive a message from it. - /// - /// - /// the destination name to resolve. - /// - /// - /// the received message. - /// - IMessage Receive(string destinationName); - - /// - /// Resolve the given destination, receive a message from it, convert the payload to the specified target type. - /// - /// - /// the target type. - /// - /// - /// the destination name to resolve. - /// - /// - /// the received message. - /// - T ReceiveAndConvert(string destinationName); -} diff --git a/src/Messaging/src/Abstractions/Core/IDestinationResolvingMessageRequestReplyOperations.cs b/src/Messaging/src/Abstractions/Core/IDestinationResolvingMessageRequestReplyOperations.cs deleted file mode 100644 index d227940a38..0000000000 --- a/src/Messaging/src/Abstractions/Core/IDestinationResolvingMessageRequestReplyOperations.cs +++ /dev/null @@ -1,230 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -/// -/// Provide operations for sending and receiving messages to and from a destination specified as a string. -/// -/// -/// the type of the destination. -/// -public interface IDestinationResolvingMessageRequestReplyOperations : IMessageRequestReplyOperations -{ - /// - /// Resolve the given destination name to a destination and send the given message, receive a reply and return it. - /// - /// - /// the name of the target destination. - /// - /// - /// the message to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task SendAndReceiveAsync(string destinationName, IMessage requestMessage, CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name, convert the payload request object to serialized form, possibly using a message converter and then wrap it as a - /// message and send it to the resolved destination, receive a reply and convert its body to the specified target type. - /// - /// - /// the type of the reply. - /// - /// - /// the name of the target destination. - /// - /// - /// the payload for the request message. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(string destinationName, object request, CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name, convert the payload request object to serialized form, possibly using a message converter and then wrap it as a - /// message with the given headers and send it to the resolved destination, receive a reply and convert its body to the specified target type. - /// - /// - /// the type of the reply. - /// - /// - /// the name of the target destination. - /// - /// - /// the payload for the request message. - /// - /// - /// the headers to include in the message. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(string destinationName, object request, IDictionary headers, - CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name, convert the payload request object to serialized form, possibly using a message converter and then wrap it as a - /// message, apply the post process, and send it to the resolved destination, receive a reply and convert its body to the specified target type. - /// - /// - /// the type of the reply. - /// - /// - /// the name of the target destination. - /// - /// - /// the payload for the request message. - /// - /// - /// post process for the request message. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(string destinationName, object request, IMessagePostProcessor requestPostProcessor, - CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name, convert the payload request object to serialized form, possibly using a message converter and then wrap it as a - /// message with the given headers, apply the post process, and send it to the resolved destination, receive a reply and convert its body to the - /// specified target type. - /// - /// - /// the type of the reply. - /// - /// - /// the name of the target destination. - /// - /// - /// the payload for the request message. - /// - /// - /// the headers to include in the message. - /// - /// - /// post process for the request message. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(string destinationName, object request, IDictionary headers, - IMessagePostProcessor requestPostProcessor, CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name to a destination and send the given message, receive a reply and return it. - /// - /// - /// the name of the target destination. - /// - /// - /// the message to send. - /// - /// - /// the received message or null if nothing received. - /// - IMessage SendAndReceive(string destinationName, IMessage requestMessage); - - /// - /// Resolve the given destination name, convert the payload request object to serialized form, possibly using a message converter and then wrap it as a - /// message and send it to the resolved destination, receive a reply and convert its body to the specified target type. - /// - /// - /// the type of the reply. - /// - /// - /// the name of the target destination. - /// - /// - /// the payload for the request message. - /// - /// - /// the converted payload of the reply message, possibly null. - /// - T ConvertSendAndReceive(string destinationName, object request); - - /// - /// Resolve the given destination name, convert the payload request object to serialized form, possibly using a message converter and then wrap it as a - /// message with the given headers and send it to the resolved destination, receive a reply and convert its body to the specified target type. - /// - /// - /// the type of the reply. - /// - /// - /// the name of the target destination. - /// - /// - /// the payload for the request message. - /// - /// - /// the headers to include in the message. - /// - /// - /// the converted payload of the reply message, possibly null. - /// - T ConvertSendAndReceive(string destinationName, object request, IDictionary headers); - - /// - /// Resolve the given destination name, convert the payload request object to serialized form, possibly using a message converter and then wrap it as a - /// message, apply the post process, and send it to the resolved destination, receive a reply and convert its body to the specified target type. - /// - /// - /// the type of the reply. - /// - /// - /// the name of the target destination. - /// - /// - /// the payload for the request message. - /// - /// - /// post process for the request message. - /// - /// - /// the converted payload of the reply message, possibly null. - /// - T ConvertSendAndReceive(string destinationName, object request, IMessagePostProcessor requestPostProcessor); - - /// - /// Resolve the given destination name, convert the payload request object to serialized form, possibly using a message converter and then wrap it as a - /// message with the given headers, apply the post process, and send it to the resolved destination, receive a reply and convert its body to the - /// specified target type. - /// - /// - /// the type of the reply. - /// - /// - /// the name of the target destination. - /// - /// - /// the payload for the request message. - /// - /// - /// the headers to include in the message. - /// - /// - /// post process for the request message. - /// - /// - /// the converted payload of the reply message, possibly null. - /// - T ConvertSendAndReceive(string destinationName, object request, IDictionary headers, IMessagePostProcessor requestPostProcessor); -} diff --git a/src/Messaging/src/Abstractions/Core/IDestinationResolvingMessageSendingOperations.cs b/src/Messaging/src/Abstractions/Core/IDestinationResolvingMessageSendingOperations.cs deleted file mode 100644 index b90c2a365c..0000000000 --- a/src/Messaging/src/Abstractions/Core/IDestinationResolvingMessageSendingOperations.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -/// -/// Provide operations for sending messages to a destination specified as a string. -/// -/// -/// the type of the destination. -/// -public interface IDestinationResolvingMessageSendingOperations : IMessageSendingOperations -{ - /// - /// Resolve the given destination name to a destination and send a message to it. - /// - /// - /// the destination name to resolve. - /// - /// - /// the message to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task SendAsync(string destinationName, IMessage message, CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name to a destination, convert the payload object to serialized form, possibly using a message converter, wrap it as a - /// message and send it to the resolved destination. - /// - /// - /// the destination name to resolve. - /// - /// - /// the payload to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(string destinationName, object payload, CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name to a destination, convert the payload object to serialized form, possibly using a message converter, wrap it as a - /// message with the given headers, and send it to the resolved destination. - /// - /// - /// the destination name to resolve. - /// - /// - /// the payload to send. - /// - /// - /// the headers to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(string destinationName, object payload, IDictionary headers, CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name to a destination, convert the payload object to serialized form, possibly using a message converter, wrap it as a - /// message, apply the post processor, and send it to the resolved destination. - /// - /// - /// the destination name to resolve. - /// - /// - /// the payload to send. - /// - /// - /// the post processor to apply. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(string destinationName, object payload, IMessagePostProcessor postProcessor, CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name to a destination, convert the payload object to serialized form, possibly using a message converter, wrap it as a - /// message with the given headers, apply the post processor and send it to the resolved destination. - /// - /// - /// the destination name to resolve. - /// - /// - /// the payload to send. - /// - /// - /// the headers to send. - /// - /// - /// the post processor to apply. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(string destinationName, object payload, IDictionary headers, IMessagePostProcessor postProcessor, - CancellationToken cancellationToken = default); - - /// - /// Resolve the given destination name to a destination and send a message to it. - /// - /// - /// the destination name to resolve. - /// - /// - /// the message to send. - /// - void Send(string destinationName, IMessage message); - - /// - /// Resolve the given destination name to a destination, convert the payload object to serialized form, possibly using a message converter, wrap it as a - /// message and send it to the resolved destination. - /// - /// - /// the destination name to resolve. - /// - /// - /// the payload to send. - /// - void ConvertAndSend(string destinationName, object payload); - - /// - /// Resolve the given destination name to a destination, convert the payload object to serialized form, possibly using a message converter, wrap it as a - /// message with the given headers, and send it to the resolved destination. - /// - /// - /// the destination name to resolve. - /// - /// - /// the payload to send. - /// - /// - /// the headers to send. - /// - void ConvertAndSend(string destinationName, object payload, IDictionary headers); - - /// - /// Resolve the given destination name to a destination, convert the payload object to serialized form, possibly using a message converter, wrap it as a - /// message, apply the post processor, and send it to the resolved destination. - /// - /// - /// the destination name to resolve. - /// - /// - /// the payload to send. - /// - /// - /// the post processor to apply. - /// - void ConvertAndSend(string destinationName, object payload, IMessagePostProcessor postProcessor); - - /// - /// Resolve the given destination name to a destination, convert the payload object to serialized form, possibly using a message converter, wrap it as a - /// message with the given headers, apply the post processor and send it to the resolved destination. - /// - /// - /// the destination name to resolve. - /// - /// - /// the payload to send. - /// - /// - /// the headers to send. - /// - /// - /// the post processor to apply. - /// - void ConvertAndSend(string destinationName, object payload, IDictionary headers, IMessagePostProcessor postProcessor); -} diff --git a/src/Messaging/src/Abstractions/Core/IMessagePostProcessor.cs b/src/Messaging/src/Abstractions/Core/IMessagePostProcessor.cs deleted file mode 100644 index 1ae785f35f..0000000000 --- a/src/Messaging/src/Abstractions/Core/IMessagePostProcessor.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -/// -/// A contract for processing a message after it has been created, either returning a modified(effectively new) message or returning the same. -/// -public interface IMessagePostProcessor -{ - /// - /// Process the message. - /// - /// - /// the message to process. - /// - /// - /// the result of post processing. - /// - IMessage PostProcessMessage(IMessage message); -} diff --git a/src/Messaging/src/Abstractions/Core/IMessageReceivingOperations.cs b/src/Messaging/src/Abstractions/Core/IMessageReceivingOperations.cs deleted file mode 100644 index d5cb69f831..0000000000 --- a/src/Messaging/src/Abstractions/Core/IMessageReceivingOperations.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -/// -/// Operations for receiving messages from a destination. -/// -/// -/// the type of the destination. -/// -public interface IMessageReceivingOperations -{ - /// - /// Receive a message from a default destination. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ReceiveAsync(CancellationToken cancellationToken = default); - - /// - /// Receive a message from the given destination. - /// - /// - /// the target destination. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ReceiveAsync(TDestination destination, CancellationToken cancellationToken = default); - - /// - /// Receive a message from a default destination and convert its payload to the specified target type. - /// - /// - /// the type of the payload. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ReceiveAndConvertAsync(CancellationToken cancellationToken = default); - - /// - /// Receive a message from the given destination and convert its payload to the specified target type. - /// - /// - /// the type of the payload. - /// - /// - /// the target destination. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ReceiveAndConvertAsync(TDestination destination, CancellationToken cancellationToken = default); - - /// - /// Receive a message from a default destination. - /// - /// - /// the received message; or null. - /// - IMessage Receive(); - - /// - /// Receive a message from the given destination. - /// - /// - /// the target destination. - /// - /// - /// the received message; or null. - /// - IMessage Receive(TDestination destination); - - /// - /// Receive a message from a default destination and convert its payload to the specified target type. - /// - /// - /// the type of the payload. - /// - /// - /// the received message; or null. - /// - T ReceiveAndConvert(); - - /// - /// Receive a message from the given destination and convert its payload to the specified target type. - /// - /// - /// the type of the payload. - /// - /// - /// the target destination. - /// - /// - /// the received message; or null. - /// - T ReceiveAndConvert(TDestination destination); -} diff --git a/src/Messaging/src/Abstractions/Core/IMessageRequestReplyOperations.cs b/src/Messaging/src/Abstractions/Core/IMessageRequestReplyOperations.cs deleted file mode 100644 index ea44a50e8b..0000000000 --- a/src/Messaging/src/Abstractions/Core/IMessageRequestReplyOperations.cs +++ /dev/null @@ -1,325 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -/// -/// Operations for sending messages to and receiving the reply from a destination. -/// -/// -/// the type of the destination. -/// -public interface IMessageRequestReplyOperations -{ - /// - /// Send a request message and receive the reply from a default destination. - /// - /// - /// the message to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task SendAndReceiveAsync(IMessage requestMessage, CancellationToken cancellationToken = default); - - /// - /// Send a request message and receive the reply from the given destination. - /// - /// - /// the target destination. - /// - /// - /// the message to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task SendAndReceiveAsync(TDestination destination, IMessage requestMessage, CancellationToken cancellationToken = default); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to a default destination, receive the - /// reply and convert its body to the specified target type. - /// - /// - /// the target type of the payload. - /// - /// - /// payload for the request message to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(object request, CancellationToken cancellationToken = default); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to a specified destination, receive the - /// reply and convert its body to the specified target type. - /// - /// - /// the target type of the payload. - /// - /// - /// the target destination. - /// - /// - /// payload for the request message to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(TDestination destination, object request, CancellationToken cancellationToken = default); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to a specified destination with the - /// given headers, receive the reply and convert its body to the specified target type. - /// - /// - /// the target type of the payload. - /// - /// - /// the target destination. - /// - /// - /// payload for the request message to send. - /// - /// - /// the headers to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(TDestination destination, object request, IDictionary headers, - CancellationToken cancellationToken = default); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to a default destination after applying - /// the post processor, receive the reply and convert its body to the specified target type. - /// - /// - /// the target type of the reply. - /// - /// - /// payload for the request message to send. - /// - /// - /// the post processor to apply. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(object request, IMessagePostProcessor requestPostProcessor, CancellationToken cancellationToken = default); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to the specified destination after - /// applying the post processor, receive the reply and convert its body to the specified target type. - /// - /// - /// the target type of the reply. - /// - /// - /// the target destination. - /// - /// - /// payload for the request message to send. - /// - /// - /// the post processor to apply. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(TDestination destination, object request, IMessagePostProcessor requestPostProcessor, - CancellationToken cancellationToken = default); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to the specified destination after - /// applying the post processor, with the specified headers, receive the reply and convert its body to the specified target type. - /// - /// - /// the target type of the reply. - /// - /// - /// the target destination. - /// - /// - /// payload for the request message to send. - /// - /// - /// the headers to send. - /// - /// - /// the post processor to apply. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertSendAndReceiveAsync(TDestination destination, object request, IDictionary headers, - IMessagePostProcessor requestPostProcessor, CancellationToken cancellationToken = default); - - /// - /// Send a request message and receive the reply from a default destination. - /// - /// - /// the message to send. - /// - /// - /// the received message; or null. - /// - IMessage SendAndReceive(IMessage requestMessage); - - /// - /// Send a request message and receive the reply from the given destination. - /// - /// - /// the target destination. - /// - /// - /// the message to send. - /// - /// - /// the received message; or null. - /// - IMessage SendAndReceive(TDestination destination, IMessage requestMessage); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to a default destination, receive the - /// reply and convert its body to the specified target type. - /// - /// - /// the target type of the payload. - /// - /// - /// payload for the request message to send. - /// - /// - /// the received message; or null. - /// - T ConvertSendAndReceive(object request); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to a specified destination, receive the - /// reply and convert its body to the specified target type. - /// - /// - /// the target type of the payload. - /// - /// - /// the target destination. - /// - /// - /// payload for the request message to send. - /// - /// - /// the received message; or null. - /// - T ConvertSendAndReceive(TDestination destination, object request); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to a specified destination with the - /// given headers, receive the reply and convert its body to the specified target type. - /// - /// - /// the target type of the payload. - /// - /// - /// the target destination. - /// - /// - /// payload for the request message to send. - /// - /// - /// the headers to send. - /// - /// - /// the received message; or null. - /// - T ConvertSendAndReceive(TDestination destination, object request, IDictionary headers); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to a default destination after applying - /// the post processor, receive the reply and convert its body to the specified target type. - /// - /// - /// the target type of the reply. - /// - /// - /// payload for the request message to send. - /// - /// - /// the post processor to apply. - /// - /// - /// the received message; or null. - /// - T ConvertSendAndReceive(object request, IMessagePostProcessor requestPostProcessor); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to the specified destination after - /// applying the post processor, receive the reply and convert its body to the specified target type. - /// - /// - /// the target type of the reply. - /// - /// - /// the target destination. - /// - /// - /// payload for the request message to send. - /// - /// - /// the post processor to apply. - /// - /// - /// the received message; or null. - /// - T ConvertSendAndReceive(TDestination destination, object request, IMessagePostProcessor requestPostProcessor); - - /// - /// Convert the given request object to serialized form, possibly using a message converter, send it as a message to the specified destination after - /// applying the post processor, with the specified headers, receive the reply and convert its body to the specified target type. - /// - /// - /// the target type of the reply. - /// - /// - /// the target destination. - /// - /// - /// payload for the request message to send. - /// - /// - /// the headers to send. - /// - /// - /// the post processor to apply. - /// - /// - /// the received message; or null. - /// - T ConvertSendAndReceive(TDestination destination, object request, IDictionary headers, IMessagePostProcessor requestPostProcessor); -} diff --git a/src/Messaging/src/Abstractions/Core/IMessageSendingOperations.cs b/src/Messaging/src/Abstractions/Core/IMessageSendingOperations.cs deleted file mode 100644 index 85542a41e0..0000000000 --- a/src/Messaging/src/Abstractions/Core/IMessageSendingOperations.cs +++ /dev/null @@ -1,259 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -/// -/// Operations for sending messages to a destination. -/// -/// -/// the type of the destination. -/// -public interface IMessageSendingOperations -{ - /// - /// Send a message to a default destination. - /// - /// - /// the message to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task SendAsync(IMessage message, CancellationToken cancellationToken = default); - - /// - /// Send a message to the given destination. - /// - /// - /// the target destination. - /// - /// - /// the message to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task SendAsync(TDestination destination, IMessage message, CancellationToken cancellationToken = default); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message and send it to a default destination. - /// - /// - /// the payload to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(object payload, CancellationToken cancellationToken = default); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message and send it to a specified destination. - /// - /// - /// the target destination. - /// - /// - /// the payload to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(TDestination destination, object payload, CancellationToken cancellationToken = default); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message with the provided headers, and send it to a - /// specified destination. - /// - /// - /// the target destination. - /// - /// - /// the payload to send. - /// - /// - /// the headers to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(TDestination destination, object payload, IDictionary headers, CancellationToken cancellationToken = default); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message, apply the post processor, and send it to the - /// default destination. - /// - /// - /// the payload to send. - /// - /// - /// the post processor to apply. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(object payload, IMessagePostProcessor postProcessor, CancellationToken cancellationToken = default); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message, apply the post processor, and send it to the - /// specified destination. - /// - /// - /// the target destination. - /// - /// - /// the payload to send. - /// - /// - /// the post processor to apply. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(TDestination destination, object payload, IMessagePostProcessor postProcessor, CancellationToken cancellationToken = default); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message, with the provided headers, apply the post - /// processor, and send it to the specified destination. - /// - /// - /// the target destination. - /// - /// - /// the payload to send. - /// - /// - /// the headers to send. - /// - /// - /// the post processor to apply. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task ConvertAndSendAsync(TDestination destination, object payload, IDictionary headers, IMessagePostProcessor postProcessor, - CancellationToken cancellationToken = default); - - /// - /// Send a message to a default destination. - /// - /// - /// the message to send. - /// - void Send(IMessage message); - - /// - /// Send a message to the given destination. - /// - /// - /// the target destination. - /// - /// - /// the message to send. - /// - void Send(TDestination destination, IMessage message); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message and send it to a default destination. - /// - /// - /// the payload to send. - /// - void ConvertAndSend(object payload); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message and send it to a specified destination. - /// - /// - /// the target destination. - /// - /// - /// the payload to send. - /// - void ConvertAndSend(TDestination destination, object payload); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message with the provided headers, and send it to a - /// specified destination. - /// - /// - /// the target destination. - /// - /// - /// the payload to send. - /// - /// - /// the headers to send. - /// - void ConvertAndSend(TDestination destination, object payload, IDictionary headers); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message, apply the post processor, and send it to the - /// default destination. - /// - /// - /// the payload to send. - /// - /// - /// the post processor to apply. - /// - void ConvertAndSend(object payload, IMessagePostProcessor postProcessor); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message, apply the post processor, and send it to the - /// specified destination. - /// - /// - /// the target destination. - /// - /// - /// the payload to send. - /// - /// - /// the post processor to apply. - /// - void ConvertAndSend(TDestination destination, object payload, IMessagePostProcessor postProcessor); - - /// - /// Convert the given object to serialized form, possibly using a message converter, wrap it as a message, with the provided headers, apply the post - /// processor, and send it to the specified destination. - /// - /// - /// the target destination. - /// - /// - /// the payload to send. - /// - /// - /// the headers to send. - /// - /// - /// the post processor to apply. - /// - void ConvertAndSend(TDestination destination, object payload, IDictionary headers, IMessagePostProcessor postProcessor); -} diff --git a/src/Messaging/src/Abstractions/Handler/Attributes/DestinationVariableAttribute.cs b/src/Messaging/src/Abstractions/Handler/Attributes/DestinationVariableAttribute.cs deleted file mode 100644 index 8533561fa6..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Attributes/DestinationVariableAttribute.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Handler.Attributes; - -/// -/// Attribute that indicates a method parameter should be bound to a template variable in a destination template string. Supported on message handling -/// methods such as those attributed with MessageMapping. -/// -[AttributeUsage(AttributeTargets.Parameter)] -public sealed class DestinationVariableAttribute : Attribute -{ - /// - /// Gets or sets the name of the destination template variable. - /// - public string Name { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// the name of the destination template variable. - /// - public DestinationVariableAttribute(string name = null) - { - Name = name ?? string.Empty; - } -} diff --git a/src/Messaging/src/Abstractions/Handler/Attributes/HeaderAttribute.cs b/src/Messaging/src/Abstractions/Handler/Attributes/HeaderAttribute.cs deleted file mode 100644 index 2474f528cd..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Attributes/HeaderAttribute.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Handler.Attributes; - -/// -/// Attribute which indicates that a method parameter should be bound to a message header. -/// -[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] -public sealed class HeaderAttribute : Attribute -{ - /// - /// Gets or sets the name of the header to bind to. - /// - public string Name { get; set; } - - /// - /// Gets or sets a value indicating whether the header binding is required. - /// - public bool Required { get; set; } - - /// - /// Gets or sets the default value to use if header is missing. - /// - public string DefaultValue { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// the name of the request header to bind to. - /// - /// - /// the default value to use as a fallback. - /// - /// - /// is the header required. - /// - public HeaderAttribute(string name = null, string defaultValue = null, bool required = true) - { - Name = name ?? string.Empty; - Required = required; - DefaultValue = defaultValue; - } -} diff --git a/src/Messaging/src/Abstractions/Handler/Attributes/HeadersAttribute.cs b/src/Messaging/src/Abstractions/Handler/Attributes/HeadersAttribute.cs deleted file mode 100644 index 31e9928918..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Attributes/HeadersAttribute.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Handler.Attributes; - -/// -/// Attribute which indicates that a method parameter should be bound to the headers of a message. -/// -[AttributeUsage(AttributeTargets.Parameter)] -public sealed class HeadersAttribute : Attribute -{ -} diff --git a/src/Messaging/src/Abstractions/Handler/Attributes/MessageExceptionHandlerAttribute.cs b/src/Messaging/src/Abstractions/Handler/Attributes/MessageExceptionHandlerAttribute.cs deleted file mode 100644 index 68ce0081c0..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Attributes/MessageExceptionHandlerAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Handler.Attributes; - -/// -/// Attribute for handling exceptions thrown from message-handling methods within a specific handler class. -/// -[AttributeUsage(AttributeTargets.Method)] -public sealed class MessageExceptionHandlerAttribute : Attribute -{ - /// - /// Gets the exceptions handled by this method. - /// - public Type[] Exceptions { get; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// the exceptions handled by this method. - /// - public MessageExceptionHandlerAttribute(params Type[] exceptions) - { - Exceptions = exceptions; - } -} diff --git a/src/Messaging/src/Abstractions/Handler/Attributes/PayloadAttribute.cs b/src/Messaging/src/Abstractions/Handler/Attributes/PayloadAttribute.cs deleted file mode 100644 index c40eb2425c..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Attributes/PayloadAttribute.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Handler.Attributes; - -/// -/// Attribute that binds a method parameter to the payload of a message. Can also be used to associate a payload to a method invocation. -/// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)] -public sealed class PayloadAttribute : Attribute -{ - /// - /// Gets or sets the expression to be evaluated against the payload. - /// - public string Expression { get; set; } - - /// - /// Gets or sets a value indicating whether the payload content is required. - /// - public bool Required { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public PayloadAttribute() - { - Expression = string.Empty; - Required = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// expression to be evaluated against the payload. - /// - /// - /// whether payload content is required. - /// - public PayloadAttribute(string expression, bool required = true) - { - Expression = expression; - Required = required; - } -} diff --git a/src/Messaging/src/Abstractions/Handler/Attributes/SendToAttribute.cs b/src/Messaging/src/Abstractions/Handler/Attributes/SendToAttribute.cs deleted file mode 100644 index 492acf0390..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Attributes/SendToAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Handler.Attributes; - -/// -/// Attribute that indicates a method's return value should be converted to a message if necessary and sent to the specified destination. -/// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] -public sealed class SendToAttribute : Attribute -{ - /// - /// Gets the destinations for any messages created by the method. - /// - public string[] Destinations { get; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// the destinations for the message created. - /// - public SendToAttribute(params string[] destinations) - { - Destinations = destinations; - } -} diff --git a/src/Messaging/src/Abstractions/Handler/Attributes/Support/IMessageHandlerMethodFactory.cs b/src/Messaging/src/Abstractions/Handler/Attributes/Support/IMessageHandlerMethodFactory.cs deleted file mode 100644 index f10aec6833..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Attributes/Support/IMessageHandlerMethodFactory.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Services; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -/// -/// A factory for invokable handler methods that is suitable to process an incoming message. -/// -public interface IMessageHandlerMethodFactory : IServiceNameAware -{ - /// - /// Create the invokable handler method that can process the specified method endpoint. - /// - /// - /// the instance of the object. - /// - /// - /// the method to invoke. - /// - /// - /// a suitable invokable handler for the method. - /// - IInvocableHandlerMethod CreateInvocableHandlerMethod(object instance, MethodInfo method); - - void Initialize(); -} diff --git a/src/Messaging/src/Abstractions/Handler/IMessageCondition.cs b/src/Messaging/src/Abstractions/Handler/IMessageCondition.cs deleted file mode 100644 index 6d7587b83b..0000000000 --- a/src/Messaging/src/Abstractions/Handler/IMessageCondition.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Handler; - -/// -/// Contract for mapping conditions to messages. -/// -public interface IMessageCondition -{ -} - -/// -/// Contract for mapping conditions to messages. -/// -/// -/// the kind of condition that this condition can be combined with. -/// -public interface IMessageCondition : IMessageCondition -{ - /// - /// Define the rules for combining this condition with another. - /// - /// - /// the condition to combine with. - /// - /// - /// the resulting message condition. - /// - T Combine(T other); - - /// - /// Check if this condition matches the given Message and returns a potentially new condition with content tailored to the current message. - /// - /// - /// the message under process. - /// - /// - /// a condition instance in case of a match; or null if no match. - /// - T GetMatchingCondition(IMessage message); - - /// - /// Compare this condition to another in the context of a specific message. - /// - /// - /// the other condition to compare to. - /// - /// - /// the message under process. - /// - /// - /// results of the comparison. - /// - int CompareTo(T other, IMessage message); -} diff --git a/src/Messaging/src/Abstractions/Handler/Invocation/IAsyncHandlerMethodReturnValueHandler.cs b/src/Messaging/src/Abstractions/Handler/Invocation/IAsyncHandlerMethodReturnValueHandler.cs deleted file mode 100644 index 6c2ef0a84c..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Invocation/IAsyncHandlerMethodReturnValueHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Messaging.Handler.Invocation; - -/// -/// Async return value handler. -/// -public interface IAsyncHandlerMethodReturnValueHandler : IHandlerMethodReturnValueHandler -{ - /// - /// Check if async return value. - /// - /// - /// the value. - /// - /// - /// the return type info. - /// - /// - /// true if the return type represents a async value. - /// - bool IsAsyncReturnValue(object returnValue, ParameterInfo parameterInfo); -} diff --git a/src/Messaging/src/Abstractions/Handler/Invocation/IHandlerMethodArgumentResolver.cs b/src/Messaging/src/Abstractions/Handler/Invocation/IHandlerMethodArgumentResolver.cs deleted file mode 100644 index 0ddf0589e6..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Invocation/IHandlerMethodArgumentResolver.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Messaging.Handler.Invocation; - -/// -/// Strategy interface for resolving method parameters into argument values in the context of a given request. -/// -public interface IHandlerMethodArgumentResolver -{ - /// - /// Determine whether the given method parameter is supported by this resolver. - /// - /// - /// the parameter info to consider. - /// - /// - /// true if it is supported. - /// - bool SupportsParameter(ParameterInfo parameter); - - /// - /// Resolves a method parameter into an argument value from a given message. - /// - /// - /// the parameter info to consider. - /// - /// - /// the message. - /// - /// - /// the resolved argument value. - /// - object ResolveArgument(ParameterInfo parameter, IMessage message); -} diff --git a/src/Messaging/src/Abstractions/Handler/Invocation/IHandlerMethodReturnValueHandler.cs b/src/Messaging/src/Abstractions/Handler/Invocation/IHandlerMethodReturnValueHandler.cs deleted file mode 100644 index 0770770a01..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Invocation/IHandlerMethodReturnValueHandler.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Messaging.Handler.Invocation; - -/// -/// Strategy interface to handle the value returned from the invocation of a method handling a Message. -/// -public interface IHandlerMethodReturnValueHandler -{ - /// - /// Determine whether the given method return type is supported by this handler. - /// - /// - /// the return parameter info. - /// - /// - /// true if it supports the supplied return type. - /// - bool SupportsReturnType(ParameterInfo returnType); - - /// - /// Handle the given return value. - /// - /// - /// the value returned from the handler method. - /// - /// - /// the type of the return value. - /// - /// - /// the message that was passed to the handler. - /// - void HandleReturnValue(object returnValue, ParameterInfo returnType, IMessage message); -} diff --git a/src/Messaging/src/Abstractions/Handler/Invocation/IInvocableHandlerMethod.cs b/src/Messaging/src/Abstractions/Handler/Invocation/IInvocableHandlerMethod.cs deleted file mode 100644 index d653e0ed32..0000000000 --- a/src/Messaging/src/Abstractions/Handler/Invocation/IInvocableHandlerMethod.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Messaging.Handler.Invocation; - -/// -/// Invokes the underlying method with argument values resolved from the current message. -/// -public interface IInvocableHandlerMethod -{ - object Handler { get; } - - MethodInfo Method { get; } - - /// - /// Gets a value indicating whether the return type of the method is void. - /// - bool IsVoid { get; } - - /// - /// Gets a value for message logging. - /// - string ShortLogMessage { get; } - - /// - /// Invoke the underlying method after resolving its argument values in the context of the given message. - /// - /// - /// the message being processed. - /// - /// - /// given arguments matched by type, not resolved. - /// - /// - /// the raw value returned from the invoked method. - /// - object Invoke(IMessage requestMessage, params object[] args); -} diff --git a/src/Messaging/src/Abstractions/IAsyncMessageHandler.cs b/src/Messaging/src/Abstractions/IAsyncMessageHandler.cs deleted file mode 100644 index 420681fab5..0000000000 --- a/src/Messaging/src/Abstractions/IAsyncMessageHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging; - -/// -/// Simple contract for handling a Message. -/// -public interface IAsyncMessageHandler -{ - /// - /// Handle the given method. - /// - /// - /// the message to process. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - Task HandleMessageAsync(IMessage message, CancellationToken cancellationToken = default); -} diff --git a/src/Messaging/src/Abstractions/IMessage.cs b/src/Messaging/src/Abstractions/IMessage.cs deleted file mode 100644 index ac06109a64..0000000000 --- a/src/Messaging/src/Abstractions/IMessage.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging; - -/// -/// A generic message representation with headers and a body. -/// -public interface IMessage -{ - /// - /// Gets the body of the message. - /// - object Payload { get; } - - /// - /// Gets the headers for the message. - /// - IMessageHeaders Headers { get; } -} - -/// -/// A generic message representation with headers and a body. -/// -/// -/// the type of the payload. -/// -public interface IMessage : IMessage -{ - /// - /// Gets the body of the message. - /// - new T Payload { get; } -} diff --git a/src/Messaging/src/Abstractions/IMessageChannel.cs b/src/Messaging/src/Abstractions/IMessageChannel.cs deleted file mode 100644 index d893370e14..0000000000 --- a/src/Messaging/src/Abstractions/IMessageChannel.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging; - -/// -/// An abstraction that defines methods for sending messages. -/// -public interface IMessageChannel : IServiceNameAware -{ - /// - /// Send a message to this channel. - /// - /// - /// the message to send. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - ValueTask SendAsync(IMessage message, CancellationToken cancellationToken = default); - - /// - /// Send a message to this channel. If the message is sent successfully, the method returns true. If the message cannot be sent due to a non-fatal - /// reason, the method returns false. The method may also throw a Exception in case of non-recoverable errors. This method may block indefinitely, - /// depending on the implementation. - /// - /// - /// the message to send. - /// - /// - /// true if the message is sent. - /// - bool Send(IMessage message); - - /// - /// Send a message, blocking until either the message is accepted or the specified timeout period elapses. - /// - /// - /// the message to send. - /// - /// - /// the timeout in milliseconds; -1 for no timeout. - /// - /// - /// true if the message is sent. - /// - bool Send(IMessage message, int timeout); -} diff --git a/src/Messaging/src/Abstractions/IMessageHandler.cs b/src/Messaging/src/Abstractions/IMessageHandler.cs deleted file mode 100644 index dd517a2039..0000000000 --- a/src/Messaging/src/Abstractions/IMessageHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging; - -/// -/// Simple contract for handling a Message. -/// -public interface IMessageHandler : IServiceNameAware -{ - /// - /// Handle the given method. - /// - /// - /// the message to process. - /// - void HandleMessage(IMessage message); -} diff --git a/src/Messaging/src/Abstractions/IMessageHeaders.cs b/src/Messaging/src/Abstractions/IMessageHeaders.cs deleted file mode 100644 index 103ad92e74..0000000000 --- a/src/Messaging/src/Abstractions/IMessageHeaders.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; - -namespace Steeltoe.Messaging; - -/// -/// The headers for a message. -/// -public interface IMessageHeaders : IDictionary, IDictionary -{ - /// - /// Gets the ID header value. - /// - string Id { get; } - - /// - /// Gets the timestamp header value. - /// - long? Timestamp { get; } - - /// - /// Gets the reply channel the message is for. - /// - object ReplyChannel { get; } - - /// - /// Gets the error channel the message is for. - /// - object ErrorChannel { get; } - - new ICollection Keys { get; } - - new ICollection Values { get; } - - new int Count { get; } - - /// - /// Gets a header value given its key. - /// - /// - /// the type of the value returned. - /// - /// - /// the name of the header. - /// - /// - /// the value or null if not found. - /// - T Get(string key); - - new IEnumerator> GetEnumerator(); -} diff --git a/src/Messaging/src/Abstractions/IPollableChannel.cs b/src/Messaging/src/Abstractions/IPollableChannel.cs deleted file mode 100644 index 1ff280a7dd..0000000000 --- a/src/Messaging/src/Abstractions/IPollableChannel.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging; - -/// -/// A MessageChannel from which messages may be actively received through polling. -/// -public interface IPollableChannel : IMessageChannel -{ - /// - /// Receive a message from this channel. - /// - /// - /// token used to signal cancellation. - /// - /// - /// a task to signal completion. - /// - ValueTask ReceiveAsync(CancellationToken cancellationToken = default); - - /// - /// Receive a message from this channel, blocking forever if necessary. - /// - /// - /// the message. - /// - IMessage Receive(); - - /// - /// Receive a message from this channel, blocking until either a message is available or the specified timeout period elapses. - /// - /// - /// the timeout value in milliseconds. - /// - /// - /// the message or null. - /// - IMessage Receive(int timeout); -} diff --git a/src/Messaging/src/Abstractions/ISubscribableChannel.cs b/src/Messaging/src/Abstractions/ISubscribableChannel.cs deleted file mode 100644 index 892b7c0d14..0000000000 --- a/src/Messaging/src/Abstractions/ISubscribableChannel.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging; - -/// -/// A MessageChannel that maintains a registry of subscribers and invokes them to handle messages sent through this channel. -/// -public interface ISubscribableChannel : IMessageChannel -{ - /// - /// Register a message handler. - /// - /// - /// the handler to register. - /// - /// - /// false if already registered; otherwise true. - /// - bool Subscribe(IMessageHandler handler); - - /// - /// Un-register a message handler. - /// - /// - /// the handler to remove. - /// - /// - /// false if not registered; otherwise true. - /// - bool Unsubscribe(IMessageHandler handler); -} diff --git a/src/Messaging/src/Abstractions/Steeltoe.Messaging.Abstractions.csproj b/src/Messaging/src/Abstractions/Steeltoe.Messaging.Abstractions.csproj deleted file mode 100644 index ffa4b8eb03..0000000000 --- a/src/Messaging/src/Abstractions/Steeltoe.Messaging.Abstractions.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net8.0;net6.0 - Steeltoe.Messaging - Abstractions for use with Steeltoe Messaging - - true - - - - - - - - diff --git a/src/Messaging/src/Abstractions/Support/IChannelInterceptor.cs b/src/Messaging/src/Abstractions/Support/IChannelInterceptor.cs deleted file mode 100644 index 123d38cbe8..0000000000 --- a/src/Messaging/src/Abstractions/Support/IChannelInterceptor.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Order; - -namespace Steeltoe.Messaging.Support; - -/// -/// Interface for interceptors that are able to view and/or modify the Messages being sent-to and/or received-from a MessageChannel. -/// -public interface IChannelInterceptor : IOrdered -{ - /// - /// Invoked before the Message is actually sent to the channel. This allows for modification of the Message if necessary. - /// - /// - /// the message being processed. - /// - /// - /// the channel the message for. - /// - /// - /// the resulting message to send; can be null. - /// - IMessage PreSend(IMessage message, IMessageChannel channel); - - /// - /// Invoked immediately after the send invocation. - /// - /// - /// the message being processed. - /// - /// - /// the channel the message for. - /// - /// - /// the return value of the send. - /// - void PostSend(IMessage message, IMessageChannel channel, bool sent); - - /// - /// Invoked after the completion of a send regardless of any exception that have been raised thus allowing for proper resource cleanup. - /// - /// - /// the message being processed. - /// - /// - /// the channel the message for. - /// - /// - /// the return value of the send. - /// - /// - /// the exception that may have occurred; can be null. - /// - void AfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception exception); - - /// - /// Invoked as soon as receive is called and before a Message is actually retrieved. - /// - /// - /// the channel the message for. - /// - /// - /// false if no receive should be done. - /// - bool PreReceive(IMessageChannel channel); - - /// - /// Invoked immediately after a Message has been retrieved but before it is returned to the caller. - /// - /// - /// the message being processed. - /// - /// - /// the channel the message is from. - /// - /// - /// the resulting message after post processing; can be null. - /// - IMessage PostReceive(IMessage message, IMessageChannel channel); - - /// - /// Invoked after the completion of a receive regardless of any exception that have been raised thus allowing for proper resource cleanup. - /// - /// - /// the message being processed. - /// - /// - /// the channel the message is from. - /// - /// - /// the exception that may have occurred; can be null. - /// - void AfterReceiveCompletion(IMessage message, IMessageChannel channel, Exception exception); -} diff --git a/src/Messaging/src/Abstractions/Support/IHeaderMapper.cs b/src/Messaging/src/Abstractions/Support/IHeaderMapper.cs deleted file mode 100644 index 9f039a5c26..0000000000 --- a/src/Messaging/src/Abstractions/Support/IHeaderMapper.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Support; - -/// -/// Generic strategy interface for mapping MessageHeaders to and from other types of objects. -/// -/// -/// type of the instance to and from which headers will be mapped. -/// -public interface IHeaderMapper -{ - /// - /// Map from the given MessageHeaders to the specified target message. - /// - /// - /// the incoming message headers. - /// - /// - /// the native target message. - /// - void FromHeaders(IMessageHeaders headers, T target); - - /// - /// Map from the given target message to abstracted MessageHeaders. - /// - /// - /// the native target message. - /// - /// - /// the mapped message headers. - /// - IMessageHeaders ToHeaders(T source); -} diff --git a/src/Messaging/src/Abstractions/Support/IInterceptableChannel.cs b/src/Messaging/src/Abstractions/Support/IInterceptableChannel.cs deleted file mode 100644 index a265a23b09..0000000000 --- a/src/Messaging/src/Abstractions/Support/IInterceptableChannel.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Support; - -/// -/// A MessageChannel that maintains a list of ChannelInterceptors and allows interception of message sending. -/// -public interface IInterceptableChannel -{ - /// - /// Set the list of channel interceptors. - /// - /// - /// the interceptors to use. - /// - void SetInterceptors(List interceptors); - - /// - /// Add an interceptor to the list. - /// - /// - /// the interceptor to add. - /// - void AddInterceptor(IChannelInterceptor interceptor); - - /// - /// Add an interceptor at the location specified by the index. - /// - /// - /// the index to add the interceptor at. - /// - /// - /// the interceptor to add. - /// - void AddInterceptor(int index, IChannelInterceptor interceptor); - - /// - /// Get the interceptors for the channel. - /// - /// - /// the list of interceptors. - /// - List GetInterceptors(); - - /// - /// Remove the specified interceptor. - /// - /// - /// the interceptor to remove. - /// - /// - /// true if successful. - /// - bool RemoveInterceptor(IChannelInterceptor interceptor); - - /// - /// Remove the interceptor at the given index. - /// - /// - /// the index to use. - /// - /// - /// the interceptor removed. - /// - IChannelInterceptor RemoveInterceptor(int index); -} diff --git a/src/Messaging/src/Abstractions/Support/IMessageHandlingRunnable.cs b/src/Messaging/src/Abstractions/Support/IMessageHandlingRunnable.cs deleted file mode 100644 index 1ea20444a0..0000000000 --- a/src/Messaging/src/Abstractions/Support/IMessageHandlingRunnable.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Support; - -/// -/// A runnable to encapsulates a message and message handler. -/// -public interface IMessageHandlingRunnable : IRunnable -{ - /// - /// Gets the message this runnable is processing. - /// - IMessage Message { get; } - - /// - /// Gets the message handler that will process this message. - /// - IMessageHandler MessageHandler { get; } -} diff --git a/src/Messaging/src/Abstractions/Support/IMessageHeaderAccessor.cs b/src/Messaging/src/Abstractions/Support/IMessageHeaderAccessor.cs deleted file mode 100644 index c79d331aa8..0000000000 --- a/src/Messaging/src/Abstractions/Support/IMessageHeaderAccessor.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Support; - -public interface IMessageHeaderAccessor -{ - string Id { get; } - - long? Timestamp { get; } - - string ContentType { get; set; } - - bool LeaveMutable { get; set; } - - bool IsMutable { get; } - - bool IsModified { get; set; } - - IMessageHeaders MessageHeaders { get; } - - string ReplyChannelName { get; set; } - - object ReplyChannel { get; set; } - - string ErrorChannelName { get; set; } - - object ErrorChannel { get; set; } - - bool EnableTimestamp { get; set; } - - IIdGenerator IdGenerator { get; set; } - - void SetImmutable(); - - IMessageHeaders ToMessageHeaders(); - - IDictionary ToDictionary(); - - object GetHeader(string headerName); - - void SetHeader(string name, object value); - - void SetHeaderIfAbsent(string name, object value); - - void RemoveHeader(string headerName); - - void RemoveHeaders(params string[] headerPatterns); - - void CopyHeaders(IDictionary headersToCopy); - - void CopyHeadersIfAbsent(IDictionary headersToCopy); -} diff --git a/src/Messaging/src/Abstractions/Support/IMessageHeaderInitializer.cs b/src/Messaging/src/Abstractions/Support/IMessageHeaderInitializer.cs deleted file mode 100644 index d42f73581d..0000000000 --- a/src/Messaging/src/Abstractions/Support/IMessageHeaderInitializer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Support; - -public interface IMessageHeaderInitializer -{ - void InitHeaders(IMessageHeaderAccessor headerAccessor); -} diff --git a/src/Messaging/src/Abstractions/Support/ITaskSchedulerChannelInterceptor.cs b/src/Messaging/src/Abstractions/Support/ITaskSchedulerChannelInterceptor.cs deleted file mode 100644 index 6240be7cf5..0000000000 --- a/src/Messaging/src/Abstractions/Support/ITaskSchedulerChannelInterceptor.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Support; - -/// -/// A specialized ChannelInterceptor for TaskScheduler based channels. -/// -public interface ITaskSchedulerChannelInterceptor : IChannelInterceptor -{ - /// - /// Invoked inside the Task submitted to the Scheduler just before calling the target MessageHandler to handle the message. - /// - /// - /// the message to be handled. - /// - /// - /// the channel the message is for. - /// - /// - /// the target handler to handle the message. - /// - /// - /// the processed message; can be new message or null. - /// - IMessage BeforeHandled(IMessage message, IMessageChannel channel, IMessageHandler handler); - - /// - /// Invoked inside the Task submitted to the Scheduler after calling the target MessageHandler regardless of the outcome (i.e.Exception raised or not) - /// thus allowing for proper resource cleanup. - /// - /// - /// the message to be handled. - /// - /// - /// the channel the message is for. - /// - /// - /// the target handler to handle the message. - /// - /// - /// any exception that might have occurred. - /// - void AfterMessageHandled(IMessage message, IMessageChannel channel, IMessageHandler handler, Exception exception); -} diff --git a/src/Messaging/src/Messaging/Converter/AbstractMessageConverter.cs b/src/Messaging/src/Messaging/Converter/AbstractMessageConverter.cs deleted file mode 100644 index 94ccdcced0..0000000000 --- a/src/Messaging/src/Messaging/Converter/AbstractMessageConverter.cs +++ /dev/null @@ -1,211 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.Converter; - -public abstract class AbstractMessageConverter : ISmartMessageConverter -{ - private readonly List _supportedMimeTypes; - - private bool _strictContentTypeMatch; - - private Type _serializedPayloadClass = typeof(byte[]); - - public virtual ICollection SupportedMimeTypes => new List(_supportedMimeTypes); - - public virtual IContentTypeResolver ContentTypeResolver { get; set; } = new DefaultContentTypeResolver(); - - public virtual bool StrictContentTypeMatch - { - get => _strictContentTypeMatch; - set - { - if (value) - { - if (SupportedMimeTypes.Count <= 0) - { - throw new InvalidOperationException("Strict match requires non-empty list of supported mime types"); - } - - if (ContentTypeResolver == null) - { - throw new InvalidOperationException("Strict match requires ContentTypeResolver"); - } - } - - _strictContentTypeMatch = value; - } - } - - public virtual Type SerializedPayloadClass - { - get => _serializedPayloadClass; - set - { - if (value != typeof(byte[]) && value != typeof(string)) - { - throw new ArgumentException("Value must be a byte array or a string.", nameof(value)); - } - - _serializedPayloadClass = value; - } - } - - public abstract string ServiceName { get; set; } - - protected AbstractMessageConverter(MimeType supportedMimeType) - { - ArgumentGuard.NotNull(supportedMimeType); - - _supportedMimeTypes = new List - { - supportedMimeType - }; - } - - protected AbstractMessageConverter(ICollection supportedMimeTypes) - { - ArgumentGuard.NotNull(supportedMimeTypes); - - _supportedMimeTypes = new List(supportedMimeTypes); - } - - public virtual T FromMessage(IMessage message) - { - return (T)FromMessage(message, typeof(T), null); - } - - public virtual T FromMessage(IMessage message, object conversionHint) - { - return (T)FromMessage(message, typeof(T), null); - } - - public virtual object FromMessage(IMessage message, Type targetType) - { - return FromMessage(message, targetType, null); - } - - public virtual object FromMessage(IMessage message, Type targetType, object conversionHint) - { - if (!CanConvertFrom(message, targetType)) - { - return null; - } - - return ConvertFromInternal(message, targetType, conversionHint); - } - - public virtual IMessage ToMessage(object payload, IMessageHeaders headers) - { - return ToMessage(payload, headers, null); - } - - public virtual IMessage ToMessage(object payload, IMessageHeaders headers, object conversionHint) - { - if (!CanConvertTo(payload, headers)) - { - return null; - } - - object payloadToUse = ConvertToInternal(payload, headers, conversionHint); - - if (payloadToUse == null) - { - return null; - } - - MimeType mimeType = GetDefaultContentType(payloadToUse); - - if (headers != null) - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetAccessor(headers, typeof(MessageHeaderAccessor)); - - if (accessor != null && accessor.IsMutable) - { - if (mimeType != null) - { - accessor.SetHeaderIfAbsent(MessageHeaders.ContentType, mimeType); - } - - return MessageBuilder.CreateMessage(payloadToUse, accessor.MessageHeaders); - } - } - - AbstractMessageBuilder builder = MessageBuilder.WithPayload(payloadToUse); - - if (headers != null) - { - builder.CopyHeaders(headers); - } - - if (mimeType != null) - { - builder.SetHeaderIfAbsent(MessageHeaders.ContentType, mimeType); - } - - return builder.Build(); - } - - public virtual bool CanConvertFrom(IMessage message, Type targetClass) - { - return Supports(targetClass) && SupportsMimeType(message.Headers); - } - - public virtual bool CanConvertTo(object payload, IMessageHeaders headers = null) - { - return Supports(payload.GetType()) && SupportsMimeType(headers); - } - - protected virtual MimeType GetDefaultContentType(object payload) - { - ICollection mimeTypes = SupportedMimeTypes; - return mimeTypes.ElementAt(0); - } - - protected virtual bool SupportsMimeType(IMessageHeaders headers) - { - if (SupportedMimeTypes.Count == 0) - { - return true; - } - - MimeType mimeType = GetMimeType(headers); - - if (mimeType == null) - { - return !StrictContentTypeMatch; - } - - foreach (MimeType current in SupportedMimeTypes) - { - if (current.Type == mimeType.Type && current.Subtype == mimeType.Subtype) - { - return true; - } - } - - return false; - } - - protected virtual MimeType GetMimeType(IMessageHeaders headers) - { - return headers != null && ContentTypeResolver != null ? ContentTypeResolver.Resolve(headers) : null; - } - - protected abstract bool Supports(Type type); - - protected virtual object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - return null; - } - - protected virtual object ConvertToInternal(object payload, IMessageHeaders headers, object conversionHint) - { - return null; - } -} diff --git a/src/Messaging/src/Messaging/Converter/AbstractTypeMapper.cs b/src/Messaging/src/Messaging/Converter/AbstractTypeMapper.cs deleted file mode 100644 index b2e695d595..0000000000 --- a/src/Messaging/src/Messaging/Converter/AbstractTypeMapper.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.Converter; - -public abstract class AbstractTypeMapper -{ - public const string DefaultClassIdFieldName = MessageHeaders.TypeId; - public const string DefaultContentClassIdFieldName = MessageHeaders.ContentTypeId; - public const string DefaultKeyClassIdFieldName = MessageHeaders.KeyTypeId; - - private static readonly ISet ContainerTypes = new[] - { - typeof(Dictionary<,>), - typeof(List<>), - typeof(HashSet<>), - typeof(LinkedList<>), - typeof(Stack<>), - typeof(Queue<>) - }.ToHashSet(); - - private readonly Dictionary _classIdMapping = new(); - - public Dictionary IdClassMapping { get; } = new(); - - public string ClassIdFieldName { get; internal set; } = DefaultClassIdFieldName; - - public string ContentClassIdFieldName { get; internal set; } = DefaultContentClassIdFieldName; - - public string KeyClassIdFieldName { get; internal set; } = DefaultKeyClassIdFieldName; - - public void SetIdClassMapping(Dictionary mapping) - { - foreach (KeyValuePair entry in mapping) - { - IdClassMapping[entry.Key] = entry.Value; - } - - CreateReverseMap(); - } - - protected virtual void AddHeader(IMessageHeaders headers, string headerName, Type type) - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(headers); - accessor.SetHeader(headerName, _classIdMapping.ContainsKey(type) ? _classIdMapping[type] : GetClassName(type)); - } - - protected virtual string RetrieveHeader(IMessageHeaders headers, string headerName) - { - string classId = RetrieveHeaderAsString(headers, headerName); - - if (classId == null) - { - throw new MessageConversionException($"failed to convert Message content. Could not resolve {headerName} in header"); - } - - return classId; - } - - protected virtual string RetrieveHeaderAsString(IMessageHeaders headers, string headerName) - { - object classIdFieldNameValue = headers.Get(headerName); - string classId = null; - - if (classIdFieldNameValue != null) - { - classId = classIdFieldNameValue.ToString(); - } - - return classId; - } - - protected virtual bool HasInferredTypeHeader(IMessageHeaders headers) - { - return FromInferredTypeHeader(headers) != null; - } - - protected Type FromInferredTypeHeader(IMessageHeaders headers) - { - return headers.Get(MessageHeaders.InferredArgumentType); - } - - protected virtual Type GetContentType(Type type) - { - if (IsContainerType(type)) - { - Type typedef = type.GetGenericTypeDefinition(); - - if (typeof(Dictionary<,>) == typedef) - { - return type.GetGenericArguments()[1]; - } - - return type.GetGenericArguments()[0]; - } - - return null; - } - - protected virtual bool IsContainerType(Type type) - { - if (type.IsGenericType) - { - Type genericType = type.GetGenericTypeDefinition(); - return ContainerTypes.Contains(genericType); - } - - return false; - } - - protected virtual Type GetKeyType(Type type) - { - if (IsContainerType(type)) - { - Type typedef = type.GetGenericTypeDefinition(); - - if (typeof(Dictionary<,>) == typedef) - { - return type.GetGenericArguments()[0]; - } - } - - return null; - } - - protected virtual string GetClassName(Type type) - { - if (IsContainerType(type)) - { - return type.GetGenericTypeDefinition().FullName; - } - - return type.ToString(); - } - - private void CreateReverseMap() - { - _classIdMapping.Clear(); - - foreach (KeyValuePair entry in IdClassMapping) - { - string id = entry.Key; - Type type = entry.Value; - _classIdMapping[type] = id; - } - } -} diff --git a/src/Messaging/src/Messaging/Converter/ByteArrayMessageConverter.cs b/src/Messaging/src/Messaging/Converter/ByteArrayMessageConverter.cs deleted file mode 100644 index 156269f987..0000000000 --- a/src/Messaging/src/Messaging/Converter/ByteArrayMessageConverter.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Converter; - -public class ByteArrayMessageConverter : AbstractMessageConverter -{ - public const string DefaultServiceName = nameof(ByteArrayMessageConverter); - - public override string ServiceName { get; set; } = DefaultServiceName; - - public ByteArrayMessageConverter() - : base(MimeTypeUtils.ApplicationOctetStream) - { - } - - protected override bool Supports(Type type) - { - return typeof(byte[]) == type; - } - - protected override object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - return message.Payload; - } - - protected override object ConvertToInternal(object payload, IMessageHeaders headers, object conversionHint) - { - return payload; - } -} diff --git a/src/Messaging/src/Messaging/Converter/CompositeMessageConverter.cs b/src/Messaging/src/Messaging/Converter/CompositeMessageConverter.cs deleted file mode 100644 index 5aa4b5c951..0000000000 --- a/src/Messaging/src/Messaging/Converter/CompositeMessageConverter.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; - -namespace Steeltoe.Messaging.Converter; - -public class CompositeMessageConverter : ISmartMessageConverter -{ - public const string DefaultServiceName = nameof(CompositeMessageConverter); - - public string ServiceName { get; set; } = DefaultServiceName; - - public List Converters { get; } - - public CompositeMessageConverter(ICollection converters) - { - ArgumentGuard.NotNullOrEmpty(converters); - - Converters = new List(converters); - } - - public object FromMessage(IMessage message, Type targetType) - { - foreach (IMessageConverter converter in Converters) - { - object result = converter.FromMessage(message, targetType); - - if (result != null) - { - return result; - } - } - - return null; - } - - public object FromMessage(IMessage message, Type targetType, object conversionHint) - { - foreach (IMessageConverter converter in Converters) - { - object result = converter is ISmartMessageConverter smartConverter - ? smartConverter.FromMessage(message, targetType, conversionHint) - : converter.FromMessage(message, targetType); - - if (result != null) - { - return result; - } - } - - return null; - } - - public T FromMessage(IMessage message, object conversionHint) - { - return (T)FromMessage(message, typeof(T), conversionHint); - } - - public T FromMessage(IMessage message) - { - return (T)FromMessage(message, typeof(T), null); - } - - public IMessage ToMessage(object payload, IMessageHeaders headers) - { - foreach (IMessageConverter converter in Converters) - { - IMessage result = converter.ToMessage(payload, headers); - - if (result != null) - { - return result; - } - } - - return null; - } - - public IMessage ToMessage(object payload, IMessageHeaders headers, object conversionHint) - { - foreach (IMessageConverter converter in Converters) - { - IMessage result = converter is ISmartMessageConverter smartConverter - ? smartConverter.ToMessage(payload, headers, conversionHint) - : converter.ToMessage(payload, headers); - - if (result != null) - { - return result; - } - } - - return null; - } - - public override string ToString() - { - return $"CompositeMessageConverter[converters={Converters.Count}]"; - } -} diff --git a/src/Messaging/src/Messaging/Converter/DefaultContentTypeResolver.cs b/src/Messaging/src/Messaging/Converter/DefaultContentTypeResolver.cs deleted file mode 100644 index 1ac0c5fc6c..0000000000 --- a/src/Messaging/src/Messaging/Converter/DefaultContentTypeResolver.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Converter; - -public class DefaultContentTypeResolver : IContentTypeResolver -{ - public MimeType DefaultMimeType { get; set; } - - public MimeType Resolve(IMessageHeaders headers) - { - if (headers == null || !headers.ContainsKey(MessageHeaders.ContentType)) - { - return DefaultMimeType; - } - - object value = headers[MessageHeaders.ContentType]; - - if (value == null) - { - return null; - } - - if (value is MimeType mimeType) - { - return mimeType; - } - - if (value is string stringValue) - { - return MimeType.ToMimeType(stringValue); - } - - throw new ArgumentException($"Unknown type for contentType header value: {value.GetType()}", nameof(headers)); - } - - public override string ToString() - { - return $"DefaultContentTypeResolver[defaultMimeType={DefaultMimeType}]"; - } -} diff --git a/src/Messaging/src/Messaging/Converter/DefaultTypeMapper.cs b/src/Messaging/src/Messaging/Converter/DefaultTypeMapper.cs deleted file mode 100644 index 5ff00bc04c..0000000000 --- a/src/Messaging/src/Messaging/Converter/DefaultTypeMapper.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Runtime.Loader; - -namespace Steeltoe.Messaging.Converter; - -public class DefaultTypeMapper : AbstractTypeMapper, ITypeMapper -{ - public TypePrecedence Precedence { get; set; } = TypePrecedence.Inferred; - - public Type DefaultType { get; set; } = typeof(object); - - public Type ToType(IMessageHeaders headers) - { - Type inferredType = GetInferredType(headers); - - if (inferredType != null && !inferredType.IsAbstract && !inferredType.IsInterface) - { - return inferredType; - } - - string typeIdHeader = RetrieveHeaderAsString(headers, ClassIdFieldName); - - if (typeIdHeader != null) - { - return FromTypeHeader(headers, typeIdHeader); - } - - if (HasInferredTypeHeader(headers)) - { - return FromInferredTypeHeader(headers); - } - - return DefaultType; - } - - public void FromType(Type type, IMessageHeaders headers) - { - AddHeader(headers, ClassIdFieldName, type); - - if (IsContainerType(type)) - { - AddHeader(headers, ContentClassIdFieldName, GetContentType(type)); - } - - Type keyType = GetKeyType(type); - - if (keyType != null) - { - AddHeader(headers, KeyClassIdFieldName, keyType); - } - } - - public Type GetInferredType(IMessageHeaders headers) - { - if (HasInferredTypeHeader(headers) && Precedence == TypePrecedence.Inferred) - { - return FromInferredTypeHeader(headers); - } - - return null; - } - - private Type FromTypeHeader(IMessageHeaders headers, string typeIdHeader) - { - Type classType = GetClassIdType(typeIdHeader); - - if (!IsContainerType(classType) || classType.IsArray) - { - return classType; - } - - Type contentClassType = GetClassIdType(RetrieveHeader(headers, ContentClassIdFieldName)); - - if (!HasKeyType(classType)) - { - return ConstructCollectionType(classType, contentClassType); - } - - Type keyClassType = GetClassIdType(RetrieveHeader(headers, KeyClassIdFieldName)); - return ConstructDictionaryType(classType, keyClassType, contentClassType); - } - - private bool HasKeyType(Type classType) - { - if (typeof(Dictionary<,>) == classType) - { - return true; - } - - return false; - } - - private Type ConstructDictionaryType(Type classType, Type keyClassType, Type contentClassType) - { - return classType.MakeGenericType(keyClassType, contentClassType); - } - - private Type ConstructCollectionType(Type classType, Type contentClassType) - { - return classType.MakeGenericType(contentClassType); - } - - private Type GetClassIdType(string classId) - { - if (IdClassMapping.ContainsKey(classId)) - { - return IdClassMapping[classId]; - } - - try - { - return Type.GetType(classId, true); - } - catch (Exception) - { - foreach (Assembly assembly in AssemblyLoadContext.Default.Assemblies) - { - Type result = assembly.GetType(classId, false); - - if (result != null) - { - return result; - } - } - - throw new MessageConversionException($"failed to resolve class name. Class not found [{classId}]"); - } - } -} diff --git a/src/Messaging/src/Messaging/Converter/GenericMessageConverter.cs b/src/Messaging/src/Messaging/Converter/GenericMessageConverter.cs deleted file mode 100644 index 5cb6021ab8..0000000000 --- a/src/Messaging/src/Messaging/Converter/GenericMessageConverter.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Converter; - -namespace Steeltoe.Messaging.Converter; - -public class GenericMessageConverter : SimpleMessageConverter -{ - private readonly IConversionService _conversionService; - - public GenericMessageConverter() - { - _conversionService = DefaultConversionService.Singleton; - } - - public GenericMessageConverter(IConversionService conversionService) - { - ArgumentGuard.NotNull(conversionService); - - _conversionService = conversionService; - } - - public override object FromMessage(IMessage message, Type targetType) - { - object payload = message.Payload; - - if (_conversionService.CanConvert(payload.GetType(), targetType)) - { - try - { - return _conversionService.Convert(payload, payload.GetType(), targetType); - } - catch (ConversionException ex) - { - throw new MessageConversionException(message, $"Failed to convert message payload '{payload}' to '{targetType.Name}'", ex); - } - } - - return targetType.IsInstanceOfType(payload) ? payload : null; - } -} diff --git a/src/Messaging/src/Messaging/Converter/MessageConversionException.cs b/src/Messaging/src/Messaging/Converter/MessageConversionException.cs deleted file mode 100644 index 5849ad4cd7..0000000000 --- a/src/Messaging/src/Messaging/Converter/MessageConversionException.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Converter; - -public class MessageConversionException : MessagingException -{ - public MessageConversionException(string message) - : base(message) - { - } - - public MessageConversionException(string message, Exception innerException) - : base(message, innerException) - { - } - - public MessageConversionException(IMessage failedMessage, string message) - : base(failedMessage, message) - { - } - - public MessageConversionException(IMessage failedMessage, string message, Exception innerException) - : base(failedMessage, message, innerException) - { - } -} diff --git a/src/Messaging/src/Messaging/Converter/NewtonJsonMessageConverter.cs b/src/Messaging/src/Messaging/Converter/NewtonJsonMessageConverter.cs deleted file mode 100644 index 5986554dde..0000000000 --- a/src/Messaging/src/Messaging/Converter/NewtonJsonMessageConverter.cs +++ /dev/null @@ -1,202 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Converter; - -public class NewtonJsonMessageConverter : AbstractMessageConverter -{ - public const string DefaultServiceName = nameof(NewtonJsonMessageConverter); - - public JsonSerializerSettings Settings { get; } - - public override string ServiceName { get; set; } = DefaultServiceName; - - public NewtonJsonMessageConverter() - : base(new MimeType("application", "json", Encoding.UTF8)) - { - Settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - } - - public NewtonJsonMessageConverter(params MimeType[] supportedMimeTypes) - : base(new List(supportedMimeTypes)) - { - Settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - } - - public override bool CanConvertFrom(IMessage message, Type targetClass) - { - if (targetClass == null || !SupportsMimeType(message.Headers)) - { - return false; - } - - return true; - } - - public override bool CanConvertTo(object payload, IMessageHeaders headers = null) - { - if (!SupportsMimeType(headers)) - { - return false; - } - - return true; - } - - protected internal static Type GetIMessageGenericType(Type type) - { - var typeFilter = new TypeFilter((t, _) => - { - Type candidate = t; - - if (candidate.IsConstructedGenericType) - { - candidate = candidate.GetGenericTypeDefinition(); - } - - return typeof(IMessage<>) == candidate; - }); - - if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(IMessage<>)) - { - return type.GenericTypeArguments[0]; - } - - if (type == typeof(IMessage<>)) - { - return null; - } - - Type[] result = type.FindInterfaces(typeFilter, null); - - if (result.Length > 0) - { - return result[0].GenericTypeArguments[0]; - } - - return null; - } - - protected internal static bool IsIMessageGenericType(Type type) - { - return GetIMessageGenericType(type) != null; - } - - protected internal static Type GetTargetType(Type targetClass, object conversionHint) - { - if (conversionHint is ParameterInfo info) - { - Type paramType = info.ParameterType; - Type messageType = GetIMessageGenericType(paramType); - - if (messageType != null) - { - return messageType; - } - - return paramType; - } - - return targetClass; - } - - protected static Encoding GetJsonEncoding(MimeType contentType) - { - if (contentType != null && contentType.Encoding != null) - { - return contentType.Encoding; - } - - return EncodingUtils.Utf8; - } - - protected override object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - Type target = GetTargetType(targetClass, conversionHint); - object payload = message.Payload; - - if (targetClass.IsInstanceOfType(payload)) - { - return payload; - } - - var serializer = JsonSerializer.Create(Settings); - - try - { - TextReader textReader = null; - - if (payload is byte[] payloadBytes) - { - var buffer = new MemoryStream(payloadBytes, false); - textReader = new StreamReader(buffer, true); - } - else - { - textReader = new StringReader(payload.ToString()); - } - - return serializer.Deserialize(textReader, target); - } - catch (Exception ex) - { - throw new MessageConversionException(message, $"Could not read JSON: {ex.Message}", ex); - } - } - - protected override object ConvertToInternal(object payload, IMessageHeaders headers, object conversionHint) - { - var serializer = JsonSerializer.Create(Settings); - - try - { - if (typeof(byte[]) == SerializedPayloadClass) - { - var memStream = new MemoryStream(1024); - Encoding encoding = GetJsonEncoding(GetMimeType(headers)); - - var writer = new StreamWriter(memStream, encoding) - { - AutoFlush = true - }; - - serializer.Serialize(writer, payload); - payload = memStream.ToArray(); - } - else - { - var writer = new StringWriter(); - serializer.Serialize(writer, payload); - payload = writer.ToString(); - } - } - catch (Exception ex) - { - throw new MessageConversionException($"Could not write JSON: {ex.Message}", ex); - } - - return payload; - } - - protected override bool Supports(Type type) - { - throw new InvalidOperationException(); - } -} diff --git a/src/Messaging/src/Messaging/Converter/SimpleMessageConverter.cs b/src/Messaging/src/Messaging/Converter/SimpleMessageConverter.cs deleted file mode 100644 index 48b80c34ef..0000000000 --- a/src/Messaging/src/Messaging/Converter/SimpleMessageConverter.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.Converter; - -public class SimpleMessageConverter : IMessageConverter -{ - public const string DefaultServiceName = nameof(SimpleMessageConverter); - - public string ServiceName { get; set; } = DefaultServiceName; - - public virtual object FromMessage(IMessage message, Type targetType) - { - object payload = message.Payload; - return ClassUtils.IsAssignableValue(targetType, payload) ? payload : null; - } - - public virtual T FromMessage(IMessage message) - { - return (T)FromMessage(message, typeof(T)); - } - - public virtual IMessage ToMessage(object payload, IMessageHeaders headers) - { - if (headers != null) - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetAccessor(headers, typeof(MessageHeaderAccessor)); - - if (accessor != null && accessor.IsMutable) - { - return MessageBuilder.CreateMessage(payload, accessor.MessageHeaders); - } - } - - return MessageBuilder.WithPayload(payload).CopyHeaders(headers).Build(); - } -} diff --git a/src/Messaging/src/Messaging/Converter/StringMessageConverter.cs b/src/Messaging/src/Messaging/Converter/StringMessageConverter.cs deleted file mode 100644 index 4e495c1f93..0000000000 --- a/src/Messaging/src/Messaging/Converter/StringMessageConverter.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common; -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Converter; - -public class StringMessageConverter : AbstractMessageConverter -{ - public const string DefaultServiceName = nameof(StringMessageConverter); - - private readonly Encoding _defaultCharset; - - public override string ServiceName { get; set; } = DefaultServiceName; - - public StringMessageConverter() - : this(Encoding.UTF8) - { - } - - public StringMessageConverter(Encoding defaultCharset) - : base(new MimeType("text", "plain", defaultCharset)) - { - ArgumentGuard.NotNull(defaultCharset); - - _defaultCharset = defaultCharset; - } - - protected override bool Supports(Type type) - { - return typeof(string) == type; - } - - protected override object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - Encoding charset = GetContentTypeCharset(GetMimeType(message.Headers)); - object payload = message.Payload; - - return payload is string ? payload : new string(charset.GetChars((byte[])payload)); - } - - protected override object ConvertToInternal(object payload, IMessageHeaders headers, object conversionHint) - { - if (typeof(byte[]) == SerializedPayloadClass) - { - Encoding charset = GetContentTypeCharset(GetMimeType(headers)); - string payStr = (string)payload; - payload = charset.GetBytes(payStr); - } - - return payload; - } - - private Encoding GetContentTypeCharset(MimeType mimeType) - { - if (mimeType != null && mimeType.Encoding != null) - { - return mimeType.Encoding; - } - - return _defaultCharset; - } -} diff --git a/src/Messaging/src/Messaging/Core/AbstractDestinationResolvingMessagingTemplate.cs b/src/Messaging/src/Messaging/Core/AbstractDestinationResolvingMessagingTemplate.cs deleted file mode 100644 index 3025d5d2e1..0000000000 --- a/src/Messaging/src/Messaging/Core/AbstractDestinationResolvingMessagingTemplate.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Messaging.Core; - -public abstract class AbstractDestinationResolvingMessagingTemplate - : AbstractMessagingTemplate, IDestinationResolvingMessageSendingOperations, - IDestinationResolvingMessageReceivingOperations, IDestinationResolvingMessageRequestReplyOperations - where TDestination : class -{ - private IDestinationResolver _destinationResolver; - - public virtual IApplicationContext ApplicationContext { get; } - - public IDestinationResolver DestinationResolver - { - get - { - _destinationResolver ??= (IDestinationResolver)ApplicationContext?.GetService(typeof(IDestinationResolver)); - return _destinationResolver; - } - set => _destinationResolver = value; - } - - protected AbstractDestinationResolvingMessagingTemplate(IApplicationContext context) - { - ApplicationContext = context; - } - - public virtual Task ConvertAndSendAsync(string destinationName, object payload, CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(destinationName, payload, null, null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string destinationName, object payload, IDictionary headers, - CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(destinationName, payload, headers, null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string destinationName, object payload, IMessagePostProcessor postProcessor, - CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(destinationName, payload, null, postProcessor, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string destinationName, object payload, IDictionary headers, IMessagePostProcessor postProcessor, - CancellationToken cancellationToken = default) - { - TDestination destination = ResolveDestination(destinationName); - return ConvertAndSendAsync(destination, payload, headers, postProcessor, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string destinationName, object request, CancellationToken cancellationToken = default) - { - TDestination destination = ResolveDestination(destinationName); - return ConvertSendAndReceiveAsync(destination, request, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string destinationName, object request, IDictionary headers, - CancellationToken cancellationToken = default) - { - TDestination destination = ResolveDestination(destinationName); - return ConvertSendAndReceiveAsync(destination, request, headers, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string destinationName, object request, IMessagePostProcessor requestPostProcessor, - CancellationToken cancellationToken = default) - { - TDestination destination = ResolveDestination(destinationName); - return ConvertSendAndReceiveAsync(destination, request, requestPostProcessor, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string destinationName, object request, IDictionary headers, - IMessagePostProcessor requestPostProcessor, CancellationToken cancellationToken = default) - { - TDestination destination = ResolveDestination(destinationName); - return ConvertSendAndReceiveAsync(destination, request, headers, requestPostProcessor, cancellationToken); - } - - public virtual Task ReceiveAsync(string destinationName, CancellationToken cancellationToken = default) - { - TDestination destination = ResolveDestination(destinationName); - return ReceiveAsync(destination, cancellationToken); - } - - public virtual Task ReceiveAndConvertAsync(string destinationName, CancellationToken cancellationToken = default) - { - TDestination destination = ResolveDestination(destinationName); - return ReceiveAndConvertAsync(destination, cancellationToken); - } - - public virtual Task SendAsync(string destinationName, IMessage message, CancellationToken cancellationToken = default) - { - TDestination destination = ResolveDestination(destinationName); - return SendAsync(destination, message, cancellationToken); - } - - public virtual Task SendAndReceiveAsync(string destinationName, IMessage requestMessage, CancellationToken cancellationToken = default) - { - TDestination destination = ResolveDestination(destinationName); - return SendAndReceiveAsync(destination, requestMessage, cancellationToken); - } - - public virtual void ConvertAndSend(string destinationName, object payload) - { - ConvertAndSend(destinationName, payload, null, null); - } - - public virtual void ConvertAndSend(string destinationName, object payload, IDictionary headers) - { - ConvertAndSend(destinationName, payload, headers, null); - } - - public virtual void ConvertAndSend(string destinationName, object payload, IMessagePostProcessor postProcessor) - { - ConvertAndSend(destinationName, payload, null, postProcessor); - } - - public virtual void ConvertAndSend(string destinationName, object payload, IDictionary headers, IMessagePostProcessor postProcessor) - { - TDestination destination = ResolveDestination(destinationName); - ConvertAndSend(destination, payload, headers, postProcessor); - } - - public virtual T ConvertSendAndReceive(string destinationName, object request) - { - TDestination destination = ResolveDestination(destinationName); - return ConvertSendAndReceive(destination, request); - } - - public virtual T ConvertSendAndReceive(string destinationName, object request, IDictionary headers) - { - TDestination destination = ResolveDestination(destinationName); - return ConvertSendAndReceive(destination, request, headers); - } - - public virtual T ConvertSendAndReceive(string destinationName, object request, IMessagePostProcessor requestPostProcessor) - { - TDestination destination = ResolveDestination(destinationName); - return ConvertSendAndReceive(destination, request, requestPostProcessor); - } - - public virtual T ConvertSendAndReceive(string destinationName, object request, IDictionary headers, - IMessagePostProcessor requestPostProcessor) - { - TDestination destination = ResolveDestination(destinationName); - return ConvertSendAndReceive(destination, request, headers, requestPostProcessor); - } - - public virtual IMessage Receive(string destinationName) - { - TDestination destination = ResolveDestination(destinationName); - return Receive(destination); - } - - public virtual T ReceiveAndConvert(string destinationName) - { - TDestination destination = ResolveDestination(destinationName); - return ReceiveAndConvert(destination); - } - - public virtual void Send(string destinationName, IMessage message) - { - TDestination destination = ResolveDestination(destinationName); - Send(destination, message); - } - - public virtual IMessage SendAndReceive(string destinationName, IMessage requestMessage) - { - TDestination destination = ResolveDestination(destinationName); - return SendAndReceive(destination, requestMessage); - } - - protected TDestination ResolveDestination(string destinationName) - { - if (DestinationResolver == null) - { - throw new InvalidOperationException("DestinationResolver is required to resolve destination names"); - } - - return DestinationResolver.ResolveDestination(destinationName); - } -} diff --git a/src/Messaging/src/Messaging/Core/AbstractMessageReceivingTemplate.cs b/src/Messaging/src/Messaging/Core/AbstractMessageReceivingTemplate.cs deleted file mode 100644 index 25f204ff51..0000000000 --- a/src/Messaging/src/Messaging/Core/AbstractMessageReceivingTemplate.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Messaging.Core; - -public abstract class AbstractMessageReceivingTemplate : AbstractMessageSendingTemplate, IMessageReceivingOperations - where TDestination : class -{ - protected virtual TDestination RequiredDefaultReceiveDestination - { - get - { - if (DefaultReceiveDestination == null) - { - throw new InvalidOperationException("No default destination configured"); - } - - return DefaultReceiveDestination; - } - } - - public virtual TDestination DefaultReceiveDestination { get; set; } - - public virtual bool ThrowReceivedExceptions { get; set; } - - public virtual Task ReceiveAsync(CancellationToken cancellationToken = default) - { - return DoReceiveAsync(RequiredDefaultReceiveDestination, cancellationToken); - } - - public virtual Task ReceiveAsync(TDestination destination, CancellationToken cancellationToken = default) - { - return DoReceiveAsync(destination, cancellationToken); - } - - public virtual Task ReceiveAndConvertAsync(CancellationToken cancellationToken = default) - { - return ReceiveAndConvertAsync(RequiredDefaultReceiveDestination, cancellationToken); - } - - public virtual async Task ReceiveAndConvertAsync(TDestination destination, CancellationToken cancellationToken = default) - { - IMessage message = await DoReceiveAsync(destination, cancellationToken); - - if (message != null) - { - return DoConvert(message); - } - - return default; - } - - public virtual IMessage Receive() - { - return DoReceive(RequiredDefaultReceiveDestination); - } - - public virtual IMessage Receive(TDestination destination) - { - return DoReceive(destination); - } - - public virtual T ReceiveAndConvert() - { - return ReceiveAndConvert(RequiredDefaultReceiveDestination); - } - - public virtual T ReceiveAndConvert(TDestination destination) - { - IMessage message = DoReceive(destination); - - if (message != null) - { - return DoConvert(message); - } - - return default; - } - - protected abstract Task DoReceiveAsync(TDestination destination, CancellationToken cancellationToken); - - protected abstract IMessage DoReceive(TDestination destination); - - protected virtual T DoConvert(IMessage message) - { - IMessageConverter messageConverter = MessageConverter; - object value = messageConverter.FromMessage(message, typeof(T)); - - if (value == null) - { - throw new MessageConversionException(message, - $"Unable to convert payload [{message.Payload}] to type [{typeof(T)}] using converter [{messageConverter}]"); - } - - return value is Exception exception && ThrowReceivedExceptions ? throw exception : (T)value; - } -} diff --git a/src/Messaging/src/Messaging/Core/AbstractMessageSendingTemplate.cs b/src/Messaging/src/Messaging/Core/AbstractMessageSendingTemplate.cs deleted file mode 100644 index a293185523..0000000000 --- a/src/Messaging/src/Messaging/Core/AbstractMessageSendingTemplate.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Messaging.Core; - -public abstract class AbstractMessageSendingTemplate : IMessageSendingOperations -{ - public const string ConversionHintHeader = "conversionHint"; - - protected virtual TDestination RequiredDefaultSendDestination => - DefaultSendDestination ?? throw new InvalidOperationException("No default destination configured"); - - public virtual TDestination DefaultSendDestination { get; set; } - - public virtual IMessageConverter MessageConverter { get; set; } = new SimpleMessageConverter(); - - public virtual Task ConvertAndSendAsync(object payload, CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(payload, null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(TDestination destination, object payload, CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(destination, payload, (IDictionary)null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(TDestination destination, object payload, IDictionary headers, - CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(destination, payload, headers, null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(object payload, IMessagePostProcessor postProcessor, CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(RequiredDefaultSendDestination, payload, postProcessor, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(TDestination destination, object payload, IMessagePostProcessor postProcessor, - CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(destination, payload, null, postProcessor, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(TDestination destination, object payload, IDictionary headers, IMessagePostProcessor postProcessor, - CancellationToken cancellationToken = default) - { - IMessage message = DoConvert(payload, headers, postProcessor); - return SendAsync(destination, message, cancellationToken); - } - - public virtual Task SendAsync(IMessage message, CancellationToken cancellationToken = default) - { - return SendAsync(RequiredDefaultSendDestination, message, cancellationToken); - } - - public virtual Task SendAsync(TDestination destination, IMessage message, CancellationToken cancellationToken = default) - { - return DoSendAsync(destination, message, cancellationToken); - } - - public virtual void ConvertAndSend(object payload) - { - ConvertAndSend(payload, null); - } - - public virtual void ConvertAndSend(TDestination destination, object payload) - { - ConvertAndSend(destination, payload, (IDictionary)null); - } - - public virtual void ConvertAndSend(TDestination destination, object payload, IDictionary headers) - { - ConvertAndSend(destination, payload, headers, null); - } - - public virtual void ConvertAndSend(object payload, IMessagePostProcessor postProcessor) - { - ConvertAndSend(RequiredDefaultSendDestination, payload, postProcessor); - } - - public virtual void ConvertAndSend(TDestination destination, object payload, IMessagePostProcessor postProcessor) - { - ConvertAndSend(destination, payload, null, postProcessor); - } - - public virtual void ConvertAndSend(TDestination destination, object payload, IDictionary headers, IMessagePostProcessor postProcessor) - { - IMessage message = DoConvert(payload, headers, postProcessor); - Send(destination, message); - } - - public virtual void Send(IMessage message) - { - Send(RequiredDefaultSendDestination, message); - } - - public virtual void Send(TDestination destination, IMessage message) - { - DoSend(destination, message); - } - - protected abstract Task DoSendAsync(TDestination destination, IMessage message, CancellationToken cancellationToken); - - protected abstract void DoSend(TDestination destination, IMessage message); - - protected virtual IMessage DoConvert(object payload, IDictionary headers, IMessagePostProcessor postProcessor) - { - IMessageHeaders messageHeaders = null; - object conversionHint = null; - headers?.TryGetValue(ConversionHintHeader, out conversionHint); - - IDictionary headersToUse = ProcessHeadersToSend(headers); - - if (headersToUse != null) - { - if (headersToUse is MessageHeaders headers1) - { - messageHeaders = headers1; - } - else - { - messageHeaders = new MessageHeaders(headersToUse, null, null); - } - } - - IMessageConverter converter = MessageConverter; - - IMessage message = converter is ISmartMessageConverter smartConverter - ? smartConverter.ToMessage(payload, messageHeaders, conversionHint) - : converter.ToMessage(payload, messageHeaders); - - if (message == null) - { - string payloadType = payload.GetType().Name; - - object contentType = null; - messageHeaders?.TryGetValue(MessageHeaders.ContentType, out contentType); - contentType ??= "unknown"; - - throw new MessageConversionException( - $"Unable to convert payload with type='{payloadType}', contentType='{contentType}', converter=[{MessageConverter}]"); - } - - if (postProcessor != null) - { - message = postProcessor.PostProcessMessage(message); - } - - return message; - } - - protected virtual IDictionary ProcessHeadersToSend(IDictionary headers) - { - return headers; - } -} diff --git a/src/Messaging/src/Messaging/Core/AbstractMessagingTemplate.cs b/src/Messaging/src/Messaging/Core/AbstractMessagingTemplate.cs deleted file mode 100644 index 70d6e46eea..0000000000 --- a/src/Messaging/src/Messaging/Core/AbstractMessagingTemplate.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -public abstract class AbstractMessagingTemplate : AbstractMessageReceivingTemplate, IMessageRequestReplyOperations - where TDestination : class -{ - public virtual Task ConvertSendAndReceiveAsync(object request, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(RequiredDefaultSendDestination, request, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(TDestination destination, object request, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(destination, request, (IDictionary)null, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(TDestination destination, object request, IDictionary headers, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(destination, request, headers, null, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(object request, IMessagePostProcessor requestPostProcessor, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(RequiredDefaultSendDestination, request, requestPostProcessor, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(TDestination destination, object request, IMessagePostProcessor requestPostProcessor, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(destination, request, null, requestPostProcessor, cancellationToken); - } - - public virtual async Task ConvertSendAndReceiveAsync(TDestination destination, object request, IDictionary headers, - IMessagePostProcessor requestPostProcessor, CancellationToken cancellationToken = default) - { - IMessage requestMessage = DoConvert(request, headers, requestPostProcessor); - IMessage replyMessage = await SendAndReceiveAsync(destination, requestMessage, cancellationToken); - - if (replyMessage != null) - { - return DoConvert(replyMessage); - } - - return default; - } - - public virtual Task SendAndReceiveAsync(IMessage requestMessage, CancellationToken cancellationToken = default) - { - return SendAndReceiveAsync(RequiredDefaultSendDestination, requestMessage, cancellationToken); - } - - public virtual Task SendAndReceiveAsync(TDestination destination, IMessage requestMessage, CancellationToken cancellationToken = default) - { - return DoSendAndReceiveAsync(destination, requestMessage, cancellationToken); - } - - public virtual T ConvertSendAndReceive(object request) - { - return ConvertSendAndReceive(RequiredDefaultSendDestination, request); - } - - public virtual T ConvertSendAndReceive(TDestination destination, object request) - { - return ConvertSendAndReceive(destination, request, (IDictionary)null); - } - - public virtual T ConvertSendAndReceive(TDestination destination, object request, IDictionary headers) - { - return ConvertSendAndReceive(destination, request, headers, null); - } - - public virtual T ConvertSendAndReceive(object request, IMessagePostProcessor requestPostProcessor) - { - return ConvertSendAndReceive(RequiredDefaultSendDestination, request, requestPostProcessor); - } - - public virtual T ConvertSendAndReceive(TDestination destination, object request, IMessagePostProcessor requestPostProcessor) - { - return ConvertSendAndReceive(destination, request, null, requestPostProcessor); - } - - public virtual T ConvertSendAndReceive(TDestination destination, object request, IDictionary headers, - IMessagePostProcessor requestPostProcessor) - { - IMessage requestMessage = DoConvert(request, headers, requestPostProcessor); - IMessage replyMessage = SendAndReceive(destination, requestMessage); - - if (replyMessage != null) - { - return DoConvert(replyMessage); - } - - return default; - } - - public virtual IMessage SendAndReceive(IMessage requestMessage) - { - return SendAndReceive(RequiredDefaultSendDestination, requestMessage); - } - - public virtual IMessage SendAndReceive(TDestination destination, IMessage requestMessage) - { - return DoSendAndReceive(destination, requestMessage); - } - - protected abstract IMessage DoSendAndReceive(TDestination destination, IMessage requestMessage); - - protected abstract Task DoSendAndReceiveAsync(TDestination destination, IMessage requestMessage, CancellationToken cancellationToken = default); -} diff --git a/src/Messaging/src/Messaging/Core/CachingDestinationResolverProxy.cs b/src/Messaging/src/Messaging/Core/CachingDestinationResolverProxy.cs deleted file mode 100644 index f3b408a160..0000000000 --- a/src/Messaging/src/Messaging/Core/CachingDestinationResolverProxy.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Steeltoe.Common; - -namespace Steeltoe.Messaging.Core; - -public class CachingDestinationResolverProxy : IDestinationResolver - where TDestination : class -{ - private readonly ConcurrentDictionary _resolvedDestinationCache = new(); - - private readonly IDestinationResolver _targetDestinationResolver; - - public CachingDestinationResolverProxy(IDestinationResolver targetDestinationResolver) - { - ArgumentGuard.NotNull(targetDestinationResolver); - - _targetDestinationResolver = targetDestinationResolver; - } - - public TDestination ResolveDestination(string name) - { - _resolvedDestinationCache.TryGetValue(name, out TDestination destination); - - if (destination == null) - { - destination = _targetDestinationResolver.ResolveDestination(name); - _resolvedDestinationCache.TryAdd(name, destination); - } - - return destination; - } - - object IDestinationResolver.ResolveDestination(string name) - { - TDestination result = ResolveDestination(name); - return result; - } -} diff --git a/src/Messaging/src/Messaging/Core/DefaultMessageChannelDestinationResolver.cs b/src/Messaging/src/Messaging/Core/DefaultMessageChannelDestinationResolver.cs deleted file mode 100644 index c1e4bfab27..0000000000 --- a/src/Messaging/src/Messaging/Core/DefaultMessageChannelDestinationResolver.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Messaging.Core; - -public class DefaultMessageChannelDestinationResolver : IDestinationResolver -{ - public IApplicationContext Context { get; } - - public DefaultMessageChannelDestinationResolver(IApplicationContext context) - { - ArgumentGuard.NotNull(context); - - Context = context; - } - - public virtual IMessageChannel ResolveDestination(string name) - { - return Context.GetService(name); - } - - object IDestinationResolver.ResolveDestination(string name) - { - IMessageChannel result = ResolveDestination(name); - return result; - } -} diff --git a/src/Messaging/src/Messaging/Core/DestinationResolutionException.cs b/src/Messaging/src/Messaging/Core/DestinationResolutionException.cs deleted file mode 100644 index 86d5ecafe7..0000000000 --- a/src/Messaging/src/Messaging/Core/DestinationResolutionException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Core; - -public class DestinationResolutionException : MessagingException -{ - public DestinationResolutionException(string message) - : base(message) - { - } - - public DestinationResolutionException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/Messaging/Core/MessageChannelTemplate.cs b/src/Messaging/src/Messaging/Core/MessageChannelTemplate.cs deleted file mode 100644 index f4687e03f1..0000000000 --- a/src/Messaging/src/Messaging/Core/MessageChannelTemplate.cs +++ /dev/null @@ -1,462 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.Core; - -public class MessageChannelTemplate : AbstractDestinationResolvingMessagingTemplate -{ - public const string DefaultSendTimeoutHeader = "sendTimeout"; - - public const string DefaultReceiveTimeoutHeader = "receiveTimeout"; - - private readonly ILogger _logger; - - private volatile int _sendTimeout = -1; - - private volatile int _receiveTimeout = -1; - - private string _sendTimeoutHeader = DefaultSendTimeoutHeader; - - private string _receiveTimeoutHeader = DefaultReceiveTimeoutHeader; - - private volatile bool _throwExceptionOnLateReply; - - public virtual int SendTimeout - { - get => _sendTimeout; - set => _sendTimeout = value; - } - - public virtual int ReceiveTimeout - { - get => _receiveTimeout; - set => _receiveTimeout = value; - } - - public virtual string SendTimeoutHeader - { - get => _sendTimeoutHeader; - set - { - ArgumentGuard.NotNull(value); - - _sendTimeoutHeader = value; - } - } - - public virtual string ReceiveTimeoutHeader - { - get => _receiveTimeoutHeader; - set - { - ArgumentGuard.NotNull(value); - - _receiveTimeoutHeader = value; - } - } - - public virtual bool ThrowExceptionOnLateReply - { - get => _throwExceptionOnLateReply; - set => _throwExceptionOnLateReply = value; - } - - public MessageChannelTemplate(ILogger logger = null) - : base(null) - { - _logger = logger; - } - - public MessageChannelTemplate(IApplicationContext context, ILogger logger = null) - : base(context) - { - _logger = logger; - } - - protected virtual IMessage ProcessMessageBeforeSend(IMessage message) - { - IMessage messageToSend = message; - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetAccessor(message, typeof(MessageHeaderAccessor)); - - if (accessor != null && accessor.IsMutable) - { - accessor.RemoveHeader(_sendTimeoutHeader); - accessor.RemoveHeader(_receiveTimeoutHeader); - accessor.SetImmutable(); - } - else if (message.Headers.ContainsKey(_sendTimeoutHeader) || message.Headers.ContainsKey(_receiveTimeoutHeader)) - { - messageToSend = MessageBuilder.FromMessage(message).SetHeader(_sendTimeoutHeader, null).SetHeader(_receiveTimeoutHeader, null).Build(); - } - - return messageToSend; - } - - protected override void DoSend(IMessageChannel destination, IMessage message) - { - DoSend(destination, message, GetSendTimeout(message)); - } - - protected void DoSend(IMessageChannel channel, IMessage message, int timeout) - { - ArgumentGuard.NotNull(channel); - - IMessage messageToSend = ProcessMessageBeforeSend(message); - bool sent = channel.Send(messageToSend, timeout); - - if (!sent) - { - throw new MessageDeliveryException(message, $"Failed to send message to channel '{channel}"); - } - } - - protected override Task DoSendAsync(IMessageChannel destination, IMessage message, CancellationToken cancellationToken) - { - return DoSendAsync(destination, message, GetSendTimeout(message), cancellationToken); - } - - protected Task DoSendAsync(IMessageChannel channel, IMessage message, int timeout, CancellationToken cancellationToken = default) - { - ArgumentGuard.NotNull(channel); - - return DoSendInternalAsync(channel, message, timeout, cancellationToken); - } - - protected async Task DoSendInternalAsync(IMessageChannel channel, IMessage message, int timeout, CancellationToken cancellationToken = default) - { - IMessage messageToSend = ProcessMessageBeforeSend(message); - bool sent = false; - - if (cancellationToken == default) - { - using var source = new CancellationTokenSource(); - source.CancelAfter(timeout); - sent = await channel.SendAsync(messageToSend, source.Token); - } - else - { - sent = await channel.SendAsync(messageToSend, cancellationToken); - } - - if (!sent) - { - throw new MessageDeliveryException(message, $"Failed to send message to channel '{channel}"); - } - } - - protected override IMessage DoReceive(IMessageChannel destination) - { - return DoReceive(destination, ReceiveTimeout); - } - - protected IMessage DoReceive(IMessageChannel channel, int timeout) - { - ArgumentGuard.NotNull(channel); - - if (channel is not IPollableChannel pollableChannel) - { - throw new InvalidOperationException("A PollableChannel is required to receive messages"); - } - - IMessage message = pollableChannel.Receive(timeout); - - if (message == null) - { - _logger?.LogTrace("Failed to receive message from channel '{channel}'", channel); - } - - return message; - } - - protected override Task DoReceiveAsync(IMessageChannel destination, CancellationToken cancellationToken) - { - return DoReceiveAsync(destination, ReceiveTimeout, cancellationToken); - } - - protected Task DoReceiveAsync(IMessageChannel channel, int timeout, CancellationToken cancellationToken = default) - { - ArgumentGuard.NotNull(channel); - - if (channel is not IPollableChannel) - { - throw new InvalidOperationException("A PollableChannel is required to receive messages"); - } - - return DoReceiveInternalAsync(channel, timeout, cancellationToken); - } - - protected async Task DoReceiveInternalAsync(IMessageChannel channel, int timeout, CancellationToken cancellationToken = default) - { - IMessage message = null; - - if (cancellationToken == default) - { - using var source = new CancellationTokenSource(); - source.CancelAfter(timeout); - message = await ((IPollableChannel)channel).ReceiveAsync(source.Token); - } - else - { - message = await ((IPollableChannel)channel).ReceiveAsync(cancellationToken); - } - - if (message == null) - { - _logger?.LogTrace("Failed to receive message from channel '{channel}'", channel); - } - - return message; - } - - protected override Task DoSendAndReceiveAsync(IMessageChannel destination, IMessage requestMessage, CancellationToken cancellationToken = default) - { - ArgumentGuard.NotNull(destination); - - return DoSendAndReceiveInternalAsync(destination, requestMessage, cancellationToken); - } - - protected async Task DoSendAndReceiveInternalAsync(IMessageChannel channel, IMessage requestMessage, - CancellationToken cancellationToken = default) - { - object originalReplyChannelHeader = requestMessage.Headers.ReplyChannel; - object originalErrorChannelHeader = requestMessage.Headers.ErrorChannel; - - int sendTimeout = GetSendTimeout(requestMessage); - int receiveTimeout = GetReceiveTimeout(requestMessage); - - var tempReplyChannel = new TemporaryReplyChannel(_throwExceptionOnLateReply); - - requestMessage = MessageBuilder.FromMessage(requestMessage).SetReplyChannel(tempReplyChannel).SetHeader(_sendTimeoutHeader, null) - .SetHeader(_receiveTimeoutHeader, null).SetErrorChannel(tempReplyChannel).Build(); - - try - { - await DoSendAsync(channel, requestMessage, sendTimeout, cancellationToken); - } - catch (Exception) - { - tempReplyChannel.SendFailed = true; - throw; - } - - IMessage replyMessage = await DoReceiveAsync(tempReplyChannel, receiveTimeout, cancellationToken); - - if (replyMessage != null) - { - replyMessage = MessageBuilder.FromMessage(replyMessage).SetHeader(MessageHeaders.ReplyChannelName, originalReplyChannelHeader) - .SetHeader(MessageHeaders.ErrorChannelName, originalErrorChannelHeader).Build(); - } - - return replyMessage; - } - - protected override IMessage DoSendAndReceive(IMessageChannel destination, IMessage requestMessage) - { - ArgumentGuard.NotNull(destination); - - object originalReplyChannelHeader = requestMessage.Headers.ReplyChannel; - object originalErrorChannelHeader = requestMessage.Headers.ErrorChannel; - - int sendTimeout = GetSendTimeout(requestMessage); - int receiveTimeout = GetReceiveTimeout(requestMessage); - - var tempReplyChannel = new TemporaryReplyChannel(_throwExceptionOnLateReply); - - requestMessage = MessageBuilder.FromMessage(requestMessage).SetReplyChannel(tempReplyChannel).SetHeader(_sendTimeoutHeader, null) - .SetHeader(_receiveTimeoutHeader, null).SetErrorChannel(tempReplyChannel).Build(); - - try - { - DoSend(destination, requestMessage, sendTimeout); - } - catch (Exception) - { - tempReplyChannel.SendFailed = true; - throw; - } - - IMessage replyMessage = DoReceive(tempReplyChannel, receiveTimeout); - - if (replyMessage != null) - { - replyMessage = MessageBuilder.FromMessage(replyMessage).SetHeader(MessageHeaders.ReplyChannelName, originalReplyChannelHeader) - .SetHeader(MessageHeaders.ErrorChannelName, originalErrorChannelHeader).Build(); - } - - return replyMessage; - } - - private static int? HeaderToInt(object headerValue) - { - if (headerValue is int intValue) - { - return intValue; - } - - if (headerValue is string stringValue) - { - return int.Parse(stringValue, CultureInfo.InvariantCulture); - } - - return null; - } - - private int GetSendTimeout(IMessage requestMessage) - { - if (requestMessage.Headers.TryGetValue(_sendTimeoutHeader, out object headerValue)) - { - int? result = HeaderToInt(headerValue); - - if (result.HasValue) - { - return result.Value; - } - } - - return _sendTimeout; - } - - private int GetReceiveTimeout(IMessage requestMessage) - { - if (requestMessage.Headers.TryGetValue(_receiveTimeoutHeader, out object headerValue)) - { - int? result = HeaderToInt(headerValue); - - if (result.HasValue) - { - return result.Value; - } - } - - return _receiveTimeout; - } - - private sealed class TemporaryReplyChannel : IPollableChannel - { - private readonly CountdownEvent _replyLatch = new(1); - - private readonly bool _throwExceptionOnLateReply; - - private volatile IMessage _replyMessage; - - public bool SendFailed { get; set; } - - public bool TimedOut { get; set; } - - public bool HasReceived { get; set; } - - public string ServiceName { get; set; } = "TempReplyChannel"; - - public TemporaryReplyChannel(bool throwExceptionOnLateReply) - { - _throwExceptionOnLateReply = throwExceptionOnLateReply; - } - - public IMessage Receive() - { - return Receive(-1); - } - - public IMessage Receive(int timeout) - { - try - { - if (timeout < 0) - { - _replyLatch.Wait(); - HasReceived = true; - } - else - { - if (_replyLatch.Wait(timeout)) - { - HasReceived = true; - } - else - { - TimedOut = true; - } - } - } - catch (Exception) - { - // Log - } - - return !TimedOut ? _replyMessage : null; - } - - public ValueTask ReceiveAsync(CancellationToken cancellationToken = default) - { - try - { - _replyLatch.Wait(cancellationToken); - - if (cancellationToken.IsCancellationRequested) - { - TimedOut = true; - } - else - { - HasReceived = true; - } - } - catch (Exception) - { - TimedOut = true; - } - - return TimedOut ? new ValueTask((IMessage)null) : new ValueTask(_replyMessage); - } - - public bool Send(IMessage message) - { - return Send(message, -1); - } - - public bool Send(IMessage message, int timeout) - { - bool alreadyReceivedReply = HasReceived; - - string errorDescription = null; - - if (TimedOut) - { - errorDescription = "Reply message received but the receiving thread has exited due to a timeout"; - } - else if (alreadyReceivedReply) - { - errorDescription = "Reply message received but the receiving thread has already received a reply"; - } - else if (SendFailed) - { - errorDescription = "Reply message received but the receiving thread has exited due to " + "an exception while sending the request message"; - } - else - { - _replyMessage = message; - } - - _replyLatch.Signal(); - - if (errorDescription != null && _throwExceptionOnLateReply) - { - throw new MessageDeliveryException(message, errorDescription); - } - - return true; - } - - public ValueTask SendAsync(IMessage message, CancellationToken cancellationToken = default) - { - return new ValueTask(Send(message, -1)); - } - } -} diff --git a/src/Messaging/src/Messaging/Handler/AbstractMessageCondition.cs b/src/Messaging/src/Messaging/Handler/AbstractMessageCondition.cs deleted file mode 100644 index 2d5029bb05..0000000000 --- a/src/Messaging/src/Messaging/Handler/AbstractMessageCondition.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Text; - -namespace Steeltoe.Messaging.Handler; - -public abstract class AbstractMessageCondition : IMessageCondition -{ - public abstract T Combine(T other); - - public abstract int CompareTo(T other, IMessage message); - - public abstract T GetMatchingCondition(IMessage message); - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not AbstractMessageCondition other || GetType() != obj.GetType()) - { - return false; - } - - IList thisContent = GetContent(); - IList otherContent = other.GetContent(); - - if (thisContent.Count != otherContent.Count) - { - return false; - } - - for (int i = 0; i < thisContent.Count; i++) - { - if (!Equals(thisContent[i], otherContent[i])) - { - return false; - } - } - - return true; - } - - public override int GetHashCode() - { - return GetContent().GetHashCode(); - } - - public override string ToString() - { - string infix = GetToStringInfix(); - - var joiner = new StringBuilder("["); - - foreach (object expression in GetContent()) - { - joiner.Append(expression + infix); - } - - return $"{joiner.ToString(0, joiner.Length - 1)}]"; - } - - protected abstract IList GetContent(); - - protected abstract string GetToStringInfix(); -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/AbstractNamedValueMethodArgumentResolver.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/AbstractNamedValueMethodArgumentResolver.cs deleted file mode 100644 index 668921530a..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/AbstractNamedValueMethodArgumentResolver.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public abstract class AbstractNamedValueMethodArgumentResolver : IHandlerMethodArgumentResolver -{ - private readonly IConversionService _conversionService; - private readonly IApplicationContext _applicationContext; - private readonly ServiceExpressionContext _expressionContext; - private readonly ConcurrentDictionary _namedValueInfoCache = new(); - - protected AbstractNamedValueMethodArgumentResolver(IConversionService conversionService, IApplicationContext context) - { - _conversionService = conversionService; - _applicationContext = context; - _expressionContext = context != null ? new ServiceExpressionContext(context) : null; - } - - public virtual object ResolveArgument(ParameterInfo parameter, IMessage message) - { - NamedValueInfo namedValueInfo = GetNamedValueInfo(parameter); - - object resolvedName = ResolveEmbeddedValuesAndExpressions(namedValueInfo.Name); - - if (resolvedName == null) - { - throw new InvalidOperationException($"Specified name must not resolve to null: [{namedValueInfo.Name}]"); - } - - object arg = ResolveArgumentInternal(parameter, message, resolvedName.ToString()); - - if (arg == null) - { - if (namedValueInfo.DefaultValue != null) - { - arg = ResolveEmbeddedValuesAndExpressions(namedValueInfo.DefaultValue); - } - else if (namedValueInfo.Required && !parameter.HasDefaultValue) - { - HandleMissingValue(namedValueInfo.Name, parameter, message); - } - - arg = HandleNullValue(namedValueInfo.Name, arg, parameter.ParameterType); - } - else if (string.Empty.Equals(arg) && namedValueInfo.DefaultValue != null) - { - arg = ResolveEmbeddedValuesAndExpressions(namedValueInfo.DefaultValue); - } - - if (!parameter.ParameterType.IsInstanceOfType(arg)) - { - arg = _conversionService.Convert(arg, arg?.GetType(), parameter.ParameterType); - } - - HandleResolvedValue(arg, namedValueInfo.Name, parameter, message); - - return arg; - } - - public virtual bool SupportsParameter(ParameterInfo parameter) - { - throw new NotImplementedException(); - } - - protected abstract NamedValueInfo CreateNamedValueInfo(ParameterInfo parameter); - - protected abstract object ResolveArgumentInternal(ParameterInfo parameter, IMessage message, string name); - - protected abstract void HandleMissingValue(string name, ParameterInfo parameter, IMessage message); - - protected virtual void HandleResolvedValue(object arg, string name, ParameterInfo parameter, IMessage message) - { - } - - private NamedValueInfo GetNamedValueInfo(ParameterInfo parameter) - { - if (!_namedValueInfoCache.TryGetValue(parameter, out NamedValueInfo namedValueInfo)) - { - namedValueInfo = CreateNamedValueInfo(parameter); - namedValueInfo = UpdateNamedValueInfo(parameter, namedValueInfo); - - if (!_namedValueInfoCache.TryAdd(parameter, namedValueInfo)) - { - _namedValueInfoCache.TryGetValue(parameter, out namedValueInfo); - } - } - - return namedValueInfo; - } - - private NamedValueInfo UpdateNamedValueInfo(ParameterInfo parameter, NamedValueInfo info) - { - string name = info.Name; - - if (string.IsNullOrEmpty(info.Name)) - { - name = parameter.Name; - - if (name == null) - { - Type type = parameter.ParameterType; - - throw new InvalidOperationException( - $"Name for argument of type [{type.Name}] not specified, and parameter name information not found in class file either."); - } - } - - return new NamedValueInfo(name, info.Required, info.DefaultValue); - } - - private object HandleNullValue(string name, object value, Type paramType) - { - if (value == null) - { - if (typeof(bool) == paramType) - { - return false; - } - - if (paramType.IsPrimitive) - { - throw new InvalidOperationException( - $"Optional {paramType} parameter '{name}' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type."); - } - } - - return value; - } - - private object ResolveEmbeddedValuesAndExpressions(string value) - { - if (_applicationContext == null || _expressionContext == null) - { - return value; - } - - string placeholdersResolved = _applicationContext.ResolveEmbeddedValue(value); - IServiceExpressionResolver exprResolver = _applicationContext.ServiceExpressionResolver; - - if (exprResolver == null) - { - return value; - } - - return exprResolver.Evaluate(placeholdersResolved, _expressionContext); - } - - protected class NamedValueInfo - { - public readonly string Name; - - public readonly bool Required; - - public readonly string DefaultValue; - - public NamedValueInfo(string name, bool required, string defaultValue = null) - { - Name = name; - Required = required; - DefaultValue = defaultValue; - } - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/AttributeExceptionHandlerMethodResolver.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/AttributeExceptionHandlerMethodResolver.cs deleted file mode 100644 index ec8778dbf0..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/AttributeExceptionHandlerMethodResolver.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class AttributeExceptionHandlerMethodResolver : AbstractExceptionHandlerMethodResolver -{ - public AttributeExceptionHandlerMethodResolver(Type handlerType) - : base(InitExceptionMappings(handlerType)) - { - } - - private static Dictionary InitExceptionMappings(Type handlerType) - { - var methods = new Dictionary(); - MethodInfo[] targets = handlerType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); - - foreach (MethodInfo method in targets) - { - var attribute = method.GetCustomAttribute(); - - if (attribute != null) - { - methods.Add(method, attribute); - } - } - - var result = new Dictionary(); - - foreach (KeyValuePair entry in methods) - { - MethodInfo method = entry.Key; - var exceptionTypes = new List(entry.Value.Exceptions); - - if (exceptionTypes.Count == 0) - { - exceptionTypes.AddRange(GetExceptionsFromMethodSignature(method)); - } - - foreach (Type exceptionType in exceptionTypes) - { - result.TryGetValue(exceptionType, out MethodInfo oldMethod); - result[exceptionType] = method; - - if (oldMethod != null && !oldMethod.Equals(method)) - { - throw new InvalidOperationException($"Ambiguous @ExceptionHandler method mapped for [{exceptionType}]: {{{oldMethod}, {method}}}"); - } - } - } - - return result; - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/DefaultMessageHandlerMethodFactory.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/DefaultMessageHandlerMethodFactory.cs deleted file mode 100644 index 843a803b9c..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/DefaultMessageHandlerMethodFactory.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class DefaultMessageHandlerMethodFactory : IMessageHandlerMethodFactory -{ - public const string DefaultServiceName = nameof(DefaultMessageHandlerMethodFactory); - - protected readonly HandlerMethodArgumentResolverComposite ArgumentResolvers = new(); - - public virtual string ServiceName { get; set; } = DefaultServiceName; - - public virtual IConversionService ConversionService { get; set; } - - public virtual IMessageConverter MessageConverter { get; set; } - - public virtual List CustomArgumentResolvers { get; set; } - - public virtual IApplicationContext ApplicationContext { get; set; } - - public DefaultMessageHandlerMethodFactory(IApplicationContext context = null) - : this(null, null, null, context) - { - } - - public DefaultMessageHandlerMethodFactory(IConversionService conversionService, IApplicationContext context = null) - : this(conversionService, null, null, context) - { - ConversionService = conversionService; - } - - public DefaultMessageHandlerMethodFactory(IConversionService conversionService, IMessageConverter converter, IApplicationContext context = null) - : this(conversionService, converter, null, context) - { - ConversionService = conversionService; - MessageConverter = converter; - } - - public DefaultMessageHandlerMethodFactory(IConversionService conversionService, IMessageConverter converter, List resolvers, - IApplicationContext context = null) - { - ConversionService = conversionService; - MessageConverter = converter; - CustomArgumentResolvers = resolvers; - - ConversionService ??= new GenericConversionService(); - - MessageConverter ??= new GenericMessageConverter(ConversionService); - - if (ArgumentResolvers.Resolvers.Count == 0) - { - ArgumentResolvers.AddResolvers(InitArgumentResolvers()); - } - - ApplicationContext = context; - } - - public virtual void SetArgumentResolvers(List argumentResolvers) - { - if (argumentResolvers == null) - { - ArgumentResolvers.Clear(); - return; - } - - if (argumentResolvers.Count > 0) - { - ArgumentResolvers.Clear(); - } - - ArgumentResolvers.AddResolvers(argumentResolvers); - } - - public virtual IInvocableHandlerMethod CreateInvocableHandlerMethod(object instance, MethodInfo method) - { - var handlerMethod = new InvocableHandlerMethod(instance, method) - { - MessageMethodArgumentResolvers = ArgumentResolvers - }; - - return handlerMethod; - } - - public virtual void Initialize() - { - ArgumentResolvers.Clear(); - - ConversionService ??= new GenericConversionService(); - - MessageConverter ??= new GenericMessageConverter(ConversionService); - - if (ArgumentResolvers.Resolvers.Count == 0) - { - ArgumentResolvers.AddResolvers(InitArgumentResolvers()); - } - } - - protected List InitArgumentResolvers() - { - var resolvers = new List - { - // Annotation-based argument resolution - new HeaderMethodArgumentResolver(ConversionService, ApplicationContext), - new HeadersMethodArgumentResolver(), - - // Type-based argument resolution - new MessageMethodArgumentResolver(MessageConverter) - }; - - if (CustomArgumentResolvers != null) - { - resolvers.AddRange(CustomArgumentResolvers); - } - - resolvers.Add(new PayloadMethodArgumentResolver(MessageConverter)); - - return resolvers; - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/DestinationVariableMethodArgumentResolver.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/DestinationVariableMethodArgumentResolver.cs deleted file mode 100644 index 39d1cc3541..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/DestinationVariableMethodArgumentResolver.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Converter; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class DestinationVariableMethodArgumentResolver : AbstractNamedValueMethodArgumentResolver -{ - public const string DestinationTemplateVariablesHeader = $"{nameof(DestinationVariableMethodArgumentResolver)}.templateVariables"; - - public DestinationVariableMethodArgumentResolver(IConversionService conversionService) - : base(conversionService, null) - { - } - - public override bool SupportsParameter(ParameterInfo parameter) - { - return parameter.GetCustomAttribute() != null; - } - - protected override NamedValueInfo CreateNamedValueInfo(ParameterInfo parameter) - { - var annotation = parameter.GetCustomAttribute(); - - if (annotation == null) - { - throw new InvalidOperationException("No DestinationVariable annotation"); - } - - return new DestinationVariableNamedValueInfo(annotation); - } - - protected override object ResolveArgumentInternal(ParameterInfo parameter, IMessage message, string name) - { - IMessageHeaders headers = message.Headers; - headers.TryGetValue(DestinationTemplateVariablesHeader, out object obj); - object result = null; - - if (obj is IDictionary vars) - { - vars.TryGetValue(name, out result); - } - - return result; - } - - protected override void HandleMissingValue(string name, ParameterInfo parameter, IMessage message) - { - throw new MessageHandlingException(message, $"Missing path template variable '{name}' for method parameter type [{parameter.ParameterType}]"); - } - - protected class DestinationVariableNamedValueInfo : NamedValueInfo - { - public DestinationVariableNamedValueInfo(DestinationVariableAttribute annotation) - : base(annotation.Name, true) - { - } - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/HeaderMethodArgumentResolver.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/HeaderMethodArgumentResolver.cs deleted file mode 100644 index acb272c795..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/HeaderMethodArgumentResolver.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class HeaderMethodArgumentResolver : AbstractNamedValueMethodArgumentResolver -{ - public HeaderMethodArgumentResolver(IConversionService conversionService, IApplicationContext context = null) - : base(conversionService, context) - { - } - - public override bool SupportsParameter(ParameterInfo parameter) - { - return parameter.GetCustomAttribute() != null; - } - - protected override NamedValueInfo CreateNamedValueInfo(ParameterInfo parameter) - { - var annotation = parameter.GetCustomAttribute(); - - if (annotation == null) - { - throw new InvalidOperationException("No Header annotation"); - } - - return new HeaderNamedValueInfo(annotation); - } - - protected override object ResolveArgumentInternal(ParameterInfo parameter, IMessage message, string name) - { - message.Headers.TryGetValue(name, out object headerValue); - object nativeHeaderValue = GetNativeHeaderValue(message, name); - - return headerValue ?? nativeHeaderValue; - } - - protected override void HandleMissingValue(string name, ParameterInfo parameter, IMessage message) - { - throw new MessageHandlingException(message, $"Missing header '{name}' for method parameter type [{parameter.ParameterType}]"); - } - - private object GetNativeHeaderValue(IMessage message, string name) - { - IDictionary> nativeHeaders = GetNativeHeaders(message); - - if (name.StartsWith("nativeHeaders.", StringComparison.Ordinal)) - { - name = name.Substring("nativeHeaders.".Length); - } - - if (nativeHeaders == null || !nativeHeaders.ContainsKey(name)) - { - return null; - } - - nativeHeaders.TryGetValue(name, out List nativeHeaderValues); - - if (nativeHeaderValues.Count == 1) - { - return nativeHeaderValues[0]; - } - - return nativeHeaderValues; - } - - private IDictionary> GetNativeHeaders(IMessage message) - { - message.Headers.TryGetValue(NativeMessageHeaderAccessor.NativeHeaders, out object result); - return (IDictionary>)result; - } - - private sealed class HeaderNamedValueInfo : NamedValueInfo - { - public HeaderNamedValueInfo(HeaderAttribute annotation) - : base(annotation.Name, annotation.Required, annotation.DefaultValue) - { - } - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/HeadersMethodArgumentResolver.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/HeadersMethodArgumentResolver.cs deleted file mode 100644 index 1eeafc0b26..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/HeadersMethodArgumentResolver.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Reflection; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class HeadersMethodArgumentResolver : IHandlerMethodArgumentResolver -{ - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - Type paramType = parameter.ParameterType; - - if (typeof(IDictionary).IsAssignableFrom(paramType)) - { - return message.Headers; - } - - if (typeof(MessageHeaderAccessor) == paramType) - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetAccessor(message, typeof(MessageHeaderAccessor)); - return accessor ?? new MessageHeaderAccessor(message); - } - - if (typeof(MessageHeaderAccessor).IsAssignableFrom(paramType)) - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetAccessor(message, typeof(MessageHeaderAccessor)); - - if (accessor != null && paramType.IsInstanceOfType(accessor)) - { - return accessor; - } - - MethodInfo method = ReflectionHelpers.FindMethod(paramType, "Wrap", new[] - { - typeof(IMessage) - }); - - if (method == null) - { - throw new InvalidOperationException($"Cannot create accessor of type {paramType} for message {message}"); - } - - return ReflectionHelpers.Invoke(method, null, new object[] - { - message - }); - } - - throw new InvalidOperationException($"Unexpected parameter of type {paramType} in method {parameter.Member}. "); - } - - public bool SupportsParameter(ParameterInfo parameter) - { - Type paramType = parameter.ParameterType; - - return (parameter.GetCustomAttribute() != null && typeof(IDictionary).IsAssignableFrom(paramType)) || - typeof(IMessageHeaders).IsAssignableFrom(paramType) || typeof(IMessageHeaderAccessor).IsAssignableFrom(paramType); - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/MessageMethodArgumentResolver.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/MessageMethodArgumentResolver.cs deleted file mode 100644 index c20fd8c512..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/MessageMethodArgumentResolver.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class MessageMethodArgumentResolver : IHandlerMethodArgumentResolver -{ - protected readonly IMessageConverter Converter; - - public MessageMethodArgumentResolver() - : this(null) - { - } - - public MessageMethodArgumentResolver(IMessageConverter converter) - { - Converter = converter; - } - - public virtual object ResolveArgument(ParameterInfo parameter, IMessage message) - { - Type targetPayloadType = GetPayloadType(parameter, message); - - object payload = message.Payload; - - if (targetPayloadType.IsInstanceOfType(payload)) - { - return message; - } - - if (IsEmptyPayload(payload)) - { - string payloadType = payload != null ? payload.GetType().ToString() : "null"; - - throw new MessageConversionException(message, - $"Cannot convert from actual payload type '{payloadType}' to expected payload type '{targetPayloadType}' when payload is empty"); - } - - payload = ConvertPayload(message, parameter, targetPayloadType); - return MessageBuilder.CreateMessage(payload, message.Headers, targetPayloadType); - } - - public virtual bool SupportsParameter(ParameterInfo parameter) - { - return typeof(IMessage).IsAssignableFrom(parameter.ParameterType); - } - - protected virtual Type GetPayloadType(ParameterInfo parameter, IMessage message) - { - if (parameter.ParameterType.IsGenericType) - { - if (parameter.ParameterType.IsConstructedGenericType) - { - return parameter.ParameterType.GenericTypeArguments[0]; - } - - var method = parameter.Member as MethodInfo; - Type[] methodArgs = null; - Type[] typeArgs = null; - - if (method.IsGenericMethod) - { - methodArgs = method.GetGenericArguments(); - } - - Type type = method.DeclaringType; - - if (type.IsGenericType) - { - typeArgs = type.GetGenericArguments(); - } - - return type.Module.ResolveType(parameter.ParameterType.MetadataToken, typeArgs, methodArgs); - } - - return typeof(object); - } - - protected virtual bool IsEmptyPayload(object payload) - { - return payload switch - { - null => true, - byte[] bytes => bytes.Length == 0, - string sPayload => string.IsNullOrEmpty(sPayload), - _ => false - }; - } - - private object ConvertPayload(IMessage message, ParameterInfo parameter, Type targetPayloadType) - { - object result = null; - - if (Converter is ISmartMessageConverter smartConverter) - { - result = smartConverter.FromMessage(message, targetPayloadType, parameter); - } - else if (Converter != null) - { - result = Converter.FromMessage(message, targetPayloadType); - } - - if (result == null) - { - string payloadType = message.Payload != null ? message.Payload.GetType().ToString() : "null"; - - throw new MessageConversionException(message, - $"No converter found from actual payload type '{payloadType}' to expected payload type '{targetPayloadType}'"); - } - - return result; - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/MethodArgumentNotValidException.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/MethodArgumentNotValidException.cs deleted file mode 100644 index 2ed3437012..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/MethodArgumentNotValidException.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class MethodArgumentNotValidException : MethodArgumentResolutionException -{ - public MethodArgumentNotValidException(IMessage failedMessage, ParameterInfo parameter) - : base(failedMessage, parameter) - { - } - - public MethodArgumentNotValidException(IMessage failedMessage, ParameterInfo parameter, string message) - : base(failedMessage, parameter, message) - { - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/MethodArgumentTypeMismatchException.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/MethodArgumentTypeMismatchException.cs deleted file mode 100644 index ca92bb984d..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/MethodArgumentTypeMismatchException.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class MethodArgumentTypeMismatchException : MethodArgumentResolutionException -{ - public MethodArgumentTypeMismatchException(IMessage failedMessage, ParameterInfo parameter, string message) - : base(failedMessage, parameter, message) - { - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/PayloadArgumentResolver.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/PayloadArgumentResolver.cs deleted file mode 100644 index 612376e11c..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/PayloadArgumentResolver.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class PayloadArgumentResolver : IHandlerMethodArgumentResolver -{ - private readonly IMessageConverter _converter; - private readonly bool _useDefaultResolution; - - public PayloadArgumentResolver(IMessageConverter messageConverter) - : this(messageConverter, true) - { - } - - public PayloadArgumentResolver(IMessageConverter messageConverter, bool useDefaultResolution) - { - ArgumentGuard.NotNull(messageConverter); - - _converter = messageConverter; - _useDefaultResolution = useDefaultResolution; - } - - public bool SupportsParameter(ParameterInfo parameter) - { - return parameter.GetCustomAttribute() != null || _useDefaultResolution; - } - - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - var ann = parameter.GetCustomAttribute(); - - if (ann != null && !string.IsNullOrEmpty(ann.Expression)) - { - throw new InvalidOperationException("Payload Attribute expressions not supported by this resolver"); - } - - object payload = message.Payload; - - if (IsEmptyPayload(payload)) - { - if (ann == null || ann.Required) - { - throw new MethodArgumentNotValidException(message, parameter, "Payload value must not be empty"); - } - - return null; - } - - Type targetClass = parameter.ParameterType; - Type payloadClass = payload.GetType(); - - if (targetClass.IsAssignableFrom(payloadClass)) - { - return payload; - } - - if (_converter is ISmartMessageConverter smartConverter) - { - payload = smartConverter.FromMessage(message, targetClass, parameter); - } - else - { - payload = _converter.FromMessage(message, targetClass); - } - - if (payload == null) - { - throw new MessageConversionException(message, $"Cannot convert from [{payloadClass.Name}] to [{targetClass.Name}] for {message}"); - } - - return payload; - } - - protected virtual bool IsEmptyPayload(object payload) - { - return payload switch - { - null => true, - byte[] bytes => bytes.Length == 0, - string sPayload => string.IsNullOrEmpty(sPayload), - _ => false - }; - } -} diff --git a/src/Messaging/src/Messaging/Handler/Attributes/Support/PayloadMethodArgumentResolver.cs b/src/Messaging/src/Messaging/Handler/Attributes/Support/PayloadMethodArgumentResolver.cs deleted file mode 100644 index fa1e9f1009..0000000000 --- a/src/Messaging/src/Messaging/Handler/Attributes/Support/PayloadMethodArgumentResolver.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Handler.Attributes.Support; - -public class PayloadMethodArgumentResolver : IHandlerMethodArgumentResolver -{ - protected readonly IMessageConverter Converter; - protected readonly bool UseDefaultResolution; - - public PayloadMethodArgumentResolver(IMessageConverter messageConverter) - : this(messageConverter, true) - { - } - - public PayloadMethodArgumentResolver(IMessageConverter messageConverter, bool useDefaultResolution) - { - ArgumentGuard.NotNull(messageConverter); - - Converter = messageConverter; - UseDefaultResolution = useDefaultResolution; - } - - public virtual bool SupportsParameter(ParameterInfo parameter) - { - return parameter.GetCustomAttribute() != null || UseDefaultResolution; - } - - public virtual object ResolveArgument(ParameterInfo parameter, IMessage message) - { - var ann = parameter.GetCustomAttribute(); - - if (ann != null && !string.IsNullOrEmpty(ann.Expression)) - { - throw new InvalidOperationException("Payload expressions not supported by this resolver"); - } - - object payload = message.Payload; - - if (IsEmptyPayload(payload)) - { - if (ann == null || ann.Required) - { - string paramName = GetParameterName(parameter); - throw new MethodArgumentNotValidException(message, parameter, $"Payload value must not be empty when binding to: {paramName}"); - } - - return null; - } - - Type targetClass = ResolveTargetClass(parameter, message); - Type payloadClass = payload.GetType(); - - if (targetClass.IsAssignableFrom(payloadClass)) - { - Validate(message, parameter, payload); - return payload; - } - - if (Converter is ISmartMessageConverter smartConverter) - { - payload = smartConverter.FromMessage(message, targetClass, parameter); - } - else - { - payload = Converter.FromMessage(message, targetClass); - } - - if (payload == null) - { - throw new MessageConversionException(message, $"Cannot convert from [{payloadClass.Name}] to [{targetClass.Name}] for {message}"); - } - - Validate(message, parameter, payload); - - return payload; - } - - protected virtual Type ResolveTargetClass(ParameterInfo parameter, IMessage message) - { - return parameter.ParameterType; - } - - protected virtual bool IsEmptyPayload(object payload) - { - return payload switch - { - null => true, - byte[] bytes => bytes.Length == 0, - string sPayload => string.IsNullOrEmpty(sPayload), - _ => false - }; - } - - protected virtual void Validate(IMessage message, ParameterInfo parameter, object target) - { - } - - private string GetParameterName(ParameterInfo param) - { - return param.Name ?? $"Arg {param.Position}"; - } -} diff --git a/src/Messaging/src/Messaging/Handler/DestinationPatternsMessageCondition.cs b/src/Messaging/src/Messaging/Handler/DestinationPatternsMessageCondition.cs deleted file mode 100644 index 7024ec51c3..0000000000 --- a/src/Messaging/src/Messaging/Handler/DestinationPatternsMessageCondition.cs +++ /dev/null @@ -1,181 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Handler; - -public class DestinationPatternsMessageCondition : AbstractMessageCondition -{ - public const string LookupDestinationHeader = "lookupDestination"; - private readonly IRouteMatcher _routeMatcher; - - public ISet Patterns { get; } - - public DestinationPatternsMessageCondition(params string[] patterns) - : this(patterns, (IPathMatcher)null) - { - } - - public DestinationPatternsMessageCondition(string[] patterns, IPathMatcher matcher) - : this(patterns, new SimpleRouteMatcher(matcher ?? new AntPathMatcher())) - { - } - - public DestinationPatternsMessageCondition(string[] patterns, IRouteMatcher routeMatcher) - : this(PrependLeadingSlash(patterns, routeMatcher), routeMatcher) - { - } - - private DestinationPatternsMessageCondition(ISet patterns, IRouteMatcher routeMatcher) - { - Patterns = patterns; - _routeMatcher = routeMatcher; - } - - private static ISet PrependLeadingSlash(string[] patterns, IRouteMatcher routeMatcher) - { - bool slashSeparator = routeMatcher.Combine("a", "a") == "a/a"; - ISet result = new HashSet(); - - foreach (string pat in patterns) - { - string pattern = pat; - - if (slashSeparator && !string.IsNullOrEmpty(pattern) && !pattern.StartsWith('/')) - { - pattern = $"/{pattern}"; - } - - result.Add(pattern); - } - - return result; - } - - public override DestinationPatternsMessageCondition Combine(DestinationPatternsMessageCondition other) - { - ISet result = new HashSet(); - - if (Patterns.Count > 0 && other.Patterns.Count > 0) - { - foreach (string pattern1 in Patterns) - { - foreach (string pattern2 in other.Patterns) - { - result.Add(_routeMatcher.Combine(pattern1, pattern2)); - } - } - } - else if (Patterns.Count > 0) - { - result = new HashSet(Patterns); - } - else if (other.Patterns.Count > 0) - { - result = new HashSet(other.Patterns); - } - else - { - result.Add(string.Empty); - } - - return new DestinationPatternsMessageCondition(result, _routeMatcher); - } - - public override DestinationPatternsMessageCondition GetMatchingCondition(IMessage message) - { - message.Headers.TryGetValue(LookupDestinationHeader, out object destination); - - if (destination == null) - { - return null; - } - - if (Patterns.Count == 0) - { - return this; - } - - List matches = null; - - foreach (string pattern in Patterns) - { - if (pattern.Equals(destination) || MatchPattern(pattern, destination)) - { - matches ??= new List(); - matches.Add(pattern); - } - } - - if (matches == null) - { - return null; - } - - matches.Sort(GetPatternComparer(destination)); - return new DestinationPatternsMessageCondition(new HashSet(matches), _routeMatcher); - } - - public override int CompareTo(DestinationPatternsMessageCondition other, IMessage message) - { - message.Headers.TryGetValue(LookupDestinationHeader, out object destination); - - if (destination == null) - { - return 0; - } - - IComparer patternComparator = GetPatternComparer(destination); - using IEnumerator iterator = Patterns.GetEnumerator(); - using IEnumerator iteratorOther = other.Patterns.GetEnumerator(); - - while (iterator.MoveNext() && iteratorOther.MoveNext()) - { - int result = patternComparator.Compare(iterator.Current, iteratorOther.Current); - - if (result != 0) - { - return result; - } - } - - if (iterator.MoveNext()) - { - return -1; - } - - if (iteratorOther.MoveNext()) - { - return 1; - } - - return 0; - } - - protected override IList GetContent() - { - return Patterns.ToList(); - } - - protected override string GetToStringInfix() - { - return " || "; - } - - private bool MatchPattern(string pattern, object destination) - { - return destination is IRoute route - ? _routeMatcher.Match(pattern, route) - : ((SimpleRouteMatcher)_routeMatcher).PathMatcher.Match(pattern, (string)destination); - } - - private IComparer GetPatternComparer(object destination) - { - return destination is IRoute route - ? _routeMatcher.GetPatternComparer(route) - : ((SimpleRouteMatcher)_routeMatcher).PathMatcher.GetPatternComparer((string)destination); - } -} diff --git a/src/Messaging/src/Messaging/Handler/HandlerMethod.cs b/src/Messaging/src/Messaging/Handler/HandlerMethod.cs deleted file mode 100644 index 128df1b3da..0000000000 --- a/src/Messaging/src/Messaging/Handler/HandlerMethod.cs +++ /dev/null @@ -1,276 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using Steeltoe.Common; - -namespace Steeltoe.Messaging.Handler; - -public class HandlerMethod -{ - // Keep these readonly for perf reasons - protected readonly Invoker InnerInvoker; - protected readonly int InnerArgCount; - protected readonly object InnerHandler; - - protected internal Invoker HandlerInvoker => InnerInvoker; - - protected internal int ArgCount => InnerArgCount; - - public object Handler => InnerHandler; - - public MethodInfo Method { get; } - - public Type HandlerType { get; } - - public HandlerMethod ResolvedFromHandlerMethod { get; } - - public virtual bool IsVoid => Method.ReturnType == typeof(void); - - public virtual string ShortLogMessage - { - get - { - int args = Method.GetParameters().Length; - return $"{HandlerType.Name}#{Method.Name}[{args} args]"; - } - } - - public virtual ParameterInfo[] MethodParameters => Method.GetParameters(); - - public virtual ParameterInfo ReturnType => Method.ReturnParameter; - - public HandlerMethod(object handler, MethodInfo handlerMethod) - { - ArgumentGuard.NotNull(handlerMethod); - - ArgumentGuard.NotNull(handler); - - InnerHandler = handler; - HandlerType = handler.GetType(); - Method = handlerMethod; - InnerArgCount = Method.GetParameters().Length; - InnerInvoker = CreateInvoker(); - } - - public HandlerMethod(object handler, string handlerMethodName, params Type[] parameterTypes) - { - ArgumentGuard.NotNullOrEmpty(handlerMethodName); - ArgumentGuard.NotNull(handler); - - InnerHandler = handler; - HandlerType = handler.GetType(); - Method = HandlerType.GetMethod(handlerMethodName, parameterTypes); - InnerArgCount = Method.GetParameters().Length; - InnerInvoker = CreateInvoker(); - } - - protected HandlerMethod(HandlerMethod handlerMethod) - { - ArgumentGuard.NotNull(handlerMethod); - - InnerHandler = handlerMethod.Handler; - HandlerType = handlerMethod.HandlerType; - Method = handlerMethod.Method; - InnerInvoker = handlerMethod.HandlerInvoker; - InnerArgCount = handlerMethod.ArgCount; - } - - private HandlerMethod(HandlerMethod handlerMethod, object handler) - { - ArgumentGuard.NotNull(handlerMethod); - ArgumentGuard.NotNull(handler); - - InnerHandler = handler; - HandlerType = handlerMethod.HandlerType; - Method = handlerMethod.Method; - InnerInvoker = handlerMethod.HandlerInvoker; - InnerArgCount = handlerMethod.ArgCount; - ResolvedFromHandlerMethod = handlerMethod; - } - - public virtual HandlerMethod CreateWithResolvedBean() - { - return new HandlerMethod(this, Handler); - } - - protected static object FindProvidedArgument(ParameterInfo parameter, params object[] providedArgs) - { - if (providedArgs != null && providedArgs.Any()) - { - foreach (object providedArg in providedArgs) - { - if (parameter.ParameterType.IsInstanceOfType(providedArg)) - { - return providedArg; - } - } - } - - return null; - } - - protected static string FormatArgumentError(ParameterInfo param, string message) - { - return $"Could not resolve parameter [{param.Position}] in {param.Member}{(!string.IsNullOrEmpty(message) ? $": {message}" : string.Empty)}"; - } - - protected virtual void AssertTargetBean(MethodInfo method, object targetBean, object[] args) - { - Type methodDeclaringClass = method.DeclaringType; - Type targetBeanClass = targetBean.GetType(); - - if (!methodDeclaringClass.IsAssignableFrom(targetBeanClass)) - { - string text = - $"The mapped handler method class '{methodDeclaringClass.Name}' is not an instance of the actual endpoint bean class '{targetBeanClass.Name}"; - - throw new InvalidOperationException(FormatInvokeError(text, args)); - } - } - - protected virtual string FormatInvokeError(string text, object[] args) - { - var sb = new StringBuilder(); - - for (int i = 0; i < args.Length; i++) - { - sb.Append(args[i] != null ? $"[{i}] [type={args[i].GetType().FullName}] [value={args[i]}]" : $"[{i}] [null]"); - - sb.Append('\n'); - } - - return $"{text}\nEndpoint [{HandlerType.Name}]\nMethod [{Method}] with argument values:\n{sb}"; - } - - private Invoker CreateInvoker() - { - Type[] methodArgTypes = GetMethodParameterTypes(); - - var dynamicMethod = new DynamicMethod($"{Method.Name}Invoker", typeof(object), new[] - { - typeof(object), - typeof(object[]) - }, Method.DeclaringType.Module); - - ILGenerator generator = dynamicMethod.GetILGenerator(128); - - // Define some locals to store args into - var argLocals = new LocalBuilder[methodArgTypes.Length]; - - for (int i = 0; i < methodArgTypes.Length; i++) - { - argLocals[i] = generator.DeclareLocal(methodArgTypes[i]); - } - - // Cast incoming arg0 to the target type - generator.Emit(OpCodes.Ldarg_0); - generator.Emit(OpCodes.Castclass, argLocals[0].LocalType); - generator.Emit(OpCodes.Stloc, argLocals[0]); - - int arrayIndex = 0; - - for (int i = 1; i < methodArgTypes.Length; i++) - { - // Load argument from incoming array - generator.Emit(OpCodes.Ldarg_1); - - // Rule suppressed due to Sonar bug: https://github.com/SonarSource/sonar-dotnet/issues/8139 -#pragma warning disable S2583 // Conditionally executed code should be reachable - if (arrayIndex <= 8) -#pragma warning restore S2583 // Conditionally executed code should be reachable - { - generator.Emit(GetLoadIntConst(arrayIndex++)); - } - else - { - generator.Emit(OpCodes.Ldc_I4, arrayIndex++); - } - - generator.Emit(OpCodes.Ldelem_Ref); - - // Cast/Unbox if needed - Type methodArgType = methodArgTypes[i]; - generator.Emit(IsValueType(methodArgType) ? OpCodes.Unbox_Any : OpCodes.Castclass, methodArgType); - - // Save to local - generator.Emit(OpCodes.Stloc, argLocals[i]); - } - - // Load all arg values from locals, including this - foreach (LocalBuilder builder in argLocals) - { - generator.Emit(OpCodes.Ldloc, builder); - } - - // Call target method - generator.EmitCall(OpCodes.Callvirt, Method, null); - - // Handle return if any - if (Method.ReturnType != typeof(void)) - { - // Box any value types - LocalBuilder returnLocal = generator.DeclareLocal(typeof(object)); - - if (IsValueType(Method.ReturnType)) - { - generator.Emit(OpCodes.Box, Method.ReturnType); - } - - generator.Emit(OpCodes.Stloc, returnLocal); - generator.Emit(OpCodes.Ldloc, returnLocal); - } - else - { - // Target method returns void, so return null - generator.Emit(OpCodes.Ldnull); - } - - // Return from invoker - generator.Emit(OpCodes.Ret); - - // Create invoker delegate - return (Invoker)dynamicMethod.CreateDelegate(typeof(Invoker)); - } - - private bool IsValueType(Type type) - { - return type.IsValueType; - } - - private OpCode GetLoadIntConst(int constant) - { - return constant switch - { - 0 => OpCodes.Ldc_I4_0, - 1 => OpCodes.Ldc_I4_1, - 2 => OpCodes.Ldc_I4_2, - 3 => OpCodes.Ldc_I4_3, - 4 => OpCodes.Ldc_I4_4, - 5 => OpCodes.Ldc_I4_5, - 6 => OpCodes.Ldc_I4_6, - 7 => OpCodes.Ldc_I4_7, - 8 => OpCodes.Ldc_I4_8, - _ => throw new InvalidOperationException() - }; - } - - private Type[] GetMethodParameterTypes() - { - ParameterInfo[] methodParameters = Method.GetParameters(); - var paramTypes = new Type[methodParameters.Length + 1]; - paramTypes[0] = Method.DeclaringType; - - for (int i = 0; i < methodParameters.Length; i++) - { - paramTypes[i + 1] = methodParameters[i].ParameterType; - } - - return paramTypes; - } - - public delegate object Invoker(object target, object[] args); -} diff --git a/src/Messaging/src/Messaging/Handler/Invocation/AbstractAsyncReturnValueHandler.cs b/src/Messaging/src/Messaging/Handler/Invocation/AbstractAsyncReturnValueHandler.cs deleted file mode 100644 index 8ec8cb171b..0000000000 --- a/src/Messaging/src/Messaging/Handler/Invocation/AbstractAsyncReturnValueHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Messaging.Handler.Invocation; - -public abstract class AbstractAsyncReturnValueHandler : IAsyncHandlerMethodReturnValueHandler -{ - public void HandleReturnValue(object returnValue, ParameterInfo returnType, IMessage message) - { - throw new InvalidOperationException("Unexpected invocation"); - } - - public virtual bool IsAsyncReturnValue(object returnValue, ParameterInfo parameterInfo) - { - return true; - } - - public abstract bool SupportsReturnType(ParameterInfo returnType); -} diff --git a/src/Messaging/src/Messaging/Handler/Invocation/AbstractExceptionHandlerMethodResolver.cs b/src/Messaging/src/Messaging/Handler/Invocation/AbstractExceptionHandlerMethodResolver.cs deleted file mode 100644 index 5ab83dc23e..0000000000 --- a/src/Messaging/src/Messaging/Handler/Invocation/AbstractExceptionHandlerMethodResolver.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Reflection; -using Steeltoe.Common; -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Handler.Invocation; - -public abstract class AbstractExceptionHandlerMethodResolver -{ - private readonly Dictionary _mappedMethods = new(); - - private readonly ConcurrentDictionary _exceptionLookupCache = new(); - - public bool HasExceptionMappings => _mappedMethods.Count > 0; - - protected AbstractExceptionHandlerMethodResolver(IDictionary mappedMethods) - { - ArgumentGuard.NotNull(mappedMethods); - - foreach (KeyValuePair entry in mappedMethods) - { - _mappedMethods[entry.Key] = entry.Value; - } - } - - protected static List GetExceptionsFromMethodSignature(MethodInfo method) - { - var result = new List(); - - foreach (ParameterInfo param in method.GetParameters()) - { - Type paramType = param.ParameterType; - - if (typeof(Exception).IsAssignableFrom(paramType)) - { - result.Add(paramType); - } - } - - if (result.Count == 0) - { - throw new InvalidOperationException($"No exception types mapped to {method}"); - } - - return result; - } - - public MethodInfo ResolveMethod(Exception exception) - { - MethodInfo method = ResolveMethodByExceptionType(exception.GetType()); - - if (method == null) - { - Exception cause = exception.InnerException; - - if (cause != null) - { - method = ResolveMethodByExceptionType(cause.GetType()); - } - } - - return method; - } - - public MethodInfo ResolveMethodByExceptionType(Type exceptionType) - { - _exceptionLookupCache.TryGetValue(exceptionType, out MethodInfo method); - - if (method == null) - { - method = GetMappedMethod(exceptionType); - - if (_exceptionLookupCache.TryAdd(exceptionType, method)) - { - return method; - } - - _exceptionLookupCache.TryGetValue(exceptionType, out method); - } - - return method; - } - - private MethodInfo GetMappedMethod(Type exceptionType) - { - var matches = new List(); - - foreach (Type mappedException in _mappedMethods.Keys) - { - if (mappedException.IsAssignableFrom(exceptionType)) - { - matches.Add(mappedException); - } - } - - if (matches.Count > 0) - { - matches.Sort(new ExceptionDepthComparator(exceptionType)); - return _mappedMethods[matches[0]]; - } - - return null; - } -} diff --git a/src/Messaging/src/Messaging/Handler/Invocation/AbstractMethodMessageHandler.cs b/src/Messaging/src/Messaging/Handler/Invocation/AbstractMethodMessageHandler.cs deleted file mode 100644 index e9de79d95e..0000000000 --- a/src/Messaging/src/Messaging/Handler/Invocation/AbstractMethodMessageHandler.cs +++ /dev/null @@ -1,457 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Reflection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.Handler.Invocation; - -public abstract class AbstractMethodMessageHandler : IMessageHandler - where T : class -{ - private readonly List _destinationPrefixes = new(); - private readonly List _customArgumentResolvers = new(); - private readonly List _customReturnValueHandlers = new(); - private readonly HandlerMethodArgumentResolverComposite _argumentResolvers = new(); - private readonly HandlerMethodReturnValueHandlerComposite _returnValueHandlers = new(); - private readonly Dictionary _handlerMethods = new(64); - private readonly Dictionary> _destinationLookup = new(64); - private readonly ConcurrentDictionary _exceptionHandlerCache = new(); - private readonly ILogger _logger; - - protected HandlerMethodArgumentResolverComposite MethodArgumentResolvers - { - get - { - if (_argumentResolvers.Resolvers.Count == 0) - { - _argumentResolvers.AddResolvers(InitArgumentResolvers()); - } - - return _argumentResolvers; - } - } - - protected HandlerMethodReturnValueHandlerComposite MethodReturnValueHandlers - { - get - { - if (_returnValueHandlers.ReturnValueHandlers.Count == 0) - { - _returnValueHandlers.AddHandlers(InitReturnValueHandlers()); - } - - return _returnValueHandlers; - } - } - - public virtual string ServiceName { get; set; } - - public virtual IList DestinationPrefixes - { - get => _destinationPrefixes; - set - { - _destinationPrefixes.Clear(); - - if (value != null) - { - foreach (string prefix in value) - { - _destinationPrefixes.Add(prefix.Trim()); - } - } - } - } - - public virtual IList CustomArgumentResolvers - { - get => _customArgumentResolvers; - set - { - _customArgumentResolvers.Clear(); - - if (value != null) - { - _customArgumentResolvers.AddRange(value); - } - } - } - - public virtual IList ArgumentResolvers - { - get => MethodArgumentResolvers.Resolvers; - set - { - if (value == null) - { - MethodArgumentResolvers.Clear(); - return; - } - - MethodArgumentResolvers.AddResolvers(value); - } - } - - public virtual IList ReturnValueHandlers - { - get => MethodReturnValueHandlers.ReturnValueHandlers; - set - { - if (value == null) - { - MethodReturnValueHandlers.Clear(); - return; - } - - MethodReturnValueHandlers.AddHandlers(value); - } - } - - public virtual IList CustomReturnValueHandlers - { - get => _customReturnValueHandlers; - set - { - _customReturnValueHandlers.Clear(); - - if (value != null) - { - _customReturnValueHandlers.AddRange(value); - } - } - } - - public virtual IDictionary HandlerMethods => new Dictionary(_handlerMethods); - - protected AbstractMethodMessageHandler(ILogger logger = null) - { - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - _logger = logger; - } - - public virtual void HandleMessage(IMessage message) - { - string destination = GetDestination(message); - - if (destination == null) - { - return; - } - - string lookupDestination = GetLookupDestination(destination); - - if (lookupDestination == null) - { - return; - } - - MessageHeaderAccessor headerAccessor = MessageHeaderAccessor.GetMutableAccessor(message); - headerAccessor.SetHeader(DestinationPatternsMessageCondition.LookupDestinationHeader, lookupDestination); - headerAccessor.LeaveMutable = true; - message = MessageBuilder.CreateMessage(message.Payload, headerAccessor.MessageHeaders); - - HandleMessageInternal(message, lookupDestination); - headerAccessor.SetImmutable(); - } - - public override string ToString() - { - return $"{GetType().Name}[prefixes={string.Join(",", DestinationPrefixes)}]"; - } - - protected abstract IList InitArgumentResolvers(); - - protected abstract IList InitReturnValueHandlers(); - - protected abstract T GetMappingForMethod(MethodInfo method, Type handlerType); - - protected abstract string GetDestination(IMessage message); - - protected abstract ISet GetDirectLookupDestinations(T mapping); - - protected abstract T GetMatchingMapping(T mapping, IMessage message); - - protected abstract IComparer GetMappingComparer(IMessage message); - - protected abstract AbstractExceptionHandlerMethodResolver CreateExceptionHandlerMethodResolverFor(Type beanType); - - protected void DetectHandlerMethods(object handler) - { - Type handlerType = null; - - if (handler is not string) - { - handlerType = handler.GetType(); - } - - if (handlerType != null) - { - var results = new Dictionary(); - MethodInfo[] methods = handlerType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); - - foreach (MethodInfo method in methods) - { - T mapping = GetMappingForMethod(method, handlerType); - - if (mapping != null) - { - results.Add(method, mapping); - } - } - - foreach (KeyValuePair entry in results) - { - RegisterHandlerMethod(handler, entry.Key, entry.Value); - } - } - } - - protected virtual string GetLookupDestination(string destination) - { - if (destination == null) - { - return null; - } - - if (_destinationPrefixes.Count == 0) - { - return destination; - } - - foreach (string prefix in _destinationPrefixes) - { - if (destination.StartsWith(prefix, StringComparison.Ordinal)) - { - return destination[prefix.Length..]; - } - } - - return null; - } - - protected virtual void RegisterHandlerMethod(object handler, MethodInfo method, T mapping) - { - ArgumentGuard.NotNull(mapping); - - HandlerMethod newHandlerMethod = CreateHandlerMethod(handler, method); - _handlerMethods.TryGetValue(mapping, out HandlerMethod oldHandlerMethod); - - if (oldHandlerMethod != null && !oldHandlerMethod.Equals(newHandlerMethod)) - { - throw new InvalidOperationException( - $"Ambiguous mapping found. Cannot map '{newHandlerMethod.Handler}' bean method \n{newHandlerMethod}\nto {mapping}: There is already '{oldHandlerMethod.Handler}' bean method\n{oldHandlerMethod} mapped."); - } - - _handlerMethods[mapping] = newHandlerMethod; - - foreach (string pattern in GetDirectLookupDestinations(mapping)) - { - if (_destinationLookup.TryGetValue(pattern, out List list)) - { - list.Add(mapping); - } - else - { - _destinationLookup.Add(pattern, new List - { - mapping - }); - } - } - } - - protected virtual HandlerMethod CreateHandlerMethod(object handler, MethodInfo method) - { - HandlerMethod handlerMethod; - handlerMethod = new HandlerMethod(handler, method); - return handlerMethod; - } - - protected virtual void HandleMessageInternal(IMessage message, string lookupDestination) - { - var matches = new List(); - - _destinationLookup.TryGetValue(lookupDestination, out List mappingsByUrl); - - if (mappingsByUrl != null) - { - AddMatchesToCollection(mappingsByUrl, message, matches); - } - - if (matches.Count == 0) - { - // No direct hits, go through all mappings - Dictionary.KeyCollection allMappings = _handlerMethods.Keys; - AddMatchesToCollection(allMappings, message, matches); - } - - if (matches.Count == 0) - { - HandleNoMatchAsync(_handlerMethods.Keys, lookupDestination, message); - return; - } - - var comparator = new MatchComparer(GetMappingComparer(message)); - matches.Sort(comparator); - - Match bestMatch = matches[0]; - - if (matches.Count > 1) - { - Match secondBestMatch = matches[1]; - - if (comparator.Compare(bestMatch, secondBestMatch) == 0) - { - MethodInfo m1 = bestMatch.HandlerMethod.Method; - MethodInfo m2 = secondBestMatch.HandlerMethod.Method; - throw new InvalidOperationException($"Ambiguous handler methods mapped for destination '{lookupDestination}': {{{m1}, {m2}}}"); - } - } - - HandleMatch(bestMatch.Mapping, bestMatch.HandlerMethod, lookupDestination, message); - } - - protected virtual void HandleMatch(T mapping, HandlerMethod handlerMethod, string lookupDestination, IMessage message) - { - handlerMethod = handlerMethod.CreateWithResolvedBean(); - - var invocable = new InvocableHandlerMethod(handlerMethod, _logger) - { - MessageMethodArgumentResolvers = MethodArgumentResolvers - }; - - try - { - object returnValue = invocable.Invoke(message); - ParameterInfo returnType = handlerMethod.ReturnType; - - if (returnType.ParameterType == typeof(void)) - { - return; - } - - if (returnValue != null && MethodReturnValueHandlers.IsAsyncReturnValue(returnValue, returnType)) - { - throw new NotImplementedException("Async is not implemented."); - } - - MethodReturnValueHandlers.HandleReturnValue(returnValue, returnType, message); - } - catch (Exception ex) - { - Exception handlingException = new MessageHandlingException(message, "Unexpected handler method invocation error", ex); - ProcessHandlerMethodException(handlerMethod, handlingException, message); - } - } - - protected virtual void ProcessHandlerMethodException(HandlerMethod handlerMethod, Exception exception, IMessage message) - { - InvocableHandlerMethod invocable = GetExceptionHandlerMethod(handlerMethod, exception); - - if (invocable == null) - { - _logger?.LogError(exception, "Unhandled exception from message handler method"); - return; - } - - invocable.MessageMethodArgumentResolvers = MethodArgumentResolvers; - - try - { - Exception cause = exception.InnerException; - - object returnValue = - cause != null ? invocable.Invoke(message, exception, cause, handlerMethod) : invocable.Invoke(message, exception, handlerMethod); - - ParameterInfo returnType = invocable.ReturnType; - - if (returnType.ParameterType == typeof(void)) - { - return; - } - - MethodReturnValueHandlers.HandleReturnValue(returnValue, returnType, message); - } - catch (Exception e) - { - _logger?.LogError(e, "Error while processing handler method exception"); - } - } - - protected virtual InvocableHandlerMethod GetExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) - { - Type beanType = handlerMethod.HandlerType; - _exceptionHandlerCache.TryGetValue(beanType, out AbstractExceptionHandlerMethodResolver resolver); - - if (resolver == null) - { - resolver = CreateExceptionHandlerMethodResolverFor(beanType); - _exceptionHandlerCache[beanType] = resolver; - } - - MethodInfo method = resolver.ResolveMethod(exception); - - if (method != null) - { - return new InvocableHandlerMethod(handlerMethod.Handler, method); - } - - return null; - } - - protected virtual Task HandleNoMatchAsync(ICollection ts, string lookupDestination, IMessage message) - { - return Task.CompletedTask; - } - - private void AddMatchesToCollection(ICollection mappingsToCheck, IMessage message, List matches) - { - foreach (T mapping in mappingsToCheck) - { - T match = GetMatchingMapping(mapping, message); - - if (match != null) - { - matches.Add(new Match(match, _handlerMethods[mapping])); - } - } - } - - protected class Match - { - public readonly T Mapping; - - public readonly HandlerMethod HandlerMethod; - - public Match(T mapping, HandlerMethod handlerMethod) - { - Mapping = mapping; - HandlerMethod = handlerMethod; - } - - public override string ToString() - { - return Mapping.ToString(); - } - } - - protected class MatchComparer : IComparer - { - private readonly IComparer _comparator; - - public MatchComparer(IComparer comparator) - { - _comparator = comparator; - } - - public int Compare(Match x, Match y) - { - return _comparator.Compare(x.Mapping, y.Mapping); - } - } -} diff --git a/src/Messaging/src/Messaging/Handler/Invocation/HandlerMethodArgumentResolverComposite.cs b/src/Messaging/src/Messaging/Handler/Invocation/HandlerMethodArgumentResolverComposite.cs deleted file mode 100644 index 8017602be2..0000000000 --- a/src/Messaging/src/Messaging/Handler/Invocation/HandlerMethodArgumentResolverComposite.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Reflection; - -namespace Steeltoe.Messaging.Handler.Invocation; - -public class HandlerMethodArgumentResolverComposite : IHandlerMethodArgumentResolver -{ - private readonly List _argumentResolvers = new(); - - private readonly ConcurrentDictionary _argumentResolverCache = new(); - - public int Count => _argumentResolvers.Count; - - public List Resolvers => new(_argumentResolvers); - - public HandlerMethodArgumentResolverComposite AddResolver(IHandlerMethodArgumentResolver argumentResolver) - { - _argumentResolvers.Add(argumentResolver); - return this; - } - - public HandlerMethodArgumentResolverComposite AddResolvers(params IHandlerMethodArgumentResolver[] resolvers) - { - if (resolvers != null) - { - _argumentResolvers.AddRange(resolvers); - } - - return this; - } - - public HandlerMethodArgumentResolverComposite AddResolvers(IList resolvers) - { - if (resolvers != null) - { - _argumentResolvers.AddRange(resolvers); - } - - return this; - } - - public void Clear() - { - _argumentResolvers.Clear(); - } - - public bool SupportsParameter(ParameterInfo parameter) - { - return GetArgumentResolver(parameter) != null; - } - - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - IHandlerMethodArgumentResolver resolver = GetArgumentResolver(parameter); - - if (resolver == null) - { - throw new InvalidOperationException( - $"Unsupported parameter type [{parameter.ParameterType.Name}]. {nameof(SupportsParameter)} should be called first."); - } - - return resolver.ResolveArgument(parameter, message); - } - - private IHandlerMethodArgumentResolver GetArgumentResolver(ParameterInfo parameter) - { - if (!_argumentResolverCache.TryGetValue(parameter, out IHandlerMethodArgumentResolver result)) - { - foreach (IHandlerMethodArgumentResolver resolver in _argumentResolvers) - { - if (resolver.SupportsParameter(parameter)) - { - result = resolver; - _argumentResolverCache.TryAdd(parameter, result); - break; - } - } - } - - return result; - } -} diff --git a/src/Messaging/src/Messaging/Handler/Invocation/HandlerMethodReturnValueHandlerComposite.cs b/src/Messaging/src/Messaging/Handler/Invocation/HandlerMethodReturnValueHandlerComposite.cs deleted file mode 100644 index aeb1ffe590..0000000000 --- a/src/Messaging/src/Messaging/Handler/Invocation/HandlerMethodReturnValueHandlerComposite.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Messaging.Handler.Invocation; - -public class HandlerMethodReturnValueHandlerComposite : IAsyncHandlerMethodReturnValueHandler -{ - private readonly List _returnValueHandlers = new(); - - public IList ReturnValueHandlers => new List(_returnValueHandlers); - - public void Clear() - { - _returnValueHandlers.Clear(); - } - - public HandlerMethodReturnValueHandlerComposite AddHandler(IHandlerMethodReturnValueHandler returnValueHandler) - { - _returnValueHandlers.Add(returnValueHandler); - return this; - } - - public HandlerMethodReturnValueHandlerComposite AddHandlers(IList handlers) - { - if (handlers != null) - { - _returnValueHandlers.AddRange(handlers); - } - - return this; - } - - public bool SupportsReturnType(ParameterInfo returnType) - { - return GetReturnValueHandler(returnType) != null; - } - - public void HandleReturnValue(object returnValue, ParameterInfo returnType, IMessage message) - { - IHandlerMethodReturnValueHandler handler = GetReturnValueHandler(returnType); - - if (handler == null) - { - throw new InvalidOperationException($"No handler for return value type: {returnType.ParameterType}"); - } - - handler.HandleReturnValue(returnValue, returnType, message); - } - - public bool IsAsyncReturnValue(object returnValue, ParameterInfo parameterInfo) - { - return GetReturnValueHandler(parameterInfo) is IAsyncHandlerMethodReturnValueHandler handler1 && - handler1.IsAsyncReturnValue(returnValue, parameterInfo); - } - - private IHandlerMethodReturnValueHandler GetReturnValueHandler(ParameterInfo returnType) - { - foreach (IHandlerMethodReturnValueHandler handler in _returnValueHandlers) - { - if (handler.SupportsReturnType(returnType)) - { - return handler; - } - } - - return null; - } -} diff --git a/src/Messaging/src/Messaging/Handler/Invocation/InvocableHandlerMethod.cs b/src/Messaging/src/Messaging/Handler/Invocation/InvocableHandlerMethod.cs deleted file mode 100644 index 55ecf4adc6..0000000000 --- a/src/Messaging/src/Messaging/Handler/Invocation/InvocableHandlerMethod.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Runtime.CompilerServices; -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Messaging.Handler.Invocation; - -public class InvocableHandlerMethod : HandlerMethod, IInvocableHandlerMethod -{ - private static readonly object[] EmptyArgs = Array.Empty(); - private readonly ILogger _logger; - - public HandlerMethodArgumentResolverComposite MessageMethodArgumentResolvers { get; set; } = new(); - - public InvocableHandlerMethod(HandlerMethod handlerMethod, ILogger logger = null) - : base(handlerMethod) - { - _logger = logger; - } - - public InvocableHandlerMethod(object bean, MethodInfo method) - : base(bean, method) - { - } - - public InvocableHandlerMethod(object bean, string methodName, params Type[] parameterTypes) - : base(bean, methodName, parameterTypes) - { - } - - public virtual object Invoke(IMessage requestMessage, params object[] args) - { - object[] argValues = GetMethodArgumentValues(requestMessage, args); - - _logger?.LogTrace("Arguments: {arguments}", string.Join(", ", args)); - - return DoInvoke(argValues); - } - - protected virtual object[] GetMethodArgumentValues(IMessage message, params object[] providedArgs) - { - ParameterInfo[] parameters = MethodParameters; - - if (parameters.Length == 0) - { - return EmptyArgs; - } - - object[] args = new object[parameters.Length]; - - for (int i = 0; i < parameters.Length; i++) - { - ParameterInfo parameter = parameters[i]; - - args[i] = FindProvidedArgument(parameter, providedArgs); - - if (args[i] != null) - { - continue; - } - - if (!MessageMethodArgumentResolvers.SupportsParameter(parameter)) - { - throw new MethodArgumentResolutionException(message, parameter, FormatArgumentError(parameter, "No suitable resolver")); - } - - try - { - args[i] = MessageMethodArgumentResolvers.ResolveArgument(parameter, message); - } - catch (Exception ex) - { - // Leave stack trace for later, exception may actually be resolved and handled.. - string error = ex.Message; - - if (!error.Contains(parameter.Name, StringComparison.Ordinal)) - { - _logger?.LogDebug(ex, $"Error resolving parameter: {parameter.Name}, error: {error}"); - } - - throw; - } - } - - return args; - } - - protected virtual object DoInvoke(params object[] args) - { - try - { - if (InnerArgCount != args.Length) - { - throw new InvalidOperationException(FormatInvokeError("Argument count mismatch", args), new TargetParameterCountException()); - } - - object result = InnerInvoker(InnerHandler, args); - - if (result is Task resultAsTask) - { - bool isAsyncMethod = Method.CustomAttributes.Any(x => x.AttributeType.Name == nameof(AsyncStateMachineAttribute)); - - if (isAsyncMethod) - { - resultAsTask.GetAwaiter().GetResult(); - } - } - - return result; - } - catch (Exception ex) when (ex is InvalidCastException) - { - AssertTargetBean(Method, Handler, args); - string text = !string.IsNullOrEmpty(ex.Message) ? ex.Message : "Illegal argument"; - throw new InvalidOperationException(FormatInvokeError(text, args)); - } - catch (Exception ex) - { - // Unwrap for HandlerExceptionResolvers ... - Exception targetException = ex.GetBaseException(); - - if (targetException != null) - { - throw targetException; - } - - throw new InvalidOperationException(FormatInvokeError("Invocation failure", args), ex); - } - } -} diff --git a/src/Messaging/src/Messaging/Handler/Invocation/MethodArgumentResolutionException.cs b/src/Messaging/src/Messaging/Handler/Invocation/MethodArgumentResolutionException.cs deleted file mode 100644 index d3f630ce1b..0000000000 --- a/src/Messaging/src/Messaging/Handler/Invocation/MethodArgumentResolutionException.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Messaging.Handler.Invocation; - -public class MethodArgumentResolutionException : MessagingException -{ - public ParameterInfo Parameter { get; } - - public MethodArgumentResolutionException(IMessage failedMessage, ParameterInfo parameter) - : base(failedMessage, GetMethodParameterMessage(parameter)) - { - Parameter = parameter; - } - - public MethodArgumentResolutionException(IMessage failedMessage, ParameterInfo parameter, string message) - : base(failedMessage, $"{GetMethodParameterMessage(parameter)}: {message}") - { - Parameter = parameter; - } - - private static string GetMethodParameterMessage(ParameterInfo parameter) - { - return $"Could not resolve method parameter at index {parameter.Position} in {parameter.Member}"; - } -} diff --git a/src/Messaging/src/Messaging/Message.cs b/src/Messaging/src/Messaging/Message.cs deleted file mode 100644 index dd4151ebfa..0000000000 --- a/src/Messaging/src/Messaging/Message.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Steeltoe.Common; -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging; - -public static class Message -{ - public static IMessage Create(T payload) - { - return (IMessage)Create(payload, typeof(T)); - } - - public static IMessage Create(T payload, IMessageHeaders headers) - { - if (headers == null) - { - return (IMessage)Create(payload, typeof(T)); - } - - return (IMessage)Create(payload, headers, typeof(T)); - } - - public static IMessage Create(T payload, IDictionary headers) - { - return (IMessage)Create(payload, new MessageHeaders(headers, null, null), typeof(T)); - } - - public static IMessage Create(object payload, Type messageType = null) - { - Type genParamType = GetGenericParamType(payload, messageType); - Type typeToCreate = typeof(Message<>).MakeGenericType(genParamType); - - return (IMessage)Activator.CreateInstance(typeToCreate, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new[] - { - payload - }, null, null); - } - - public static IMessage Create(object payload, IMessageHeaders headers, Type messageType = null) - { - Type genParamType = GetGenericParamType(payload, messageType); - Type typeToCreate = typeof(Message<>).MakeGenericType(genParamType); - - return (IMessage)Activator.CreateInstance(typeToCreate, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new[] - { - payload, - headers - }, null, null); - } - - public static IMessage Create(object payload, IDictionary headers, Type messageType = null) - { - Type genParamType = GetGenericParamType(payload, messageType); - Type typeToCreate = typeof(Message<>).MakeGenericType(genParamType); - - return (IMessage)Activator.CreateInstance(typeToCreate, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new[] - { - payload, - headers - }, null, null); - } - - private static Type GetGenericParamType(object payload, Type messageType) - { - if (payload == null && messageType == null) - { - return typeof(object); - } - - if (messageType != null) - { - return messageType; - } - - return payload.GetType(); - } -} - -public class Message : IMessage -{ - protected readonly TPayload InnerPayload; - - protected readonly IMessageHeaders InnerHeaders; - - object IMessage.Payload => Payload; - - public TPayload Payload => InnerPayload; - - public IMessageHeaders Headers => InnerHeaders; - - protected internal Message(TPayload payload) - : this(payload, new MessageHeaders()) - { - } - - protected internal Message(TPayload payload, IDictionary headers) - : this(payload, new MessageHeaders(headers, null, null)) - { - } - - protected internal Message(TPayload payload, IMessageHeaders headers) - { - ArgumentGuard.NotNull(payload); - ArgumentGuard.NotNull(headers); - - InnerPayload = payload; - InnerHeaders = headers; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not Message other) - { - return false; - } - - return ObjectEquality.ObjectOrCollectionEquals(InnerPayload, other.InnerPayload) && InnerHeaders.Equals(other.InnerHeaders); - } - - public override int GetHashCode() - { - return HashCode.Combine(ObjectEquality.GetObjectOrCollectionHashCode(InnerPayload), InnerHeaders.GetHashCode()); - } - - public override string ToString() - { - var sb = new StringBuilder(GetType().Name); - sb.Append(" [payload="); - - if (InnerPayload is byte[]) - { - byte[] arr = (byte[])(object)InnerPayload; - sb.Append("byte[").Append(arr.Length).Append(']'); - } - else - { - sb.Append(InnerPayload); - } - - sb.Append(", headers=").Append(InnerHeaders).Append(']'); - return sb.ToString(); - } -} diff --git a/src/Messaging/src/Messaging/MessageDeliveryException.cs b/src/Messaging/src/Messaging/MessageDeliveryException.cs deleted file mode 100644 index 2c1975fca3..0000000000 --- a/src/Messaging/src/Messaging/MessageDeliveryException.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging; - -public class MessageDeliveryException : MessagingException -{ - public MessageDeliveryException(string message) - : base(message) - { - } - - public MessageDeliveryException(IMessage failedMessage) - : base(failedMessage) - { - } - - public MessageDeliveryException(IMessage failedMessage, string message) - : base(failedMessage, message) - { - } - - public MessageDeliveryException(IMessage failedMessage, Exception innerException) - : base(failedMessage, innerException) - { - } - - public MessageDeliveryException(IMessage failedMessage, string message, Exception innerException) - : base(failedMessage, message, innerException) - { - } -} diff --git a/src/Messaging/src/Messaging/MessageHandlingException.cs b/src/Messaging/src/Messaging/MessageHandlingException.cs deleted file mode 100644 index 2516b79ff5..0000000000 --- a/src/Messaging/src/Messaging/MessageHandlingException.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging; - -public class MessageHandlingException : MessagingException -{ - public MessageHandlingException(IMessage failedMessage) - : base(failedMessage) - { - } - - public MessageHandlingException(IMessage failedMessage, string message) - : base(failedMessage, message) - { - } - - public MessageHandlingException(IMessage failedMessage, Exception innerException) - : base(failedMessage, innerException) - { - } - - public MessageHandlingException(IMessage failedMessage, string message, Exception innerException) - : base(failedMessage, message, innerException) - { - } -} diff --git a/src/Messaging/src/Messaging/MessageHeaders.cs b/src/Messaging/src/Messaging/MessageHeaders.cs deleted file mode 100644 index 1418345abf..0000000000 --- a/src/Messaging/src/Messaging/MessageHeaders.cs +++ /dev/null @@ -1,386 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging; - -public class MessageHeaders : IMessageHeaders -{ - public const string Internal = "internal_"; - - public const string IdName = "id"; - public const string TimestampName = "timestamp"; - public const string ContentType = "contentType"; - public const string ReplyChannelName = "replyChannel"; - public const string ErrorChannelName = "errorChannel"; - public const string InferredArgumentType = $"{Internal}InferredArgumentType"; - - public const string ContentTypeJson = "application/json"; - public const string ContentTypeTextPlain = "text/plain"; - public const string ContentTypeBytes = "application/octet-stream"; - public const string ContentTypeJsonAlt = "text/x-json"; - public const string ContentTypeXml = "application/xml"; - public const string ContentTypeJavaSerializedObject = "application/x-java-serialized-object"; - public const string ContentTypeDotNetSerializedObject = "application/x-dotnet-serialized-object"; - - public const string TypeId = "__TypeId__"; - public const string ContentTypeId = "__ContentTypeId__"; - public const string KeyTypeId = "__KeyTypeId__"; - private static readonly IIdGenerator DefaultIdGenerator = new DefaultIdGenerator(); - - public static readonly string IdValueNone = string.Empty; - private static volatile IIdGenerator _idGenerator; - - protected readonly IDictionary Headers; - - internal static IIdGenerator IdGenerator - { - get => _idGenerator ?? DefaultIdGenerator; - set => _idGenerator = value; - } - - ICollection IDictionary.Keys => new List(Keys); - - ICollection IDictionary.Values => (ICollection)Values; - - bool IDictionary.IsFixedSize => false; - - bool ICollection.IsSynchronized => false; - - object ICollection.SyncRoot => throw new NotImplementedException(); - - protected internal virtual IDictionary RawHeaders => Headers; - - public virtual string Id - { - get - { - if (!Headers.TryGetValue(IdName, out object result)) - { - return null; - } - - return result.ToString(); - } - } - - public virtual long? Timestamp - { - get - { - if (!Headers.TryGetValue(TimestampName, out object result)) - { - return null; - } - - return (long)result; - } - } - - public virtual object ReplyChannel - { - get - { - Headers.TryGetValue(ReplyChannelName, out object chan); - return chan; - } - } - - public virtual object ErrorChannel - { - get - { - Headers.TryGetValue(ErrorChannelName, out object chan); - return chan; - } - } - - public virtual ICollection Keys => Headers.Keys; - - public virtual ICollection Values => Headers.Values; - - public virtual int Count => Headers.Count; - - public virtual bool IsReadOnly => true; - - public virtual bool IsSynchronized => false; - - public virtual object SyncRoot => throw new InvalidOperationException(); - - public virtual bool IsFixedSize => true; - - object IDictionary.this[object key] - { - get => Get((string)key); - set => throw new InvalidOperationException(); - } - - public virtual object this[string key] - { - get => Get(key); - set => throw new InvalidOperationException(); - } - - public virtual object this[object key] - { - get - { - if (key is string asString) - { - return Get(asString); - } - - return null; - } - set => throw new InvalidOperationException(); - } - - public MessageHeaders(IDictionary headers = null) - : this(headers, null, null) - { - } - - public MessageHeaders(IDictionary headers, string id, long? timestamp) - { - Headers = headers != null ? new Dictionary(headers) : new Dictionary(); - UpdateHeaders(id, timestamp); - } - - protected MessageHeaders(MessageHeaders other) - { - Headers = other.RawHeaders; - } - - public static MessageHeaders From(MessageHeaders other) - { - return new MessageHeaders(other); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not MessageHeaders other) - { - return false; - } - - return ContentsEqual(other.Headers); - } - - public override int GetHashCode() - { - return Headers.GetHashCode(); - } - - public override string ToString() - { - return Headers.ToString(); - } - - public virtual bool ContainsKey(string key) - { - return Headers.ContainsKey(key); - } - - public virtual bool TryGetValue(string key, out object value) - { - return Headers.TryGetValue(key, out value); - } - - public virtual void Add(string key, object value) - { - Headers.Add(key, value); - } - - public virtual void Add(object key, object value) - { - throw new InvalidOperationException(); - } - - public virtual void Add(KeyValuePair item) - { - Headers.Add(item); - } - - public virtual void Clear() - { - Headers.Clear(); - } - - public virtual bool Contains(KeyValuePair item) - { - return Headers.Contains(item); - } - - public virtual bool Contains(object key) - { - if (key is string asString) - { - return TryGetValue(asString, out object _); - } - - return false; - } - - public virtual void CopyTo(KeyValuePair[] array, int arrayIndex) - { - Headers.CopyTo(array, arrayIndex); - } - - public virtual void CopyTo(Array array, int index) - { - throw new InvalidOperationException(); - } - - public virtual void Remove(object key) - { - throw new InvalidOperationException(); - } - - public virtual bool Remove(string key) - { - return Headers.Remove(key); - } - - public virtual bool Remove(KeyValuePair item) - { - return Headers.Remove(item); - } - - public virtual IEnumerator> GetEnumerator() - { - return Headers.GetEnumerator(); - } - - public virtual T Get(string key) - { - if (TryGetValue(key, out object val)) - { - return (T)val; - } - - return default; - } - - IDictionaryEnumerator IDictionary.GetEnumerator() - { - using IEnumerator> enumerator = GetEnumerator(); - return new StringObjectDictionaryEnumerator(enumerator); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - void IDictionary.Add(object key, object value) - { - Add((string)key, value); - } - - bool IDictionary.Contains(object key) - { - return Headers.ContainsKey((string)key); - } - - void IDictionary.Remove(object key) - { - Headers.Remove((string)key); - } - - void ICollection.CopyTo(Array array, int index) - { - var collection = new KeyValuePair[array.Length]; - Headers.CopyTo(collection, index); - collection.CopyTo(array, 0); - } - - private bool ContentsEqual(IDictionary other) - { - if (ReferenceEquals(other, null)) - { - return false; - } - - if (Headers.Count != other.Count) - { - return false; - } - - foreach (KeyValuePair pair in Headers) - { - if (!other.TryGetValue(pair.Key, out object otherValue)) - { - return false; - } - - if (!Equals(pair.Value, otherValue)) - { - return false; - } - } - - return true; - } - - protected void UpdateHeaders(string id, long? timestamp) - { - if (id == null) - { - Headers[IdName] = IdGenerator.GenerateId(); - } - else if (id == IdValueNone) - { - Headers.Remove(IdName); - } - else - { - Headers[IdName] = id; - } - - if (timestamp == null) - { - Headers[TimestampName] = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - } - else if (timestamp.Value < 0) - { - Headers.Remove(TimestampName); - } - else - { - Headers[TimestampName] = timestamp.Value; - } - } - - private sealed class StringObjectDictionaryEnumerator : IDictionaryEnumerator - { - private readonly IEnumerator> _enumerator; - - public object Current => _enumerator.Current; - public DictionaryEntry Entry => new(_enumerator.Current.Key, _enumerator.Current.Value); - public object Key => _enumerator.Current.Key; - public object Value => _enumerator.Current.Value; - - public StringObjectDictionaryEnumerator(IEnumerator> enumerator) - { - _enumerator = enumerator; - } - - public bool MoveNext() - { - return _enumerator.MoveNext(); - } - - public void Reset() - { - _enumerator.Reset(); - } - } -} diff --git a/src/Messaging/src/Messaging/MessagingException.cs b/src/Messaging/src/Messaging/MessagingException.cs deleted file mode 100644 index ef41120fa8..0000000000 --- a/src/Messaging/src/Messaging/MessagingException.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging; - -public class MessagingException : Exception -{ - public IMessage FailedMessage { get; } - - public MessagingException(IMessage failedMessage) - { - FailedMessage = failedMessage; - } - - public MessagingException(string message) - : base(message) - { - FailedMessage = null; - } - - public MessagingException(string message, Exception innerException) - : base(message, innerException) - { - FailedMessage = null; - } - - public MessagingException(IMessage failedMessage, string message) - : base(message) - { - FailedMessage = failedMessage; - } - - public MessagingException(IMessage failedMessage, Exception innerException) - : base(null, innerException) - { - FailedMessage = failedMessage; - } - - public MessagingException(IMessage failedMessage, string message, Exception innerException) - : base(message, innerException) - { - FailedMessage = failedMessage; - } - - public override string ToString() - { - return base.ToString() + (FailedMessage == null ? string.Empty : ", failedMessage=" + FailedMessage); - } -} diff --git a/src/Messaging/src/Messaging/Properties/AssemblyInfo.cs b/src/Messaging/src/Messaging/Properties/AssemblyInfo.cs deleted file mode 100644 index a3938078fd..0000000000 --- a/src/Messaging/src/Messaging/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Steeltoe.Messaging.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Stream")] -[assembly: InternalsVisibleTo("Steeltoe.Integration")] -[assembly: InternalsVisibleTo("Steeltoe.Integration.Test")] diff --git a/src/Messaging/src/Messaging/Steeltoe.Messaging.csproj b/src/Messaging/src/Messaging/Steeltoe.Messaging.csproj deleted file mode 100644 index b725df7f72..0000000000 --- a/src/Messaging/src/Messaging/Steeltoe.Messaging.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - net8.0;net6.0 - Steeltoe Messaging Base - Messaging, NET Core, Spring, Spring Cloud - true - - - - - - - - - - - - - diff --git a/src/Messaging/src/Messaging/Support/AbstractChannelInterceptor.cs b/src/Messaging/src/Messaging/Support/AbstractChannelInterceptor.cs deleted file mode 100644 index 3f41b853c6..0000000000 --- a/src/Messaging/src/Messaging/Support/AbstractChannelInterceptor.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Order; - -namespace Steeltoe.Messaging.Support; - -public abstract class AbstractChannelInterceptor : AbstractOrdered, IChannelInterceptor -{ - protected AbstractChannelInterceptor() - { - } - - protected AbstractChannelInterceptor(int order) - : base(order) - { - } - - public virtual void AfterReceiveCompletion(IMessage message, IMessageChannel channel, Exception exception) - { - } - - public virtual void AfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception exception) - { - } - - public virtual IMessage PostReceive(IMessage message, IMessageChannel channel) - { - return message; - } - - public virtual void PostSend(IMessage message, IMessageChannel channel, bool sent) - { - } - - public virtual bool PreReceive(IMessageChannel channel) - { - return true; - } - - public virtual IMessage PreSend(IMessage message, IMessageChannel channel) - { - return message; - } -} diff --git a/src/Messaging/src/Messaging/Support/AbstractHeaderMapper.cs b/src/Messaging/src/Messaging/Support/AbstractHeaderMapper.cs deleted file mode 100644 index b36884c63b..0000000000 --- a/src/Messaging/src/Messaging/Support/AbstractHeaderMapper.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Messaging.Support; - -public abstract class AbstractHeaderMapper : IHeaderMapper -{ - protected readonly ILogger Logger; - - private string _inboundPrefix = string.Empty; - - private string _outboundPrefix = string.Empty; - - public string InboundPrefix - { - get => _inboundPrefix; - set => _inboundPrefix = value ?? string.Empty; - } - - public string OutboundPrefix - { - get => _outboundPrefix; - set => _outboundPrefix = value ?? string.Empty; - } - - protected AbstractHeaderMapper(ILogger logger = null) - { - Logger = logger; - } - - public abstract void FromHeaders(IMessageHeaders headers, T target); - - public abstract IMessageHeaders ToHeaders(T source); - - protected virtual string FromHeaderName(string headerName) - { - string propertyName = headerName; - - if (!string.IsNullOrEmpty(_outboundPrefix) && !propertyName.StartsWith(_outboundPrefix, StringComparison.Ordinal)) - { - propertyName = _outboundPrefix + headerName; - } - - return propertyName; - } - - protected virtual string ToHeaderName(string propertyName) - { - string headerName = propertyName; - - if (!string.IsNullOrEmpty(_inboundPrefix) && !headerName.StartsWith(_inboundPrefix, StringComparison.Ordinal)) - { - headerName = _inboundPrefix + propertyName; - } - - return headerName; - } - - protected virtual TValue GetHeaderIfAvailable(IDictionary headers, string name) - { - headers.TryGetValue(name, out object value); - - if (value == null) - { - return default; - } - - Type type = typeof(TValue); - - if (!type.IsInstanceOfType(value)) - { - Logger?.LogDebug("Skipping header '{headerName}': expected type [{type}], but got [{valueType}]", name, type, value.GetType()); - return default; - } - - return (TValue)value; - } -} diff --git a/src/Messaging/src/Messaging/Support/AbstractMessageBuilder.cs b/src/Messaging/src/Messaging/Support/AbstractMessageBuilder.cs deleted file mode 100644 index b1b88de448..0000000000 --- a/src/Messaging/src/Messaging/Support/AbstractMessageBuilder.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; - -namespace Steeltoe.Messaging.Support; - -public abstract class AbstractMessageBuilder -{ - protected readonly object Payload; - - protected readonly IMessage OriginalMessage; - - protected MessageHeaderAccessor headerAccessor; - - protected AbstractMessageBuilder() - { - } - - protected AbstractMessageBuilder(IMessage message) - { - ArgumentGuard.NotNull(message); - - Payload = message.Payload; - OriginalMessage = message; - headerAccessor = new MessageHeaderAccessor(message); - } - - protected AbstractMessageBuilder(MessageHeaderAccessor accessor) - { - ArgumentGuard.NotNull(accessor); - - Payload = null; - OriginalMessage = null; - headerAccessor = accessor; - } - - protected AbstractMessageBuilder(object payload, MessageHeaderAccessor accessor) - { - ArgumentGuard.NotNull(payload); - ArgumentGuard.NotNull(accessor); - - Payload = payload; - OriginalMessage = null; - headerAccessor = accessor; - } - - public abstract AbstractMessageBuilder SetHeaders(MessageHeaderAccessor accessor); - - public abstract AbstractMessageBuilder SetHeader(string headerName, object headerValue); - - public abstract AbstractMessageBuilder SetHeaderIfAbsent(string headerName, object headerValue); - - public abstract AbstractMessageBuilder RemoveHeaders(params string[] headerPatterns); - - public abstract AbstractMessageBuilder RemoveHeader(string headerName); - - public abstract AbstractMessageBuilder CopyHeaders(IDictionary headersToCopy); - - public abstract AbstractMessageBuilder CopyHeadersIfAbsent(IDictionary headersToCopy); - - public abstract AbstractMessageBuilder SetReplyChannel(IMessageChannel replyChannel); - - public abstract AbstractMessageBuilder SetReplyChannelName(string replyChannelName); - - public abstract AbstractMessageBuilder SetErrorChannel(IMessageChannel errorChannel); - - public abstract AbstractMessageBuilder SetErrorChannelName(string errorChannelName); - - public virtual IMessage Build() - { - if (OriginalMessage != null && !headerAccessor.IsModified) - { - return OriginalMessage; - } - - IMessageHeaders headersToUse = headerAccessor.ToMessageHeaders(); - return Message.Create(Payload, headersToUse, Payload.GetType()); - } -} diff --git a/src/Messaging/src/Messaging/Support/AbstractMessageChannel.cs b/src/Messaging/src/Messaging/Support/AbstractMessageChannel.cs deleted file mode 100644 index 330df85e82..0000000000 --- a/src/Messaging/src/Messaging/Support/AbstractMessageChannel.cs +++ /dev/null @@ -1,317 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Threading.Channels; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Order; - -namespace Steeltoe.Messaging.Support; - -public abstract class AbstractMessageChannel : Channel, IMessageChannel, IInterceptableChannel -{ - public const int IndefiniteTimeout = -1; - - private readonly object _lock = new(); - private List _interceptors = new(); - - public virtual string ServiceName { get; set; } - - public ILogger Logger { get; set; } - - protected AbstractMessageChannel(ILogger logger = null) - { - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - Logger = logger; - } - - public virtual void SetInterceptors(List interceptors) - { - lock (_lock) - { - interceptors.Sort(new OrderComparer()); - _interceptors = interceptors; - } - } - - public virtual void AddInterceptor(IChannelInterceptor interceptor) - { - lock (_lock) - { - var interceptors = new List(_interceptors) - { - interceptor - }; - - _interceptors = interceptors; - } - } - - public virtual void AddInterceptor(int index, IChannelInterceptor interceptor) - { - lock (_lock) - { - var interceptors = new List(_interceptors); - interceptors.Insert(index, interceptor); - _interceptors = interceptors; - } - } - - public virtual List GetInterceptors() - { - lock (_lock) - { - return new List(_interceptors); - } - } - - public virtual bool RemoveInterceptor(IChannelInterceptor interceptor) - { - lock (_lock) - { - var interceptors = new List(_interceptors); - bool result = interceptors.Remove(interceptor); - _interceptors = interceptors; - return result; - } - } - - public virtual IChannelInterceptor RemoveInterceptor(int index) - { - lock (_lock) - { - var interceptors = new List(_interceptors); - IChannelInterceptor existing = interceptors[index]; - interceptors.RemoveAt(index); - _interceptors = interceptors; - return existing; - } - } - - public virtual bool Send(IMessage message) - { - return Send(message, IndefiniteTimeout); - } - - public virtual bool Send(IMessage message, int timeout) - { - ArgumentGuard.NotNull(message); - - return DoSend(message, timeout); - } - - public virtual ValueTask SendAsync(IMessage message, CancellationToken cancellationToken = default) - { - return new ValueTask(DoSend(message, cancellationToken)); - } - - public override string ToString() - { - return ServiceName; - } - - protected virtual bool DoSend(IMessage message, int timeout) - { - if (timeout <= 0) - { - return DoSend(message, CancellationToken.None); - } - - using var source = new CancellationTokenSource(); - source.CancelAfter(timeout); - return DoSend(message, source.Token); - } - - protected virtual bool DoSend(IMessage message, CancellationToken cancellationToken) - { - ArgumentGuard.NotNull(message); - - IMessage messageToUse = message; - List interceptors = _interceptors; - ChannelInterceptorChain chain = null; - - if (interceptors.Count > 0) - { - chain = new ChannelInterceptorChain(this); - } - - bool sent = false; - - try - { - if (chain != null) - { - messageToUse = chain.ApplyPreSend(messageToUse, this); - - if (messageToUse == null) - { - return false; - } - } - - sent = DoSendInternal(messageToUse, cancellationToken); - chain?.ApplyPostSend(messageToUse, this, sent); - chain?.TriggerAfterSendCompletion(messageToUse, this, sent, null); - return sent; - } - catch (Exception ex) - { - chain?.TriggerAfterSendCompletion(messageToUse, this, sent, ex); - - if (ex is MessagingException) - { - throw; - } - - throw new MessageDeliveryException(messageToUse, $"Failed to send message to {ServiceName}", ex); - } - } - - protected abstract bool DoSendInternal(IMessage message, CancellationToken cancellationToken); - - protected class ChannelInterceptorChain - { - private readonly AbstractMessageChannel _channel; - private readonly List _interceptors; - private int _sendInterceptorIndex; - - private int _receiveInterceptorIndex; - - public ChannelInterceptorChain(AbstractMessageChannel channel) - { - _channel = channel; - _interceptors = channel._interceptors; - _sendInterceptorIndex = -1; - _receiveInterceptorIndex = -1; - } - - public IMessage ApplyPreSend(IMessage message, IMessageChannel channel) - { - if (_interceptors.Count == 0) - { - return message; - } - - IMessage messageToUse = message; - - foreach (IChannelInterceptor interceptor in _interceptors) - { - IMessage resolvedMessage = interceptor.PreSend(messageToUse, channel); - - if (resolvedMessage == null) - { - string name = interceptor.GetType().Name; - _channel.Logger?.LogDebug("{name} returned null from PreSend, i.e. precluding the send.", name); - TriggerAfterSendCompletion(messageToUse, channel, false, null); - return null; - } - - messageToUse = resolvedMessage; - _sendInterceptorIndex++; - } - - return messageToUse; - } - - public void ApplyPostSend(IMessage message, IMessageChannel channel, bool sent) - { - if (_interceptors.Count == 0) - { - return; - } - - foreach (IChannelInterceptor interceptor in _interceptors) - { - interceptor.PostSend(message, channel, sent); - } - } - - public void TriggerAfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception ex) - { - if (_sendInterceptorIndex == -1) - { - return; - } - - for (int i = _sendInterceptorIndex; i >= 0; i--) - { - IChannelInterceptor interceptor = _interceptors[i]; - - try - { - interceptor.AfterSendCompletion(message, channel, sent, ex); - } - catch (Exception ex2) - { - _channel.Logger?.LogError(ex2, "Exception from afterSendCompletion in {interceptor} ", interceptor); - } - } - } - - public bool ApplyPreReceive(IMessageChannel channel) - { - if (_interceptors.Count == 0) - { - return true; - } - - foreach (IChannelInterceptor interceptor in _interceptors) - { - if (!interceptor.PreReceive(channel)) - { - TriggerAfterReceiveCompletion(null, channel, null); - return false; - } - - _receiveInterceptorIndex++; - } - - return true; - } - - public IMessage ApplyPostReceive(IMessage message, IMessageChannel channel) - { - if (_interceptors.Count == 0) - { - return message; - } - - IMessage messageToUse = message; - - foreach (IChannelInterceptor interceptor in _interceptors) - { - messageToUse = interceptor.PostReceive(messageToUse, channel); - - if (messageToUse == null) - { - return null; - } - } - - return messageToUse; - } - - public void TriggerAfterReceiveCompletion(IMessage message, IMessageChannel channel, Exception ex) - { - if (_receiveInterceptorIndex == -1) - { - return; - } - - for (int i = _receiveInterceptorIndex; i >= 0; i--) - { - IChannelInterceptor interceptor = _interceptors[i]; - - try - { - interceptor.AfterReceiveCompletion(message, channel, ex); - } - catch (Exception ex2) - { - _channel.Logger?.LogError(ex2, "Exception from afterReceiveCompletion in: {interceptor} ", interceptor); - } - } - } - } -} diff --git a/src/Messaging/src/Messaging/Support/AbstractMessageChannelWriter.cs b/src/Messaging/src/Messaging/Support/AbstractMessageChannelWriter.cs deleted file mode 100644 index 1442089f74..0000000000 --- a/src/Messaging/src/Messaging/Support/AbstractMessageChannelWriter.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Threading.Channels; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; - -namespace Steeltoe.Messaging.Support; - -public abstract class AbstractMessageChannelWriter : ChannelWriter -{ - protected AbstractMessageChannel channel; - protected ILogger logger; - - protected AbstractMessageChannelWriter(AbstractMessageChannel channel, ILogger logger = null) - { - ArgumentGuard.NotNull(channel); - - this.channel = channel; - this.logger = logger; - } - - public override bool TryComplete(Exception error = null) - { - return false; - } - - public override bool TryWrite(IMessage message) - { - return channel.Send(message); - } - - public override ValueTask WaitToWriteAsync(CancellationToken cancellationToken = default) - { - return cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) : new ValueTask(true); - } - - public override ValueTask WriteAsync(IMessage message, CancellationToken cancellationToken = default) - { - if (TryWrite(message)) - { - return default; - } - - return new ValueTask(Task.FromException(new MessageDeliveryException(message))); - } -} diff --git a/src/Messaging/src/Messaging/Support/AbstractSubscribableChannel.cs b/src/Messaging/src/Messaging/Support/AbstractSubscribableChannel.cs deleted file mode 100644 index b23544a057..0000000000 --- a/src/Messaging/src/Messaging/Support/AbstractSubscribableChannel.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Messaging.Support; - -public abstract class AbstractSubscribableChannel : AbstractMessageChannel, ISubscribableChannel -{ - private readonly object _lock = new(); - internal HashSet Handlers = new(); - - public virtual int SubscriberCount => Handlers.Count; - - public virtual ISet Subscribers - { - get - { - lock (_lock) - { - return new HashSet(Handlers); - } - } - } - - protected AbstractSubscribableChannel(ILogger logger = null) - : base(logger) - { - } - - public virtual bool HasSubscription(IMessageHandler handler) - { - lock (_lock) - { - return Handlers.Contains(handler); - } - } - - public virtual bool Subscribe(IMessageHandler handler) - { - lock (_lock) - { - var handlers = new HashSet(Handlers); - bool result = handlers.Add(handler); - - if (result) - { - Logger?.LogDebug("{serviceName} added to {handler} ", ServiceName, handler); - Handlers = handlers; - } - - return result; - } - } - - public virtual bool Unsubscribe(IMessageHandler handler) - { - lock (_lock) - { - var handlers = new HashSet(Handlers); - bool result = handlers.Remove(handler); - - if (result) - { - Logger?.LogDebug("{serviceName} removed from {handler} ", ServiceName, handler); - Handlers = handlers; - } - - return result; - } - } -} diff --git a/src/Messaging/src/Messaging/Support/AbstractTaskSchedulerChannelInterceptor.cs b/src/Messaging/src/Messaging/Support/AbstractTaskSchedulerChannelInterceptor.cs deleted file mode 100644 index 88594d97a2..0000000000 --- a/src/Messaging/src/Messaging/Support/AbstractTaskSchedulerChannelInterceptor.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Support; - -public abstract class AbstractTaskSchedulerChannelInterceptor : AbstractChannelInterceptor, ITaskSchedulerChannelInterceptor -{ - public virtual void AfterMessageHandled(IMessage message, IMessageChannel channel, IMessageHandler handler, Exception exception) - { - } - - public virtual IMessage BeforeHandled(IMessage message, IMessageChannel channel, IMessageHandler handler) - { - return message; - } -} diff --git a/src/Messaging/src/Messaging/Support/ErrorMessage.cs b/src/Messaging/src/Messaging/Support/ErrorMessage.cs deleted file mode 100644 index ada29d736d..0000000000 --- a/src/Messaging/src/Messaging/Support/ErrorMessage.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Support; - -public class ErrorMessage : Message -{ - public IMessage OriginalMessage { get; } - - public ErrorMessage(Exception payload) - : base(payload) - { - OriginalMessage = null; - } - - public ErrorMessage(Exception payload, IDictionary headers) - : base(payload, headers) - { - OriginalMessage = null; - } - - public ErrorMessage(Exception payload, IMessageHeaders headers) - : base(payload, headers) - { - OriginalMessage = null; - } - - public ErrorMessage(Exception payload, IMessage originalMessage) - : base(payload) - { - OriginalMessage = originalMessage; - } - - public ErrorMessage(Exception payload, IDictionary headers, IMessage originalMessage) - : base(payload, headers) - { - OriginalMessage = originalMessage; - } - - public ErrorMessage(Exception payload, IMessageHeaders headers, IMessage originalMessage) - : base(payload, headers) - { - OriginalMessage = originalMessage; - } - - public override string ToString() - { - if (OriginalMessage == null) - { - return base.ToString(); - } - - return $"{base.ToString()} for original {OriginalMessage}"; - } -} diff --git a/src/Messaging/src/Messaging/Support/IdTimestampMessageHeaderInitializer.cs b/src/Messaging/src/Messaging/Support/IdTimestampMessageHeaderInitializer.cs deleted file mode 100644 index f48893af19..0000000000 --- a/src/Messaging/src/Messaging/Support/IdTimestampMessageHeaderInitializer.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Support; - -public class IdTimestampMessageHeaderInitializer : IMessageHeaderInitializer -{ - public IIdGenerator IdGenerator { get; set; } - - public bool EnableTimestamp { get; set; } - - public void SetDisableIdGeneration() - { - IdGenerator = new DisabledIdGenerator(); - } - - public void InitHeaders(IMessageHeaderAccessor headerAccessor) - { - IIdGenerator idGenerator = IdGenerator; - - if (idGenerator != null) - { - headerAccessor.IdGenerator = idGenerator; - } - - headerAccessor.EnableTimestamp = EnableTimestamp; - } - - internal sealed class DisabledIdGenerator : IIdGenerator - { - public string GenerateId() - { - return MessageHeaders.IdValueNone; - } - } -} diff --git a/src/Messaging/src/Messaging/Support/ImmutableMessageChannelInterceptor.cs b/src/Messaging/src/Messaging/Support/ImmutableMessageChannelInterceptor.cs deleted file mode 100644 index 6dc619f09f..0000000000 --- a/src/Messaging/src/Messaging/Support/ImmutableMessageChannelInterceptor.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Support; - -public class ImmutableMessageChannelInterceptor : AbstractChannelInterceptor -{ - public ImmutableMessageChannelInterceptor() - : base(0) - { - } - - public ImmutableMessageChannelInterceptor(int order) - : base(order) - { - } - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetAccessor(message, typeof(MessageHeaderAccessor)); - - if (accessor != null && accessor.IsMutable) - { - accessor.SetImmutable(); - } - - return message; - } -} diff --git a/src/Messaging/src/Messaging/Support/MessageBuilder.cs b/src/Messaging/src/Messaging/Support/MessageBuilder.cs deleted file mode 100644 index fe2786c022..0000000000 --- a/src/Messaging/src/Messaging/Support/MessageBuilder.cs +++ /dev/null @@ -1,195 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common; - -namespace Steeltoe.Messaging.Support; - -public static class MessageBuilder -{ - public static AbstractMessageBuilder FromMessage(IMessage message) - { - return new MessageBuilder(message); - } - - public static AbstractMessageBuilder FromMessage(IMessage message, Type payloadType = null) - { - Type genParamType = GetGenericParamType(message, payloadType); - Type typeToCreate = typeof(MessageBuilder<>).MakeGenericType(genParamType); - - return (AbstractMessageBuilder)Activator.CreateInstance(typeToCreate, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, - new object[] - { - message - }, null, null); - } - - public static AbstractMessageBuilder WithPayload(TPayload payload) - { - return new MessageBuilder(payload, new MessageHeaderAccessor()); - } - - public static AbstractMessageBuilder WithPayload(object payload, Type payloadType = null) - { - Type genParamType = GetGenericParamType(payload, payloadType); - Type typeToCreate = typeof(MessageBuilder<>).MakeGenericType(genParamType); - - return (AbstractMessageBuilder)Activator.CreateInstance(typeToCreate, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new[] - { - payload, - new MessageHeaderAccessor() - }, null, null); - } - - public static IMessage CreateMessage(TPayload payload, IMessageHeaders messageHeaders) - { - return (IMessage)CreateMessage(payload, messageHeaders, typeof(TPayload)); - } - - public static IMessage CreateMessage(object payload, IMessageHeaders messageHeaders, Type payloadType = null) - { - ArgumentGuard.NotNull(payload); - ArgumentGuard.NotNull(messageHeaders); - - return Message.Create(payload, messageHeaders, payloadType); - } - - public static Type GetGenericParamType(IMessage target, Type messagePayloadType) - { - if (target == null && messagePayloadType == null) - { - return typeof(object); - } - - if (messagePayloadType != null) - { - return messagePayloadType; - } - - Type targetType = target.GetType(); - - if (targetType.IsGenericType) - { - return targetType.GetGenericArguments()[0]; - } - - return typeof(object); - } - - public static Type GetGenericParamType(object payload, Type messagePayloadType) - { - if (payload == null && messagePayloadType == null) - { - return typeof(object); - } - - if (messagePayloadType != null) - { - return messagePayloadType; - } - - return payload.GetType(); - } -} - -public class MessageBuilder : AbstractMessageBuilder -{ - protected internal MessageBuilder() - { - } - - protected internal MessageBuilder(IMessage message) - : base(message) - { - } - - protected internal MessageBuilder(IMessage message) - : base(message) - { - } - - protected internal MessageBuilder(MessageHeaderAccessor accessor) - : base(accessor) - { - } - - protected internal MessageBuilder(TPayload payload, MessageHeaderAccessor accessor) - : base(payload, accessor) - { - } - - public override AbstractMessageBuilder SetHeaders(MessageHeaderAccessor accessor) - { - ArgumentGuard.NotNull(accessor); - - headerAccessor = accessor; - return this; - } - - public override AbstractMessageBuilder SetHeader(string headerName, object headerValue) - { - headerAccessor.SetHeader(headerName, headerValue); - return this; - } - - public override AbstractMessageBuilder SetHeaderIfAbsent(string headerName, object headerValue) - { - headerAccessor.SetHeaderIfAbsent(headerName, headerValue); - return this; - } - - public override AbstractMessageBuilder RemoveHeaders(params string[] headerPatterns) - { - headerAccessor.RemoveHeaders(headerPatterns); - return this; - } - - public override AbstractMessageBuilder RemoveHeader(string headerName) - { - headerAccessor.RemoveHeader(headerName); - return this; - } - - public override AbstractMessageBuilder CopyHeaders(IDictionary headersToCopy) - { - headerAccessor.CopyHeaders(headersToCopy); - return this; - } - - public override AbstractMessageBuilder CopyHeadersIfAbsent(IDictionary headersToCopy) - { - headerAccessor.CopyHeadersIfAbsent(headersToCopy); - return this; - } - - public override AbstractMessageBuilder SetReplyChannel(IMessageChannel replyChannel) - { - headerAccessor.ReplyChannel = replyChannel; - return this; - } - - public override AbstractMessageBuilder SetReplyChannelName(string replyChannelName) - { - headerAccessor.ReplyChannelName = replyChannelName; - return this; - } - - public override AbstractMessageBuilder SetErrorChannel(IMessageChannel errorChannel) - { - headerAccessor.ErrorChannel = errorChannel; - return this; - } - - public override AbstractMessageBuilder SetErrorChannelName(string errorChannelName) - { - headerAccessor.ErrorChannelName = errorChannelName; - return this; - } - - public new IMessage Build() - { - return (IMessage)base.Build(); - } -} diff --git a/src/Messaging/src/Messaging/Support/MessageHeaderAccessor.cs b/src/Messaging/src/Messaging/Support/MessageHeaderAccessor.cs deleted file mode 100644 index 4056fc2415..0000000000 --- a/src/Messaging/src/Messaging/Support/MessageHeaderAccessor.cs +++ /dev/null @@ -1,545 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Text; -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.Support; - -public class MessageHeaderAccessor : IMessageHeaderAccessor -{ - private static readonly MimeType[] ReadableMimeTypes = - { - MimeTypeUtils.ApplicationJson, - MimeTypeUtils.ApplicationXml, - new("text", "*"), - new("application", "*+json"), - new("application", "*+xml") - }; - - public static readonly Encoding DefaultCharset = Encoding.UTF8; - - private bool _leaveMutable; - - protected AccessorMessageHeaders headers; - - private Encoding Encoding - { - get - { - var contentType = MimeType.ToMimeType(ContentType); - Encoding charset = contentType?.Encoding; - return charset ?? DefaultCharset; - } - } - - public virtual bool EnableTimestamp { get; set; } - - public virtual IIdGenerator IdGenerator { get; set; } - - public virtual bool LeaveMutable - { - get => _leaveMutable; - set - { - if (!headers.IsMutable) - { - throw new InvalidOperationException("Already immutable"); - } - - _leaveMutable = value; - } - } - - public virtual bool IsMutable => headers.IsMutable; - - public virtual bool IsModified { get; set; } - - public virtual IMessageHeaders MessageHeaders - { - get - { - if (!_leaveMutable) - { - SetImmutable(); - } - - return headers; - } - } - - public virtual string Id - { - get - { - if (GetHeader(Messaging.MessageHeaders.IdName) == null) - { - return null; - } - - return GetHeader(Messaging.MessageHeaders.IdName).ToString(); - } - } - - public virtual long? Timestamp - { - get - { - object value = GetHeader(Messaging.MessageHeaders.TimestampName); - - if (value == null) - { - return null; - } - - return value is long longVal ? longVal : long.Parse(value.ToString(), CultureInfo.InvariantCulture); - } - } - - public virtual string ContentType - { - get - { - object value = GetHeader(Messaging.MessageHeaders.ContentType); - - if (value == null) - { - return null; - } - - return value.ToString(); - } - set => SetHeader(Messaging.MessageHeaders.ContentType, value); - } - - public virtual string ReplyChannelName - { - get => GetHeader(Messaging.MessageHeaders.ReplyChannelName) as string; - set => SetHeader(Messaging.MessageHeaders.ReplyChannelName, value); - } - - public virtual object ReplyChannel - { - get => GetHeader(Messaging.MessageHeaders.ReplyChannelName); - set => SetHeader(Messaging.MessageHeaders.ReplyChannelName, value); - } - - public virtual string ErrorChannelName - { - get => GetHeader(Messaging.MessageHeaders.ErrorChannelName) as string; - set => SetHeader(Messaging.MessageHeaders.ErrorChannelName, value); - } - - public virtual object ErrorChannel - { - get => GetHeader(Messaging.MessageHeaders.ErrorChannelName); - set => SetHeader(Messaging.MessageHeaders.ErrorChannelName, value); - } - - public MessageHeaderAccessor() - : this((IMessage)null) - { - } - - public MessageHeaderAccessor(IMessage message) - { - headers = new AccessorMessageHeaders(this, message?.Headers); - } - - public MessageHeaderAccessor(MessageHeaders headers) - { - this.headers = new AccessorMessageHeaders(this, headers); - } - - public static T GetAccessor(IMessage message) - where T : MessageHeaderAccessor - { - return (T)GetAccessor(message.Headers, typeof(T)); - } - - public static MessageHeaderAccessor GetAccessor(IMessage message, Type accessorType) - { - return GetAccessor(message.Headers, accessorType); - } - - public static T GetAccessor(IMessageHeaders messageHeaders) - where T : MessageHeaderAccessor - { - return (T)GetAccessor(messageHeaders, typeof(T)); - } - - public static MessageHeaderAccessor GetAccessor(IMessageHeaders messageHeaders, Type accessorType) - { - if (messageHeaders is AccessorMessageHeaders accessorMessageHeaders) - { - MessageHeaderAccessor headerAccessor = accessorMessageHeaders.Accessor; - - if (accessorType == null || accessorType.IsInstanceOfType(headerAccessor)) - { - return headerAccessor; - } - } - - return null; - } - - public static T GetMutableAccessor(IMessage message) - where T : MessageHeaderAccessor - { - return (T)GetMutableAccessor(message, typeof(T)); - } - - public static T GetMutableAccessor(IMessageHeaders messageHeaders) - where T : MessageHeaderAccessor - { - return (T)GetMutableAccessor(messageHeaders, typeof(T)); - } - - public static MessageHeaderAccessor GetMutableAccessor(IMessage message, Type accessorType = null) - { - return GetMutableAccessor(message.Headers, accessorType); - } - - public static MessageHeaderAccessor GetMutableAccessor(IMessageHeaders headers, Type accessorType = null) - { - MessageHeaderAccessor messageHeaderAccessor = null; - - if (headers is AccessorMessageHeaders accessorMessageHeaders) - { - MessageHeaderAccessor headerAccessor = accessorMessageHeaders.Accessor; - - if (accessorType == null || accessorType.IsInstanceOfType(headerAccessor)) - { - messageHeaderAccessor = headerAccessor.IsMutable ? headerAccessor : headerAccessor.CreateMutableAccessor(headers); - } - } - - if (messageHeaderAccessor == null && accessorType == null && headers is MessageHeaders msgHeaders) - { - messageHeaderAccessor = new MessageHeaderAccessor(msgHeaders); - } - - return messageHeaderAccessor; - } - - public virtual void SetImmutable() - { - headers.SetImmutable(); - } - - public virtual IMessageHeaders ToMessageHeaders() - { - return new MessageHeaders(headers); - } - - public virtual IDictionary ToDictionary() - { - return new Dictionary(headers); - } - - // Generic header accessors - public virtual object GetHeader(string headerName) - { - return headers.TryGetValue(headerName, out object value) ? value : null; - } - - public virtual void SetHeader(string name, object value) - { - if (IsReadOnly(name)) - { - throw new ArgumentException($"'{name}' header is read-only.", nameof(name)); - } - - VerifyType(name, value); - - if (value != null) - { - // Modify header if necessary - if (!ObjectEquality.ObjectOrCollectionEquals(value, GetHeader(name))) - { - IsModified = true; - headers.RawHeaders[name] = value; - } - } - else - { - // Remove header if available - if (headers.ContainsKey(name)) - { - IsModified = true; - headers.RawHeaders.Remove(name); - } - } - } - - public virtual void SetHeaderIfAbsent(string name, object value) - { - if (GetHeader(name) == null) - { - SetHeader(name, value); - } - } - - public virtual void RemoveHeader(string headerName) - { - if (!string.IsNullOrEmpty(headerName) && !IsReadOnly(headerName)) - { - SetHeader(headerName, null); - } - } - - public virtual void RemoveHeaders(params string[] headerPatterns) - { - var headersToRemove = new List(); - - foreach (string pattern in headerPatterns) - { - if (!string.IsNullOrEmpty(pattern)) - { - if (pattern.Contains('*')) - { - headersToRemove.AddRange(GetMatchingHeaderNames(pattern, headers)); - } - else - { - headersToRemove.Add(pattern); - } - } - } - - foreach (string headerToRemove in headersToRemove) - { - RemoveHeader(headerToRemove); - } - } - - public virtual void CopyHeaders(IDictionary headersToCopy) - { - if (headersToCopy != null) - { - foreach (KeyValuePair kvp in headersToCopy) - { - if (!IsReadOnly(kvp.Key)) - { - SetHeader(kvp.Key, kvp.Value); - } - } - } - } - - public virtual void CopyHeadersIfAbsent(IDictionary headersToCopy) - { - if (headersToCopy != null) - { - foreach (KeyValuePair kvp in headersToCopy) - { - if (!IsReadOnly(kvp.Key)) - { - SetHeaderIfAbsent(kvp.Key, kvp.Value); - } - } - } - } - - // Log message stuff - public virtual string GetShortLogMessage(object payload) - { - return $"headers={headers}{GetShortPayloadLogMessage(payload)}"; - } - - public virtual string GetDetailedLogMessage(object payload) - { - return $"headers={headers}{GetDetailedPayloadLogMessage(payload)}"; - } - - public override string ToString() - { - return $"{GetType().Name} [headers={headers}]"; - } - - protected virtual MessageHeaderAccessor CreateMutableAccessor(IMessage message) - { - return CreateMutableAccessor(message.Headers); - } - - protected virtual MessageHeaderAccessor CreateMutableAccessor(IMessageHeaders messageHeaders) - { - if (messageHeaders is not MessageHeaders asHeaders) - { - throw new InvalidOperationException( - $"Unable to create mutable accessor, message has no headers or headers are not of type {nameof(MessageHeaders)}."); - } - - return new MessageHeaderAccessor(asHeaders); - } - - protected virtual bool IsReadOnly(string headerName) - { - return headerName == Messaging.MessageHeaders.IdName || headerName == Messaging.MessageHeaders.TimestampName; - } - - protected virtual void VerifyType(string headerName, object headerValue) - { - if (headerName == null || headerValue == null) - { - return; - } - - if (headerName != Messaging.MessageHeaders.ErrorChannelName && - !Messaging.MessageHeaders.ReplyChannelName.EndsWith(headerName, StringComparison.Ordinal)) - { - return; - } - - if (headerValue is not (IMessageChannel or string)) - { - throw new ArgumentException($"'{headerName}' header value must be a {nameof(IMessageChannel)} or {nameof(String)}.", nameof(headerValue)); - } - } - - protected virtual string GetShortPayloadLogMessage(object payload) - { - switch (payload) - { - case string sPayload: - { - string payloadText = sPayload; - return payloadText.Length < 80 ? $" payload={payloadText}" : $" payload={payloadText.Substring(0, 80)}...(truncated)"; - } - - case byte[] bytes: - if (IsReadableContentType()) - { - return bytes.Length < 80 - ? $" payload={new string(Encoding.GetChars(bytes))}" - : $" payload={new string(Encoding.GetChars(bytes, 0, 80))}...(truncated)"; - } - - return $" payload=byte[{bytes.Length}]"; - - default: - { - string payloadText = payload.ToString(); - return payloadText.Length < 80 ? $" payload={payloadText}" : $" payload={payload.GetType().Name}@{payload}"; - } - } - } - - protected virtual string GetDetailedPayloadLogMessage(object payload) - { - if (payload is string) - { - return $" payload={payload}"; - } - - if (payload is byte[] bytes) - { - if (IsReadableContentType()) - { - return $" payload={new string(Encoding.GetChars(bytes))}"; - } - - return $" payload=byte[{bytes.Length}]"; - } - - return $" payload={payload}"; - } - - protected virtual bool IsReadableContentType() - { - var contentType = MimeType.ToMimeType(ContentType); - - foreach (MimeType mimeType in ReadableMimeTypes) - { - if (mimeType.Includes(contentType)) - { - return true; - } - } - - return false; - } - - private List GetMatchingHeaderNames(string pattern, IDictionary headers) - { - if (headers == null) - { - return new List(); - } - - var matchingHeaderNames = new List(); - - foreach (string key in headers.Keys) - { - if (PatternMatchUtils.SimpleMatch(pattern, key)) - { - matchingHeaderNames.Add(key); - } - } - - return matchingHeaderNames; - } - - protected class AccessorMessageHeaders : MessageHeaders - { - private bool _mutable = true; - protected MessageHeaderAccessor accessor; - - public new IDictionary RawHeaders - { - get - { - if (!_mutable) - { - throw new InvalidOperationException(); - } - - return base.RawHeaders; - } - } - - public virtual bool IsMutable => _mutable; - - public virtual MessageHeaderAccessor Accessor => accessor; - - public AccessorMessageHeaders(MessageHeaderAccessor accessor, IDictionary headers) - : base(headers, IdValueNone, -1L) - { - this.accessor = accessor; - } - - public AccessorMessageHeaders(MessageHeaderAccessor accessor, MessageHeaders other) - : base(other) - { - this.accessor = accessor; - } - - public virtual void SetImmutable() - { - if (!_mutable) - { - return; - } - - if (Id == null) - { - IIdGenerator idGenerator = accessor.IdGenerator ?? IdGenerator; - string id = idGenerator.GenerateId(); - - if (id != IdValueNone) - { - RawHeaders[IdName] = id; - } - } - - if (Timestamp == null && accessor.EnableTimestamp) - { - RawHeaders[TimestampName] = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - } - - _mutable = false; - } - } -} diff --git a/src/Messaging/src/Messaging/Support/NativeMessageHeaderAccessor.cs b/src/Messaging/src/Messaging/Support/NativeMessageHeaderAccessor.cs deleted file mode 100644 index c151f9ea00..0000000000 --- a/src/Messaging/src/Messaging/Support/NativeMessageHeaderAccessor.cs +++ /dev/null @@ -1,231 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Support; - -public class NativeMessageHeaderAccessor : MessageHeaderAccessor -{ - public const string NativeHeaders = "nativeHeaders"; - - private static readonly Dictionary> Empty = new(); - - protected internal NativeMessageHeaderAccessor() - : this((IDictionary>)null) - { - } - - protected internal NativeMessageHeaderAccessor(IDictionary> nativeHeaders) - { - if (nativeHeaders != null && nativeHeaders.Count > 0) - { - SetHeader(NativeHeaders, new Dictionary>(nativeHeaders)); - } - } - - protected internal NativeMessageHeaderAccessor(IMessage message) - : base(message) - { - if (message != null) - { - var map = (IDictionary>)GetHeader(NativeHeaders); - - if (map != null) - { - // Force removal since setHeader checks for equality - RemoveHeader(NativeHeaders); - SetHeader(NativeHeaders, new Dictionary>(map)); - } - } - } - - public static string GetFirstNativeHeader(string headerName, IDictionary headers) - { - headers.TryGetValue(NativeHeaders, out object obj); - - if (obj is IDictionary> map) - { - map.TryGetValue(headerName, out List values); - - if (values != null) - { - return values[0]; - } - } - - return null; - } - - public string GetFirstNativeHeader(string headerName) - { - IDictionary> map = GetNativeHeaders(); - - if (map != null) - { - map.TryGetValue(headerName, out List values); - - if (values != null) - { - return values[0]; - } - } - - return null; - } - - public virtual IDictionary> ToNativeHeaderDictionary() - { - IDictionary> map = GetNativeHeaders(); - return map != null ? new Dictionary>(map) : Empty; - } - - public override void SetImmutable() - { - if (IsMutable) - { - IDictionary> map = GetNativeHeaders(); - - if (map != null) - { - // Force removal since setHeader checks for equality - RemoveHeader(NativeHeaders); - SetHeader(NativeHeaders, new Dictionary>(map)); - } - - base.SetImmutable(); - } - } - - public bool ContainsNativeHeader(string headerName) - { - IDictionary> map = GetNativeHeaders(); - return map != null && map.ContainsKey(headerName); - } - - public List GetNativeHeader(string headerName) - { - IDictionary> map = GetNativeHeaders(); - - if (map != null) - { - map.TryGetValue(headerName, out List result); - return result; - } - - return null; - } - - public void SetNativeHeader(string name, string value) - { - if (!IsMutable) - { - throw new InvalidOperationException("Already immutable"); - } - - IDictionary> map = GetNativeHeaders(); - - if (value == null) - { - if (map != null && map.TryGetValue(name, out _)) - { - IsModified = true; - map.Remove(name); - } - - return; - } - - if (map == null) - { - map = new Dictionary>(); - SetHeader(NativeHeaders, map); - } - - var values = new List - { - value - }; - - if (!values.Equals(GetHeader(name))) - { - IsModified = true; - map[name] = values; - } - } - - public void AddNativeHeader(string name, string value) - { - if (!IsMutable) - { - throw new InvalidOperationException("Already immutable"); - } - - if (value == null) - { - return; - } - - IDictionary> nativeHeaders = GetNativeHeaders(); - - if (nativeHeaders == null) - { - nativeHeaders = new Dictionary>(); - SetHeader(NativeHeaders, nativeHeaders); - } - - if (!nativeHeaders.TryGetValue(name, out List values)) - { - values = new List(); - nativeHeaders.Add(name, values); - } - - values.Add(value); - IsModified = true; - } - - public void AddNativeHeaders(IDictionary> headers) - { - if (headers == null) - { - return; - } - - foreach (KeyValuePair> entry in headers) - { - string key = entry.Key; - List values = entry.Value; - - foreach (string val in values) - { - AddNativeHeader(key, val); - } - } - } - - public List RemoveNativeHeader(string name) - { - if (!IsMutable) - { - throw new InvalidOperationException("Already immutable"); - } - - IDictionary> nativeHeaders = GetNativeHeaders(); - - if (nativeHeaders == null) - { - return null; - } - - if (nativeHeaders.TryGetValue(name, out List existing)) - { - nativeHeaders.Remove(name); - } - - return existing; - } - - protected virtual IDictionary> GetNativeHeaders() - { - return (IDictionary>)GetHeader(NativeHeaders); - } -} diff --git a/src/Messaging/src/Messaging/Support/NotSupportedChannelReader.cs b/src/Messaging/src/Messaging/Support/NotSupportedChannelReader.cs deleted file mode 100644 index 5ba07b42b0..0000000000 --- a/src/Messaging/src/Messaging/Support/NotSupportedChannelReader.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Threading.Channels; - -namespace Steeltoe.Messaging.Support; - -public class NotSupportedChannelReader : ChannelReader -{ - public override Task Completion => throw new NotSupportedException("This channel does not implement ChannelReaders"); - - public override IAsyncEnumerable ReadAllAsync(CancellationToken cancellationToken = default) - { - throw new NotSupportedException("This channel does not implement ChannelReaders"); - } - - public override ValueTask ReadAsync(CancellationToken cancellationToken = default) - { - throw new NotSupportedException("This channel does not implement ChannelReaders"); - } - - public override bool TryRead(out IMessage item) - { - throw new NotSupportedException("This channel does not implement ChannelReaders"); - } - - public override ValueTask WaitToReadAsync(CancellationToken cancellationToken = default) - { - throw new NotSupportedException("This channel does not implement ChannelReaders"); - } -} diff --git a/src/Messaging/src/Messaging/Support/NotSupportedChannelWriter.cs b/src/Messaging/src/Messaging/Support/NotSupportedChannelWriter.cs deleted file mode 100644 index 3152572d9d..0000000000 --- a/src/Messaging/src/Messaging/Support/NotSupportedChannelWriter.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Threading.Channels; - -namespace Steeltoe.Messaging.Support; - -public class NotSupportedChannelWriter : ChannelWriter -{ - public override bool TryComplete(Exception error = null) - { - throw new NotSupportedException("This channel does not implement ChannelWriters"); - } - - public override bool TryWrite(IMessage item) - { - throw new NotSupportedException("This channel does not implement ChannelWriters"); - } - - public override ValueTask WaitToWriteAsync(CancellationToken cancellationToken = default) - { - throw new NotSupportedException("This channel does not implement ChannelWriters"); - } - - public override ValueTask WriteAsync(IMessage item, CancellationToken cancellationToken = default) - { - throw new NotSupportedException("This channel does not implement ChannelWriters"); - } -} diff --git a/src/Messaging/src/Messaging/Support/TaskSchedulerSubscribableChannel.cs b/src/Messaging/src/Messaging/Support/TaskSchedulerSubscribableChannel.cs deleted file mode 100644 index 2c569ea9a9..0000000000 --- a/src/Messaging/src/Messaging/Support/TaskSchedulerSubscribableChannel.cs +++ /dev/null @@ -1,237 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Messaging.Support; - -public class TaskSchedulerSubscribableChannel : AbstractSubscribableChannel -{ - private readonly object _lock = new(); - protected List schedulerInterceptors = new(); - - protected TaskScheduler Scheduler { get; } - - protected TaskFactory Factory { get; } - - public TaskSchedulerSubscribableChannel(ILogger logger = null) - : this(null, logger) - { - } - - public TaskSchedulerSubscribableChannel(TaskScheduler scheduler, ILogger logger = null) - : base(logger) - { - Scheduler = scheduler; - - if (Scheduler != null) - { - Factory = new TaskFactory(Scheduler); - } - - Writer = new TaskSchedulerSubscribableChannelWriter(this, logger); - Reader = new NotSupportedChannelReader(); - } - - public override void SetInterceptors(List interceptors) - { - base.SetInterceptors(interceptors); - - lock (_lock) - { - var newInterceptors = new List(); - - foreach (IChannelInterceptor interceptor in interceptors) - { - if (interceptor is ITaskSchedulerChannelInterceptor interceptor1) - { - newInterceptors.Add(interceptor1); - } - } - - schedulerInterceptors = newInterceptors; - } - } - - public override void AddInterceptor(IChannelInterceptor interceptor) - { - base.AddInterceptor(interceptor); - UpdateInterceptorsFor(interceptor); - } - - public override void AddInterceptor(int index, IChannelInterceptor interceptor) - { - base.AddInterceptor(index, interceptor); - UpdateInterceptorsFor(interceptor); - } - - protected override bool DoSendInternal(IMessage message, CancellationToken cancellationToken) - { - List interceptors = schedulerInterceptors; - HashSet handlers = Handlers; - - foreach (IMessageHandler handler in handlers) - { - if (Scheduler == null) - { - Invoke(interceptors, message, handler); - } - else - { - Task task = Factory.StartNew(() => Invoke(interceptors, message, handler), cancellationToken); - task.GetAwaiter().GetResult(); - } - } - - return true; - } - - private void Invoke(List interceptors, IMessage message, IMessageHandler handler) - { - if (interceptors.Count > 0) - { - var sendTask = new SendTask(this, interceptors, message, handler); - sendTask.Run(); - } - else - { - InvokeDirect(message, handler); - } - } - - private void InvokeDirect(IMessage message, IMessageHandler handler) - { - try - { - handler.HandleMessage(message); - } - catch (Exception ex) - { - if (ex is MessagingException) - { - throw; - } - - string description = $"Failed to handle {message} to {this} in {handler}"; - throw new MessageDeliveryException(message, description, ex); - } - } - - private void UpdateInterceptorsFor(IChannelInterceptor interceptor) - { - if (interceptor is ITaskSchedulerChannelInterceptor interceptor1) - { - lock (_lock) - { - var interceptors = new List(schedulerInterceptors) - { - interceptor1 - }; - - schedulerInterceptors = interceptors; - } - } - } - - private record struct SendTask : IMessageHandlingRunnable - { - private readonly TaskSchedulerSubscribableChannel _channel; - private readonly List _interceptors; - private int _interceptorIndex; - - public IMessage Message { get; } - - public IMessageHandler MessageHandler { get; } - - public SendTask(TaskSchedulerSubscribableChannel channel, List interceptors, IMessage message, - IMessageHandler messageHandler) - { - _channel = channel; - Message = message; - MessageHandler = messageHandler; - _interceptors = interceptors; - _interceptorIndex = -1; - } - - public bool Run() - { - IMessage message = Message; - - try - { - message = ApplyBeforeHandled(message); - - if (message == null) - { - return false; - } - - MessageHandler.HandleMessage(message); - TriggerAfterMessageHandled(message, null); - return true; - } - catch (Exception ex) - { - TriggerAfterMessageHandled(message, ex); - - if (ex is MessagingException) - { - throw; - } - - string description = $"Failed to handle {message} to {this} in {MessageHandler}"; - throw new MessageDeliveryException(message, description, ex); - } - } - - private IMessage ApplyBeforeHandled(IMessage message) - { - if (_interceptors.Count == 0) - { - return message; - } - - IMessage messageToUse = message; - - foreach (ITaskSchedulerChannelInterceptor interceptor in _interceptors) - { - messageToUse = interceptor.BeforeHandled(messageToUse, _channel, MessageHandler); - - if (messageToUse == null) - { - string name = interceptor.GetType().Name; - _channel.Logger?.LogDebug("{name} returned null from beforeHandle, i.e. precluding the send.", name); - TriggerAfterMessageHandled(message, null); - return null; - } - - _interceptorIndex++; - } - - return messageToUse; - } - - private void TriggerAfterMessageHandled(IMessage message, Exception ex) - { - if (_interceptorIndex == -1) - { - return; - } - - for (int i = _interceptorIndex; i >= 0; i--) - { - ITaskSchedulerChannelInterceptor interceptor = _channel.schedulerInterceptors[i]; - - try - { - interceptor.AfterMessageHandled(message, _channel, MessageHandler, ex); - } - catch (Exception ex2) - { - _channel.Logger?.LogError(ex2, "Exception from afterMessageHandled in {interceptor}", interceptor); - } - } - } - } -} diff --git a/src/Messaging/src/Messaging/Support/TaskSchedulerSubscribableChannelWriter.cs b/src/Messaging/src/Messaging/Support/TaskSchedulerSubscribableChannelWriter.cs deleted file mode 100644 index 6b04b47f5f..0000000000 --- a/src/Messaging/src/Messaging/Support/TaskSchedulerSubscribableChannelWriter.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Messaging.Support; - -public class TaskSchedulerSubscribableChannelWriter : AbstractMessageChannelWriter -{ - public virtual TaskSchedulerSubscribableChannel Channel => (TaskSchedulerSubscribableChannel)channel; - - public TaskSchedulerSubscribableChannelWriter(TaskSchedulerSubscribableChannel channel, ILogger logger = null) - : base(channel, logger) - { - } - - public override bool TryComplete(Exception error = null) - { - return false; - } - - public override bool TryWrite(IMessage message) - { - return channel.Send(message, 0); - } - - public override ValueTask WaitToWriteAsync(CancellationToken cancellationToken = default) - { - if (cancellationToken.IsCancellationRequested) - { - return new ValueTask(Task.FromCanceled(cancellationToken)); - } - - if (Channel.SubscriberCount > 0) - { - return new ValueTask(true); - } - - return new ValueTask(false); - } -} diff --git a/src/Messaging/src/RabbitMQ/Attributes/DeclareAnonymousQueueAttribute.cs b/src/Messaging/src/RabbitMQ/Attributes/DeclareAnonymousQueueAttribute.cs deleted file mode 100644 index f26b239971..0000000000 --- a/src/Messaging/src/RabbitMQ/Attributes/DeclareAnonymousQueueAttribute.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Attributes; - -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = true)] -public sealed class DeclareAnonymousQueueAttribute : DeclareQueueBaseAttribute -{ - internal string Name { get; } - - public string Id { get; set; } - - public DeclareAnonymousQueueAttribute(string id) - { - ArgumentGuard.NotNullOrEmpty(id); - - Id = id; - Name = Base64UrlNamingStrategy.Default.GenerateName(); - } -} diff --git a/src/Messaging/src/RabbitMQ/Attributes/DeclareExchangeAttribute.cs b/src/Messaging/src/RabbitMQ/Attributes/DeclareExchangeAttribute.cs deleted file mode 100644 index 4a4762dbc8..0000000000 --- a/src/Messaging/src/RabbitMQ/Attributes/DeclareExchangeAttribute.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Configuration; - -namespace Steeltoe.Messaging.RabbitMQ.Attributes; - -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = true)] -public sealed class DeclareExchangeAttribute : Attribute -{ - public string Name { get; set; } - - public string Type { get; set; } = ExchangeType.Direct; - - public string Durable { get; set; } = "True"; - - public string AutoDelete { get; set; } = "False"; - - public string Internal { get; set; } = "False"; - - public string IgnoreDeclarationExceptions { get; set; } = "False"; - - public string Delayed { get; set; } = "False"; - - public string Declare { get; set; } = "True"; - - public string Admin - { - get - { - if (Admins.Length == 0) - { - return null; - } - - return Admins[0]; - } - set - { - Admins = new[] - { - value - }; - } - } - - public string[] Admins { get; set; } = Array.Empty(); -} diff --git a/src/Messaging/src/RabbitMQ/Attributes/DeclareQueueAttribute.cs b/src/Messaging/src/RabbitMQ/Attributes/DeclareQueueAttribute.cs deleted file mode 100644 index a6a83c41cc..0000000000 --- a/src/Messaging/src/RabbitMQ/Attributes/DeclareQueueAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Attributes; - -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = true)] -public sealed class DeclareQueueAttribute : DeclareQueueBaseAttribute -{ - public string Name { get; set; } = string.Empty; -} diff --git a/src/Messaging/src/RabbitMQ/Attributes/DeclareQueueBaseAttribute.cs b/src/Messaging/src/RabbitMQ/Attributes/DeclareQueueBaseAttribute.cs deleted file mode 100644 index 8fb44509d7..0000000000 --- a/src/Messaging/src/RabbitMQ/Attributes/DeclareQueueBaseAttribute.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Attributes; - -[AttributeUsage(AttributeTargets.Class, Inherited = false)] -public abstract class DeclareQueueBaseAttribute : Attribute -{ - public virtual string Durable { get; set; } = string.Empty; - - public virtual string Exclusive { get; set; } = string.Empty; - - public virtual string AutoDelete { get; set; } = string.Empty; - - public virtual string IgnoreDeclarationExceptions { get; set; } = "False"; - - public virtual string Declare { get; set; } = "True"; - - public virtual string Admin - { - get - { - if (Admins.Length == 0) - { - return null; - } - - return Admins[0]; - } - set - { - Admins = new[] - { - value - }; - } - } - - public virtual string[] Admins { get; set; } = Array.Empty(); -} diff --git a/src/Messaging/src/RabbitMQ/Attributes/DeclareQueueBindingAttribute.cs b/src/Messaging/src/RabbitMQ/Attributes/DeclareQueueBindingAttribute.cs deleted file mode 100644 index d6c65153a9..0000000000 --- a/src/Messaging/src/RabbitMQ/Attributes/DeclareQueueBindingAttribute.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Attributes; - -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = true)] -public sealed class DeclareQueueBindingAttribute : Attribute -{ - private string _bindingName; - - public string Name - { - get - { - if (_bindingName == null) - { - return $"{ExchangeName}.{QueueName}"; - } - - return _bindingName; - } - set => _bindingName = value; - } - - public string QueueName { get; set; } = string.Empty; - - public string ExchangeName { get; set; } = string.Empty; - - public string RoutingKey - { - get - { - if (RoutingKeys.Length == 0) - { - return null; - } - - return RoutingKeys[0]; - } - set - { - RoutingKeys = new[] - { - value - }; - } - } - - public string[] RoutingKeys { get; set; } = Array.Empty(); - - public string IgnoreDeclarationExceptions { get; set; } = "False"; - - public string Declare { get; set; } = "True"; - - public string Admin - { - get - { - if (Admins.Length == 0) - { - return null; - } - - return Admins[0]; - } - set - { - Admins = new[] - { - value - }; - } - } - - public string[] Admins { get; set; } = Array.Empty(); -} diff --git a/src/Messaging/src/RabbitMQ/Attributes/RabbitHandlerAttribute.cs b/src/Messaging/src/RabbitMQ/Attributes/RabbitHandlerAttribute.cs deleted file mode 100644 index 6861f63f44..0000000000 --- a/src/Messaging/src/RabbitMQ/Attributes/RabbitHandlerAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Attributes; - -[AttributeUsage(AttributeTargets.Method)] -public sealed class RabbitHandlerAttribute : Attribute -{ - public bool IsDefault { get; set; } - - public RabbitHandlerAttribute(bool isDefault = false) - { - IsDefault = isDefault; - } -} diff --git a/src/Messaging/src/RabbitMQ/Attributes/RabbitListenerAttribute.cs b/src/Messaging/src/RabbitMQ/Attributes/RabbitListenerAttribute.cs deleted file mode 100644 index 6cbb5127ba..0000000000 --- a/src/Messaging/src/RabbitMQ/Attributes/RabbitListenerAttribute.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Attributes; - -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = true)] -public sealed class RabbitListenerAttribute : Attribute -{ - public string Id { get; set; } = string.Empty; - - public string ContainerFactory { get; set; } = string.Empty; - - public string Queue - { - get - { - if (Queues.Length == 0) - { - return null; - } - - return Queues[0]; - } - set - { - Queues = new[] - { - value - }; - } - } - - public string[] Queues { get; set; } - - public string Binding - { - get - { - if (Bindings.Length == 0) - { - return null; - } - - return Bindings[0]; - } - set - { - Bindings = new[] - { - value - }; - } - } - - public string[] Bindings { get; set; } = Array.Empty(); - - public bool Exclusive { get; set; } - - public string Priority { get; set; } = string.Empty; - - public string Admin { get; set; } = string.Empty; - - public string ReturnExceptions { get; set; } = string.Empty; - - public string ErrorHandler { get; set; } = string.Empty; - - public string Concurrency { get; set; } = string.Empty; - - public string AutoStartup { get; set; } = string.Empty; - - public string AckMode { get; set; } = string.Empty; - - public string ReplyPostProcessor { get; set; } = string.Empty; - - public string Group { get; set; } - - public RabbitListenerAttribute(params string[] queues) - { - Queues = queues; - } -} diff --git a/src/Messaging/src/RabbitMQ/Batch/IBatchingStrategy.cs b/src/Messaging/src/RabbitMQ/Batch/IBatchingStrategy.cs deleted file mode 100644 index 00db170b23..0000000000 --- a/src/Messaging/src/RabbitMQ/Batch/IBatchingStrategy.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Batch; - -public interface IBatchingStrategy -{ - MessageBatch? AddToBatch(string exchange, string routingKey, IMessage message); - - DateTime? NextRelease(); - - ICollection ReleaseBatches(); - - bool CanDebatch(IMessageHeaders properties); - - void DeBatch(IMessage message, Action fragmentConsumer); -} diff --git a/src/Messaging/src/RabbitMQ/Batch/MessageBatch.cs b/src/Messaging/src/RabbitMQ/Batch/MessageBatch.cs deleted file mode 100644 index c6205b9e8a..0000000000 --- a/src/Messaging/src/RabbitMQ/Batch/MessageBatch.cs +++ /dev/null @@ -1,7 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Batch; - -public record struct MessageBatch(string Exchange, string RoutingKey, IMessage Message); diff --git a/src/Messaging/src/RabbitMQ/Batch/SimpleBatchingStrategy.cs b/src/Messaging/src/RabbitMQ/Batch/SimpleBatchingStrategy.cs deleted file mode 100644 index 91356f90d0..0000000000 --- a/src/Messaging/src/RabbitMQ/Batch/SimpleBatchingStrategy.cs +++ /dev/null @@ -1,213 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Buffers.Binary; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; - -namespace Steeltoe.Messaging.RabbitMQ.Batch; - -public class SimpleBatchingStrategy : IBatchingStrategy -{ - private readonly int _batchSize; - private readonly int _bufferLimit; - private readonly long _timeout; - private readonly List> _messages = new(); - private readonly List _empty = new(); - - private string _exchange; - private string _routingKey; - private int _currentSize; - - public SimpleBatchingStrategy(int batchSize, int bufferLimit, long timeout) - { - _batchSize = batchSize; - _bufferLimit = bufferLimit; - _timeout = timeout; - } - - public MessageBatch? AddToBatch(string exchange, string routingKey, IMessage message) - { - if (_exchange != null && _exchange != exchange) - { - throw new ArgumentException("Cannot send to different exchanges in the same batch.", nameof(exchange)); - } - - _exchange = exchange; - - if (_routingKey != null && _routingKey != routingKey) - { - throw new ArgumentException("Cannot send with different routing keys in the same batch.", nameof(routingKey)); - } - - if (message is not IMessage bytesMessage) - { - throw new ArgumentException($"{nameof(SimpleBatchingStrategy)} only supports messages with byte[] payloads.", nameof(message)); - } - - _routingKey = routingKey; - - int bufferUse = 4 + bytesMessage.Payload.Length; - MessageBatch? batch = null; - - if (_messages.Count > 0 && _currentSize + bufferUse > _bufferLimit) - { - batch = DoReleaseBatch(); - _exchange = exchange; - _routingKey = routingKey; - } - - _currentSize += bufferUse; - _messages.Add(bytesMessage); - - if (batch == null && (_messages.Count >= _batchSize || _currentSize >= _bufferLimit)) - { - batch = DoReleaseBatch(); - } - - return batch; - } - - public DateTime? NextRelease() - { - if (_messages.Count == 0 || _timeout <= 0) - { - return null; - } - - if (_currentSize >= _bufferLimit) - { - // release immediately, we're already over the limit - return DateTime.UtcNow; - } - - return DateTime.UtcNow + TimeSpan.FromMilliseconds(_timeout); - } - - public ICollection ReleaseBatches() - { - MessageBatch? batch = DoReleaseBatch(); - - if (batch == null) - { - return _empty; - } - - return new List - { - batch.Value - }; - } - - public bool CanDebatch(IMessageHeaders properties) - { - if (properties.TryGetValue(RabbitMessageHeaders.SpringBatchFormat, out object value)) - { - return value as string == RabbitMessageHeaders.BatchFormatLengthHeader4; - } - - return false; - } - - public void DeBatch(IMessage message, Action fragmentConsumer) - { - if (message is not IMessage bytesMessage) - { - throw new ArgumentException($"{nameof(SimpleBatchingStrategy)} only supports messages with byte[] payloads.", nameof(message)); - } - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(bytesMessage); - var byteBuffer = new Span(bytesMessage.Payload); - accessor.RemoveHeader(RabbitMessageHeaders.SpringBatchFormat); - int bodyLength = bytesMessage.Payload.Length; - int index = 0; - - while (index < bodyLength) - { - accessor = RabbitHeaderAccessor.GetMutableAccessor(bytesMessage); - Span slice = byteBuffer.Slice(index); - int length = BinaryPrimitives.ReadInt32BigEndian(slice); - index += 4; - - if (length < 0 || length > bodyLength - index) - { - throw new ListenerExecutionFailedException("Bad batched message received", - new MessageConversionException($"Insufficient batch data at offset {index}"), bytesMessage); - } - - byte[] body = new byte[length]; - slice = byteBuffer.Slice(index); - - for (int i = 0; i < length; i++) - { - body[i] = slice[i]; - } - - index += length; - - accessor.ContentLength = length; - - // Caveat - shared MessageProperties. - if (index >= bodyLength) - { - accessor.LastInBatch = true; - } - - IMessage fragment = Message.Create(body, accessor.MessageHeaders); - - fragmentConsumer(fragment); - } - } - - private MessageBatch? DoReleaseBatch() - { - if (_messages.Count < 1) - { - return null; - } - - IMessage message = AssembleMessage(); - var messageBatch = new MessageBatch(_exchange, _routingKey, message); - _messages.Clear(); - _currentSize = 0; - _exchange = null; - _routingKey = null; - return messageBatch; - } - - private IMessage AssembleMessage() - { - if (_messages.Count == 1) - { - return _messages[0]; - } - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(_messages[0]); - byte[] body = new byte[_currentSize]; - - var bytes = new Span(body); - int index = 0; - - foreach (IMessage message in _messages) - { - Span slice = bytes.Slice(index); - BinaryPrimitives.WriteInt32BigEndian(slice, message.Payload.Length); - index += 4; - - slice = bytes.Slice(index); - - for (int i = 0; i < message.Payload.Length; i++) - { - slice[i] = message.Payload[i]; - } - - index += message.Payload.Length; - } - - accessor.SetHeader(RabbitMessageHeaders.SpringBatchFormat, RabbitMessageHeaders.BatchFormatLengthHeader4); - accessor.SetHeader(RabbitMessageHeaders.BatchSize, _messages.Count); - return Message.Create(body, accessor.MessageHeaders); - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/AbstractBuilder.cs b/src/Messaging/src/RabbitMQ/Configuration/AbstractBuilder.cs deleted file mode 100644 index f18ab3fb7d..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/AbstractBuilder.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public abstract class AbstractBuilder -{ - protected Dictionary Arguments { get; private set; } - - protected Dictionary GetOrCreateArguments() - { - Arguments ??= new Dictionary(); - return Arguments; - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/AbstractDeclarable.cs b/src/Messaging/src/RabbitMQ/Configuration/AbstractDeclarable.cs deleted file mode 100644 index c70adc74cc..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/AbstractDeclarable.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public abstract class AbstractDeclarable : IDeclarable -{ - public bool ShouldDeclare { get; set; } - - public Dictionary Arguments { get; set; } - - public List DeclaringAdmins { get; set; } = new(); - - public bool IgnoreDeclarationExceptions { get; set; } - - protected AbstractDeclarable(Dictionary arguments) - { - ShouldDeclare = true; - Arguments = arguments != null ? new Dictionary(arguments) : new Dictionary(); - } - - public virtual void SetAdminsThatShouldDeclare(params object[] adminArgs) - { - var admins = new List(); - - if (adminArgs != null) - { - if (adminArgs.Length > 1) - { - foreach (object a in adminArgs) - { - if (a == null) - { - throw new InvalidOperationException("'admins' cannot contain null elements"); - } - } - } - - if (adminArgs.Length > 0 && !(adminArgs.Length == 1 && adminArgs[0] == null)) - { - admins.AddRange(adminArgs); - } - } - - DeclaringAdmins = admins; - } - - public void AddArgument(string name, object value) - { - Arguments[name] = value; - } - - public object RemoveArgument(string name) - { - Arguments.Remove(name, out object result); - return result; - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/AbstractExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/AbstractExchange.cs deleted file mode 100644 index a4d2efa489..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/AbstractExchange.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public abstract class AbstractExchange : AbstractDeclarable, IExchange -{ - public string ServiceName { get; set; } - - public string ExchangeName { get; set; } - - public bool IsDurable { get; set; } - - public bool IsAutoDelete { get; set; } - - public bool IsDelayed { get; set; } - - public bool IsInternal { get; set; } - - public abstract string Type { get; } - - protected AbstractExchange(string exchangeName) - : this(exchangeName, true, false) - { - } - - protected AbstractExchange(string exchangeName, bool durable, bool autoDelete) - : this(exchangeName, durable, autoDelete, null) - { - } - - protected AbstractExchange(string exchangeName, bool durable, bool autoDelete, Dictionary arguments) - : base(arguments) - { - ExchangeName = exchangeName; - ServiceName = !string.IsNullOrEmpty(exchangeName) ? exchangeName : $"exchange@{RuntimeHelpers.GetHashCode(this)}"; - IsDurable = durable; - IsAutoDelete = autoDelete; - } - - public override string ToString() - { - return $"Exchange [name={ExchangeName}, type={Type}, durable={IsDurable}, autoDelete={IsAutoDelete}, internal={IsInternal}, arguments={Arguments}]"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/AbstractRabbitListenerContainerFactory.cs b/src/Messaging/src/RabbitMQ/Configuration/AbstractRabbitListenerContainerFactory.cs deleted file mode 100644 index 22ef79e157..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/AbstractRabbitListenerContainerFactory.cs +++ /dev/null @@ -1,312 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Retry; -using Steeltoe.Common.Transaction; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using SimpleMessageConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter.SimpleMessageConverter; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public abstract class AbstractRabbitListenerContainerFactory : IRabbitListenerContainerFactory - where TContainer : AbstractMessageListenerContainer -{ - private readonly IOptionsMonitor _optionsMonitor; - protected readonly ILogger Logger; - protected readonly ILoggerFactory LoggerFactory; - - private ISmartMessageConverter _messageConverter; - - protected internal RabbitOptions Options - { - get - { - if (_optionsMonitor != null) - { - return _optionsMonitor.CurrentValue; - } - - return null; - } - } - - public IApplicationContext ApplicationContext { get; set; } - - public IConnectionFactory ConnectionFactory { get; set; } - - public IErrorHandler ErrorHandler { get; set; } - - public ISmartMessageConverter MessageConverter - { - get - { - _messageConverter ??= ApplicationContext?.GetService() ?? - new SimpleMessageConverter(LoggerFactory?.CreateLogger()); - - return _messageConverter; - } - set => _messageConverter = value; - } - - public AcknowledgeMode? AcknowledgeMode { get; set; } - - public bool? IsChannelTransacted { get; set; } - - public IPlatformTransactionManager TransactionManager { get; set; } - - public int? PrefetchCount { get; set; } - - public bool? DefaultRequeueRejected { get; set; } - - public int? RecoveryInterval { get; set; } - - public IBackOff RecoveryBackOff { get; set; } - - public bool? MissingQueuesFatal { get; set; } - - public bool? MismatchedQueuesFatal { get; set; } - - public IConsumerTagStrategy ConsumerTagStrategy { get; set; } - - public int? IdleEventInterval { get; set; } - - public int? FailedDeclarationRetryInterval { get; set; } - - public bool? AutoStartup { get; set; } - - public int Phase { get; set; } - - public List AfterReceivePostProcessors { get; set; } - - public List BeforeSendReplyPostProcessors { get; set; } - - public RetryTemplate RetryTemplate { get; set; } - - public IRecoveryCallback ReplyRecoveryCallback { get; set; } - - public Action ContainerCustomizer { get; set; } - - public bool BatchListener { get; set; } - - public IBatchingStrategy BatchingStrategy { get; set; } - - public bool? DeBatchingEnabled { get; set; } - - public virtual string ServiceName { get; set; } - - public bool PossibleAuthenticationFailureFatal { get; set; } = true; - - protected AbstractRabbitListenerContainerFactory(IApplicationContext applicationContext, ILoggerFactory loggerFactory = null) - { - ApplicationContext = applicationContext; - LoggerFactory = loggerFactory; - } - - protected AbstractRabbitListenerContainerFactory(IApplicationContext applicationContext, IConnectionFactory connectionFactory, - ILoggerFactory loggerFactory = null) - { - ApplicationContext = applicationContext; - LoggerFactory = loggerFactory; - ConnectionFactory = connectionFactory; - } - - protected AbstractRabbitListenerContainerFactory(IApplicationContext applicationContext, IOptionsMonitor optionsMonitor, - IConnectionFactory connectionFactory, ILoggerFactory loggerFactory = null) - { - ApplicationContext = applicationContext; - LoggerFactory = loggerFactory; - ConnectionFactory = connectionFactory; - _optionsMonitor = optionsMonitor; - } - - public void SetAfterReceivePostProcessors(params IMessagePostProcessor[] postProcessors) - { - ArgumentGuard.NotNull(postProcessors); - ArgumentGuard.ElementsNotNull(postProcessors); - - AfterReceivePostProcessors = new List(postProcessors); - } - - public void SetBeforeSendReplyPostProcessors(params IMessagePostProcessor[] postProcessors) - { - ArgumentGuard.NotNull(postProcessors); - ArgumentGuard.ElementsNotNull(postProcessors); - - BeforeSendReplyPostProcessors = new List(postProcessors); - } - - public TContainer CreateListenerContainer(IRabbitListenerEndpoint endpoint) - { - TContainer instance = CreateContainerInstance(); - - instance.ConnectionFactory = ConnectionFactory; - instance.ErrorHandler = ErrorHandler; - instance.PossibleAuthenticationFailureFatal = PossibleAuthenticationFailureFatal; - - if (MessageConverter != null && endpoint != null) - { - endpoint.MessageConverter = MessageConverter; - } - - if (AcknowledgeMode.HasValue) - { - instance.AcknowledgeMode = AcknowledgeMode.Value; - } - - if (IsChannelTransacted.HasValue) - { - instance.IsChannelTransacted = IsChannelTransacted.Value; - } - - if (ApplicationContext != null) - { - instance.ApplicationContext = ApplicationContext; - } - - if (TransactionManager != null) - { - instance.TransactionManager = TransactionManager; - } - - if (PrefetchCount.HasValue) - { - instance.PrefetchCount = PrefetchCount.Value; - } - - if (DefaultRequeueRejected.HasValue) - { - instance.DefaultRequeueRejected = DefaultRequeueRejected.Value; - } - - if (RecoveryBackOff != null) - { - instance.RecoveryBackOff = RecoveryBackOff; - } - - if (MismatchedQueuesFatal.HasValue) - { - instance.MismatchedQueuesFatal = MismatchedQueuesFatal.Value; - } - - if (MissingQueuesFatal.HasValue) - { - instance.MissingQueuesFatal = MissingQueuesFatal.Value; - } - - if (ConsumerTagStrategy != null) - { - instance.ConsumerTagStrategy = ConsumerTagStrategy; - } - - if (IdleEventInterval.HasValue) - { - instance.IdleEventInterval = IdleEventInterval.Value; - } - - if (FailedDeclarationRetryInterval.HasValue) - { - instance.FailedDeclarationRetryInterval = FailedDeclarationRetryInterval.Value; - } - - if (AutoStartup.HasValue) - { - instance.IsAutoStartup = AutoStartup.Value; - } - - instance.Phase = Phase; - - if (AfterReceivePostProcessors != null) - { - instance.SetAfterReceivePostProcessors(AfterReceivePostProcessors.ToArray()); - } - - if (DeBatchingEnabled.HasValue) - { - instance.IsDeBatchingEnabled = DeBatchingEnabled.Value; - } - - if (BatchListener && DeBatchingEnabled.HasValue) - { - // turn off container debatching by default for batch listeners - instance.IsDeBatchingEnabled = false; - } - - if (endpoint != null) - { - if (endpoint.AutoStartup.HasValue) - { - instance.IsAutoStartup = endpoint.AutoStartup.Value; - } - - if (endpoint.AckMode.HasValue) - { - instance.AcknowledgeMode = endpoint.AckMode.Value; - } - - if (BatchingStrategy != null) - { - endpoint.BatchingStrategy = BatchingStrategy; - } - - instance.ListenerId = endpoint.Id; - endpoint.BatchListener = BatchListener; - endpoint.SetupListenerContainer(instance); - } - - if (instance.MessageListener is AbstractMessageListenerAdapter adapterListener) - { - if (BeforeSendReplyPostProcessors != null) - { - adapterListener.SetBeforeSendReplyPostProcessors(BeforeSendReplyPostProcessors.ToArray()); - } - - if (RetryTemplate != null) - { - adapterListener.RetryTemplate = RetryTemplate; - - if (ReplyRecoveryCallback != null) - { - adapterListener.RecoveryCallback = ReplyRecoveryCallback; - } - } - - if (DefaultRequeueRejected.HasValue) - { - adapterListener.DefaultRequeueRejected = DefaultRequeueRejected.Value; - } - - if (endpoint != null && endpoint.ReplyPostProcessor != null) - { - adapterListener.ReplyPostProcessor = endpoint.ReplyPostProcessor; - } - } - - InitializeContainer(instance, endpoint); - - ContainerCustomizer?.Invoke(instance); - - return instance; - } - - IMessageListenerContainer IRabbitListenerContainerFactory.CreateListenerContainer(IRabbitListenerEndpoint endpoint) - { - return CreateListenerContainer(endpoint); - } - - protected abstract TContainer CreateContainerInstance(); - - protected virtual void InitializeContainer(TContainer instance, IRabbitListenerEndpoint endpoint) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/AnonymousQueue.cs b/src/Messaging/src/RabbitMQ/Configuration/AnonymousQueue.cs deleted file mode 100644 index e86daad06f..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/AnonymousQueue.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class AnonymousQueue : Queue -{ - public AnonymousQueue() - : this((Dictionary)null) - { - } - - public AnonymousQueue(string serviceName) - : this(Base64UrlNamingStrategy.Default, null) - { - ServiceName = serviceName; - } - - public AnonymousQueue(Dictionary arguments) - : this(Base64UrlNamingStrategy.Default, arguments) - { - } - - public AnonymousQueue(INamingStrategy namingStrategy) - : this(namingStrategy, null) - { - } - - public AnonymousQueue(INamingStrategy namingStrategy, Dictionary arguments) - : base(namingStrategy.GenerateName(), false, true, true, arguments) - { - if (!Arguments.ContainsKey(XQueueMasterLocator)) - { - MasterLocator = "client-local"; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/Binding.cs b/src/Messaging/src/RabbitMQ/Configuration/Binding.cs deleted file mode 100644 index fc1b1174de..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/Binding.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class Binding : AbstractDeclarable, IBinding -{ - public string ServiceName { get; set; } - - public string Destination { get; set; } - - public string Exchange { get; set; } - - public string RoutingKey { get; set; } - - public DestinationType Type { get; set; } - - public bool IsDestinationQueue => Type == DestinationType.Queue; - - public string BindingName { get; set; } - - public Binding(string bindingName) - : base(null) - { - BindingName = ServiceName = bindingName; - } - - public Binding(string bindingName, string destination, DestinationType destinationType, string exchange, string routingKey, - Dictionary arguments) - : base(arguments) - { - BindingName = ServiceName = bindingName; - Destination = destination; - Type = destinationType; - Exchange = exchange; - RoutingKey = routingKey; - } - - internal static IBinding Create(string bindingName, string destination, DestinationType destinationType, string exchange, string routingKey, - Dictionary arguments) - { - if (destinationType == DestinationType.Exchange) - { - return new ExchangeBinding(bindingName, destination, exchange, routingKey, arguments); - } - - return new QueueBinding(bindingName, destination, exchange, routingKey, arguments); - } - - public override string ToString() - { - return $"Binding [bindingName={BindingName}, destination={Destination}, exchange={Exchange}, routingKey={RoutingKey}, arguments={Arguments}]"; - } - - public enum DestinationType - { - Queue, - Exchange - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/BindingBuilder.cs b/src/Messaging/src/RabbitMQ/Configuration/BindingBuilder.cs deleted file mode 100644 index 75ec183c6d..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/BindingBuilder.cs +++ /dev/null @@ -1,308 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using static Steeltoe.Messaging.RabbitMQ.Configuration.Binding; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class BindingBuilder -{ - public static DestinationConfigurer Bind(IQueue queue) - { - return new DestinationConfigurer(queue.QueueName, DestinationType.Queue); - } - - public static DestinationConfigurer Bind(IExchange exchange) - { - return new DestinationConfigurer(exchange.ExchangeName, DestinationType.Exchange); - } - - public static IBinding Create(string bindingName, DestinationType type) - { - if (type == DestinationType.Exchange) - { - return new ExchangeBinding(bindingName); - } - - return new QueueBinding(bindingName); - } - - public class DestinationConfigurer - { - public string Name { get; } - - public DestinationType Type { get; } - - public DestinationConfigurer(string destinationName, DestinationType type) - { - Name = destinationName; - Type = type; - } - - public IBinding To(FanOutExchange exchange) - { - string bindingName = $"{exchange.ExchangeName}.{Name}"; - return Binding.Create(bindingName, Name, Type, exchange.ExchangeName, string.Empty, new Dictionary()); - } - - public HeadersExchangeMapConfigurer To(HeadersExchange exchange) - { - return new HeadersExchangeMapConfigurer(this, exchange); - } - - public DirectExchangeRoutingKeyConfigurer To(DirectExchange exchange) - { - return new DirectExchangeRoutingKeyConfigurer(this, exchange); - } - - public TopicExchangeRoutingKeyConfigurer To(TopicExchange exchange) - { - return new TopicExchangeRoutingKeyConfigurer(this, exchange); - } - - public GenericExchangeRoutingKeyConfigurer To(IExchange exchange) - { - return new GenericExchangeRoutingKeyConfigurer(this, exchange); - } - } - - public class HeadersExchangeMapConfigurer - { - public DestinationConfigurer Destination { get; } - - public HeadersExchange Exchange { get; } - - public HeadersExchangeMapConfigurer(DestinationConfigurer destination, HeadersExchange exchange) - { - Destination = destination; - Exchange = exchange; - } - - private static Dictionary CreateMapForKeys(params string[] keys) - { - var map = new Dictionary(); - - foreach (string key in keys) - { - map[key] = null; - } - - return map; - } - - public HeadersExchangeSingleValueBindingCreator Where(string key) - { - return new HeadersExchangeSingleValueBindingCreator(this, key); - } - - public HeadersExchangeKeysBindingCreator WhereAny(params string[] headerKeys) - { - return new HeadersExchangeKeysBindingCreator(this, headerKeys, false); - } - - public HeadersExchangeMapBindingCreator WhereAny(Dictionary headerValues) - { - return new HeadersExchangeMapBindingCreator(this, headerValues, false); - } - - public HeadersExchangeKeysBindingCreator WhereAll(params string[] headerKeys) - { - return new HeadersExchangeKeysBindingCreator(this, headerKeys, true); - } - - public HeadersExchangeMapBindingCreator WhereAll(Dictionary headerValues) - { - return new HeadersExchangeMapBindingCreator(this, headerValues, true); - } - - public class HeadersExchangeSingleValueBindingCreator - { - private readonly string _key; - private readonly HeadersExchangeMapConfigurer _configurer; - - public HeadersExchangeSingleValueBindingCreator(HeadersExchangeMapConfigurer configurer, string key) - { - ArgumentGuard.NotNull(key); - - _key = key; - _configurer = configurer; - } - - public IBinding Exists() - { - string bindingName = $"{_configurer.Exchange.ExchangeName}.{_configurer.Destination.Name}"; - - return Binding.Create(bindingName, _configurer.Destination.Name, _configurer.Destination.Type, _configurer.Exchange.ExchangeName, string.Empty, - CreateMapForKeys(_key)); - } - - public IBinding Matches(object value) - { - var map = new Dictionary - { - [_key] = value - }; - - string bindingName = $"{_configurer.Exchange.ExchangeName}.{_configurer.Destination.Name}"; - - return Binding.Create(bindingName, _configurer.Destination.Name, _configurer.Destination.Type, _configurer.Exchange.ExchangeName, string.Empty, - map); - } - } - - public class HeadersExchangeKeysBindingCreator - { - private readonly Dictionary _headerMap; - private readonly HeadersExchangeMapConfigurer _configurer; - - public HeadersExchangeKeysBindingCreator(HeadersExchangeMapConfigurer configurer, string[] headerKeys, bool matchAll) - { - ArgumentGuard.NotNullOrEmpty(headerKeys); - - _headerMap = CreateMapForKeys(headerKeys); - _headerMap["x-match"] = matchAll ? "all" : "any"; - _configurer = configurer; - } - - public IBinding Exist() - { - string bindingName = $"{_configurer.Exchange.ExchangeName}.{_configurer.Destination.Name}"; - - return Binding.Create(bindingName, _configurer.Destination.Name, _configurer.Destination.Type, _configurer.Exchange.ExchangeName, string.Empty, - _headerMap); - } - } - - public class HeadersExchangeMapBindingCreator - { - private readonly Dictionary _headerMap; - private readonly HeadersExchangeMapConfigurer _configurer; - - public HeadersExchangeMapBindingCreator(HeadersExchangeMapConfigurer configurer, Dictionary headerMap, bool matchAll) - { - ArgumentGuard.NotNullOrEmpty(headerMap); - - _headerMap = new Dictionary(headerMap) - { - ["x-match"] = matchAll ? "all" : "any" - }; - - _configurer = configurer; - } - - public IBinding Match() - { - string bindingName = $"{_configurer.Exchange.ExchangeName}.{_configurer.Destination.Name}"; - - return Binding.Create(bindingName, _configurer.Destination.Name, _configurer.Destination.Type, _configurer.Exchange.ExchangeName, string.Empty, - _headerMap); - } - } - } - - public abstract class AbstractRoutingKeyConfigurer - { - public DestinationConfigurer Destination { get; } - - public string ExchangeName { get; } - - protected AbstractRoutingKeyConfigurer(DestinationConfigurer destination, string exchange) - { - Destination = destination; - ExchangeName = exchange; - } - } - - public class TopicExchangeRoutingKeyConfigurer : AbstractRoutingKeyConfigurer - { - public TopicExchangeRoutingKeyConfigurer(DestinationConfigurer destination, TopicExchange exchange) - : base(destination, exchange.ExchangeName) - { - } - - public IBinding With(string routingKey) - { - string bindingName = $"{ExchangeName}.{Destination.Name}"; - return Binding.Create(bindingName, Destination.Name, Destination.Type, ExchangeName, routingKey, new Dictionary()); - } - - public IBinding With(Enum routingKeyEnum) - { - string bindingName = $"{ExchangeName}.{Destination.Name}"; - return Binding.Create(bindingName, Destination.Name, Destination.Type, ExchangeName, routingKeyEnum.ToString(), new Dictionary()); - } - } - - public class GenericExchangeRoutingKeyConfigurer : AbstractRoutingKeyConfigurer - { - public GenericExchangeRoutingKeyConfigurer(DestinationConfigurer destination, IExchange exchange) - : base(destination, exchange.ExchangeName) - { - } - - public GenericArgumentsConfigurer With(string routingKey) - { - return new GenericArgumentsConfigurer(this, routingKey); - } - - public GenericArgumentsConfigurer With(Enum routingKeyEnum) - { - return new GenericArgumentsConfigurer(this, routingKeyEnum.ToString()); - } - } - - public class GenericArgumentsConfigurer - { - private readonly GenericExchangeRoutingKeyConfigurer _configurer; - - private readonly string _routingKey; - - public GenericArgumentsConfigurer(GenericExchangeRoutingKeyConfigurer configurer, string routingKey) - { - _configurer = configurer; - _routingKey = routingKey; - } - - public IBinding And(Dictionary map) - { - string bindingName = $"{_configurer.ExchangeName}.{_configurer.Destination.Name}"; - return Binding.Create(bindingName, _configurer.Destination.Name, _configurer.Destination.Type, _configurer.ExchangeName, _routingKey, map); - } - - public IBinding NoArgs() - { - string bindingName = $"{_configurer.ExchangeName}.{_configurer.Destination.Name}"; - - return Binding.Create(bindingName, _configurer.Destination.Name, _configurer.Destination.Type, _configurer.ExchangeName, _routingKey, - new Dictionary()); - } - } - - public class DirectExchangeRoutingKeyConfigurer : AbstractRoutingKeyConfigurer - { - public DirectExchangeRoutingKeyConfigurer(DestinationConfigurer destination, DirectExchange exchange) - : base(destination, exchange.ExchangeName) - { - } - - public IBinding With(string routingKey) - { - string bindingName = $"{ExchangeName}.{Destination.Name}"; - return Binding.Create(bindingName, Destination.Name, Destination.Type, ExchangeName, routingKey, new Dictionary()); - } - - public IBinding With(Enum routingKeyEnum) - { - string bindingName = $"{ExchangeName}.{Destination.Name}"; - return Binding.Create(bindingName, Destination.Name, Destination.Type, ExchangeName, routingKeyEnum.ToString(), new Dictionary()); - } - - public IBinding WithQueueName() - { - string bindingName = $"{ExchangeName}.{Destination.Name}"; - return Binding.Create(bindingName, Destination.Name, Destination.Type, ExchangeName, Destination.Name, new Dictionary()); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/ConfigurationUtils.cs b/src/Messaging/src/RabbitMQ/Configuration/ConfigurationUtils.cs deleted file mode 100644 index 02367bcacf..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/ConfigurationUtils.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text.RegularExpressions; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -internal static class ConfigurationUtils -{ - private static readonly Regex Regex = new(@"\s+"); - - public static bool IsExpression(string expression) - { - if (expression.StartsWith("#{", StringComparison.Ordinal) && expression.EndsWith('}')) - { - return true; - } - - return false; - } - - public static string ExtractExpressionString(string expression) - { - expression = expression.Substring(2, expression.Length - 2 - 1); - return ReplaceWhitespace(expression, string.Empty); - } - - public static bool IsServiceReference(string expression) - { - return expression[0] == '@'; - } - - public static string ExtractServiceName(string expression) - { - if (expression[0] == '@') - { - return expression.Substring(1); - } - - return expression; - } - - public static string ReplaceWhitespace(string input, string replacement) - { - return Regex.Replace(input, replacement); - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/ContainerType.cs b/src/Messaging/src/RabbitMQ/Configuration/ContainerType.cs deleted file mode 100644 index 77264c732a..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/ContainerType.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public enum ContainerType -{ - Direct -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/CustomExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/CustomExchange.cs deleted file mode 100644 index d5a7d1c136..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/CustomExchange.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class CustomExchange : AbstractExchange, ICustomExchange -{ - public override string Type { get; } - - public CustomExchange(string name, string type) - : base(name) - { - Type = type; - } - - public CustomExchange(string name, string type, bool durable, bool autoDelete) - : base(name, durable, autoDelete) - { - Type = type; - } - - public CustomExchange(string name, string type, bool durable, bool autoDelete, Dictionary arguments) - : base(name, durable, autoDelete, arguments) - { - Type = type; - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/Declarables.cs b/src/Messaging/src/RabbitMQ/Configuration/Declarables.cs deleted file mode 100644 index 3ddcd72eb5..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/Declarables.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class Declarables : IServiceNameAware -{ - public List DeclarableList { get; } - - public string ServiceName { get; set; } - - public Declarables(string name, params IDeclarable[] declarables) - { - ArgumentGuard.NotNull(declarables); - ArgumentGuard.NotNull(name); - - DeclarableList = new List(declarables); - ServiceName = name; - } - - public Declarables(string name, List declarables) - { - ArgumentGuard.NotNull(declarables); - ArgumentGuard.NotNull(name); - - DeclarableList = new List(declarables); - ServiceName = name; - } - - public IEnumerable GetDeclarablesByType() - { - return DeclarableList.OfType(); - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/DirectExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/DirectExchange.cs deleted file mode 100644 index 0419dea006..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/DirectExchange.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class DirectExchange : AbstractExchange, IDirectExchange -{ - public static readonly DirectExchange Default = new(string.Empty); - - public override string Type { get; } = ExchangeType.Direct; - - public DirectExchange(string name) - : base(name) - { - } - - public DirectExchange(string name, bool durable, bool autoDelete) - : base(name, durable, autoDelete) - { - } - - public DirectExchange(string name, bool durable, bool autoDelete, Dictionary arguments) - : base(name, durable, autoDelete, arguments) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/DirectRabbitListenerContainerFactory.cs b/src/Messaging/src/RabbitMQ/Configuration/DirectRabbitListenerContainerFactory.cs deleted file mode 100644 index e0df21ff23..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/DirectRabbitListenerContainerFactory.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Retry; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class DirectRabbitListenerContainerFactory : AbstractRabbitListenerContainerFactory -{ - public const string DefaultServiceName = "rabbitListenerContainerFactory"; - - public int? MonitorInterval { get; set; } - - public int? ConsumersPerQueue { get; set; } = 1; - - public int? MessagesPerAck { get; set; } - - public int? AckTimeout { get; set; } - - public DirectRabbitListenerContainerFactory(IApplicationContext applicationContext, ILoggerFactory loggerFactory = null) - : base(applicationContext, loggerFactory) - { - } - - public DirectRabbitListenerContainerFactory(IApplicationContext applicationContext, IConnectionFactory connectionFactory, - ILoggerFactory loggerFactory = null) - : base(applicationContext, connectionFactory, loggerFactory) - { - } - - [ActivatorUtilitiesConstructor] - public DirectRabbitListenerContainerFactory(IApplicationContext applicationContext, IOptionsMonitor optionsMonitor, - IConnectionFactory connectionFactory, ILoggerFactory loggerFactory = null) - : base(applicationContext, optionsMonitor, connectionFactory, loggerFactory) - { - Configure(Options); - } - - protected override DirectMessageListenerContainer CreateContainerInstance() - { - return new DirectMessageListenerContainer(ApplicationContext, ConnectionFactory, null, LoggerFactory); - } - - protected override void InitializeContainer(DirectMessageListenerContainer instance, IRabbitListenerEndpoint endpoint) - { - base.InitializeContainer(instance, endpoint); - - if (MonitorInterval.HasValue) - { - instance.MonitorInterval = MonitorInterval.Value; - } - - if (MessagesPerAck.HasValue) - { - instance.MessagesPerAck = MessagesPerAck.Value; - } - - if (AckTimeout.HasValue) - { - instance.AckTimeout = AckTimeout.Value; - } - - if (endpoint != null && endpoint.Concurrency.HasValue) - { - instance.ConsumersPerQueue = endpoint.Concurrency.Value; - } - else if (ConsumersPerQueue.HasValue) - { - instance.ConsumersPerQueue = ConsumersPerQueue.Value; - } - - instance.RetryTemplate = RetryTemplate; - instance.Recoverer = ReplyRecoveryCallback; - } - - private void Configure(RabbitOptions options) - { - RabbitOptions.DirectContainerOptions containerOptions = options.Listener.Direct; - AutoStartup = containerOptions.AutoStartup; - PossibleAuthenticationFailureFatal = containerOptions.PossibleAuthenticationFailureFatal; - - if (containerOptions.AcknowledgeMode.HasValue) - { - AcknowledgeMode = containerOptions.AcknowledgeMode.Value; - } - - if (containerOptions.Prefetch.HasValue) - { - PrefetchCount = containerOptions.Prefetch.Value; - } - - DefaultRequeueRejected = containerOptions.DefaultRequeueRejected; - - if (containerOptions.IdleEventInterval.HasValue) - { - int asMilliseconds = (int)containerOptions.IdleEventInterval.Value.TotalMilliseconds; - IdleEventInterval = asMilliseconds; - } - - MissingQueuesFatal = containerOptions.MissingQueuesFatal; - RabbitOptions.ListenerRetryOptions retryOptions = containerOptions.Retry; - - if (retryOptions.Enabled) - { - RetryTemplate = new PollyRetryTemplate(retryOptions.MaxAttempts, (int)retryOptions.InitialInterval.TotalMilliseconds, - (int)retryOptions.MaxInterval.TotalMilliseconds, retryOptions.Multiplier, Logger); - - ReplyRecoveryCallback = new RejectAndDoNotRequeueRecoverer(); - } - - if (containerOptions.ConsumersPerQueue.HasValue) - { - ConsumersPerQueue = containerOptions.ConsumersPerQueue.Value; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/ExchangeBinding.cs b/src/Messaging/src/RabbitMQ/Configuration/ExchangeBinding.cs deleted file mode 100644 index 36629c1580..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/ExchangeBinding.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class ExchangeBinding : Binding, IExchangeBinding -{ - public ExchangeBinding(string bindingName) - : base(bindingName) - { - } - - public ExchangeBinding(string name, string exchangeDestination, string exchange, string routingKey, Dictionary arguments) - : base(name, exchangeDestination, DestinationType.Exchange, exchange, routingKey, arguments) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/ExchangeBuilder.cs b/src/Messaging/src/RabbitMQ/Configuration/ExchangeBuilder.cs deleted file mode 100644 index 07fdda863e..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/ExchangeBuilder.cs +++ /dev/null @@ -1,158 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class ExchangeBuilder : AbstractBuilder -{ - private readonly string _name; - private readonly string _type; - private bool _autoDelete; - private bool _durable = true; - private bool _internal; - private bool _delayed; - private bool _ignoreDeclarationExceptions; - private bool _declare = true; - private List _declaringAdmins; - - public ExchangeBuilder(string name, string type) - { - _name = name; - _type = type; - } - - public static ExchangeBuilder DirectExchange(string name) - { - return new ExchangeBuilder(name, ExchangeType.Direct); - } - - public static ExchangeBuilder TopicExchange(string name) - { - return new ExchangeBuilder(name, ExchangeType.Topic); - } - - public static ExchangeBuilder FanOutExchange(string name) - { - return new ExchangeBuilder(name, ExchangeType.FanOut); - } - - public static ExchangeBuilder HeadersExchange(string name) - { - return new ExchangeBuilder(name, ExchangeType.Headers); - } - - public static IExchange Create(string exchangeName, string exchangeType) - { - if (ExchangeType.Direct.Equals(exchangeType, StringComparison.OrdinalIgnoreCase)) - { - return new DirectExchange(exchangeName); - } - - if (ExchangeType.Topic.Equals(exchangeType, StringComparison.OrdinalIgnoreCase)) - { - return new TopicExchange(exchangeName); - } - - if (ExchangeType.FanOut.Equals(exchangeType, StringComparison.OrdinalIgnoreCase)) - { - return new FanOutExchange(exchangeName); - } - - if (ExchangeType.Headers.Equals(exchangeType, StringComparison.OrdinalIgnoreCase)) - { - return new HeadersExchange(exchangeName); - } - - return new CustomExchange(exchangeName, exchangeType); - } - - public ExchangeBuilder AutoDelete() - { - _autoDelete = true; - return this; - } - - public ExchangeBuilder Durable(bool isDurable) - { - _durable = isDurable; - return this; - } - - public ExchangeBuilder WithArgument(string key, object value) - { - GetOrCreateArguments().Add(key, value); - return this; - } - - public ExchangeBuilder WithArguments(Dictionary arguments) - { - Dictionary args = GetOrCreateArguments(); - - foreach (KeyValuePair arg in arguments) - { - args.Add(arg.Key, arg.Value); - } - - return this; - } - - public ExchangeBuilder Alternate(string exchange) - { - return WithArgument("alternate-exchange", exchange); - } - - public ExchangeBuilder Internal() - { - _internal = true; - return this; - } - - public ExchangeBuilder Delayed() - { - _delayed = true; - return this; - } - - public ExchangeBuilder IgnoreDeclarationExceptions() - { - _ignoreDeclarationExceptions = true; - return this; - } - - public ExchangeBuilder SuppressDeclaration() - { - _declare = false; - return this; - } - - public ExchangeBuilder Admins(params object[] declaringAdmins) - { - ArgumentGuard.NotNull(declaringAdmins); - ArgumentGuard.ElementsNotNull(declaringAdmins); - - _declaringAdmins = new List(declaringAdmins); - return this; - } - - public AbstractExchange Build() - { - var exchange = Create(_name, _type) as AbstractExchange; - exchange.IsDurable = _durable; - exchange.IsAutoDelete = _autoDelete; - exchange.Arguments = Arguments; - exchange.IsInternal = _internal; - exchange.IsDelayed = _delayed; - exchange.IgnoreDeclarationExceptions = _ignoreDeclarationExceptions; - exchange.ShouldDeclare = _declare; - - if (_declaringAdmins != null && _declaringAdmins.Count > 0) - { - exchange.SetAdminsThatShouldDeclare(_declaringAdmins.ToArray()); - } - - return exchange; - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/ExchangeType.cs b/src/Messaging/src/RabbitMQ/Configuration/ExchangeType.cs deleted file mode 100644 index 188b76aa7a..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/ExchangeType.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public static class ExchangeType -{ - public const string Direct = "direct"; - public const string Topic = "topic"; - public const string FanOut = "fanout"; - public const string Headers = "headers"; - public const string System = "system"; -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/FanoutExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/FanoutExchange.cs deleted file mode 100644 index 0fc270d073..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/FanoutExchange.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class FanOutExchange : AbstractExchange, IFanOutExchange -{ - public override string Type { get; } = ExchangeType.FanOut; - - public FanOutExchange(string name) - : base(name) - { - } - - public FanOutExchange(string name, bool durable, bool autoDelete) - : base(name, durable, autoDelete) - { - } - - public FanOutExchange(string name, bool durable, bool autoDelete, Dictionary arguments) - : base(name, durable, autoDelete, arguments) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/HeadersExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/HeadersExchange.cs deleted file mode 100644 index 582cdb0412..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/HeadersExchange.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class HeadersExchange : AbstractExchange, IHeadersExchange -{ - public override string Type { get; } = ExchangeType.Headers; - - public HeadersExchange(string name) - : base(name) - { - } - - public HeadersExchange(string name, bool durable, bool autoDelete) - : base(name, durable, autoDelete) - { - } - - public HeadersExchange(string name, bool durable, bool autoDelete, Dictionary arguments) - : base(name, durable, autoDelete, arguments) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IBinding.cs b/src/Messaging/src/RabbitMQ/Configuration/IBinding.cs deleted file mode 100644 index 747c509a72..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IBinding.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; -using static Steeltoe.Messaging.RabbitMQ.Configuration.Binding; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IBinding : IDeclarable, IServiceNameAware -{ - public string BindingName { get; set; } - - public string Destination { get; set; } - - public string Exchange { get; set; } - - public string RoutingKey { get; set; } - - public DestinationType Type { get; set; } - - public bool IsDestinationQueue { get; } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/ICustomExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/ICustomExchange.cs deleted file mode 100644 index a79bb3d9d2..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/ICustomExchange.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface ICustomExchange : IExchange -{ -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IDeclarable.cs b/src/Messaging/src/RabbitMQ/Configuration/IDeclarable.cs deleted file mode 100644 index 5c64550262..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IDeclarable.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IDeclarable -{ - /// - /// Gets or sets a value indicating whether this object should be declared. - /// - bool ShouldDeclare { get; set; } - - /// - /// Gets or sets a collection of Admins that should declare this object. - /// - List DeclaringAdmins { get; set; } - - /// - /// Gets or sets a value indicating whether should ignore exceptions. - /// - public bool IgnoreDeclarationExceptions { get; set; } - - /// - /// Gets or sets the arguments for this declarable. - /// - public Dictionary Arguments { get; set; } - - /// - /// Adds an argument to the declarable. - /// - /// - /// the argument name. - /// - /// - /// the argument value. - /// - void AddArgument(string name, object value); - - /// - /// Remove an argument from the declarable. - /// - /// - /// the argument name. - /// - /// - /// the value if present. - /// - object RemoveArgument(string name); -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IDeclarableCustomizer.cs b/src/Messaging/src/RabbitMQ/Configuration/IDeclarableCustomizer.cs deleted file mode 100644 index edd4241df2..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IDeclarableCustomizer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IDeclarableCustomizer -{ - IDeclarable Apply(IDeclarable declarable); -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IDirectExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/IDirectExchange.cs deleted file mode 100644 index 189d4a35f0..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IDirectExchange.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IDirectExchange : IExchange -{ -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/IExchange.cs deleted file mode 100644 index b116880667..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IExchange.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IExchange : IDeclarable, IServiceNameAware -{ - string ExchangeName { get; set; } - - string Type { get; } - - bool IsDurable { get; set; } - - bool IsAutoDelete { get; set; } - - bool IsDelayed { get; set; } - - bool IsInternal { get; set; } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IExchangeBinding.cs b/src/Messaging/src/RabbitMQ/Configuration/IExchangeBinding.cs deleted file mode 100644 index 863ec4f237..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IExchangeBinding.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IExchangeBinding : IBinding -{ -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IFanoutExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/IFanoutExchange.cs deleted file mode 100644 index 5f1cf6329d..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IFanoutExchange.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IFanOutExchange : IExchange -{ -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IHeadersExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/IHeadersExchange.cs deleted file mode 100644 index c88bc77b86..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IHeadersExchange.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IHeadersExchange : IExchange -{ -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IQueue.cs b/src/Messaging/src/RabbitMQ/Configuration/IQueue.cs deleted file mode 100644 index c2456f1685..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IQueue.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IQueue : IDeclarable, IServiceNameAware -{ - string QueueName { get; set; } - - string ActualName { get; set; } - - bool IsDurable { get; set; } - - bool IsExclusive { get; set; } - - bool IsAutoDelete { get; set; } - - string MasterLocator { get; set; } - - object Clone(); -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IQueueBinding.cs b/src/Messaging/src/RabbitMQ/Configuration/IQueueBinding.cs deleted file mode 100644 index ffed7153c4..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IQueueBinding.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IQueueBinding : IBinding -{ -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IRabbitListenerAttributeProcessor.cs b/src/Messaging/src/RabbitMQ/Configuration/IRabbitListenerAttributeProcessor.cs deleted file mode 100644 index de7948470e..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IRabbitListenerAttributeProcessor.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IRabbitListenerAttributeProcessor : IServiceNameAware -{ - void Initialize(); -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/IRabbitListenerConfigurer.cs b/src/Messaging/src/RabbitMQ/Configuration/IRabbitListenerConfigurer.cs deleted file mode 100644 index 22c4325ee2..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/IRabbitListenerConfigurer.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Listener; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface IRabbitListenerConfigurer -{ - void ConfigureRabbitListeners(IRabbitListenerEndpointRegistrar registrar); -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/ITopicExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/ITopicExchange.cs deleted file mode 100644 index d70e7ec9f0..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/ITopicExchange.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public interface ITopicExchange : IExchange -{ -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/Queue.cs b/src/Messaging/src/RabbitMQ/Configuration/Queue.cs deleted file mode 100644 index 98154e7cea..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/Queue.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; -using Steeltoe.Common; -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class Queue : AbstractDeclarable, IQueue, ICloneable -{ - public const string XQueueMasterLocator = "x-queue-master-locator"; - - public string ServiceName { get; set; } - - public string QueueName { get; set; } - - public string ActualName { get; set; } - - public bool IsDurable { get; set; } - - public bool IsExclusive { get; set; } - - public bool IsAutoDelete { get; set; } - - public string MasterLocator - { - get - { - Arguments.TryGetValue(XQueueMasterLocator, out object result); - return result as string; - } - set - { - if (value == null) - { - RemoveArgument(XQueueMasterLocator); - } - else - { - AddArgument(XQueueMasterLocator, value); - } - } - } - - public Queue(string queueName) - : this(queueName, true, false, false) - { - } - - public Queue(string queueName, bool durable) - : this(queueName, durable, false, false, null) - { - } - - public Queue(string queueName, bool durable, bool exclusive, bool autoDelete) - : this(queueName, durable, exclusive, autoDelete, null) - { - } - - public Queue(string queueName, bool durable, bool exclusive, bool autoDelete, Dictionary arguments) - : base(arguments) - { - ArgumentGuard.NotNull(queueName); - - QueueName = queueName; - ServiceName = !string.IsNullOrEmpty(queueName) ? queueName : $"queue@{RuntimeHelpers.GetHashCode(this)}"; - ActualName = !string.IsNullOrEmpty(queueName) ? queueName : Base64UrlNamingStrategy.Default.GenerateName() + "_awaiting_declaration"; - IsDurable = durable; - IsExclusive = exclusive; - IsAutoDelete = autoDelete; - } - - public object Clone() - { - var queue = new Queue(QueueName, IsDurable, IsExclusive, IsAutoDelete, new Dictionary(Arguments)) - { - ActualName = ActualName - }; - - return queue; - } - - public override string ToString() - { - return - $"Queue [name={QueueName}, durable={IsDurable}, autoDelete={IsAutoDelete}, exclusive={IsExclusive}, arguments={Arguments}, actualName={ActualName}]"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/QueueBinding.cs b/src/Messaging/src/RabbitMQ/Configuration/QueueBinding.cs deleted file mode 100644 index 8d71475661..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/QueueBinding.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class QueueBinding : Binding, IQueueBinding -{ - public QueueBinding(string bindingName) - : base(bindingName) - { - } - - public QueueBinding(string name, string queueDestination, string exchange, string routingKey, Dictionary arguments) - : base(name, queueDestination, DestinationType.Queue, exchange, routingKey, arguments) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/QueueBuilder.cs b/src/Messaging/src/RabbitMQ/Configuration/QueueBuilder.cs deleted file mode 100644 index 8978739195..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/QueueBuilder.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class QueueBuilder : AbstractBuilder -{ - private static readonly INamingStrategy NamingStrategy = Base64UrlNamingStrategy.Default; - private readonly string _name; - private bool _durable; - private bool _exclusive; - private bool _autoDelete; - - private QueueBuilder(string name) - { - _name = name; - } - - public static QueueBuilder Durable() - { - return Durable(NamingStrategy.GenerateName()); - } - - public static QueueBuilder Durable(string name) - { - return new QueueBuilder(name) - { - _durable = true - }; - } - - public static QueueBuilder NonDurable() - { - return new QueueBuilder(NamingStrategy.GenerateName()); - } - - public static QueueBuilder NonDurable(string name) - { - return new QueueBuilder(name); - } - - public QueueBuilder Exclusive() - { - _exclusive = true; - return this; - } - - public QueueBuilder AutoDelete() - { - _autoDelete = true; - return this; - } - - public QueueBuilder WithArgument(string key, object value) - { - GetOrCreateArguments()[key] = value; - return this; - } - - public QueueBuilder WithArguments(Dictionary arguments) - { - Dictionary args = GetOrCreateArguments(); - - foreach (KeyValuePair arg in arguments) - { - args[arg.Key] = arg.Value; - } - - return this; - } - - public QueueBuilder Ttl(int value) - { - return WithArgument("x-message-ttl", value); - } - - public QueueBuilder Expires(int value) - { - return WithArgument("x-expires", value); - } - - public QueueBuilder MaxLength(int count) - { - return WithArgument("x-max-length", count); - } - - public QueueBuilder MaxLengthBytes(int bytes) - { - return WithArgument("x-max-length-bytes", bytes); - } - - public QueueBuilder Overflow(OverFlow value) - { - return WithArgument("x-overflow", value.Value); - } - - public QueueBuilder DeadLetterExchange(string exchange) - { - return WithArgument("x-dead-letter-exchange", exchange); - } - - public QueueBuilder DeadLetterRoutingKey(string key) - { - return WithArgument("x-dead-letter-routing-key", key); - } - - public QueueBuilder MaxPriority(int value) - { - return WithArgument("x-max-priority", value); - } - - public QueueBuilder Lazy() - { - return WithArgument("x-queue-mode", "lazy"); - } - - public QueueBuilder Masterlocator(MasterLocator locator) - { - return WithArgument("x-queue-master-locator", locator.Value); - } - - public QueueBuilder SingleActiveConsumer() - { - return WithArgument("x-single-active-consumer", true); - } - - public QueueBuilder Quorum() - { - return WithArgument("x-queue-type", "quorum"); - } - - public QueueBuilder DeliveryLimit(int limit) - { - return WithArgument("x-delivery-limit", limit); - } - - public IQueue Build() - { - return new Queue(_name, _durable, _exclusive, _autoDelete, Arguments); - } - - public class OverFlow - { - public static OverFlow DropHead { get; } = new("drop-head"); - - public static OverFlow RejectPublish { get; } = new("reject-publish"); - - public string Value { get; } - - private OverFlow(string value) - { - Value = value; - } - } - - public class MasterLocator - { - public static MasterLocator MinMasters { get; } = new("min-masters"); - - public static MasterLocator ClientLocal { get; } = new("client-local"); - - public static MasterLocator Random { get; } = new("random"); - - public string Value { get; } - - private MasterLocator(string value) - { - Value = value; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/QueueInformation.cs b/src/Messaging/src/RabbitMQ/Configuration/QueueInformation.cs deleted file mode 100644 index 5d4a0832b0..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/QueueInformation.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class QueueInformation -{ - public string Name { get; } - - public uint MessageCount { get; } - - public uint ConsumerCount { get; } - - public QueueInformation(string name, uint messageCount, uint consumerCount) - { - Name = name; - MessageCount = messageCount; - ConsumerCount = consumerCount; - } - - public override int GetHashCode() - { - return HashCode.Combine(Name, MessageCount, ConsumerCount); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not QueueInformation other || GetType() != obj.GetType()) - { - return false; - } - - return Name == other.Name; - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/RabbitListenerAttributeProcessor.cs b/src/Messaging/src/RabbitMQ/Configuration/RabbitListenerAttributeProcessor.cs deleted file mode 100644 index 39942173ce..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/RabbitListenerAttributeProcessor.cs +++ /dev/null @@ -1,523 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Configuration; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Order; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class RabbitListenerAttributeProcessor : IRabbitListenerAttributeProcessor, IOrdered -{ - public const string DefaultServiceName = nameof(RabbitListenerAttributeProcessor); - - private readonly List _rabbitListenerMetadata; - private readonly ILoggerFactory _loggerFactory; - private readonly ILogger _logger; - private int _counter; - private IApplicationContext _applicationContext; - - internal IMessageHandlerMethodFactory MessageHandlerMethodFactory { get; set; } - - public IApplicationContext ApplicationContext - { - get => _applicationContext; - set - { - _applicationContext = value; - - if (_applicationContext != null) - { - Resolver = _applicationContext.ServiceExpressionResolver ?? new StandardServiceExpressionResolver(); - ExpressionContext = new ServiceExpressionContext(_applicationContext); - } - } - } - - public int Order { get; } = AbstractOrdered.LowestPrecedence; - - public IServiceExpressionResolver Resolver { get; set; } = new StandardServiceExpressionResolver(); - - public IServiceExpressionContext ExpressionContext { get; set; } - - public IServiceResolver ServiceResolver { get; set; } - - public IRabbitListenerEndpointRegistrar Registrar { get; } - - public IRabbitListenerEndpointRegistry EndpointRegistry { get; } - - public Encoding Charset { get; set; } = EncodingUtils.Utf8; - - public string ContainerFactoryServiceName { get; set; } = DirectRabbitListenerContainerFactory.DefaultServiceName; - - public string ServiceName { get; set; } = DefaultServiceName; - - public RabbitListenerAttributeProcessor(IApplicationContext applicationContext, IRabbitListenerEndpointRegistry endpointRegistry, - IRabbitListenerEndpointRegistrar registrar, IMessageHandlerMethodFactory messageHandlerMethodFactory, - IEnumerable rabbitListeners, ILoggerFactory loggerFactory = null) - { - ApplicationContext = applicationContext; - EndpointRegistry = endpointRegistry; - registrar.EndpointRegistry = endpointRegistry; - Registrar = registrar; - MessageHandlerMethodFactory = messageHandlerMethodFactory; - _rabbitListenerMetadata = rabbitListeners.ToList(); - _loggerFactory = loggerFactory; - _logger = _loggerFactory?.CreateLogger(); - } - - public void Initialize() - { - _logger?.LogDebug("RabbitListenerAttributeProcessor initializing"); - - foreach (RabbitListenerMetadata metadata in _rabbitListenerMetadata) - { - object bean = CreateTargetBean(metadata.TargetClass); - string beanName = metadata.TargetClass.Name; - - foreach (RabbitListenerMetadata.ListenerMethod lm in metadata.ListenerMethods) - { - foreach (RabbitListenerAttribute rabbitListener in lm.Attributes) - { - ProcessAmqpListener(rabbitListener, lm.Method, bean, beanName); - } - } - - if (metadata.HandlerMethods.Count > 0) - { - ProcessMultiMethodListeners(metadata.ClassAnnotations, metadata.HandlerMethods, bean, beanName); - } - } - - Registrar.ApplicationContext = ApplicationContext; - IEnumerable instances = ApplicationContext.GetServices(); - - foreach (IRabbitListenerConfigurer configurer in instances) - { - configurer.ConfigureRabbitListeners(Registrar); - } - - if (ContainerFactoryServiceName != null) - { - Registrar.ContainerFactoryServiceName = ContainerFactoryServiceName; - } - - IMessageHandlerMethodFactory handlerMethodFactory = Registrar.MessageHandlerMethodFactory; - - if (handlerMethodFactory != null) - { - MessageHandlerMethodFactory = handlerMethodFactory; - } - - _logger?.LogDebug("Initializing IRabbitListenerEndpointRegistrar"); - Registrar.Initialize(); - } - - protected void ProcessMultiMethodListeners(List classLevelListeners, List multiMethods, object bean, string beanName) - { - var checkedMethods = new List(); - MethodInfo defaultMethod = null; - - foreach (MethodInfo method in multiMethods) - { - var attribute = method.GetCustomAttribute(); - - if (attribute == null) - { - throw new InvalidOperationException("MultiMethod must contain RabbitHandlerAttribute"); - } - - if (attribute.IsDefault) - { - MethodInfo toAssert = defaultMethod; - - if (toAssert != null) - { - throw new InvalidOperationException($"Only one RabbitHandlerAttribute can be marked 'isDefault', found: {toAssert} and {method}"); - } - - defaultMethod = method; - } - - _logger?.LogDebug("Adding RabbitHandler method {handlerMethod} from type {type}", method, method.DeclaringType); - checkedMethods.Add(method); - } - - foreach (RabbitListenerAttribute classLevelListener in classLevelListeners) - { - var endpoint = new MultiMethodRabbitListenerEndpoint(ApplicationContext, checkedMethods, defaultMethod, bean, _loggerFactory); - ProcessListener(endpoint, classLevelListener, bean, bean.GetType(), beanName); - } - } - - protected void ProcessListener(MethodRabbitListenerEndpoint endpoint, RabbitListenerAttribute rabbitListener, object bean, object target, string beanName) - { - endpoint.MessageHandlerMethodFactory = MessageHandlerMethodFactory; - endpoint.Id = GetEndpointId(rabbitListener); - endpoint.SetQueueNames(ResolveQueues(rabbitListener)); - endpoint.Concurrency = ResolveExpressionAsInteger(rabbitListener.Concurrency, "Concurrency"); - endpoint.ApplicationContext = ApplicationContext; - endpoint.ReturnExceptions = ResolveExpressionAsBoolean(rabbitListener.ReturnExceptions, "ReturnExceptions"); - - string group = rabbitListener.Group; - - if (!string.IsNullOrEmpty(group)) - { - endpoint.Group = group; - } - - string autoStartup = rabbitListener.AutoStartup; - - if (!string.IsNullOrEmpty(autoStartup)) - { - endpoint.AutoStartup = ResolveExpressionAsBoolean(autoStartup, "AutoStartup"); - } - - endpoint.Exclusive = rabbitListener.Exclusive; - - endpoint.Priority = ResolveExpressionAsInteger(rabbitListener.Priority, "Priority"); - - ResolveErrorHandler(endpoint, rabbitListener); - ResolveAdmin(endpoint, rabbitListener, target); - ResolveAckMode(endpoint, rabbitListener); - ResolvePostProcessor(endpoint, rabbitListener, target, beanName); - IRabbitListenerContainerFactory factory = ResolveContainerFactory(rabbitListener, target); - - Registrar.RegisterEndpoint(endpoint, factory); - } - - private void ProcessAmqpListener(RabbitListenerAttribute rabbitListener, MethodInfo method, object bean, string beanName) - { - _logger?.LogDebug("Adding RabbitListener method {method} from type {type}", method, method.DeclaringType); - - var endpoint = new MethodRabbitListenerEndpoint(ApplicationContext, method, bean, _loggerFactory) - { - Method = method - }; - - ProcessListener(endpoint, rabbitListener, bean, method, beanName); - } - - private void ResolvePostProcessor(MethodRabbitListenerEndpoint endpoint, RabbitListenerAttribute rabbitListener, object target, string name) - { - string ppBeanName = Resolve(rabbitListener.ReplyPostProcessor); - - if (!string.IsNullOrEmpty(ppBeanName)) - { - if (ApplicationContext == null) - { - throw new InvalidOperationException("IApplicationContext must be set to resolve reply post processor by name"); - } - - var pp = ApplicationContext.GetService(ppBeanName); - - if (pp == null) - { - throw new InvalidOperationException( - $"Could not register rabbit listener endpoint on [{target}], no IReplyPostProcessor with id '{name}' was found"); - } - - endpoint.ReplyPostProcessor = pp; - } - } - - private IRabbitListenerContainerFactory ResolveContainerFactory(RabbitListenerAttribute rabbitListener, object factoryTarget) - { - IRabbitListenerContainerFactory factory = null; - string containerFactoryBeanName = Resolve(rabbitListener.ContainerFactory); - - if (!string.IsNullOrEmpty(containerFactoryBeanName)) - { - if (ApplicationContext == null) - { - throw new InvalidOperationException("IApplicationContext must be set to resolve container factory by name"); - } - - factory = ApplicationContext.GetService(containerFactoryBeanName); - - if (factory == null) - { - throw new InvalidOperationException( - $"Could not register rabbit listener endpoint on [{factoryTarget}], no IRabbitListenerContainerFactory with id '{containerFactoryBeanName}' was found"); - } - } - - return factory; - } - - private void ResolveAdmin(MethodRabbitListenerEndpoint endpoint, RabbitListenerAttribute rabbitListener, object adminTarget) - { - string rabbitAdmin = Resolve(rabbitListener.Admin); - - if (!string.IsNullOrEmpty(rabbitAdmin)) - { - if (ApplicationContext == null) - { - throw new InvalidOperationException("IApplicationContext must be set to resolve RabbitAdmin by name"); - } - - endpoint.Admin = ApplicationContext.GetService(rabbitAdmin); - - if (endpoint.Admin == null) - { - throw new InvalidOperationException( - $"Could not register rabbit listener endpoint on [{adminTarget}], no RabbitAdmin with id '{rabbitAdmin}' was found"); - } - } - } - - private void ResolveErrorHandler(MethodRabbitListenerEndpoint endpoint, RabbitListenerAttribute rabbitListener) - { - if (!string.IsNullOrEmpty(rabbitListener.ErrorHandler)) - { - object errorHandler = ResolveExpression(rabbitListener.ErrorHandler); - - switch (errorHandler) - { - case IRabbitListenerErrorHandler rabbitListenerErrorHandler: - endpoint.ErrorHandler = rabbitListenerErrorHandler; - break; - case string errorHandlerName: - if (ApplicationContext == null) - { - throw new InvalidOperationException("IApplicationContext must be set to resolve ErrorHandler by name"); - } - - endpoint.ErrorHandler = ApplicationContext.GetService(errorHandlerName); - - if (endpoint.ErrorHandler == null) - { - throw new InvalidOperationException($"Failed to resolve ErrorHandler by name using: {errorHandlerName}"); - } - - break; - default: - throw new InvalidOperationException("ErrorHandler must resolve to a String or IRabbitListenerErrorHandler"); - } - } - } - - private void ResolveAckMode(MethodRabbitListenerEndpoint endpoint, RabbitListenerAttribute rabbitListener) - { - if (!string.IsNullOrEmpty(rabbitListener.AckMode)) - { - object ackMode = ResolveExpression(rabbitListener.AckMode); - - if (ackMode is AcknowledgeMode mode) - { - endpoint.AckMode = mode; - } - else if (ackMode is string ackModeString) - { - endpoint.AckMode = (AcknowledgeMode)Enum.Parse(typeof(AcknowledgeMode), ackModeString); - } - else - { - throw new InvalidOperationException("AckMode must resolve to a String or AcknowledgeMode enumeration"); - } - } - } - - private bool ResolveExpressionAsBoolean(string value, string propertyName, bool defaultValue = false) - { - if (!string.IsNullOrEmpty(value)) - { - object resolved = ResolveExpression(value); - - if (resolved is bool boolean) - { - return boolean; - } - - if (resolved is string resolvedString) - { - if (bool.TryParse(resolvedString, out bool result)) - { - return result; - } - - throw new InvalidOperationException($"Unable to resolve {propertyName} to a bool using {resolvedString}"); - } - } - - return defaultValue; - } - - private int? ResolveExpressionAsInteger(string value, string propertyName) - { - if (!string.IsNullOrEmpty(value)) - { - object resolved = ResolveExpression(value); - - if (resolved is int resolvedInt) - { - return resolvedInt; - } - - if (resolved is string resolvedString && int.TryParse(resolvedString, out int result)) - { - return result; - } - - throw new InvalidOperationException($"Unable to resolve {propertyName} to an int using {resolved}"); - } - - return null; - } - - private string[] ResolveQueues(RabbitListenerAttribute rabbitListener) - { - // var allQueues = ApplicationContext.GetServices() - string[] queues = rabbitListener.Queues; - - var result = new List(); - - if (queues.Length > 0) - { - foreach (string queueExpression in queues) - { - ResolveQueue(queueExpression, result); - } - } - - // var allBindings = ApplicationContext.GetServices() - string[] bindings = rabbitListener.Bindings; - - if (bindings.Length > 0) - { - ResolveBindingDeclaration(rabbitListener, result); - } - - return result.ToArray(); - } - - private void ResolveQueue(string queueExpression, List results) - { - string qRef = queueExpression; - - if (ResolveExpression(queueExpression) is not IQueue queue) - { - qRef = Resolve(queueExpression); - queue = ApplicationContext.GetService(qRef); - } - - if (queue != null) - { - qRef = queue.QueueName; - } - - results.Add(qRef); - } - - private void ResolveBindingDeclaration(RabbitListenerAttribute rabbitListener, List results) - { - foreach (string bindingExpression in rabbitListener.Bindings) - { - if (ResolveExpression(bindingExpression) is not IBinding binding) - { - string bindingName = Resolve(bindingExpression); - binding = ApplicationContext.GetService(bindingName); - - if (binding == null) - { - throw new InvalidOperationException($"Unable to resolve binding: {bindingExpression} using: {bindingName}"); - } - } - - ResolveQueue(binding.Destination, results); - } - } - - private void ResolveAsString(object resolvedValue, List result, bool canBeQueue, string what) - { - object resolvedValueToUse = resolvedValue; - - if (resolvedValue is string[] v) - { - resolvedValueToUse = new List(v); - } - - if (canBeQueue && resolvedValueToUse is IQueue queue) - { - result.Add(queue.QueueName); - } - else if (resolvedValueToUse is string asString) - { - result.Add(asString); - } - else if (resolvedValueToUse is IEnumerable enumerable) - { - foreach (object o in enumerable) - { - ResolveAsString(o, result, canBeQueue, what); - } - } - else - { - throw new InvalidOperationException( - $"RabbitListenerAttribute {what} can't resolve {resolvedValue} as a String[] or a String{(canBeQueue ? " or a Queue" : string.Empty)}"); - } - } - - private object ResolveExpression(string value) - { - string resolvedValue = Resolve(value); - return Resolver.Evaluate(resolvedValue, ExpressionContext); - } - - private string Resolve(string value) - { - string result = value; - - if (ApplicationContext != null) - { - IConfiguration configuration = ApplicationContext.Configuration; - - if (configuration != null) - { - result = PropertyPlaceholderHelper.ResolvePlaceholders(value, configuration, _logger); - } - } - - return result; - } - - private string GetEndpointId(RabbitListenerAttribute rabbitListener) - { - if (!string.IsNullOrEmpty(rabbitListener.Id)) - { - return Resolve(rabbitListener.Id); - } - - return $"Steeltoe.Messaging.Rabbit.RabbitListenerEndpointContainer#{Interlocked.Increment(ref _counter)}"; - } - - private object CreateTargetBean(Type implementationType) - { - try - { - _logger?.LogDebug("Creating RabbitListener service {serviceType}.", implementationType); - return ApplicationContext.ServiceProvider.GetService(implementationType); - } - catch (Exception e) - { - // Log - _logger?.LogError(e, "Error creating RabbitListener service {serviceType}.", implementationType); - throw new InvalidOperationException($"Unable to CreateInstance of type containing RabbitListener method, Type: {implementationType}", e); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/RabbitListenerDeclareAttributeProcessor.cs b/src/Messaging/src/RabbitMQ/Configuration/RabbitListenerDeclareAttributeProcessor.cs deleted file mode 100644 index 5a1a7ad6c8..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/RabbitListenerDeclareAttributeProcessor.cs +++ /dev/null @@ -1,316 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common; -using Steeltoe.Common.Configuration; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public static class RabbitListenerDeclareAttributeProcessor -{ - private static readonly Dictionary QueueMap = new(); - private static readonly Dictionary BindingMap = new(); - - internal static void ProcessDeclareAttributes(IServiceCollection services, IConfiguration configuration, Type targetClass) - { - ArgumentGuard.NotNull(targetClass); - - List declareQueues = GetAllAttributes(targetClass); - List queues = ProcessDeclareQueues(services, configuration, declareQueues); - UpdateQueueDeclarations(queues); - - List declareAnonQueues = GetAllAttributes(targetClass); - List anonQueues = ProcessDeclareAnonymousQueues(services, configuration, declareAnonQueues); - UpdateQueueDeclarations(anonQueues); - - List declareExchanges = GetAllAttributes(targetClass); - ProcessDeclareExchanges(services, configuration, declareExchanges); - - List declareBindings = GetAllAttributes(targetClass); - List bindings = ProcessDeclareQueueBindings(services, configuration, declareBindings); - UpdateBindingDeclarations(bindings); - } - - private static List ProcessDeclareQueueBindings(IServiceCollection services, IConfiguration configuration, - List declareBindings) - { - var bindings = new List(); - - foreach (DeclareQueueBindingAttribute b in declareBindings) - { - string bindingName = PropertyPlaceholderHelper.ResolvePlaceholders(b.Name, configuration); - - if (b.RoutingKeys.Length > 0) - { - foreach (string key in b.RoutingKeys) - { - QueueBinding binding = BuildBinding(bindingName, b, configuration, key); - services.AddRabbitBinding(binding); - bindings.Add(binding); - } - } - else - { - QueueBinding binding = BuildBinding(bindingName, b, configuration); - services.AddRabbitBinding(binding); - bindings.Add(binding); - } - } - - return bindings; - } - - private static void ProcessDeclareExchanges(IServiceCollection services, IConfiguration configuration, List declareExchanges) - { - foreach (DeclareExchangeAttribute e in declareExchanges) - { - string exchangeName = PropertyPlaceholderHelper.ResolvePlaceholders(e.Name, configuration); - IExchange exchange = CreateExchange(exchangeName, e.Type); - - if (!string.IsNullOrEmpty(e.Durable)) - { - exchange.IsDurable = GetBoolean(e.Durable, configuration, nameof(e.Durable)); - } - - if (!string.IsNullOrEmpty(e.Delayed)) - { - exchange.IsDelayed = GetBoolean(e.Delayed, configuration, nameof(e.Delayed)); - } - - if (!string.IsNullOrEmpty(e.AutoDelete)) - { - exchange.IsAutoDelete = GetBoolean(e.AutoDelete, configuration, nameof(e.AutoDelete)); - } - - if (!string.IsNullOrEmpty(e.IgnoreDeclarationExceptions)) - { - exchange.IgnoreDeclarationExceptions = GetBoolean(e.IgnoreDeclarationExceptions, configuration, nameof(e.IgnoreDeclarationExceptions)); - } - - if (!string.IsNullOrEmpty(e.Declare)) - { - exchange.ShouldDeclare = GetBoolean(e.Declare, configuration, nameof(e.Declare)); - } - - if (!string.IsNullOrEmpty(e.Internal)) - { - exchange.IsInternal = GetBoolean(e.Internal, configuration, nameof(e.Internal)); - } - - if (e.Admins.Length > 0) - { - foreach (string a in e.Admins) - { - exchange.DeclaringAdmins.Add(a); - } - } - - services.AddRabbitExchange(exchange); - } - } - - private static void UpdateBindingDeclarations(List bindings) - { - foreach (QueueBinding binding in bindings) - { - BindingMap.TryAdd(binding.ServiceName, binding); - } - } - - private static void UpdateQueueDeclarations(List queues) - { - foreach (Queue queue in queues) - { - QueueMap.TryAdd(queue.ServiceName, queue); - } - } - - private static QueueBinding BuildBinding(string bindingName, DeclareQueueBindingAttribute b, IConfiguration configuration, string routingKey = null) - { - var binding = new QueueBinding(bindingName); - - if (!string.IsNullOrEmpty(b.QueueName)) - { - string reference = PropertyPlaceholderHelper.ResolvePlaceholders(b.QueueName, configuration); - - if (ConfigurationUtils.IsExpression(reference)) - { - reference = ConfigurationUtils.ExtractExpressionString(reference); - - if (ConfigurationUtils.IsServiceReference(reference)) - { - reference = ConfigurationUtils.ExtractServiceName(reference); - } - } - - if (QueueMap.TryGetValue(reference, out Queue queueRef)) - { - reference = queueRef.QueueName; - } - - binding.Destination = reference; - } - - binding.Exchange = !string.IsNullOrEmpty(b.ExchangeName) ? PropertyPlaceholderHelper.ResolvePlaceholders(b.ExchangeName, configuration) : string.Empty; - - if (!string.IsNullOrEmpty(b.IgnoreDeclarationExceptions)) - { - binding.IgnoreDeclarationExceptions = GetBoolean(b.IgnoreDeclarationExceptions, configuration, nameof(b.IgnoreDeclarationExceptions)); - } - - if (!string.IsNullOrEmpty(b.Declare)) - { - binding.ShouldDeclare = GetBoolean(b.Declare, configuration, nameof(b.Declare)); - } - - binding.RoutingKey = !string.IsNullOrEmpty(routingKey) ? PropertyPlaceholderHelper.ResolvePlaceholders(routingKey, configuration) : string.Empty; - - if (b.Admins.Length > 0) - { - foreach (string a in b.Admins) - { - binding.DeclaringAdmins.Add(a); - } - } - - return binding; - } - - private static IExchange CreateExchange(string name, string type) - { - if (type == ExchangeType.Direct) - { - return new DirectExchange(name); - } - - if (type == ExchangeType.FanOut) - { - return new FanOutExchange(name); - } - - if (type == ExchangeType.Headers) - { - return new HeadersExchange(name); - } - - if (type == ExchangeType.Topic) - { - return new TopicExchange(name); - } - - if (type == ExchangeType.System) - { - return new CustomExchange(name, ExchangeType.System); - } - - throw new InvalidOperationException($"Unable to determine exchange type {type}"); - } - - private static List ProcessDeclareQueues(IServiceCollection services, IConfiguration configuration, List declareQueues) - { - var queues = new List(); - - foreach (DeclareQueueAttribute q in declareQueues) - { - string queueName = PropertyPlaceholderHelper.ResolvePlaceholders(q.Name, configuration); - var queue = new Queue(queueName); - UpdateQueue(queue, q, configuration); - services.AddRabbitQueue(queue); - queues.Add(queue); - } - - return queues; - } - - private static List ProcessDeclareAnonymousQueues(IServiceCollection services, IConfiguration configuration, - List declareQueues) - { - var queues = new List(); - - foreach (DeclareAnonymousQueueAttribute q in declareQueues) - { - var queue = new Queue(q.Name, false, true, true) - { - ServiceName = q.Id - }; - - UpdateQueue(queue, q, configuration); - services.AddRabbitQueue(queue); - queues.Add(queue); - } - - return queues; - } - - private static void UpdateQueue(Queue queue, DeclareQueueBaseAttribute q, IConfiguration configuration) - { - if (!string.IsNullOrEmpty(q.Durable)) - { - queue.IsDurable = GetBoolean(q.Durable, configuration, nameof(q.Durable)); - } - - if (!string.IsNullOrEmpty(q.Exclusive)) - { - queue.IsExclusive = GetBoolean(q.Exclusive, configuration, nameof(q.Exclusive)); - } - - if (!string.IsNullOrEmpty(q.AutoDelete)) - { - queue.IsAutoDelete = GetBoolean(q.AutoDelete, configuration, nameof(q.AutoDelete)); - } - - if (!string.IsNullOrEmpty(q.IgnoreDeclarationExceptions)) - { - queue.IgnoreDeclarationExceptions = GetBoolean(q.IgnoreDeclarationExceptions, configuration, nameof(q.IgnoreDeclarationExceptions)); - } - - if (!string.IsNullOrEmpty(q.Declare)) - { - queue.ShouldDeclare = GetBoolean(q.Declare, configuration, nameof(q.Declare)); - } - - if (q.Admins.Length > 0) - { - foreach (string a in q.Admins) - { - queue.DeclaringAdmins.Add(a); - } - } - } - - private static bool GetBoolean(string value, IConfiguration configuration, string name) - { - value = PropertyPlaceholderHelper.ResolvePlaceholders(value, configuration); - - if (bool.TryParse(value, out bool result)) - { - return result; - } - - throw new InvalidOperationException($"Unable to parse annotation property: {name}"); - } - - private static List GetAllAttributes(Type targetClass) - where T : Attribute - { - var results = new List(); - - IEnumerable classLevel = targetClass.GetCustomAttributes(); - results.AddRange(classLevel); - MethodInfo[] reflectMethods = targetClass.GetMethods(BindingFlags.Public | BindingFlags.Instance); - - foreach (MethodInfo m in reflectMethods) - { - IEnumerable methodLevel = m.GetCustomAttributes(); - results.AddRange(methodLevel); - } - - return results; - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/RabbitListenerMetadata.cs b/src/Messaging/src/RabbitMQ/Configuration/RabbitListenerMetadata.cs deleted file mode 100644 index 8df9a846ca..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/RabbitListenerMetadata.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Listener; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class RabbitListenerMetadata -{ - internal static readonly Dictionary TypeCache = new(); - internal static readonly HashSet Groups = new(); - - internal List ListenerMethods { get; } - - internal List HandlerMethods { get; } - - internal List ClassAnnotations { get; } - - internal Type TargetClass { get; } - - internal RabbitListenerMetadata(Type targetClass, List methods, List multiMethods, - List classLevelListeners) - { - TargetClass = targetClass; - ListenerMethods = methods; - HandlerMethods = multiMethods; - ClassAnnotations = classLevelListeners; - } - - internal RabbitListenerMetadata() - { - ListenerMethods = new List(); - HandlerMethods = new List(); - ClassAnnotations = new List(); - } - - internal static RabbitListenerMetadata BuildMetadata(IServiceCollection services, Type targetClass) - { - ArgumentGuard.NotNull(targetClass); - - if (TypeCache.TryGetValue(targetClass, out _)) - { - return null; - } - - List classLevelListeners = targetClass.GetCustomAttributes().ToList(); - Validate(services, classLevelListeners); - var methods = new List(); - var multiMethods = new List(); - MethodInfo[] reflectMethods = targetClass.GetMethods(BindingFlags.Public | BindingFlags.Instance); - - foreach (MethodInfo m in reflectMethods) - { - List methodLevelListeners = m.GetCustomAttributes().ToList(); - - if (methodLevelListeners.Count > 0) - { - Validate(services, methodLevelListeners); - methods.Add(new ListenerMethod(m, methodLevelListeners)); - } - - if (classLevelListeners.Count > 0) - { - IEnumerable handlerAttributes = m.GetCustomAttributes(); - - if (handlerAttributes.Any()) - { - multiMethods.Add(m); - } - } - } - - if (methods.Count == 0 && multiMethods.Count == 0) - { - return null; - } - - return new RabbitListenerMetadata(targetClass, methods, multiMethods, classLevelListeners); - } - - private static void Validate(IServiceCollection services, List listenerAttributes) - { - foreach (RabbitListenerAttribute listener in listenerAttributes) - { - string[] queues = listener.Queues; - string[] bindings = listener.Bindings; - - if (bindings.Length > 0 && queues.Length > 0) - { - throw new InvalidOperationException("RabbitListenerAttribute can have either 'Queues' or 'Bindings' set, but not both"); - } - - if (!string.IsNullOrEmpty(listener.Group) && !Groups.Contains(listener.Group)) - { - Groups.Add(listener.Group); - services.AddSingleton(new MessageListenerContainerCollection(listener.Group)); - } - } - } - - internal static void Reset() - { - TypeCache.Clear(); - } - - internal sealed class ListenerMethod - { - public MethodInfo Method { get; } - - public List Attributes { get; } - - public ListenerMethod(MethodInfo method, List attributes) - { - Method = method; - Attributes = attributes; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/RabbitOptions.cs b/src/Messaging/src/RabbitMQ/Configuration/RabbitOptions.cs deleted file mode 100644 index 33f31fd379..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/RabbitOptions.cs +++ /dev/null @@ -1,370 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Security.Authentication; -using Steeltoe.Messaging.RabbitMQ.Core; -using static Steeltoe.Messaging.RabbitMQ.Connection.CachingConnectionFactory; - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class RabbitOptions -{ - public const string Prefix = "spring:rabbitmq"; - - public const string DefaultHost = "localhost"; - public const int DefaultPort = 5672; - public const string DefaultUsername = "guest"; - public const string DefaultPassword = "guest"; - - private List
ParsedAddresses => ParseAddresses(Addresses); - - public string Host { get; set; } = DefaultHost; - - public int Port { get; set; } = DefaultPort; - - public string Addresses { get; set; } - - public string Username { get; set; } = DefaultUsername; - - public string Password { get; set; } = DefaultPassword; - - public SslOptions Ssl { get; set; } - - public string VirtualHost { get; set; } - - public TimeSpan? RequestedHeartbeat { get; set; } - - public bool PublisherConfirms { get; set; } - - public bool PublisherReturns { get; set; } - - public TimeSpan? ConnectionTimeout { get; set; } - - public CacheOptions Cache { get; set; } - - public ListenerOptions Listener { get; set; } - - public TemplateOptions Template { get; set; } - - public RabbitOptions() - { - Ssl = new SslOptions(); - Cache = new CacheOptions(); - Listener = new ListenerOptions(); - Template = new TemplateOptions(); - } - - public string DetermineHost() - { - List
parsed = ParsedAddresses; - - if (parsed.Count == 0) - { - return Host; - } - - return parsed[0].Host; - } - - public int DeterminePort() - { - List
parsed = ParsedAddresses; - - if (parsed.Count == 0) - { - return Port; - } - - return parsed[0].Port; - } - - public string DetermineAddresses() - { - List
parsed = ParsedAddresses; - - if (parsed.Count == 0) - { - return null; - } - - var addressStrings = new List(); - - foreach (Address parsedAddress in parsed) - { - addressStrings.Add($"{parsedAddress.Host}:{parsedAddress.Port}"); - } - - return string.Join(',', addressStrings); - } - - private List
ParseAddresses(string addresses) - { - var parsedAddresses = new List
(); - - if (!string.IsNullOrEmpty(addresses)) - { - string[] splitAddresses = addresses.Split(',', StringSplitOptions.RemoveEmptyEntries); - - foreach (string address in splitAddresses) - { - string trim = address.Trim(); - parsedAddresses.Add(new Address(trim)); - } - } - - return parsedAddresses; - } - - public string DetermineUsername() - { - List
parsed = ParsedAddresses; - - if (parsed.Count == 0) - { - return Username; - } - - Address address = parsed[0]; - return address.Username ?? Username; - } - - public string DeterminePassword() - { - List
parsed = ParsedAddresses; - - if (parsed.Count == 0) - { - return Password; - } - - Address address = parsed[0]; - return address.Password ?? Password; - } - - public string DetermineVirtualHost() - { - List
parsed = ParsedAddresses; - - if (parsed.Count == 0) - { - return VirtualHost; - } - - Address address = parsed[0]; - return address.VirtualHost ?? VirtualHost; - } - - public bool DetermineSslEnabled() - { - List
parsed = ParsedAddresses; - - if (parsed.Count == 0) - { - return Ssl.Enabled; - } - - Address address = parsed[0]; - return address.SecureConnection ?? Ssl.Enabled; - } - - public class SslOptions - { - public bool Enabled { get; set; } - - public bool ValidateServerCertificate { get; set; } = - true; // SslPolicyErrors.RemoteCertificateNotAvailable, SslPolicyErrors.RemoteCertificateChainErrors - - public bool VerifyHostname { get; set; } = true; - - public string CertPath { get; set; } - - public string CertPassPhrase { get; set; } - - public string ServerHostName { get; set; } - - public SslProtocols Algorithm { get; set; } = SslProtocols.Tls13 | SslProtocols.Tls12; - } - - public class CacheOptions - { - public ChannelOptions Channel { get; set; } = new(); - - public ConnectionOptions Connection { get; set; } = new(); - } - - public class ListenerOptions - { - public ContainerType Type { get; } = ContainerType.Direct; - - public DirectContainerOptions Direct { get; set; } = new(); - } - - public class TemplateOptions - { - public RetryOptions Retry { get; set; } = new(); - - public bool Mandatory { get; set; } - - public TimeSpan? ReceiveTimeout { get; set; } - - public TimeSpan? ReplyTimeout { get; set; } - - public string Exchange { get; set; } = string.Empty; - - public string RoutingKey { get; set; } = string.Empty; - - public string DefaultReceiveQueue { get; set; } - } - - public class ChannelOptions - { - public int? Size { get; set; } - - public TimeSpan? CheckoutTimeout { get; set; } - } - - public class ConnectionOptions - { - public CachingMode Mode { get; set; } = CachingMode.Channel; - - public int? Size { get; set; } - } - - public class DirectContainerOptions - { - public bool AutoStartup { get; set; } = true; - - public AcknowledgeMode? AcknowledgeMode { get; set; } - - public int? Prefetch { get; set; } - - public bool DefaultRequeueRejected { get; set; } = true; - - public TimeSpan? IdleEventInterval { get; set; } - - public ListenerRetryOptions Retry { get; set; } = new(); - - public bool MissingQueuesFatal { get; set; } - - public int? ConsumersPerQueue { get; set; } - - public bool PossibleAuthenticationFailureFatal { get; set; } = true; - } - - public class RetryOptions - { - public bool Enabled { get; set; } - - public int MaxAttempts { get; set; } = 3; - - public TimeSpan InitialInterval { get; set; } = TimeSpan.FromMilliseconds(1000); - - public double Multiplier { get; set; } = 1.0d; - - public TimeSpan MaxInterval { get; set; } = TimeSpan.FromMilliseconds(10000); - } - - public class ListenerRetryOptions : RetryOptions - { - public bool Stateless { get; set; } = true; - } - - private sealed class Address - { - private const string PrefixAmqp = "amqp://"; - private const string PrefixAmqpSecure = "amqps://"; - - public string Host { get; private set; } - - public int Port { get; private set; } - - public string Username { get; private set; } - - public string Password { get; private set; } - - public string VirtualHost { get; private set; } - - public bool? SecureConnection { get; private set; } - - public Address(string input) - { - input = input.Trim(); - input = TrimPrefix(input); - input = ParseUsernameAndPassword(input); - input = ParseVirtualHost(input); - ParseHostAndPort(input); - } - - private string TrimPrefix(string input) - { - if (input.StartsWith(PrefixAmqp, StringComparison.Ordinal)) - { - input = input.Substring(PrefixAmqp.Length); - SecureConnection = false; - } - else if (input.StartsWith(PrefixAmqpSecure, StringComparison.Ordinal)) - { - input = input.Substring(PrefixAmqpSecure.Length); - SecureConnection = true; - } - - return input; - } - - private string ParseUsernameAndPassword(string input) - { - if (input.Contains('@')) - { - string[] split = input.Split('@', StringSplitOptions.RemoveEmptyEntries); - string credentials = split[0]; - input = split[1]; - split = credentials.Split(':', StringSplitOptions.RemoveEmptyEntries); - Username = split[0]; - - if (split.Length > 0) - { - Password = split[1]; - } - } - - return input; - } - - private string ParseVirtualHost(string input) - { - int hostIndex = input.IndexOf('/'); - - if (hostIndex >= 0) - { - VirtualHost = input.Substring(hostIndex + 1); - - if (string.IsNullOrEmpty(VirtualHost)) - { - VirtualHost = "/"; - } - - input = input.Substring(0, hostIndex); - } - - return input; - } - - private void ParseHostAndPort(string input) - { - int portIndex = input.IndexOf(':'); - - if (portIndex == -1) - { - Host = input; - Port = DefaultPort; - } - else - { - Host = input.Substring(0, portIndex); - Port = int.Parse(input.Substring(portIndex + 1), CultureInfo.InvariantCulture); - } - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Configuration/TopicExchange.cs b/src/Messaging/src/RabbitMQ/Configuration/TopicExchange.cs deleted file mode 100644 index 27acbe14c8..0000000000 --- a/src/Messaging/src/RabbitMQ/Configuration/TopicExchange.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Configuration; - -public class TopicExchange : AbstractExchange, ITopicExchange -{ - public override string Type { get; } = ExchangeType.Topic; - - public TopicExchange(string name) - : base(name) - { - } - - public TopicExchange(string name, bool durable, bool autoDelete) - : base(name, durable, autoDelete) - { - } - - public TopicExchange(string name, bool durable, bool autoDelete, Dictionary arguments) - : base(name, durable, autoDelete, arguments) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/AbstractConnectionFactory.cs b/src/Messaging/src/RabbitMQ/Connection/AbstractConnectionFactory.cs deleted file mode 100644 index aea668e0a7..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/AbstractConnectionFactory.cs +++ /dev/null @@ -1,396 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using RabbitMQ.Client.Events; -using Steeltoe.Common; -using Steeltoe.Common.Net; -using Steeltoe.Messaging.RabbitMQ.Support; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public abstract class AbstractConnectionFactory : IConnectionFactory -{ - private const string PublisherSuffix = ".publisher"; - public const int DefaultCloseTimeout = 30000; - private readonly CompositeConnectionListener _connectionListener; - private readonly CompositeChannelListener _channelListener; - private readonly Random _random = new(); - protected readonly ILoggerFactory LoggerFactory; - protected readonly ILogger Logger; - protected readonly RC.IConnectionFactory InnerRabbitConnectionFactory; - private int _defaultConnectionNameStrategyCounter; - - protected virtual AbstractConnectionFactory AbstractPublisherConnectionFactory => (AbstractConnectionFactory)PublisherConnectionFactory; - - public virtual RC.ConnectionFactory RabbitConnectionFactory => InnerRabbitConnectionFactory as RC.ConnectionFactory; - - public virtual string Username - { - get => InnerRabbitConnectionFactory.UserName; - set => InnerRabbitConnectionFactory.UserName = value; - } - - public virtual string Password - { - get => InnerRabbitConnectionFactory.Password; - set => InnerRabbitConnectionFactory.Password = value; - } - - public virtual Uri Uri - { - get => InnerRabbitConnectionFactory.Uri; - set => InnerRabbitConnectionFactory.Uri = value; - } - - public virtual string Host - { - get => RabbitConnectionFactory == null ? "localhost" : RabbitConnectionFactory.HostName; - set => RabbitConnectionFactory.HostName = value; - } - - public virtual int Port - { - get => RabbitConnectionFactory?.Port ?? -1; - set => RabbitConnectionFactory.Port = value; - } - - public virtual string VirtualHost - { - get => InnerRabbitConnectionFactory.VirtualHost; - set => InnerRabbitConnectionFactory.VirtualHost = value; - } - - public virtual ushort RequestedHeartBeat - { - get => InnerRabbitConnectionFactory.RequestedHeartbeat; - set => InnerRabbitConnectionFactory.RequestedHeartbeat = value; - } - - public virtual int ConnectionTimeout - { - get => RabbitConnectionFactory?.RequestedConnectionTimeout ?? 30000; - set => RabbitConnectionFactory.RequestedConnectionTimeout = value; - } - - public virtual int CloseTimeout { get; set; } = DefaultCloseTimeout; - - public virtual string ServiceName { get; set; } - - public virtual bool ShuffleAddresses { get; set; } - - public virtual List Addresses { get; set; } - - public virtual bool HasPublisherConnectionFactory => PublisherConnectionFactory != null; - - public virtual IConnectionFactory PublisherConnectionFactory { get; protected set; } - - public virtual IBlockedListener BlockedListener { get; set; } - - public virtual IRecoveryListener RecoveryListener { get; set; } - - public virtual bool IsSimplePublisherConfirms { get; set; } - - public virtual bool IsPublisherConfirms { get; set; } - - public virtual bool IsPublisherReturns { get; set; } - - public virtual IConnectionListener ConnectionListener => _connectionListener; - - public virtual IChannelListener ChannelListener => _channelListener; - - protected AbstractConnectionFactory(RC.IConnectionFactory rabbitConnectionFactory, ILoggerFactory loggerFactory = null) - : this(rabbitConnectionFactory, null, loggerFactory) - { - } - - protected AbstractConnectionFactory(RC.IConnectionFactory rabbitConnectionFactory, AbstractConnectionFactory publisherConnectionFactory, - ILoggerFactory loggerFactory = null) - { - ArgumentGuard.NotNull(rabbitConnectionFactory); - - LoggerFactory = loggerFactory; - Logger = LoggerFactory?.CreateLogger(GetType()); - InnerRabbitConnectionFactory = rabbitConnectionFactory; - _connectionListener = new CompositeConnectionListener(LoggerFactory?.CreateLogger()); - _channelListener = new CompositeChannelListener(LoggerFactory?.CreateLogger()); - PublisherConnectionFactory = publisherConnectionFactory; - RecoveryListener = new DefaultRecoveryListener(LoggerFactory?.CreateLogger()); - BlockedListener = new DefaultBlockedListener(LoggerFactory?.CreateLogger()); - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public virtual void SetConnectionListeners(List listeners) - { - _connectionListener.SetListeners(listeners); - - if (PublisherConnectionFactory != null) - { - AbstractPublisherConnectionFactory.SetConnectionListeners(listeners); - } - } - - public virtual void AddConnectionListener(IConnectionListener connectionListener) - { - _connectionListener.AddListener(connectionListener); - - if (PublisherConnectionFactory != null) - { - PublisherConnectionFactory.AddConnectionListener(connectionListener); - } - } - - public virtual bool RemoveConnectionListener(IConnectionListener connectionListener) - { - bool result = _connectionListener.RemoveListener(connectionListener); - - if (PublisherConnectionFactory != null) - { - PublisherConnectionFactory.RemoveConnectionListener(connectionListener); - } - - return result; - } - - public virtual void ClearConnectionListeners() - { - _connectionListener.ClearListeners(); - - if (PublisherConnectionFactory != null) - { - PublisherConnectionFactory.ClearConnectionListeners(); - } - } - - public virtual void SetChannelListeners(List listeners) - { - _channelListener.SetListeners(listeners); - } - - public virtual void AddChannelListener(IChannelListener listener) - { - _channelListener.AddListener(listener); - - if (PublisherConnectionFactory != null) - { - AbstractPublisherConnectionFactory.AddChannelListener(listener); - } - } - - public virtual void SetRecoveryListener(IRecoveryListener recoveryListener) - { - RecoveryListener = recoveryListener; - - if (PublisherConnectionFactory != null) - { - AbstractPublisherConnectionFactory.SetRecoveryListener(recoveryListener); - } - } - - public virtual void SetBlockedListener(IBlockedListener blockedListener) - { - BlockedListener = blockedListener; - - if (PublisherConnectionFactory != null) - { - AbstractPublisherConnectionFactory.SetBlockedListener(blockedListener); - } - } - - public virtual void SetAddresses(string addresses) - { - if (!string.IsNullOrEmpty(addresses)) - { - RC.AmqpTcpEndpoint[] endpoints = RC.AmqpTcpEndpoint.ParseMultiple(addresses); - - if (endpoints.Length > 0) - { - Addresses = endpoints.ToList(); - - if (PublisherConnectionFactory != null) - { - AbstractPublisherConnectionFactory.SetAddresses(addresses); - } - - return; - } - } - - Logger?.LogInformation("SetAddresses() called with an empty value, will be using the host+port properties for connections"); - Addresses = null; - } - - public abstract IConnection CreateConnection(); - - public virtual void Destroy() - { - PublisherConnectionFactory?.Destroy(); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - Destroy(); - } - } - - public override string ToString() - { - return ServiceName; - } - - protected internal virtual void ConnectionShutdownCompleted(object sender, RC.ShutdownEventArgs args) - { - ConnectionListener.OnShutDown(args); - } - - protected virtual IConnection CreateBareConnection() - { - try - { - string connectionName = ObtainNewConnectionName(); - - RC.IConnection rabbitConnection = Connect(connectionName); - - var connection = new SimpleConnection(rabbitConnection, CloseTimeout, LoggerFactory?.CreateLogger()); - - Logger?.LogInformation("Created new connection: {connectionName}/{connection}", connectionName, connection); - - if (rabbitConnection != null && RecoveryListener != null) - { - rabbitConnection.RecoverySucceeded += RecoveryListener.HandleRecoverySucceeded; - rabbitConnection.ConnectionRecoveryError += RecoveryListener.HandleConnectionRecoveryError; - } - - if (rabbitConnection != null && BlockedListener != null) - { - rabbitConnection.ConnectionBlocked += BlockedListener.HandleBlocked; - rabbitConnection.ConnectionUnblocked += BlockedListener.HandleUnblocked; - } - - if (rabbitConnection != null) - { - rabbitConnection.ConnectionShutdown += ConnectionShutdownCompleted; - } - - return connection; - } - catch (Exception e) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - protected virtual string GetDefaultHostName() - { - string temp; - - try - { - var inetUtils = new InetUtils(new InetOptions(), Logger); - HostInfo hostInfo = inetUtils.FindFirstNonLoopbackHostInfo(); - temp = hostInfo.Hostname; - Logger?.LogDebug("Using hostname [{name}] for hostname.", temp); - } - catch (Exception e) - { - Logger?.LogWarning(e, "Could not get host name, using 'localhost' as default value"); - temp = "localhost"; - } - - return temp; - } - - protected virtual string ObtainNewConnectionName() - { - return $"{ServiceName}:{Interlocked.Increment(ref _defaultConnectionNameStrategyCounter)}{PublisherSuffix}"; - } - - private RC.IConnection Connect(string connectionName) - { - RC.IConnection rabbitConnection; - - if (Addresses != null) - { - List addressesToConnect = Addresses; - - if (ShuffleAddresses && addressesToConnect.Count > 1) - { - RC.AmqpTcpEndpoint[] list = addressesToConnect.ToArray(); - Shuffle(list); - addressesToConnect = list.ToList(); - } - - Logger?.LogInformation("Attempting to connect to: {address}", addressesToConnect); - - rabbitConnection = InnerRabbitConnectionFactory.CreateConnection(addressesToConnect); - } - else - { - Logger?.LogInformation("Attempting to connect to: {host}:{port}", Host, Port); - rabbitConnection = InnerRabbitConnectionFactory.CreateConnection(connectionName); - } - - return rabbitConnection; - } - - private void Shuffle(T[] array) - { - int n = array.Length; - - for (int i = 0; i < n - 1; i++) - { - int r = i + _random.Next(n - i); - (array[r], array[i]) = (array[i], array[r]); - } - } - - private sealed class DefaultBlockedListener : IBlockedListener - { - private readonly ILogger _logger; - - public DefaultBlockedListener(ILogger logger) - { - _logger = logger; - } - - public void HandleBlocked(object sender, ConnectionBlockedEventArgs args) - { - _logger?.LogInformation("Connection blocked: {reason}", args.Reason); - } - - public void HandleUnblocked(object sender, EventArgs args) - { - _logger?.LogInformation("Connection unblocked: {args}", args); - } - } - - private sealed class DefaultRecoveryListener : IRecoveryListener - { - private readonly ILogger _logger; - - public DefaultRecoveryListener(ILogger logger) - { - _logger = logger; - } - - public void HandleConnectionRecoveryError(object sender, ConnectionRecoveryErrorEventArgs args) - { - _logger?.LogDebug(args.Exception, "Connection recovery failed"); - } - - public void HandleRecoverySucceeded(object sender, EventArgs args) - { - _logger?.LogDebug("Connection recovery succeed"); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/AbstractRoutingConnectionFactory.cs b/src/Messaging/src/RabbitMQ/Connection/AbstractRoutingConnectionFactory.cs deleted file mode 100644 index 817cfa092e..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/AbstractRoutingConnectionFactory.cs +++ /dev/null @@ -1,180 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Steeltoe.Common; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public abstract class AbstractRoutingConnectionFactory : IConnectionFactory, IRoutingConnectionFactory -{ - private readonly ConcurrentDictionary _targetConnectionFactories = new(); - - private readonly List _connectionListeners = new(); - - public IConnectionFactory DefaultTargetConnectionFactory { get; set; } - - public bool LenientFallback { get; set; } - - public string Host => DetermineTargetConnectionFactory().Host; - - public int Port => DetermineTargetConnectionFactory().Port; - - public string VirtualHost => DetermineTargetConnectionFactory().VirtualHost; - - public string Username => DetermineTargetConnectionFactory().Username; - - public IConnectionFactory PublisherConnectionFactory => null; - - public bool IsSimplePublisherConfirms => false; - - public bool IsPublisherConfirms => false; - - public bool IsPublisherReturns => false; - - public abstract string ServiceName { get; set; } - - public virtual IConnectionFactory GetTargetConnectionFactory(object key) - { - _targetConnectionFactories.TryGetValue(key, out IConnectionFactory result); - return result; - } - - public virtual void SetTargetConnectionFactories(Dictionary targetConnectionFactories) - { - ArgumentGuard.NotNull(targetConnectionFactories); - - foreach (IConnectionFactory factory in targetConnectionFactories.Values) - { - if (factory == null) - { - throw new ArgumentException($"Values in {nameof(targetConnectionFactories)} cannot contain nulls.", nameof(targetConnectionFactories)); - } - - foreach (KeyValuePair kvp in targetConnectionFactories) - { - _targetConnectionFactories[kvp.Key] = kvp.Value; - } - } - } - - public virtual IConnection CreateConnection() - { - return DetermineTargetConnectionFactory().CreateConnection(); - } - - public virtual void AddConnectionListener(IConnectionListener connectionListener) - { - foreach (IConnectionFactory connectionFactory in _targetConnectionFactories.Values) - { - connectionFactory.AddConnectionListener(connectionListener); - } - - if (DefaultTargetConnectionFactory != null) - { - DefaultTargetConnectionFactory.AddConnectionListener(connectionListener); - } - - _connectionListeners.Add(connectionListener); - } - - public virtual bool RemoveConnectionListener(IConnectionListener connectionListener) - { - bool removed = false; - - foreach (IConnectionFactory connectionFactory in _targetConnectionFactories.Values) - { - bool listenerRemoved = connectionFactory.RemoveConnectionListener(connectionListener); - - if (!removed) - { - removed = listenerRemoved; - } - } - - if (DefaultTargetConnectionFactory != null) - { - bool listenerRemoved = DefaultTargetConnectionFactory.RemoveConnectionListener(connectionListener); - - if (!removed) - { - removed = listenerRemoved; - } - } - - _connectionListeners.Remove(connectionListener); - return removed; - } - - public virtual void ClearConnectionListeners() - { - foreach (IConnectionFactory connectionFactory in _targetConnectionFactories.Values) - { - connectionFactory.ClearConnectionListeners(); - } - - if (DefaultTargetConnectionFactory != null) - { - DefaultTargetConnectionFactory.ClearConnectionListeners(); - } - - _connectionListeners.Clear(); - } - - public virtual void Destroy() - { - // Do nothing - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - } - - public virtual void AddTargetConnectionFactory(object key, IConnectionFactory connectionFactory) - { - _targetConnectionFactories[key] = connectionFactory; - - foreach (IConnectionListener listener in _connectionListeners) - { - connectionFactory.AddConnectionListener(listener); - } - } - - public virtual IConnectionFactory DetermineTargetConnectionFactory() - { - object lookupKey = DetermineCurrentLookupKey(); - IConnectionFactory connectionFactory = null; - - if (lookupKey != null) - { - _targetConnectionFactories.TryGetValue(lookupKey, out connectionFactory); - } - - if (connectionFactory == null && (LenientFallback || lookupKey == null)) - { - connectionFactory = DefaultTargetConnectionFactory; - } - - if (connectionFactory == null) - { - throw new InvalidOperationException($"Cannot determine target ConnectionFactory for lookup key [{lookupKey}]"); - } - - return connectionFactory; - } - - public virtual IConnectionFactory RemoveTargetConnectionFactory(object key) - { - _targetConnectionFactories.TryRemove(key, out IConnectionFactory connectionFactory); - return connectionFactory; - } - - public abstract object DetermineCurrentLookupKey(); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/CachingConnectionFactory.cs b/src/Messaging/src/RabbitMQ/Connection/CachingConnectionFactory.cs deleted file mode 100644 index 48b198c264..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/CachingConnectionFactory.cs +++ /dev/null @@ -1,2578 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Net.Security; -using System.Runtime.CompilerServices; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using RabbitMQ.Client.Events; -using RabbitMQ.Client.Exceptions; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class CachingConnectionFactory : AbstractConnectionFactory, IShutdownListener -{ - private const int DefaultChannelCacheSize = 25; - internal const string DefaultServiceName = ""; - - private readonly object _connectionMonitor = new(); - private readonly Dictionary _channelHighWaterMarks = new(); - private readonly AtomicInteger _connectionHighWaterMark = new(); - private readonly IOptionsMonitor _optionsMonitor; - - // Internal for unit tests - internal readonly Dictionary CheckoutPermits = new(); - internal readonly LinkedList CachedChannelsNonTransactional = new(); - internal readonly LinkedList CachedChannelsTransactional = new(); - internal readonly HashSet AllocatedConnections = new(); - internal readonly LinkedList IdleConnections = new(); - internal readonly Dictionary> AllocatedConnectionNonTransactionalChannels = new(); - internal readonly Dictionary> AllocatedConnectionTransactionalChannels = new(); - internal readonly ChannelCachingConnectionProxy Connection; - - private int _channelCacheSize = DefaultChannelCacheSize; - private int _connectionCacheSize = 1; - private int _connectionLimit = int.MaxValue; - private bool _publisherReturns; - private ConfirmType _confirmType = ConfirmType.None; - private int _channelCheckoutTimeout; - private IConditionalExceptionLogger _closeExceptionLogger = new DefaultChannelCloseLogger(); - private bool _active = true; - internal bool Stopped; - - protected internal RabbitOptions Options - { - get - { - if (_optionsMonitor != null) - { - return _optionsMonitor.CurrentValue; - } - - return null; - } - } - - public CachingConnectionFactory PublisherCachingConnectionFactory - { - get => (CachingConnectionFactory)PublisherConnectionFactory; - set => PublisherConnectionFactory = value; - } - - public int ChannelCacheSize - { - get => _channelCacheSize; - set - { - if (value < 1) - { - throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be zero or negative."); - } - - _channelCacheSize = value; - - if (PublisherConnectionFactory != null) - { - PublisherCachingConnectionFactory.ChannelCacheSize = value; - } - } - } - - public CachingMode CacheMode { get; set; } = CachingMode.Channel; - - public int ConnectionCacheSize - { - get => _connectionCacheSize; - set - { - if (value < 1) - { - throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be zero or negative."); - } - - _connectionCacheSize = value; - - if (PublisherConnectionFactory != null) - { - PublisherCachingConnectionFactory.ConnectionCacheSize = value; - } - } - } - - public int ConnectionLimit - { - get => _connectionLimit; - set - { - if (value < 1) - { - throw new ArgumentOutOfRangeException(nameof(value), value, "Value cannot be zero or negative."); - } - - _connectionLimit = value; - - if (PublisherConnectionFactory != null) - { - PublisherCachingConnectionFactory.ConnectionLimit = value; - } - } - } - - public override bool IsPublisherReturns - { - get => _publisherReturns; - set - { - _publisherReturns = value; - - if (PublisherConnectionFactory != null) - { - PublisherCachingConnectionFactory.IsPublisherReturns = value; - } - } - } - - public ConfirmType PublisherConfirmType - { - get => _confirmType; - set - { - _confirmType = value; - - if (PublisherConnectionFactory != null) - { - PublisherCachingConnectionFactory.PublisherConfirmType = value; - } - } - } - - public override bool IsPublisherConfirms => _confirmType == ConfirmType.Correlated; - - public override bool IsSimplePublisherConfirms => _confirmType == ConfirmType.Simple; - - public int ChannelCheckoutTimeout - { - get => _channelCheckoutTimeout; - set - { - _channelCheckoutTimeout = value; - - if (PublisherConnectionFactory != null) - { - PublisherCachingConnectionFactory.ChannelCheckoutTimeout = value; - } - } - } - - public IConditionalExceptionLogger CloseExceptionLogger - { - get => _closeExceptionLogger; - set - { - _closeExceptionLogger = value; - - if (PublisherConnectionFactory != null) - { - PublisherCachingConnectionFactory.CloseExceptionLogger = value; - } - } - } - - public IPublisherCallbackChannelFactory PublisherCallbackChannelFactory { get; set; } - - public CachingConnectionFactory(ILoggerFactory loggerFactory = null) - : this(null, -1, loggerFactory) - { - } - - [ActivatorUtilitiesConstructor] - public CachingConnectionFactory(IOptionsMonitor optionsMonitor, ILoggerFactory loggerFactory = null) - : base(NewRabbitConnectionFactory(), loggerFactory) - { - _optionsMonitor = optionsMonitor; - Connection = new ChannelCachingConnectionProxy(this, null, loggerFactory?.CreateLogger()); - ConfigureRabbitConnectionFactory(Options); - PublisherConnectionFactory = new CachingConnectionFactory(InnerRabbitConnectionFactory, true, CachingMode.Channel, loggerFactory); - PublisherCallbackChannelFactory = new DefaultPublisherCallbackFactory(loggerFactory); - Configure(Options); - InitCacheWaterMarks(); - ServiceName = DefaultServiceName; - } - - public CachingConnectionFactory(string hostname, ILoggerFactory loggerFactory = null) - : this(hostname, -1, loggerFactory) - { - } - - public CachingConnectionFactory(int port, ILoggerFactory loggerFactory = null) - : this(null, port, loggerFactory) - { - } - - public CachingConnectionFactory(string hostNameArg, int port, ILoggerFactory loggerFactory = null) - : base(NewRabbitConnectionFactory(), loggerFactory) - { - string hostname = hostNameArg; - - if (string.IsNullOrEmpty(hostname)) - { - hostname = GetDefaultHostName(); - } - - Connection = new ChannelCachingConnectionProxy(this, null, loggerFactory?.CreateLogger()); - Host = hostname; - Port = port; - PublisherConnectionFactory = new CachingConnectionFactory(InnerRabbitConnectionFactory, true, CachingMode.Channel, loggerFactory); - PublisherCallbackChannelFactory = new DefaultPublisherCallbackFactory(loggerFactory); - InitCacheWaterMarks(); - ServiceName = DefaultServiceName; - } - - public CachingConnectionFactory(string hostNameArg, int port, RC.IConnectionFactory connectionFactory, ILoggerFactory loggerFactory = null) - : base(connectionFactory, loggerFactory) - { - string hostname = hostNameArg; - - if (string.IsNullOrEmpty(hostname)) - { - hostname = GetDefaultHostName(); - } - - Connection = new ChannelCachingConnectionProxy(this, null, loggerFactory?.CreateLogger()); - Host = hostname; - Port = port; - PublisherConnectionFactory = new CachingConnectionFactory(InnerRabbitConnectionFactory, true, CachingMode.Channel, loggerFactory); - PublisherCallbackChannelFactory = new DefaultPublisherCallbackFactory(loggerFactory); - InitCacheWaterMarks(); - ServiceName = DefaultServiceName; - } - - public CachingConnectionFactory(Uri uri, ILoggerFactory loggerFactory = null) - : this(uri, CachingMode.Channel, loggerFactory) - { - } - - public CachingConnectionFactory(Uri uri, CachingMode cachingMode = CachingMode.Channel, ILoggerFactory loggerFactory = null) - : base(NewRabbitConnectionFactory(), loggerFactory) - { - Connection = new ChannelCachingConnectionProxy(this, null, loggerFactory?.CreateLogger()); - CacheMode = cachingMode; - Uri = uri; - PublisherConnectionFactory = new CachingConnectionFactory(InnerRabbitConnectionFactory, true, cachingMode, loggerFactory); - PublisherCallbackChannelFactory = new DefaultPublisherCallbackFactory(loggerFactory); - InitCacheWaterMarks(); - ServiceName = DefaultServiceName; - } - - protected internal CachingConnectionFactory(RC.IConnectionFactory rabbitConnectionFactory, ILoggerFactory loggerFactory = null) - : this(rabbitConnectionFactory, false, CachingMode.Channel, loggerFactory) - { - } - - protected internal CachingConnectionFactory(RC.IConnectionFactory rabbitConnectionFactory, bool isPublisherFactory, - CachingMode cachingMode = CachingMode.Channel, ILoggerFactory loggerFactory = null) - : base(rabbitConnectionFactory, loggerFactory) - { - CacheMode = cachingMode; - Connection = new ChannelCachingConnectionProxy(this, null, loggerFactory?.CreateLogger()); - PublisherCallbackChannelFactory = new DefaultPublisherCallbackFactory(loggerFactory); - - if (!isPublisherFactory) - { - if (RabbitConnectionFactory != null && (RabbitConnectionFactory.AutomaticRecoveryEnabled || RabbitConnectionFactory.TopologyRecoveryEnabled)) - { - RabbitConnectionFactory.AutomaticRecoveryEnabled = false; - RabbitConnectionFactory.TopologyRecoveryEnabled = false; - - Logger?.LogWarning("***\nAutomatic Recovery was Enabled in the provided connection factory;\n" + - "while Steeltoe is generally compatible with this feature, there\n" + "are some corner cases where problems arise. Steeltoe\n" + - "prefers to use its own recovery mechanisms; when this option is true, you may receive\n" + - "odd Exception's until the connection is recovered.\n"); - } - - PublisherConnectionFactory = new CachingConnectionFactory(InnerRabbitConnectionFactory, true, cachingMode, loggerFactory); - } - else - { - PublisherConnectionFactory = null; - } - - InitCacheWaterMarks(); - ServiceName = DefaultServiceName; - } - - private static RC.ConnectionFactory NewRabbitConnectionFactory() - { - var connectionFactory = new RC.ConnectionFactory - { - AutomaticRecoveryEnabled = false - }; - - return connectionFactory; - } - - public override void SetConnectionListeners(List listeners) - { - base.SetConnectionListeners(listeners); - - if (Connection.Target != null) - { - ConnectionListener.OnCreate(Connection); - } - } - - public override void AddConnectionListener(IConnectionListener connectionListener) - { - base.AddConnectionListener(connectionListener); - - if (Connection.Target != null) - { - connectionListener.OnCreate(Connection); - } - } - - public void ChannelShutdownCompleted(object sender, RC.ShutdownEventArgs args) - { - _closeExceptionLogger.Log(Logger, "Channel shutdown", args); - ChannelListener.OnShutDown(args); - } - - public override IConnection CreateConnection() - { - if (Stopped) - { - throw new RabbitApplicationContextClosedException("The ConnectionFactory is disposed and can no longer create connections."); - } - - lock (_connectionMonitor) - { - if (CacheMode == CachingMode.Channel) - { - if (Connection.Target == null) - { - Connection.Target = CreateBareConnection(); - - // invoke the listener *after* this.connection is assigned - if (!CheckoutPermits.ContainsKey(Connection)) - { - CheckoutPermits.Add(Connection, new SemaphoreSlim(ChannelCacheSize)); - } - - Connection.CloseNotified = 0; - ConnectionListener.OnCreate(Connection); - } - - return Connection; - } - - if (CacheMode == CachingMode.Connection) - { - return GetConnectionFromCache(); - } - } - - return null; - } - - public override void Destroy() - { - base.Destroy(); - ResetConnection(); - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - Destroy(); - Stopped = true; - } - - base.Dispose(disposing); - } - - public IDictionary GetCacheProperties() - { - var props = new Dictionary - { - { "cacheMode", CacheMode.ToString() } - }; - - lock (_connectionMonitor) - { - props.Add("channelCacheSize", _channelCacheSize); - - if (CacheMode == CachingMode.Connection) - { - props.Add("connectionCacheSize", _connectionCacheSize); - props.Add("openConnections", CountOpenConnections()); - props.Add("idleConnections", IdleConnections.Count); - props.Add("idleConnectionsHighWater", _connectionHighWaterMark.Value); - - foreach (ChannelCachingConnectionProxy proxy in AllocatedConnections) - { - PutConnectionName(props, proxy, $":{proxy.LocalPort}"); - } - - foreach (KeyValuePair> entry in AllocatedConnectionTransactionalChannels) - { - int port = entry.Key.LocalPort; - - if (port > 0 && entry.Key.IsOpen) - { - LinkedList channelList = entry.Value; - props.Add($"idleChannelsTx:{port}", channelList.Count); - props.Add($"idleChannelsTxHighWater:{port}", _channelHighWaterMarks[RuntimeHelpers.GetHashCode(channelList)].Value); - } - } - - foreach (KeyValuePair> entry in AllocatedConnectionNonTransactionalChannels) - { - int port = entry.Key.LocalPort; - - if (port > 0 && entry.Key.IsOpen) - { - LinkedList channelList = entry.Value; - props.Add($"idleChannelsNotTx:{port}", channelList.Count); - props.Add($"idleChannelsNotTxHighWater:{port}", _channelHighWaterMarks[RuntimeHelpers.GetHashCode(channelList)].Value); - } - } - } - else - { - props.Add("localPort", Connection.Target == null ? 0 : Connection.LocalPort); - props.Add("idleChannelsTx", CachedChannelsTransactional.Count); - props.Add("idleChannelsNotTx", CachedChannelsNonTransactional.Count); - props.Add("idleChannelsTxHighWater", _channelHighWaterMarks[RuntimeHelpers.GetHashCode(CachedChannelsTransactional)].Value); - props.Add("idleChannelsNotTxHighWater", _channelHighWaterMarks[RuntimeHelpers.GetHashCode(CachedChannelsNonTransactional)].Value); - PutConnectionName(props, Connection, string.Empty); - } - } - - return props; - } - - public void ResetConnection() - { - lock (_connectionMonitor) - { - if (Connection.Target != null) - { - Connection.Dispose(); - } - - foreach (ChannelCachingConnectionProxy c in AllocatedConnections) - { - c.Dispose(); - } - - foreach (KeyValuePair c in _channelHighWaterMarks) - { - c.Value.Value = 0; - } - - _connectionHighWaterMark.Value = 0; - } - - if (PublisherConnectionFactory != null) - { - PublisherCachingConnectionFactory.ResetConnection(); - } - } - - // Used in unit test - internal int CountOpenConnections() - { - return AllocatedConnections.Count(conn => conn.IsOpen); - } - - protected void Reset(LinkedList channels, LinkedList txChannels, Dictionary channelsAwaitingAcks) - { - _active = false; - CloseAndClear(channels); - CloseAndClear(txChannels); - CloseChannels(channelsAwaitingAcks.Values); - channelsAwaitingAcks.Clear(); - _active = true; - } - - protected void CloseAndClear(LinkedList theChannels) - { - lock (theChannels) - { - CloseChannels(theChannels); - theChannels.Clear(); - } - } - - protected void CloseChannels(ICollection theChannels) - { - foreach (IChannelProxy channel in theChannels) - { - try - { - channel.Close(); - } - catch (Exception ex) - { - Logger?.LogTrace(ex, "Could not close cached Rabbit Channel"); - } - } - } - - private void Configure(RabbitOptions options) - { - SetAddresses(options.DetermineAddresses()); - IsPublisherConfirms = options.PublisherConfirms; - IsPublisherReturns = options.PublisherReturns; - RabbitOptions.ChannelOptions cacheChannel = options.Cache.Channel; - - if (cacheChannel.Size.HasValue) - { - ChannelCacheSize = cacheChannel.Size.Value; - } - - if (cacheChannel.CheckoutTimeout.HasValue) - { - int asMilliseconds = (int)cacheChannel.CheckoutTimeout.Value.TotalMilliseconds; - ChannelCheckoutTimeout = asMilliseconds; - } - - RabbitOptions.ConnectionOptions cacheConnection = options.Cache.Connection; - CacheMode = cacheConnection.Mode; - - if (cacheConnection.Size.HasValue) - { - ConnectionCacheSize = cacheConnection.Size.Value; - } - } - - private void PutConnectionName(IDictionary props, IConnectionProxy connection, string keySuffix) - { - IConnection targetConnection = connection.TargetConnection; - - if (targetConnection != null) - { - RC.IConnection del = targetConnection.Connection; - - if (del != null) - { - string name = del.ClientProvidedName; - - if (name != null) - { - props.Add($"connectionName{keySuffix}", name); - } - } - } - } - - private void ConfigureRabbitConnectionFactory(RabbitOptions options) - { - var factory = InnerRabbitConnectionFactory as RC.ConnectionFactory; - string host = options.DetermineHost(); - - if (host != null) - { - factory.HostName = host; - } - - factory.Port = options.DeterminePort(); - string userName = options.DetermineUsername(); - - if (userName != null) - { - factory.UserName = userName; - } - - string password = options.DeterminePassword(); - - if (password != null) - { - factory.Password = password; - } - - string virtualHost = options.DetermineVirtualHost(); - - if (virtualHost != null) - { - factory.VirtualHost = virtualHost; - } - - if (options.RequestedHeartbeat.HasValue) - { - ushort asShortSeconds = (ushort)options.RequestedHeartbeat.Value.TotalSeconds; - factory.RequestedHeartbeat = asShortSeconds; - } - - if (options.DetermineSslEnabled()) - { - factory.Ssl.Enabled = true; - - if (!options.Ssl.ValidateServerCertificate) - { - factory.Ssl.AcceptablePolicyErrors = SslPolicyErrors.RemoteCertificateNotAvailable | SslPolicyErrors.RemoteCertificateChainErrors; - } - - if (!options.Ssl.VerifyHostname) - { - factory.Ssl.AcceptablePolicyErrors = SslPolicyErrors.RemoteCertificateNameMismatch; - } - else - { - factory.Ssl.ServerName = options.Ssl.ServerHostName; - } - - if (!string.IsNullOrEmpty(options.Ssl.CertPath)) - { - factory.Ssl.CertPath = options.Ssl.CertPath; - } - - if (!string.IsNullOrEmpty(options.Ssl.CertPassPhrase)) - { - factory.Ssl.CertPassphrase = options.Ssl.CertPassPhrase; - } - - factory.Ssl.Version = options.Ssl.Algorithm; - } - - if (options.ConnectionTimeout.HasValue) - { - int asMilliseconds = (int)options.ConnectionTimeout.Value.TotalMilliseconds; - factory.RequestedConnectionTimeout = asMilliseconds; - } - } - - private RC.IModel GetChannel(ChannelCachingConnectionProxy connection, bool transactional) - { - SemaphoreSlim permits = null; - - if (ChannelCheckoutTimeout > 0) - { - permits = ObtainPermits(connection); - } - - LinkedList channelList = DetermineChannelList(connection, transactional); - IChannelProxy channel = null; - - if (connection.IsOpen) - { - channel = FindOpenChannel(channelList, channel); - - if (channel != null) - { - Logger?.LogTrace("Found cached Rabbit Channel: {channel}", channel); - } - } - - if (channel == null) - { - try - { - channel = GetCachedChannelProxy(connection, channelList, transactional); - } - catch (Exception exception) - { - if (permits != null) - { - permits.Release(); - - Logger?.LogDebug(exception, "Could not get channel; released permit for {connection}, remaining {permits}", connection, - permits.CurrentCount); - } - - throw; - } - } - - return channel; - } - - private SemaphoreSlim ObtainPermits(ChannelCachingConnectionProxy connection) - { - if (CheckoutPermits.TryGetValue(connection, out SemaphoreSlim permits)) - { - try - { - if (!permits.Wait(ChannelCheckoutTimeout)) - { - throw new RabbitTimeoutException("No available channels"); - } - - Logger?.LogDebug("Acquired permit for {connection}, remaining {permits}", connection, permits.CurrentCount); - } - catch (ObjectDisposedException e) - { - throw new RabbitTimeoutException("Failure while acquiring a channel", e); - } - } - else - { - throw new InvalidOperationException($"No permits map entry for {connection}"); - } - - return permits; - } - - private IChannelProxy FindOpenChannel(LinkedList channelList, IChannelProxy channelArg) - { - IChannelProxy channel = channelArg; - - lock (channelList) - { - while (channelList.Count > 0) - { - channel = channelList.First.Value; - channelList.RemoveFirst(); - Logger?.LogTrace("{channel} retrieved from cache", channel); - - if (channel.IsOpen) - { - break; - } - - CleanUpClosedChannel(channel); - channel = null; - } - } - - return channel; - } - - private void CleanUpClosedChannel(IChannelProxy channel) - { - try - { - RC.IModel target = channel.TargetChannel; - - if (target != null) - { - target.Close(); - /* - * To remove it from auto-recovery if so configured, - * and nack any pending confirms if PublisherCallbackChannel. - */ - } - } - catch (AlreadyClosedException exception) - { - Logger?.LogTrace(exception, "{channel} is already closed", channel); - } - catch (TimeoutException exception) - { - Logger?.LogWarning(exception, "TimeoutException closing channel {channel}", channel); - } - catch (Exception exception) - { - Logger?.LogDebug(exception, "Unexpected Exception closing channel {channel}", channel); - } - } - - private LinkedList DetermineChannelList(ChannelCachingConnectionProxy connection, bool transactional) - { - LinkedList channelList; - - if (CacheMode == CachingMode.Channel) - { - channelList = transactional ? CachedChannelsTransactional : CachedChannelsNonTransactional; - } - else - { - if (transactional) - { - AllocatedConnectionTransactionalChannels.TryGetValue(connection, out channelList); - } - else - { - AllocatedConnectionNonTransactionalChannels.TryGetValue(connection, out channelList); - } - } - - if (channelList == null) - { - throw new InvalidOperationException($"No channel list for connection {connection}"); - } - - return channelList; - } - - private IChannelProxy GetCachedChannelProxy(ChannelCachingConnectionProxy connection, LinkedList channelList, bool transactional) - { - RC.IModel targetChannel = CreateBareChannel(connection, transactional); - Logger?.LogDebug("Creating cached Rabbit Channel from {targetChannel}", targetChannel); - ChannelListener.OnCreate(targetChannel, transactional); - - if (_confirmType == ConfirmType.Correlated || IsPublisherReturns) - { - return new CachedPublisherCallbackChannelProxy(this, connection, targetChannel, channelList, transactional, - LoggerFactory?.CreateLogger()); - } - - return new CachedChannelProxy(this, connection, targetChannel, channelList, transactional, LoggerFactory?.CreateLogger()); - } - - private RC.IModel CreateBareChannel(ChannelCachingConnectionProxy connection, bool transactional) - { - if (CacheMode == CachingMode.Channel) - { - if (!Connection.IsOpen) - { - lock (_connectionMonitor) - { - if (!Connection.IsOpen) - { - Connection.NotifyCloseIfNecessary(); - } - - if (!Connection.IsOpen) - { - Connection.Target = null; - CreateConnection(); - } - } - } - - return DoCreateBareChannel(Connection, transactional); - } - - if (CacheMode == CachingMode.Connection) - { - if (!connection.IsOpen) - { - lock (_connectionMonitor) - { - AllocatedConnectionNonTransactionalChannels.TryGetValue(connection, out LinkedList proxies); - proxies?.Clear(); - connection.NotifyCloseIfNecessary(); - RefreshProxyConnection(connection); - } - } - - return DoCreateBareChannel(connection, transactional); - } - - return null; - } - - private RC.IModel DoCreateBareChannel(ChannelCachingConnectionProxy conn, bool transactional) - { - RC.IModel channel = conn.CreateBareChannel(transactional); - - if (_confirmType != ConfirmType.None) - { - try - { - channel.ConfirmSelect(); - } - catch (Exception e) - { - Logger?.LogError(e, "Could not configure the channel to receive publisher confirms"); - } - } - - if ((IsPublisherConfirms || IsPublisherReturns) && channel is not PublisherCallbackChannel) - { - channel = PublisherCallbackChannelFactory.CreateChannel(channel); - } - - if (channel != null) - { - channel.ModelShutdown += ChannelShutdownCompleted; - } - - return channel; - } - - private long CurrentTimeMillis() - { - return DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond; - } - - private IConnection GetConnectionFromCache() - { - ChannelCachingConnectionProxy cachedConnection = FindIdleConnection(); - long now = CurrentTimeMillis(); - - if (cachedConnection == null && CountOpenConnections() >= ConnectionLimit) - { - cachedConnection = WaitForConnection(now); - } - - if (cachedConnection == null) - { - if (CountOpenConnections() >= ConnectionLimit && CurrentTimeMillis() - now >= ChannelCheckoutTimeout) - { - throw new RabbitTimeoutException("Timed out attempting to get a connection"); - } - - cachedConnection = new ChannelCachingConnectionProxy(this, CreateBareConnection(), Logger); - Logger?.LogDebug("Adding new connection '{connection}'", cachedConnection); - - AllocatedConnections.Add(cachedConnection); - - var nonTrans = new LinkedList(); - var trans = new LinkedList(); - AllocatedConnectionNonTransactionalChannels[cachedConnection] = nonTrans; - AllocatedConnectionTransactionalChannels[cachedConnection] = trans; - - _channelHighWaterMarks[RuntimeHelpers.GetHashCode(nonTrans)] = new AtomicInteger(); - _channelHighWaterMarks[RuntimeHelpers.GetHashCode(trans)] = new AtomicInteger(); - - CheckoutPermits[cachedConnection] = new SemaphoreSlim(ChannelCacheSize); - - ConnectionListener.OnCreate(cachedConnection); - } - else if (!cachedConnection.IsOpen) - { - try - { - RefreshProxyConnection(cachedConnection); - } - catch (Exception) - { - IdleConnections.AddLast(cachedConnection); - } - } - else - { - Logger?.LogDebug("Obtained connection '{connection}' from cache", cachedConnection); - } - - return cachedConnection; - } - - private ChannelCachingConnectionProxy WaitForConnection(long now) - { - ChannelCachingConnectionProxy cachedConnection = null; - - while (cachedConnection == null && CurrentTimeMillis() - now < ChannelCheckoutTimeout) - { - if (CountOpenConnections() >= ConnectionLimit) - { - try - { - if (Monitor.Wait(_connectionMonitor, ChannelCheckoutTimeout)) - { - cachedConnection = FindIdleConnection(); - } - } - catch (Exception e) - { - Logger?.LogError(e, "Exception while waiting for a connection"); - throw new RabbitException("Interrupted while waiting for a connection", e); - } - } - } - - return cachedConnection; - } - - private ChannelCachingConnectionProxy FindIdleConnection() - { - ChannelCachingConnectionProxy cachedConnection = null; - ChannelCachingConnectionProxy lastIdle = IdleConnections.Last?.Value; - - while (cachedConnection == null) - { - cachedConnection = IdleConnections.First?.Value; - - if (cachedConnection != null) - { - IdleConnections.RemoveFirst(); - - if (!cachedConnection.IsOpen) - { - Logger?.LogDebug("Skipping closed connection '{connection}'", cachedConnection); - cachedConnection.NotifyCloseIfNecessary(); - IdleConnections.AddLast(cachedConnection); - - if (cachedConnection == lastIdle) - { - // all of the idle connections are closed. - cachedConnection = IdleConnections.First?.Value; - - if (cachedConnection != null) - { - IdleConnections.RemoveFirst(); - } - - break; - } - - cachedConnection = null; - } - } - else - { - break; - } - } - - return cachedConnection; - } - - private void RefreshProxyConnection(ChannelCachingConnectionProxy connection) - { - connection.Dispose(); - connection.NotifyCloseIfNecessary(); - connection.Target = CreateBareConnection(); - connection.CloseNotified = 0; - ConnectionListener.OnCreate(connection); - Logger?.LogDebug("Refreshed existing connection '{connection}'", connection); - } - - private void InitCacheWaterMarks() - { - _channelHighWaterMarks[RuntimeHelpers.GetHashCode(CachedChannelsNonTransactional)] = new AtomicInteger(); - _channelHighWaterMarks[RuntimeHelpers.GetHashCode(CachedChannelsTransactional)] = new AtomicInteger(); - } - - public enum CachingMode - { - Channel, - Connection - } - - public enum ConfirmType - { - Simple, - Correlated, - None - } - - internal sealed class CachedPublisherCallbackChannelProxy : CachedChannelProxy, IPublisherCallbackChannel - { - private IPublisherCallbackChannel PublisherCallbackChannel => (IPublisherCallbackChannel)target; - - public RC.IModel Channel => target; - - public CachedPublisherCallbackChannelProxy(CachingConnectionFactory factory, ChannelCachingConnectionProxy connection, RC.IModel target, - LinkedList channelList, bool transactional, ILogger logger) - : base(factory, connection, target, channelList, transactional, logger) - { - } - - public void AddListener(IPublisherCallbackChannel.IListener listener) - { - PublisherCallbackChannel.AddListener(listener); - } - - public IList Expire(IPublisherCallbackChannel.IListener listener, long cutoffTime) - { - return PublisherCallbackChannel.Expire(listener, cutoffTime); - } - - public int GetPendingConfirmsCount(IPublisherCallbackChannel.IListener listener) - { - return PublisherCallbackChannel.GetPendingConfirmsCount(listener); - } - - public int GetPendingConfirmsCount() - { - return PublisherCallbackChannel.GetPendingConfirmsCount(); - } - - public void AddPendingConfirm(IPublisherCallbackChannel.IListener listener, ulong sequence, PendingConfirm pendingConfirm) - { - PublisherCallbackChannel.AddPendingConfirm(listener, sequence, pendingConfirm); - } - - public void SetAfterAckCallback(Action callback) - { - PublisherCallbackChannel.SetAfterAckCallback(callback); - } - - public override string ToString() - { - return $"Cached Rabbit Channel: {target}, conn: {TheConnection}"; - } - - protected override void ReturnToCache() - { - if (Factory._active && PublisherConfirms) - { - TheConnection.ChannelsAwaitingAcks[target] = this; - - ((IPublisherCallbackChannel)target).SetAfterAckCallback(c => - { - TheConnection.ChannelsAwaitingAcks.Remove(c); - DoReturnToCache(); - }); - } - else - { - DoReturnToCache(); - } - } - } - - internal class CachedChannelProxy : IChannelProxy - { - protected const int AsyncCloseTimeout = 5_000; - protected readonly CachingConnectionFactory Factory; - protected readonly ChannelCachingConnectionProxy TheConnection; - protected readonly LinkedList ChannelList; - protected readonly int ChannelListIdentity; - protected readonly object TargetMonitor = new(); - protected readonly bool Transactional; - protected readonly bool ConfirmSelected; - protected readonly bool PublisherConfirms; - protected readonly ILogger Logger; - protected RC.IModel target; - protected bool txStarted; - - public RC.IModel TargetChannel => target; - - public bool IsTransactional => Transactional; - - public bool IsConfirmSelected => ConfirmSelected; - - public int ChannelNumber - { - get - { - try - { - PreInvoke(); - return target.ChannelNumber; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public RC.ShutdownEventArgs CloseReason - { - get - { - try - { - PreInvoke(); - return target.CloseReason; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public RC.IBasicConsumer DefaultConsumer - { - get - { - try - { - PreInvoke(); - return target.DefaultConsumer; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - set - { - try - { - PreInvoke(); - target.DefaultConsumer = value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public bool IsClosed => target == null || target.IsClosed; - - public bool IsOpen => target != null && target.IsOpen; - - public ulong NextPublishSeqNo - { - get - { - try - { - PreInvoke(); - return target.NextPublishSeqNo; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public TimeSpan ContinuationTimeout - { - get - { - try - { - PreInvoke(); - return target.ContinuationTimeout; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - set - { - try - { - PreInvoke(); - target.ContinuationTimeout = value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public event EventHandler BasicAcks - { - add - { - try - { - PreInvoke(); - target.BasicAcks += value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - remove - { - try - { - PreInvoke(); - target.BasicAcks -= value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public event EventHandler BasicNacks - { - add - { - try - { - PreInvoke(); - target.BasicNacks += value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - remove - { - try - { - PreInvoke(); - target.BasicNacks -= value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public event EventHandler BasicRecoverOk - { - add - { - try - { - PreInvoke(); - target.BasicRecoverOk += value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - remove - { - try - { - PreInvoke(); - target.BasicRecoverOk -= value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public event EventHandler BasicReturn - { - add - { - try - { - PreInvoke(); - target.BasicReturn += value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - remove - { - try - { - PreInvoke(); - target.BasicReturn -= value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public event EventHandler CallbackException - { - add - { - try - { - PreInvoke(); - target.CallbackException += value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - remove - { - try - { - PreInvoke(); - target.CallbackException -= value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public event EventHandler FlowControl - { - add - { - try - { - PreInvoke(); - target.FlowControl += value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - remove - { - try - { - PreInvoke(); - target.FlowControl -= value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public event EventHandler ModelShutdown - { - add - { - try - { - PreInvoke(); - target.ModelShutdown += value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - remove - { - try - { - PreInvoke(); - target.ModelShutdown -= value; - } - catch (Exception e) - { - PostException(e); - throw; - } - } - } - - public CachedChannelProxy(CachingConnectionFactory factory, ChannelCachingConnectionProxy connection, RC.IModel target, - LinkedList channelList, bool transactional, ILogger logger) - { - Factory = factory; - TheConnection = connection; - this.target = target; - ChannelList = channelList; - ChannelListIdentity = RuntimeHelpers.GetHashCode(channelList); - Transactional = transactional; - ConfirmSelected = Factory.IsSimplePublisherConfirms; - PublisherConfirms = Factory.IsPublisherConfirms; - Logger = logger; - } - - public void Abort() - { - try - { - PreInvoke(); - target.Abort(); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void Abort(ushort replyCode, string replyText) - { - try - { - PreInvoke(); - target.Abort(replyCode, replyText); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void BasicAck(ulong deliveryTag, bool multiple) - { - try - { - if (target == null || !target.IsOpen) - { - throw new InvalidOperationException("Channel closed; cannot ack/nack"); - } - - PreInvoke(); - target.BasicAck(deliveryTag, multiple); - - if (Transactional) - { - txStarted = true; - } - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void BasicCancel(string consumerTag) - { - try - { - PreInvoke(); - target.BasicCancel(consumerTag); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public string BasicConsume(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, IDictionary arguments, - RC.IBasicConsumer consumer) - { - try - { - PreInvoke(); - return target.BasicConsume(queue, autoAck, consumerTag, noLocal, exclusive, arguments, consumer); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public RC.BasicGetResult BasicGet(string queue, bool autoAck) - { - try - { - PreInvoke(); - return target.BasicGet(queue, autoAck); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void BasicNack(ulong deliveryTag, bool multiple, bool requeue) - { - try - { - if (target == null || !target.IsOpen) - { - throw new InvalidOperationException("Channel closed; cannot ack/nack"); - } - - PreInvoke(); - target.BasicNack(deliveryTag, multiple, requeue); - - if (Transactional) - { - txStarted = true; - } - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void BasicPublish(string exchange, string routingKey, bool mandatory, RC.IBasicProperties basicProperties, byte[] body) - { - try - { - PreInvoke(); - target.BasicPublish(exchange, routingKey, mandatory, basicProperties, body); - - if (Transactional) - { - txStarted = true; - } - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void BasicQos(uint prefetchSize, ushort prefetchCount, bool global) - { - try - { - PreInvoke(); - target.BasicQos(prefetchSize, prefetchCount, global); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void BasicRecover(bool requeue) - { - try - { - PreInvoke(); - target.BasicRecover(requeue); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void BasicRecoverAsync(bool requeue) - { - try - { - PreInvoke(); - target.BasicRecoverAsync(requeue); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void BasicReject(ulong deliveryTag, bool requeue) - { - try - { - if (target == null || !target.IsOpen) - { - throw new InvalidOperationException("Channel closed; cannot ack/nack"); - } - - PreInvoke(); - target.BasicReject(deliveryTag, requeue); - - if (Transactional) - { - txStarted = true; - } - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void Close() - { - DoClose(); - } - - public void Close(ushort replyCode, string replyText) - { - DoClose(); - } - - public void ConfirmSelect() - { - try - { - PreInvoke(); - target.ConfirmSelect(); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public uint ConsumerCount(string queue) - { - try - { - PreInvoke(); - return target.ConsumerCount(queue); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public RC.IBasicProperties CreateBasicProperties() - { - try - { - PreInvoke(); - return target.CreateBasicProperties(); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public RC.IBasicPublishBatch CreateBasicPublishBatch() - { - try - { - PreInvoke(); - return target.CreateBasicPublishBatch(); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void ExchangeBind(string destination, string source, string routingKey, IDictionary arguments) - { - try - { - PreInvoke(); - target.ExchangeBind(destination, source, routingKey, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void ExchangeBindNoWait(string destination, string source, string routingKey, IDictionary arguments) - { - try - { - PreInvoke(); - target.ExchangeBindNoWait(destination, source, routingKey, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments) - { - try - { - PreInvoke(); - target.ExchangeDeclare(exchange, type, durable, autoDelete, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void ExchangeDeclareNoWait(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments) - { - try - { - PreInvoke(); - target.ExchangeDeclareNoWait(exchange, type, durable, autoDelete, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void ExchangeDeclarePassive(string exchange) - { - try - { - PreInvoke(); - target.ExchangeDeclarePassive(exchange); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void ExchangeDelete(string exchange, bool ifUnused) - { - try - { - PreInvoke(); - target.ExchangeDelete(exchange, ifUnused); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void ExchangeDeleteNoWait(string exchange, bool ifUnused) - { - try - { - PreInvoke(); - target.ExchangeDeleteNoWait(exchange, ifUnused); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void ExchangeUnbind(string destination, string source, string routingKey, IDictionary arguments) - { - try - { - PreInvoke(); - target.ExchangeUnbind(destination, source, routingKey, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void ExchangeUnbindNoWait(string destination, string source, string routingKey, IDictionary arguments) - { - try - { - PreInvoke(); - target.ExchangeUnbindNoWait(destination, source, routingKey, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public uint MessageCount(string queue) - { - try - { - PreInvoke(); - return target.MessageCount(queue); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void QueueBind(string queue, string exchange, string routingKey, IDictionary arguments) - { - try - { - PreInvoke(); - target.QueueBind(queue, exchange, routingKey, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void QueueBindNoWait(string queue, string exchange, string routingKey, IDictionary arguments) - { - try - { - PreInvoke(); - target.QueueBindNoWait(queue, exchange, routingKey, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public RC.QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) - { - try - { - PreInvoke(); - return target.QueueDeclare(queue, durable, exclusive, autoDelete, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void QueueDeclareNoWait(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) - { - try - { - PreInvoke(); - target.QueueDeclareNoWait(queue, durable, exclusive, autoDelete, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public RC.QueueDeclareOk QueueDeclarePassive(string queue) - { - try - { - PreInvoke(); - return target.QueueDeclarePassive(queue); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public uint QueueDelete(string queue, bool ifUnused, bool ifEmpty) - { - try - { - PreInvoke(); - return target.QueueDelete(queue, ifUnused, ifEmpty); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void QueueDeleteNoWait(string queue, bool ifUnused, bool ifEmpty) - { - try - { - PreInvoke(); - target.QueueDeleteNoWait(queue, ifUnused, ifEmpty); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public uint QueuePurge(string queue) - { - try - { - PreInvoke(); - return target.QueuePurge(queue); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void QueueUnbind(string queue, string exchange, string routingKey, IDictionary arguments) - { - try - { - PreInvoke(); - target.QueueUnbind(queue, exchange, routingKey, arguments); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void TxCommit() - { - try - { - PreInvoke(); - target.TxCommit(); - - if (Transactional) - { - txStarted = false; - } - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void TxRollback() - { - try - { - PreInvoke(); - target.TxRollback(); - - if (Transactional) - { - txStarted = false; - } - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void TxSelect() - { - try - { - PreInvoke(); - - if (!Transactional) - { - throw new InvalidOperationException("Cannot start transaction on non-transactional channel"); - } - - target.TxSelect(); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public bool WaitForConfirms() - { - try - { - PreInvoke(); - return target.WaitForConfirms(); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public bool WaitForConfirms(TimeSpan timeout) - { - try - { - PreInvoke(); - return target.WaitForConfirms(timeout); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public bool WaitForConfirms(TimeSpan timeout, out bool timedOut) - { - try - { - PreInvoke(); - return target.WaitForConfirms(timeout, out timedOut); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void WaitForConfirmsOrDie() - { - try - { - PreInvoke(); - target.WaitForConfirmsOrDie(); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void WaitForConfirmsOrDie(TimeSpan timeout) - { - try - { - PreInvoke(); - target.WaitForConfirmsOrDie(timeout); - } - catch (Exception e) - { - PostException(e); - throw; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - } - - public override string ToString() - { - return $"Cached Rabbit Channel: {target}, conn: {TheConnection}"; - } - - protected virtual void PostException(Exception e) - { - if (target == null || !target.IsOpen) - { - // Basic re-connection logic... - Logger?.LogDebug(e, "Detected closed channel on exception. Re-initializing: {target}", target); - target = null; - - lock (TargetMonitor) - { - target ??= Factory.CreateBareChannel(TheConnection, Transactional); - } - } - } - - protected virtual void PreInvoke() - { - if (target == null || !target.IsOpen) - { - if (target is IPublisherCallbackChannel) - { - target.Close(); - throw new RabbitException("PublisherCallbackChannel is closed"); - } - - if (txStarted) - { - txStarted = false; - throw new InvalidOperationException("Channel closed during transaction"); - } - - target = null; - } - - lock (TargetMonitor) - { - target ??= Factory.CreateBareChannel(TheConnection, Transactional); - } - } - - protected virtual void ReleasePermitIfNecessary() - { - if (Factory.ChannelCheckoutTimeout > 0) - { - /* - * Only release a permit if this is a normal close; if the channel is - * in the list, it means we're closing a cached channel (for which a permit - * has already been released). - */ - lock (ChannelList) - { - if (ChannelList.Contains(this)) - { - return; - } - } - - if (Factory.CheckoutPermits.TryGetValue(TheConnection, out SemaphoreSlim permits)) - { - permits.Release(); - Logger?.LogDebug("Released permit for '{connection}', remaining: {permits}", TheConnection, permits.CurrentCount); - } - else - { - Logger?.LogError("LEAKAGE: No permits map entry for {connection}", TheConnection); - } - } - } - - protected virtual void ReturnToCache() - { - DoReturnToCache(); - } - - protected void DoReturnToCache() - { - lock (ChannelList) - { - // Allow for multiple close calls... - if (Factory._active) - { - if (!ChannelList.Contains(this)) - { - Logger?.LogTrace("Returning cached channel: {channel}", target); - ReleasePermitIfNecessary(); - ChannelList.AddLast(this); - SetHighWaterMark(); - } - } - else - { - if (IsOpen) - { - try - { - PhysicalClose(); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception while doing PhysicalClose()"); - } - } - } - } - } - - protected virtual void LogicalClose() - { - if (target == null) - { - return; - } - - if (!target.IsOpen) - { - lock (TargetMonitor) - { - if (!target.IsOpen) - { - if (target is IPublisherCallbackChannel) - { - target.Close(); // emit nacks if necessary - } - - if (ChannelList.Contains(this)) - { - ChannelList.Remove(this); - } - else - { - ReleasePermitIfNecessary(); - } - - target = null; - return; - } - } - } - - ReturnToCache(); - } - - protected void SetHighWaterMark() - { - if (Factory._channelHighWaterMarks.TryGetValue(ChannelListIdentity, out AtomicInteger hwm)) - { - // No need for atomicity since we're synced on the channel list - int prev = hwm.Value; - int size = ChannelList.Count; - - if (size > prev) - { - hwm.Value = size; - } - } - } - - protected virtual void PhysicalClose() - { - Logger?.LogDebug("Closing cached channel: {channel}", target); - - if (target == null) - { - return; - } - - bool asyncClose = false; - - try - { - if (Factory._active && (Factory.IsPublisherConfirms || Factory.IsPublisherReturns)) - { - asyncClose = true; - AsyncClose(); - } - else - { - target.Close(); - } - } - catch (AlreadyClosedException e) - { - Logger?.LogTrace(e, "{channel} is already closed", target); - } - finally - { - target = null; - - if (!asyncClose) - { - ReleasePermitIfNecessary(); - } - } - } - - protected void AsyncClose() - { - RC.IModel channel = target; - - try - { - Task.Run(() => - { - Logger?.LogDebug("Starting AsyncClose processing"); - - try - { - if (Factory.IsPublisherConfirms) - { - channel.WaitForConfirmsOrDie(TimeSpan.FromMilliseconds(AsyncCloseTimeout)); - } - else - { - Thread.Sleep(AsyncCloseTimeout); - } - } - catch (Exception e) - { - Logger?.LogError(e, "Exception in AsyncClose processing"); - } - finally - { - try - { - channel.Close(); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception in AsyncClose issued channel close"); - } - finally - { - ReleasePermitIfNecessary(); - } - } - }); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception while running AsyncClose processing"); - } - } - - protected virtual void DoClose() - { - if (Factory._active) - { - lock (ChannelList) - { - if (Factory._active && !RabbitUtils.IsPhysicalCloseRequired() && - (ChannelList.Count < Factory.ChannelCacheSize || ChannelList.Contains(this))) - { - LogicalClose(); - return; - } - } - } - - PhysicalClose(); - } - } - - internal sealed class ChannelCachingConnectionProxy : IConnectionProxy - { - private readonly CachingConnectionFactory _factory; - private readonly ILogger _logger; - internal readonly Dictionary ChannelsAwaitingAcks = new(); - internal int CloseNotified; - - internal IConnection Target { get; set; } - - public bool IsOpen => Target != null && Target.IsOpen; - - public IConnection TargetConnection => Target; - - public RC.IConnection Connection => Target.Connection; - - public int LocalPort - { - get - { - IConnection target = Target; - - if (target != null) - { - return target.LocalPort; - } - - return 0; - } - } - - public ChannelCachingConnectionProxy(CachingConnectionFactory factory, IConnection target, ILogger logger) - { - Target = target; - _factory = factory; - _logger = logger; - } - - public RC.IModel CreateChannel(bool transactional = false) - { - return _factory.GetChannel(this, transactional); - } - - public RC.IModel CreateBareChannel(bool transactional) - { - if (Target == null) - { - throw new InvalidOperationException("Can't create channel - no target connection."); - } - - return Target.CreateChannel(transactional); - } - - public void AddBlockedListener(IBlockedListener listener) - { - if (Target == null) - { - throw new InvalidOperationException("Can't add blocked listener - no target connection."); - } - - Target.AddBlockedListener(listener); - } - - public bool RemoveBlockedListener(IBlockedListener listener) - { - if (Target == null) - { - throw new InvalidOperationException("Can't remove blocked listener - no target connection."); - } - - return Target.RemoveBlockedListener(listener); - } - - public void Close() - { - if (_factory.CacheMode == CachingMode.Connection) - { - lock (_factory._connectionMonitor) - { - if (!_factory.IdleConnections.Contains(this)) - { - if (!IsOpen || CountOpenIdleConnections() >= _factory.ConnectionCacheSize) - { - _logger?.LogDebug("Completely closing connection '{connection}'", this); - Dispose(); - } - - _logger?.LogDebug("Returning connection '{connection}' to cache", this); - - _factory.IdleConnections.AddLast(this); - int idleConnectionsSize = _factory.IdleConnections.Count; - - if (_factory._connectionHighWaterMark.Value < idleConnectionsSize) - { - _factory._connectionHighWaterMark.Value = idleConnectionsSize; - } - - Monitor.PulseAll(_factory._connectionMonitor); - } - } - } - } - - public void Dispose() - { - if (_factory.CacheMode == CachingMode.Channel) - { - _factory.Reset(_factory.CachedChannelsNonTransactional, _factory.CachedChannelsTransactional, ChannelsAwaitingAcks); - } - else - { - _factory.AllocatedConnectionNonTransactionalChannels.TryGetValue(this, out LinkedList nonTrans); - _factory.AllocatedConnectionTransactionalChannels.TryGetValue(this, out LinkedList trans); - _factory.Reset(nonTrans, trans, ChannelsAwaitingAcks); - } - - if (Target != null) - { - RabbitUtils.CloseConnection(Target, _logger); - NotifyCloseIfNecessary(); - } - - Target = null; - } - - public void NotifyCloseIfNecessary() - { - if (Interlocked.CompareExchange(ref CloseNotified, 1, 0) == 0) - { - _factory.ConnectionListener.OnClose(this); - } - } - - public override string ToString() - { - return - $"Proxy@{RuntimeHelpers.GetHashCode(this)} {(_factory.CacheMode == CachingMode.Channel ? "Shared " : "Dedicated ")}Rabbit Connection: {Target}"; - } - - private int CountOpenIdleConnections() - { - int n = 0; - - foreach (ChannelCachingConnectionProxy proxy in _factory.IdleConnections) - { - if (proxy.IsOpen) - { - n++; - } - } - - return n; - } - } - - private sealed class DefaultChannelCloseLogger : IConditionalExceptionLogger - { - public void Log(ILogger logger, string message, object cause) - { - logger?.LogError("Unexpected invocation of {type}, with {message}:{cause} ", GetType(), message, cause); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/CompositeChannelListener.cs b/src/Messaging/src/RabbitMQ/Connection/CompositeChannelListener.cs deleted file mode 100644 index bd1ae87a9d..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/CompositeChannelListener.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class CompositeChannelListener : IChannelListener -{ - private readonly object _lock = new(); - private readonly ILogger _logger; - - private List _channelListeners = new(); - - public CompositeChannelListener(ILogger logger = null) - { - _logger = logger; - } - - public void OnCreate(RC.IModel channel, bool transactional) - { - _logger?.LogDebug("OnCreate"); - List listeners = _channelListeners; - - foreach (IChannelListener listener in listeners) - { - listener.OnCreate(channel, transactional); - } - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - _logger?.LogDebug("OnShutDown"); - List listeners = _channelListeners; - - foreach (IChannelListener listener in listeners) - { - listener.OnShutDown(args); - } - } - - public void SetListeners(List channelListeners) - { - _channelListeners = channelListeners; - } - - public void AddListener(IChannelListener channelListener) - { - lock (_lock) - { - var listeners = new List(_channelListeners) - { - channelListener - }; - - _channelListeners = listeners; - } - } - - public bool RemoveListener(IChannelListener channelListener) - { - lock (_lock) - { - if (_channelListeners.Contains(channelListener)) - { - var listeners = new List(_channelListeners); - listeners.Remove(channelListener); - _channelListeners = listeners; - return true; - } - - return false; - } - } - - public void ClearListeners() - { - _channelListeners = new List(); - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/CompositeConnectionListener.cs b/src/Messaging/src/RabbitMQ/Connection/CompositeConnectionListener.cs deleted file mode 100644 index 49fa568340..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/CompositeConnectionListener.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class CompositeConnectionListener : IConnectionListener -{ - private readonly object _lock = new(); - private readonly ILogger _logger; - - private List _connectionListeners = new(); - - public CompositeConnectionListener(ILogger logger = null) - { - _logger = logger; - } - - public void OnClose(IConnection connection) - { - _logger?.LogDebug("OnClose"); - List listeners = _connectionListeners; - - foreach (IConnectionListener listener in listeners) - { - listener.OnClose(connection); - } - } - - public void OnCreate(IConnection connection) - { - _logger?.LogDebug("OnCreate"); - List listeners = _connectionListeners; - - foreach (IConnectionListener listener in listeners) - { - listener.OnCreate(connection); - } - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - _logger?.LogDebug("OnShutDown"); - List listeners = _connectionListeners; - - foreach (IConnectionListener listener in listeners) - { - listener.OnShutDown(args); - } - } - - public void SetListeners(List connectionListeners) - { - _connectionListeners = connectionListeners; - } - - public void AddListener(IConnectionListener connectionListener) - { - lock (_lock) - { - var listeners = new List(_connectionListeners) - { - connectionListener - }; - - _connectionListeners = listeners; - } - } - - public bool RemoveListener(IConnectionListener connectionListener) - { - lock (_lock) - { - if (_connectionListeners.Contains(connectionListener)) - { - var listeners = new List(_connectionListeners); - listeners.Remove(connectionListener); - _connectionListeners = listeners; - return true; - } - - return false; - } - } - - public void ClearListeners() - { - _connectionListeners = new List(); - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/ConnectionFactoryUtils.cs b/src/Messaging/src/RabbitMQ/Connection/ConnectionFactoryUtils.cs deleted file mode 100644 index fd16b76d77..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/ConnectionFactoryUtils.cs +++ /dev/null @@ -1,251 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Transaction; -using Steeltoe.Messaging.RabbitMQ.Support; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public static class ConnectionFactoryUtils -{ - public static RabbitResourceHolder GetTransactionalResourceHolder(IConnectionFactory connectionFactory, bool synchedLocalTransactionAllowed, - bool publisherConnectionIfPossible) - { - return DoGetTransactionalResourceHolder(connectionFactory, - new RabbitResourceFactory(connectionFactory, synchedLocalTransactionAllowed, publisherConnectionIfPossible)); - } - - public static RabbitResourceHolder GetTransactionalResourceHolder(IConnectionFactory connectionFactory, bool synchedLocalTransactionAllowed) - { - return GetTransactionalResourceHolder(connectionFactory, synchedLocalTransactionAllowed, false); - } - - public static bool IsChannelTransactional(RC.IModel channel, IConnectionFactory connectionFactory) - { - if (channel == null || connectionFactory == null) - { - return false; - } - - var resourceHolder = (RabbitResourceHolder)TransactionSynchronizationManager.GetResource(connectionFactory); - return resourceHolder != null && resourceHolder.ContainsChannel(channel); - } - - public static void ReleaseResources(RabbitResourceHolder resourceHolder) - { - if (resourceHolder == null || resourceHolder.SynchronizedWithTransaction) - { - return; - } - - RabbitUtils.CloseChannel(resourceHolder.GetChannel()); - RabbitUtils.CloseConnection(resourceHolder.GetConnection()); - } - - public static RabbitResourceHolder BindResourceToTransaction(RabbitResourceHolder resourceHolder, IConnectionFactory connectionFactory, bool synched) - { - if (TransactionSynchronizationManager.HasResource(connectionFactory) || !TransactionSynchronizationManager.IsActualTransactionActive() || !synched) - { - return (RabbitResourceHolder)TransactionSynchronizationManager.GetResource(connectionFactory); - } - - TransactionSynchronizationManager.BindResource(connectionFactory, resourceHolder); - resourceHolder.SynchronizedWithTransaction = true; - - if (TransactionSynchronizationManager.IsSynchronizationActive()) - { - TransactionSynchronizationManager.RegisterSynchronization(new RabbitResourceSynchronization(resourceHolder, connectionFactory)); - } - - return resourceHolder; - } - - public static IConnection CreateConnection(IConnectionFactory connectionFactory, bool publisherConnectionIfPossible) - { - if (publisherConnectionIfPossible) - { - IConnectionFactory publisherFactory = connectionFactory.PublisherConnectionFactory; - - if (publisherFactory != null) - { - return publisherFactory.CreateConnection(); - } - } - - return connectionFactory.CreateConnection(); - } - - public static void RegisterDeliveryTag(IConnectionFactory connectionFactory, RC.IModel channel, ulong tag) - { - ArgumentGuard.NotNull(connectionFactory); - - var resourceHolder = (RabbitResourceHolder)TransactionSynchronizationManager.GetResource(connectionFactory); - - if (resourceHolder != null) - { - resourceHolder.AddDeliveryTag(channel, tag); - } - } - - private static RabbitResourceHolder DoGetTransactionalResourceHolder(IConnectionFactory connectionFactory, IResourceFactory resourceFactory) - { - ArgumentGuard.NotNull(connectionFactory); - ArgumentGuard.NotNull(resourceFactory); - - var resourceHolder = (RabbitResourceHolder)TransactionSynchronizationManager.GetResource(connectionFactory); - - if (resourceHolder != null) - { - RC.IModel model = resourceFactory.GetChannel(resourceHolder); - - if (model != null) - { - return resourceHolder; - } - } - - RabbitResourceHolder resourceHolderToUse = resourceHolder ?? new RabbitResourceHolder(); - - IConnection connection = resourceFactory.GetConnection(resourceHolderToUse); - RC.IModel channel; - - try - { - /* - * If we are in a listener container, first see if there's a channel registered - * for this consumer and the consumer is using the same connection factory. - */ - channel = ConsumerChannelRegistry.GetConsumerChannel(connectionFactory); - - if (channel == null && connection == null) - { - connection = resourceFactory.CreateConnection2(); - - if (resourceHolder == null) - { - /* - * While creating a connection, a connection listener might have created a - * transactional channel and bound it to the transaction. - */ - resourceHolder = (RabbitResourceHolder)TransactionSynchronizationManager.GetResource(connectionFactory); - - if (resourceHolder != null) - { - channel = resourceHolder.GetChannel(); - resourceHolderToUse = resourceHolder; - } - } - - resourceHolderToUse.AddConnection(connection); - } - - channel ??= resourceFactory.CreateChannel(connection); - - resourceHolderToUse.AddChannel(channel, connection); - - if (!resourceHolderToUse.Equals(resourceHolder)) - { - BindResourceToTransaction(resourceHolderToUse, connectionFactory, resourceFactory.IsSynchedLocalTransactionAllowed); - } - - return resourceHolderToUse; - } - catch (Exception ex) - { - RabbitUtils.CloseConnection(connection); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(ex); - } - } - - public interface IResourceFactory - { - bool IsSynchedLocalTransactionAllowed { get; } - - RC.IModel GetChannel(RabbitResourceHolder holder); - - IConnection GetConnection(RabbitResourceHolder holder); - - IConnection CreateConnection2(); - - RC.IModel CreateChannel(IConnection connection); - } - - private sealed class RabbitResourceFactory : IResourceFactory - { - public IConnectionFactory ConnectionFactory { get; } - - public bool IsSynchedLocalTransactionAllowed { get; } - - public bool PublisherConnectionIfPossible { get; } - - public RabbitResourceFactory(IConnectionFactory connectionFactory, bool synchedLocalTransactionAllowed, bool publisherConnectionIfPossible) - { - ConnectionFactory = connectionFactory; - IsSynchedLocalTransactionAllowed = synchedLocalTransactionAllowed; - PublisherConnectionIfPossible = publisherConnectionIfPossible; - } - - public RC.IModel CreateChannel(IConnection connection) - { - return connection.CreateChannel(IsSynchedLocalTransactionAllowed); - } - - public IConnection CreateConnection2() - { - return CreateConnection(ConnectionFactory, PublisherConnectionIfPossible); - } - - public RC.IModel GetChannel(RabbitResourceHolder holder) - { - return holder.GetChannel(); - } - - public IConnection GetConnection(RabbitResourceHolder holder) - { - return holder.GetConnection(); - } - } - - private sealed class RabbitResourceSynchronization : ResourceHolderSynchronization - { - private readonly RabbitResourceHolder _resourceHolder; - - public RabbitResourceSynchronization(RabbitResourceHolder resourceHolder, object resourceKey) - : base(resourceHolder, resourceKey) - { - _resourceHolder = resourceHolder; - } - - public override void AfterCompletion(int status) - { - if (status == AbstractTransactionSynchronization.StatusCommitted) - { - _resourceHolder.CommitAll(); - } - else - { - _resourceHolder.RollbackAll(); - } - - if (_resourceHolder.ReleaseAfterCompletion) - { - _resourceHolder.SynchronizedWithTransaction = false; - } - - base.AfterCompletion(status); - } - - protected override bool ShouldReleaseBeforeCompletion() - { - return false; - } - - protected override void ReleaseResource(RabbitResourceHolder resourceHolder, object resourceKey) - { - ReleaseResources(resourceHolder); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/ConsumerChannelRegistry.cs b/src/Messaging/src/RabbitMQ/Connection/ConsumerChannelRegistry.cs deleted file mode 100644 index 22d31678f4..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/ConsumerChannelRegistry.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public static class ConsumerChannelRegistry -{ - private static readonly AsyncLocal ConsumerChannel = new(); - - public static void RegisterConsumerChannel(RC.IModel channel, IConnectionFactory connectionFactory, ILogger logger = null) - { - logger?.LogDebug("Registering consumer channel {channel} from factory {factory}", channel, connectionFactory); - ConsumerChannel.Value = new ChannelHolder(channel, connectionFactory); - } - - public static void UnRegisterConsumerChannel(ILogger logger = null) - { - logger?.LogDebug("Unregistering consumer channel {channel}", ConsumerChannel.Value); - ConsumerChannel.Value = null; - } - - public static RC.IModel GetConsumerChannel() - { - ChannelHolder channelHolder = ConsumerChannel.Value; - RC.IModel channel = null; - - if (channelHolder != null) - { - channel = channelHolder.Channel; - } - - return channel; - } - - public static RC.IModel GetConsumerChannel(IConnectionFactory connectionFactory) - { - ChannelHolder channelHolder = ConsumerChannel.Value; - RC.IModel channel = null; - - if (channelHolder != null && channelHolder.ConnectionFactory == connectionFactory) - { - channel = channelHolder.Channel; - } - - return channel; - } - - private sealed class ChannelHolder - { - public RC.IModel Channel { get; } - - public IConnectionFactory ConnectionFactory { get; } - - public ChannelHolder(RC.IModel channel, IConnectionFactory connectionFactory) - { - Channel = channel; - ConnectionFactory = connectionFactory; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/CorrelationData.cs b/src/Messaging/src/RabbitMQ/Connection/CorrelationData.cs deleted file mode 100644 index 57d63f36e7..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/CorrelationData.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class CorrelationData // : Correlation -{ - public virtual string Id { get; set; } - - public virtual IMessage ReturnedMessage { get; set; } - - public virtual TaskCompletionSource FutureSource { get; } - - public virtual Task Future => FutureSource.Task; - - public CorrelationData(string id) - { - Id = id; - FutureSource = new TaskCompletionSource(); - } - - public override string ToString() - { - return $"CorrelationData [id={Id}]"; - } - - public class Confirm - { - public bool Ack { get; } - - public string Reason { get; } - - public Confirm(bool ack, string reason) - { - Ack = ack; - Reason = reason; - } - - public override string ToString() - { - return $"Confirm [ack={Ack}{(Reason != null ? $", reason={Reason}" : string.Empty)}]"; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/DefaultPublisherCallbackFactory.cs b/src/Messaging/src/RabbitMQ/Connection/DefaultPublisherCallbackFactory.cs deleted file mode 100644 index f3c3969276..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/DefaultPublisherCallbackFactory.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class DefaultPublisherCallbackFactory : IPublisherCallbackChannelFactory -{ - private readonly ILoggerFactory _loggerFactory; - - public DefaultPublisherCallbackFactory(ILoggerFactory loggerFactory) - { - _loggerFactory = loggerFactory; - } - - public IPublisherCallbackChannel CreateChannel(RC.IModel channel) - { - return new PublisherCallbackChannel(channel, _loggerFactory?.CreateLogger()); - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IBlockedListener.cs b/src/Messaging/src/RabbitMQ/Connection/IBlockedListener.cs deleted file mode 100644 index 46151f4929..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IBlockedListener.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client.Events; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IBlockedListener -{ - void HandleBlocked(object sender, ConnectionBlockedEventArgs args); - - void HandleUnblocked(object sender, EventArgs args); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IChannelListener.cs b/src/Messaging/src/RabbitMQ/Connection/IChannelListener.cs deleted file mode 100644 index 647870e256..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IChannelListener.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IChannelListener -{ - /// - /// Called when a channel has been created. - /// - /// - /// the created channel. - /// - /// - /// true if channel is transactional. - /// - void OnCreate(RC.IModel channel, bool transactional); - - /// - /// Called when a channel has been shutdown. - /// - /// - /// the shutdown event arguments. - /// - void OnShutDown(RC.ShutdownEventArgs args); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IChannelProxy.cs b/src/Messaging/src/RabbitMQ/Connection/IChannelProxy.cs deleted file mode 100644 index b12754007f..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IChannelProxy.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IChannelProxy : RC.IModel -{ - RC.IModel TargetChannel { get; } - - bool IsTransactional { get; } - - bool IsConfirmSelected { get; } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IConnection.cs b/src/Messaging/src/RabbitMQ/Connection/IConnection.cs deleted file mode 100644 index ad1536e542..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IConnection.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IConnection : IDisposable -{ - /// - /// Gets a value indicating whether the connection is open. - /// - bool IsOpen { get; } - - /// - /// Gets the local port of the connection. - /// - int LocalPort { get; } - - /// - /// Gets the underlying RabbitMQ connection. - /// - RC.IConnection Connection { get; } - - /// - /// Create a new channel, using an internally allocated channel number. - /// - /// - /// true if transaction support on channel. - /// - /// - /// the new channel. - /// - RC.IModel CreateChannel(bool transactional = false); - - /// - /// Close the connection. - /// - void Close(); - - /// - /// Add a Blocked listener to the connection. - /// - /// - /// the listener to add. - /// - void AddBlockedListener(IBlockedListener listener); - - /// - /// Remove a Blocked listener from the connection. - /// - /// - /// the listener to remove. - /// - /// - /// true if successful. - /// - bool RemoveBlockedListener(IBlockedListener listener); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IConnectionFactory.cs b/src/Messaging/src/RabbitMQ/Connection/IConnectionFactory.cs deleted file mode 100644 index 3257a81634..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IConnectionFactory.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IConnectionFactory : IDisposable, IServiceNameAware -{ - /// - /// Gets the host name for the connection factory. - /// - string Host { get; } - - /// - /// Gets the port number for this connection factory. - /// - int Port { get; } - - /// - /// Gets the virtual host name for the connection factory. - /// - string VirtualHost { get; } - - /// - /// Gets the user name for the connection factory. - /// - string Username { get; } - - /// - /// Gets the publisher connection factory that will be used. - /// - IConnectionFactory PublisherConnectionFactory { get; } - - /// - /// Gets a value indicating whether if simple publisher confirms are enabled. - /// - bool IsSimplePublisherConfirms { get; } - - /// - /// Gets a value indicating whether if publisher confirms are enabled. - /// - bool IsPublisherConfirms { get; } - - /// - /// Gets a value indicating whether if publisher returns are enabled. - /// - bool IsPublisherReturns { get; } - - /// - /// Add a connection listener to this factory. - /// - /// - /// the listener to add. - /// - void AddConnectionListener(IConnectionListener connectionListener); - - /// - /// Remove a connection factory from this factory. - /// - /// - /// the listener to remove. - /// - /// - /// true if removed. - /// - bool RemoveConnectionListener(IConnectionListener connectionListener); - - /// - /// Remove all connection listeners. - /// - void ClearConnectionListeners(); - - /// - /// Create a connection. - /// - /// - /// the connection if successful. - /// - IConnection CreateConnection(); - - /// - /// Close underlying shared connection. The factory is still able to create new connections after this call. - /// - void Destroy(); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IConnectionListener.cs b/src/Messaging/src/RabbitMQ/Connection/IConnectionListener.cs deleted file mode 100644 index ffbca72ce6..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IConnectionListener.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IConnectionListener -{ - /// - /// Called when a new connection is established. - /// - /// - /// the connection. - /// - void OnCreate(IConnection connection); - - /// - /// Called when connection is closed. - /// - /// - /// the connection. - /// - void OnClose(IConnection connection); - - /// - /// Called when connection is forced to close. - /// - /// - /// the event. - /// - void OnShutDown(RC.ShutdownEventArgs args); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IConnectionProxy.cs b/src/Messaging/src/RabbitMQ/Connection/IConnectionProxy.cs deleted file mode 100644 index 7b7b817ad1..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IConnectionProxy.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IConnectionProxy : IConnection -{ - IConnection TargetConnection { get; } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IPublisherCallbackChannel.cs b/src/Messaging/src/RabbitMQ/Connection/IPublisherCallbackChannel.cs deleted file mode 100644 index c2258c3bb0..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IPublisherCallbackChannel.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IPublisherCallbackChannel : RC.IModel -{ - /// - /// Gets the underlying RabbitMQ model. - /// - RC.IModel Channel { get; } - - /// - /// Add a publisher callback listener. - /// - /// - /// the listener to add. - /// - void AddListener(IListener listener); - - /// - /// Expire (remove) any pending confirmations created before the specified cutoff time for the supplied listener and return them to the caller. - /// - /// - /// the listener. - /// - /// - /// the time before which expired messages were created. - /// - /// - /// the list of expired confirms. - /// - IList Expire(IListener listener, long cutoffTime); - - /// - /// Get the total pending confirm count for the listener. - /// - /// - /// the listener to get confirm count for. - /// - /// - /// the count of pending confirms. - /// - int GetPendingConfirmsCount(IListener listener); - - /// - /// Gets the total pending confirm count. - /// - /// - /// the total count. - /// - int GetPendingConfirmsCount(); - - /// - /// Add a pending confirmation to this channels map. - /// - /// - /// the listener the pending confirm is for. - /// - /// - /// the key to the map. - /// - /// - /// the pending confirm. - /// - void AddPendingConfirm(IListener listener, ulong sequence, PendingConfirm pendingConfirm); - - /// - /// Set a callback to be invoked after the ack/nack has been handled. - /// - /// - /// the callback. - /// - void SetAfterAckCallback(Action callback); - - public interface IListener - { - /// - /// Gets the UUID used to identify this listener for returns. - /// - string Uuid { get; } - - /// - /// Gets a value indicating whether this is a confirm listener. - /// - bool IsConfirmListener { get; } - - /// - /// Gets a value indicating whether this is a returns listener. - /// - bool IsReturnListener { get; } - - /// - /// Invoked by the channel when a confirmation is received. - /// - /// - /// the pending confirmation. - /// - /// - /// true when an ack; false when a nack. - /// - void HandleConfirm(PendingConfirm pendingConfirm, bool ack); - - /// - /// Invoked when a basic return command is received. - /// - /// - /// the reason code of the return. - /// - /// - /// the text from the broker describing the return. - /// - /// - /// the exchange the returned message was originally published to. - /// - /// - /// the routing key used when the message was originally published. - /// - /// - /// the content header of the message. - /// - /// - /// the body of the message. - /// - void HandleReturn(int replyCode, string replyText, string exchange, string routingKey, RC.IBasicProperties properties, byte[] body); - - /// - /// When called this listener should remove all references to the channel. - /// - /// - /// the channel. - /// - void Revoke(RC.IModel channel); - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IPublisherCallbackChannelFactory.cs b/src/Messaging/src/RabbitMQ/Connection/IPublisherCallbackChannelFactory.cs deleted file mode 100644 index 1521ea0045..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IPublisherCallbackChannelFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -/// -/// Factory for creating publisher callbacks. -/// -public interface IPublisherCallbackChannelFactory -{ - /// - /// Create a publisher callback for the given channel. - /// - /// - /// the channel. - /// - /// - /// the callback. - /// - IPublisherCallbackChannel CreateChannel(RC.IModel channel); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IRecoveryListener.cs b/src/Messaging/src/RabbitMQ/Connection/IRecoveryListener.cs deleted file mode 100644 index 215555712b..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IRecoveryListener.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client.Events; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IRecoveryListener -{ - void HandleConnectionRecoveryError(object sender, ConnectionRecoveryErrorEventArgs args); - - void HandleRecoverySucceeded(object sender, EventArgs args); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IRoutingConnectionFactory.cs b/src/Messaging/src/RabbitMQ/Connection/IRoutingConnectionFactory.cs deleted file mode 100644 index 01f5342215..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IRoutingConnectionFactory.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IRoutingConnectionFactory -{ - /// - /// Returns the ConnectionFactory bound to given lookup key, or null if one does not exist. - /// - /// - /// the lookup key to which the factory is bound. - /// - /// - /// the factory if found. - /// - IConnectionFactory GetTargetConnectionFactory(object key); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IShutDownChannelListener.cs b/src/Messaging/src/RabbitMQ/Connection/IShutDownChannelListener.cs deleted file mode 100644 index bab0b5bf05..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IShutDownChannelListener.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IShutDownChannelListener : IChannelListener -{ -} diff --git a/src/Messaging/src/RabbitMQ/Connection/IShutdownListener.cs b/src/Messaging/src/RabbitMQ/Connection/IShutdownListener.cs deleted file mode 100644 index 154be41a46..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/IShutdownListener.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public interface IShutdownListener -{ - void ChannelShutdownCompleted(object sender, RC.ShutdownEventArgs args); -} diff --git a/src/Messaging/src/RabbitMQ/Connection/PendingConfirm.cs b/src/Messaging/src/RabbitMQ/Connection/PendingConfirm.cs deleted file mode 100644 index 6fafda0c8d..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/PendingConfirm.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class PendingConfirm -{ - public CorrelationData CorrelationInfo { get; } - - public long Timestamp { get; } - - public string Cause { get; set; } - - public PendingConfirm(CorrelationData correlationData, long timestamp) - { - CorrelationInfo = correlationData; - Timestamp = timestamp; - } - - public override string ToString() - { - return $"PendingConfirm [correlationInfo={CorrelationInfo}{(Cause == null ? string.Empty : $" cause={Cause}")}]"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/PublisherCallbackChannel.cs b/src/Messaging/src/RabbitMQ/Connection/PublisherCallbackChannel.cs deleted file mode 100644 index 4820d39510..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/PublisherCallbackChannel.cs +++ /dev/null @@ -1,704 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; -using RabbitMQ.Client.Events; -using RabbitMQ.Client.Exceptions; -using Steeltoe.Common; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Support; -using static Steeltoe.Messaging.RabbitMQ.Connection.CorrelationData; -using static Steeltoe.Messaging.RabbitMQ.Connection.IPublisherCallbackChannel; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class PublisherCallbackChannel : IPublisherCallbackChannel -{ - public const string ReturnedMessageCorrelationKey = "spring_returned_message_correlation"; - public const string ReturnListenerCorrelationKey = "spring_listener_return_correlation"; - public const string ReturnListenerError = $"No '{ReturnListenerCorrelationKey}' header in returned message"; - private readonly List _emptyConfirms = new(); - private readonly IMessageHeadersConverter _converter = new DefaultMessageHeadersConverter(); - private readonly ILogger _logger; - private readonly object _lock = new(); - private readonly ConcurrentDictionary> _pendingConfirms = new(); - private readonly ConcurrentDictionary _listeners = new(); - private readonly SortedDictionary _listenerForSeq = new(); - private readonly ConcurrentDictionary _pendingReturns = new(); - private Action _afterAckCallback; - - public virtual RC.IModel Channel { get; } - - public virtual int ChannelNumber => Channel.ChannelNumber; - - public virtual RC.ShutdownEventArgs CloseReason => Channel.CloseReason; - - public virtual RC.IBasicConsumer DefaultConsumer - { - get => Channel.DefaultConsumer; - set => Channel.DefaultConsumer = value; - } - - public virtual bool IsClosed => Channel.IsClosed; - - public virtual bool IsOpen => Channel.IsOpen; - - public virtual ulong NextPublishSeqNo => Channel.NextPublishSeqNo; - - public virtual TimeSpan ContinuationTimeout - { - get => Channel.ContinuationTimeout; - set => Channel.ContinuationTimeout = value; - } - - public virtual event EventHandler BasicAcks - { - add => Channel.BasicAcks += value; - remove => Channel.BasicAcks -= value; - } - - public virtual event EventHandler BasicNacks - { - add => Channel.BasicNacks += value; - remove => Channel.BasicNacks -= value; - } - - public virtual event EventHandler BasicRecoverOk - { - add => Channel.BasicRecoverOk += value; - remove => Channel.BasicRecoverOk -= value; - } - - public virtual event EventHandler BasicReturn - { - add => Channel.BasicReturn += value; - remove => Channel.BasicReturn -= value; - } - - public virtual event EventHandler CallbackException - { - add => Channel.CallbackException += value; - remove => Channel.CallbackException -= value; - } - - public virtual event EventHandler FlowControl - { - add => Channel.FlowControl += value; - remove => Channel.FlowControl -= value; - } - - public virtual event EventHandler ModelShutdown - { - add => Channel.ModelShutdown += value; - remove => Channel.ModelShutdown -= value; - } - - public PublisherCallbackChannel(RC.IModel channel, ILogger logger = null) - { - Channel = channel; - _logger = logger; - channel.ModelShutdown += ShutdownCompleted; - } - - public virtual IList Expire(IListener listener, long cutoffTime) - { - lock (_lock) - { - if (!_pendingConfirms.TryGetValue(listener, out SortedDictionary pendingConfirmsForListener)) - { - return _emptyConfirms; - } - - var expired = new List(); - var toRemove = new List(); - - foreach (KeyValuePair kvp in pendingConfirmsForListener) - { - PendingConfirm pendingConfirm = kvp.Value; - - if (pendingConfirm.Timestamp < cutoffTime) - { - expired.Add(pendingConfirm); - toRemove.Add(kvp.Key); - CorrelationData correlationData = pendingConfirm.CorrelationInfo; - - if (correlationData != null && !string.IsNullOrEmpty(correlationData.Id)) - { - _pendingReturns.Remove(correlationData.Id, out PendingConfirm _); - } - } - else - { - break; - } - } - - foreach (ulong key in toRemove) - { - pendingConfirmsForListener.Remove(key); - } - - return expired; - } - } - - public virtual int GetPendingConfirmsCount(IListener listener) - { - lock (_lock) - { - if (!_pendingConfirms.TryGetValue(listener, out SortedDictionary pendingConfirmsForListener)) - { - return 0; - } - - return pendingConfirmsForListener.Count; - } - } - - public virtual int GetPendingConfirmsCount() - { - lock (_lock) - { - return _pendingConfirms.Values.Select(p => p.Count).Sum(); - } - } - - public virtual void AddListener(IListener listener) - { - ArgumentGuard.NotNull(listener); - - if (_listeners.Count == 0) - { - Channel.BasicAcks += HandleAck; - Channel.BasicNacks += HandleNack; - Channel.BasicReturn += HandleReturn; - } - - if (_listeners.TryAdd(listener.Uuid, listener)) - { - _pendingConfirms[listener] = new SortedDictionary(); - _logger?.LogDebug("Added listener {listener}", listener); - } - } - - public virtual void AddPendingConfirm(IListener listener, ulong sequence, PendingConfirm pendingConfirm) - { - lock (_lock) - { - if (!_pendingConfirms.TryGetValue(listener, out SortedDictionary pendingConfirmsForListener)) - { - throw new ArgumentException("Listener not found in pending confirms.", nameof(listener)); - } - - pendingConfirmsForListener[sequence] = pendingConfirm; - _listenerForSeq[sequence] = listener; - - if (pendingConfirm.CorrelationInfo != null) - { - string returnCorrelation = pendingConfirm.CorrelationInfo.Id; - - if (!string.IsNullOrEmpty(returnCorrelation)) - { - _pendingReturns[returnCorrelation] = pendingConfirm; - } - } - } - } - - public virtual void SetAfterAckCallback(Action callback) - { - if (GetPendingConfirmsCount() == 0 && callback != null) - { - callback(this); - } - else - { - _afterAckCallback = callback; - } - } - - public virtual void Abort() - { - Channel.Abort(); - } - - public virtual void Abort(ushort replyCode, string replyText) - { - Channel.Abort(replyCode, replyText); - } - - public virtual void BasicAck(ulong deliveryTag, bool multiple) - { - Channel.BasicAck(deliveryTag, multiple); - } - - public virtual void BasicCancel(string consumerTag) - { - Channel.BasicCancel(consumerTag); - } - - public virtual string BasicConsume(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, IDictionary arguments, - RC.IBasicConsumer consumer) - { - return Channel.BasicConsume(queue, autoAck, consumerTag, noLocal, exclusive, arguments, consumer); - } - - public virtual RC.BasicGetResult BasicGet(string queue, bool autoAck) - { - return Channel.BasicGet(queue, autoAck); - } - - public virtual void BasicNack(ulong deliveryTag, bool multiple, bool requeue) - { - Channel.BasicNack(deliveryTag, multiple, requeue); - } - - public virtual void BasicPublish(string exchange, string routingKey, bool mandatory, RC.IBasicProperties basicProperties, byte[] body) - { - Channel.BasicPublish(exchange, routingKey, mandatory, basicProperties, body); - } - - public virtual void BasicQos(uint prefetchSize, ushort prefetchCount, bool global) - { - Channel.BasicQos(prefetchSize, prefetchCount, global); - } - - public virtual void BasicRecover(bool requeue) - { - Channel.BasicRecover(requeue); - } - - public virtual void BasicRecoverAsync(bool requeue) - { - Channel.BasicRecoverAsync(requeue); - } - - public virtual void BasicReject(ulong deliveryTag, bool requeue) - { - Channel.BasicReject(deliveryTag, requeue); - } - - public virtual void Close() - { - _logger?.LogDebug("Closing channel {channel}", Channel); - - try - { - Channel.Close(); - } - catch (AlreadyClosedException e) - { - _logger?.LogTrace(e, "Channel {channel} is already closed", Channel); - } - - ShutdownCompleted("Channel closed by application"); - } - - public virtual void Close(ushort replyCode, string replyText) - { - Channel.Close(replyCode, replyText); - } - - public virtual void ConfirmSelect() - { - Channel.ConfirmSelect(); - } - - public virtual uint ConsumerCount(string queue) - { - return Channel.ConsumerCount(queue); - } - - public virtual RC.IBasicProperties CreateBasicProperties() - { - return Channel.CreateBasicProperties(); - } - - public virtual RC.IBasicPublishBatch CreateBasicPublishBatch() - { - return Channel.CreateBasicPublishBatch(); - } - - public virtual void ExchangeBind(string destination, string source, string routingKey, IDictionary arguments) - { - Channel.ExchangeBind(destination, source, routingKey, arguments); - } - - public virtual void ExchangeBindNoWait(string destination, string source, string routingKey, IDictionary arguments) - { - Channel.ExchangeBindNoWait(destination, source, routingKey, arguments); - } - - public virtual void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments) - { - Channel.ExchangeDeclare(exchange, type, durable, autoDelete, arguments); - } - - public virtual void ExchangeDeclareNoWait(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments) - { - Channel.ExchangeDeclareNoWait(exchange, type, durable, autoDelete, arguments); - } - - public virtual void ExchangeDeclarePassive(string exchange) - { - Channel.ExchangeDeclarePassive(exchange); - } - - public virtual void ExchangeDelete(string exchange, bool ifUnused) - { - Channel.ExchangeDelete(exchange, ifUnused); - } - - public virtual void ExchangeDeleteNoWait(string exchange, bool ifUnused) - { - Channel.ExchangeDeleteNoWait(exchange, ifUnused); - } - - public virtual void ExchangeUnbind(string destination, string source, string routingKey, IDictionary arguments) - { - Channel.ExchangeUnbind(destination, source, routingKey, arguments); - } - - public virtual void ExchangeUnbindNoWait(string destination, string source, string routingKey, IDictionary arguments) - { - Channel.ExchangeUnbindNoWait(destination, source, routingKey, arguments); - } - - public virtual uint MessageCount(string queue) - { - return Channel.MessageCount(queue); - } - - public virtual void QueueBind(string queue, string exchange, string routingKey, IDictionary arguments) - { - Channel.QueueBind(queue, exchange, routingKey, arguments); - } - - public virtual void QueueBindNoWait(string queue, string exchange, string routingKey, IDictionary arguments) - { - Channel.QueueBindNoWait(queue, exchange, routingKey, arguments); - } - - public virtual RC.QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) - { - return Channel.QueueDeclare(queue, durable, exclusive, autoDelete, arguments); - } - - public virtual void QueueDeclareNoWait(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) - { - Channel.QueueDeclareNoWait(queue, durable, exclusive, autoDelete, arguments); - } - - public virtual RC.QueueDeclareOk QueueDeclarePassive(string queue) - { - return Channel.QueueDeclarePassive(queue); - } - - public virtual uint QueueDelete(string queue, bool ifUnused, bool ifEmpty) - { - return Channel.QueueDelete(queue, ifUnused, ifEmpty); - } - - public virtual void QueueDeleteNoWait(string queue, bool ifUnused, bool ifEmpty) - { - Channel.QueueDeleteNoWait(queue, ifUnused, ifEmpty); - } - - public virtual uint QueuePurge(string queue) - { - return Channel.QueuePurge(queue); - } - - public virtual void QueueUnbind(string queue, string exchange, string routingKey, IDictionary arguments) - { - Channel.QueueBind(queue, exchange, routingKey, arguments); - } - - public virtual void TxCommit() - { - Channel.TxCommit(); - } - - public virtual void TxRollback() - { - Channel.TxRollback(); - } - - public virtual void TxSelect() - { - Channel.TxSelect(); - } - - public virtual bool WaitForConfirms() - { - return Channel.WaitForConfirms(); - } - - public virtual bool WaitForConfirms(TimeSpan timeout) - { - return Channel.WaitForConfirms(timeout); - } - - public virtual bool WaitForConfirms(TimeSpan timeout, out bool timedOut) - { - return Channel.WaitForConfirms(timeout, out timedOut); - } - - public virtual void WaitForConfirmsOrDie() - { - Channel.WaitForConfirmsOrDie(); - } - - public virtual void WaitForConfirmsOrDie(TimeSpan timeout) - { - Channel.WaitForConfirmsOrDie(timeout); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - } - - private void ShutdownCompleted(object sender, RC.ShutdownEventArgs e) - { - ShutdownCompleted(e.ReplyText); - } - - private void ShutdownCompleted(string cause) - { - GenerateNacksForPendingAcks(cause); - } - - private void GenerateNacksForPendingAcks(string cause) - { - lock (_lock) - { - foreach (KeyValuePair> entry in _pendingConfirms) - { - IListener listener = entry.Key; - - foreach (KeyValuePair confirmEntry in entry.Value) - { - confirmEntry.Value.Cause = cause; - _logger?.LogDebug("{channel} PC:Nack:(close):{confirmEntry}", this, confirmEntry.Key); - ProcessAck(confirmEntry.Key, false, false, false); - } - - listener.Revoke(this); - } - - _logger?.LogDebug("PendingConfirms cleared"); - _pendingConfirms.Clear(); - _listenerForSeq.Clear(); - _listeners.Clear(); - } - } - - private void HandleReturn(object sender, BasicReturnEventArgs args) - { - RC.IBasicProperties properties = args.BasicProperties; - - IMessageHeaders messageProperties = - _converter.ToMessageHeaders(properties, new Envelope(0, false, args.Exchange, args.RoutingKey), EncodingUtils.GetDefaultEncoding()); - - if (properties.Headers.TryGetValue(ReturnedMessageCorrelationKey, out object returnCorrelation) && - _pendingReturns.Remove(returnCorrelation.ToString(), out PendingConfirm confirm) && confirm.CorrelationInfo != null) - { - confirm.CorrelationInfo.ReturnedMessage = Message.Create(args.Body, messageProperties); - } - - string uuidObject = messageProperties.Get(ReturnListenerCorrelationKey); - - IListener listener = null; - - if (uuidObject != null) - { - _listeners.TryGetValue(uuidObject, out listener); - } - else - { - _logger?.LogError(ReturnListenerError); - } - - if (listener == null || !listener.IsReturnListener) - { - _logger?.LogWarning("No Listener for returned message"); - } - else - { - IListener listenerToInvoke = listener; - - try - { - listenerToInvoke.HandleReturn(args.ReplyCode, args.ReplyText, args.Exchange, args.RoutingKey, properties, args.Body); - } - catch (Exception e) - { - _logger?.LogError(e, "Exception delivering returned message "); - } - } - } - - private void HandleAck(object sender, BasicAckEventArgs args) - { - _logger?.LogDebug("{channel} PC:Ack: {deliveryTag}:{multiple}", this, args.DeliveryTag, args.Multiple); - ProcessAck(args.DeliveryTag, true, args.Multiple, true); - } - - private void HandleNack(object sender, BasicNackEventArgs args) - { - _logger?.LogDebug("{channel} PC:Nack: {deliveryTag}:{multiple}", this, args.DeliveryTag, args.Multiple); - ProcessAck(args.DeliveryTag, false, args.Multiple, true); - } - - private void ProcessAck(ulong seq, bool ack, bool multiple, bool remove) - { - lock (_lock) - { - try - { - DoProcessAck(seq, ack, multiple, remove); - } - catch (Exception e) - { - _logger?.LogError(e, "Failed to process publisher confirm"); - } - } - } - - private void DoProcessAck(ulong seq, bool ack, bool multiple, bool remove) - { - if (multiple) - { - ProcessMultipleAck(seq, ack); - } - else - { - if (_listenerForSeq.TryGetValue(seq, out IListener listener)) - { - PendingConfirm pendingConfirm = null; - - if (_pendingConfirms.TryGetValue(listener, out SortedDictionary confirmsForListener)) - { - if (remove) - { - confirmsForListener.Remove(seq, out pendingConfirm); - } - else - { - confirmsForListener.TryGetValue(seq, out pendingConfirm); - } - } - - if (pendingConfirm != null) - { - CorrelationData correlationData = pendingConfirm.CorrelationInfo; - - if (correlationData != null) - { - correlationData.FutureSource.SetResult(new Confirm(ack, pendingConfirm.Cause)); - - if (!string.IsNullOrEmpty(correlationData.Id)) - { - _pendingReturns.Remove(correlationData.Id, out _); - } - } - - DoHandleConfirm(ack, listener, pendingConfirm); - } - } - else - { - _logger?.LogDebug("{channel} No listener for seq: {seq}", Channel, seq); - } - } - } - - private void ProcessMultipleAck(ulong seq, bool ack) - { - // Piggy-backed ack - extract all Listeners for this and earlier - // sequences. Then, for each Listener, handle each of it's acks. - // Finally, remove the sequences from listenerForSeq. - IEnumerable> involvedListeners = _listenerForSeq.Where(kvp => kvp.Key < seq + 1); - - // eliminate duplicates - IEnumerable listenersForAcks = involvedListeners.Select(kvp => kvp.Value).Distinct(); - - foreach (IListener involvedListener in listenersForAcks) - { - // find all unack'd confirms for this listener and handle them - if (_pendingConfirms.TryGetValue(involvedListener, out SortedDictionary confirmsMap)) - { - IEnumerable> confirms = confirmsMap.Where(kvp => kvp.Key < seq + 1); - - foreach (KeyValuePair entry in confirms) - { - PendingConfirm value = entry.Value; - CorrelationData correlationData = value.CorrelationInfo; - - if (correlationData != null) - { - correlationData.FutureSource.SetResult(new Confirm(ack, value.Cause)); - - if (!string.IsNullOrEmpty(correlationData.Id)) - { - _pendingReturns.Remove(correlationData.Id, out _); - } - } - - DoHandleConfirm(ack, involvedListener, value); - } - } - } - - var sequence = new List(involvedListeners.Select(kvp => kvp.Key)); - - foreach (ulong key in sequence) - { - _listenerForSeq.Remove(key); - } - } - - private void DoHandleConfirm(bool ack, IListener listener, PendingConfirm pendingConfirm) - { - Task.Run(() => - { - try - { - if (listener.IsConfirmListener) - { - _logger?.LogDebug("Sending confirm {confirm}", pendingConfirm); - listener.HandleConfirm(pendingConfirm, ack); - } - } - catch (Exception e) - { - _logger?.LogError(e, "Exception delivering confirm"); - } - finally - { - try - { - if (_afterAckCallback != null && GetPendingConfirmsCount() == 0) - { - _afterAckCallback(this); - _afterAckCallback = null; - } - } - catch (Exception e) - { - _logger?.LogError(e, "Failed to invoke afterAckCallback"); - } - } - }); - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/RabbitResourceHolder.cs b/src/Messaging/src/RabbitMQ/Connection/RabbitResourceHolder.cs deleted file mode 100644 index 0965a07f73..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/RabbitResourceHolder.cs +++ /dev/null @@ -1,194 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Transaction; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class RabbitResourceHolder : ResourceHolderSupport -{ - private readonly List _connections = new(); - - private readonly List _channels = new(); - - private readonly Dictionary> _channelsPerConnection = new(); - - private readonly Dictionary> _deliveryTags = new(); - - private readonly ILogger _logger; - - public bool ReleaseAfterCompletion { get; } - - public bool RequeueOnRollback { get; set; } = true; - - public RabbitResourceHolder(ILogger logger = null) - { - _logger = logger; - ReleaseAfterCompletion = true; - } - - public RabbitResourceHolder(RC.IModel channel, bool releaseAfterCompletion, ILogger logger = null) - { - AddChannel(channel); - ReleaseAfterCompletion = releaseAfterCompletion; - _logger = logger; - } - - public void AddConnection(IConnection connection) - { - ArgumentGuard.NotNull(connection); - - if (!_connections.Contains(connection)) - { - _connections.Add(connection); - } - } - - public void AddChannel(RC.IModel channel) - { - AddChannel(channel, null); - } - - public void AddChannel(RC.IModel channel, IConnection connection) - { - ArgumentGuard.NotNull(channel); - - if (!_channels.Contains(channel)) - { - _channels.Add(channel); - - if (connection != null) - { - if (!_channelsPerConnection.TryGetValue(connection, out List channelsForConnection)) - { - channelsForConnection = new List(); - _channelsPerConnection[connection] = channelsForConnection; - } - - channelsForConnection.Add(channel); - } - } - } - - public bool ContainsChannel(RC.IModel channel) - { - return _channels.Contains(channel); - } - - public IConnection GetConnection() - { - return _connections.Count > 0 ? _connections[0] : null; - } - - public RC.IModel GetChannel() - { - return _channels.Count > 0 ? _channels[0] : null; - } - - public void CommitAll() - { - try - { - foreach (RC.IModel channel in _channels) - { - if (_deliveryTags.TryGetValue(channel, out List tags)) - { - foreach (ulong deliveryTag in tags) - { - channel.BasicAck(deliveryTag, false); - } - } - - channel.TxCommit(); - } - } - catch (Exception e) - { - _logger?.LogError(e, "failed to commit RabbitMQ transaction"); - throw new RabbitException("failed to commit RabbitMQ transaction", e); - } - } - - public void CloseAll() - { - foreach (RC.IModel channel in _channels) - { - try - { - if (channel != ConsumerChannelRegistry.GetConsumerChannel()) - { - channel.Close(); - } - else - { - _logger?.LogDebug("Skipping close of consumer channel: {channel}", channel); - } - } - catch (Exception ex) - { - _logger?.LogDebug(ex, "Could not close synchronized Rabbit Channel after transaction"); - } - } - - foreach (IConnection con in _connections) - { - RabbitUtils.CloseConnection(con); - } - - _connections.Clear(); - _channels.Clear(); - _channelsPerConnection.Clear(); - } - - public void AddDeliveryTag(RC.IModel channel, ulong deliveryTag) - { - if (_deliveryTags.TryGetValue(channel, out List tags)) - { - tags.Add(deliveryTag); - } - else - { - tags = new List - { - deliveryTag - }; - - _deliveryTags.Add(channel, tags); - } - } - - public void RollbackAll() - { - foreach (RC.IModel channel in _channels) - { - _logger?.LogDebug("Rolling back messages to channel: {channel}", channel); - - RabbitUtils.RollbackIfNecessary(channel); - - if (_deliveryTags.TryGetValue(channel, out List tags)) - { - foreach (ulong deliveryTag in tags) - { - try - { - channel.BasicReject(deliveryTag, RequeueOnRollback); - } - catch (Exception ex) - { - _logger?.LogError(ex, "Error Rolling back messages to {channel}", channel); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(ex); - } - } - - // Need to commit the reject (=nack) - RabbitUtils.CommitIfNecessary(channel); - } - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/RabbitUtils.cs b/src/Messaging/src/RabbitMQ/Connection/RabbitUtils.cs deleted file mode 100644 index 4e97bbb5ab..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/RabbitUtils.cs +++ /dev/null @@ -1,308 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using RabbitMQ.Client.Exceptions; -using RabbitMQ.Client.Impl; -using Steeltoe.Common; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public static class RabbitUtils -{ - public const int ReplySuccess = 200; - public const int NotFound = 404; - public const int PreconditionFailed = 406; - public const int CommandInvalid = 503; - - public const ushort ExchangeClassId = 40; - public const ushort QueueClassId = 50; - public const ushort DeclareMethodId = 10; - public const ushort ChannelCloseClassId = 20; - public const ushort ChannelCloseMethodId = 40; - public const ushort ConnectionCloseClassId = 10; - public const ushort ConnectionCloseMethodId = 50; - - private static readonly AsyncLocal PhysicalCloseRequired = new(); - - public static void CloseConnection(IConnection connection, ILogger logger = null) - { - if (connection != null) - { - try - { - connection.Close(); - } - catch (AlreadyClosedException) - { - // empty - } - catch (Exception ex) - { - logger?.LogDebug(ex, "Ignoring Connection exception - assuming already closed"); - } - } - } - - public static void CloseChannel(RC.IModel channel, ILogger logger = null) - { - if (channel != null) - { - try - { - channel.Close(); - } - catch (AlreadyClosedException) - { - // empty - } - catch (ShutdownSignalException sig) - { - if (!IsNormalShutdown(sig.Args)) - { - logger?.LogDebug(sig, "Unexpected exception on closing RabbitMQ Channel"); - } - } - catch (Exception ex) - { - logger?.LogDebug(ex, "Unexpected exception on closing RabbitMQ Channel"); - } - } - } - - public static void CommitIfNecessary(RC.IModel channel, ILogger logger = null) - { - ArgumentGuard.NotNull(channel); - - try - { - channel.TxCommit(); - } - catch (Exception ex) - { - logger?.LogError(ex, "Error during TxCommit"); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(ex); - } - } - - public static void RollbackIfNecessary(RC.IModel channel, ILogger logger = null) - { - ArgumentGuard.NotNull(channel); - - try - { - channel.TxRollback(); - } - catch (Exception ex) - { - logger?.LogError(ex, "Error during TxCommit"); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(ex); - } - } - - public static void CloseMessageConsumer(RC.IModel channel, List consumerTags, bool transactional, ILogger logger = null) - { - if (!channel.IsOpen) - { - return; - } - - try - { - foreach (string consumerTag in consumerTags) - { - Cancel(channel, consumerTag); - } - - if (transactional) - { - // Re-queue in-flight messages if any (after the consumer is cancelled to prevent the broker from simply - // sending them back to us). Does not require a tx.commit. - channel.BasicRecover(true); - } - - // If not transactional then we are auto-acking (at least as of 1.0.0.M2) so there is nothing to recover. - // Messages are going to be lost in general. - } - catch (Exception ex) - { - logger?.LogError(ex, "Exception during CloseMessageConsumer"); - - throw RabbitExceptionTranslator.ConvertRabbitAccessException(ex); - } - } - - public static void Cancel(RC.IModel channel, string consumerTag, ILogger logger = null) - { - try - { - channel.BasicCancel(consumerTag); - } - catch (AlreadyClosedException e) - { - logger?.LogTrace(e, "{channel} is already closed", channel); - } - catch (Exception e) - { - logger?.LogDebug(e, "Error performing 'basicCancel' on {channel}", channel); - } - } - - public static void DeclareTransactional(RC.IModel channel, ILogger logger = null) - { - try - { - channel.TxSelect(); - } - catch (IOException e) - { - logger?.LogDebug(e, "Error performing 'txSelect' on {channel}", channel); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - public static void SetPhysicalCloseRequired(RC.IModel channel, bool b) - { - if (channel is IChannelProxy) - { - PhysicalCloseRequired.Value = b; - } - } - - public static bool IsPhysicalCloseRequired() - { - bool? mustClose = PhysicalCloseRequired.Value; - - if (mustClose == null) - { - mustClose = false; - } - else - { - PhysicalCloseRequired.Value = null; - } - - return mustClose.Value; - } - - public static bool IsNormalChannelClose(RC.ShutdownEventArgs args) - { - if (IsNormalShutdown(args)) - { - return true; - } - - if (args.ClassId == ChannelCloseClassId && args.MethodId == ChannelCloseMethodId && args.ReplyCode == ReplySuccess && args.ReplyText == "OK") - { - return true; - } - - return args.Initiator == RC.ShutdownInitiator.Application && args.ClassId == 0 && args.MethodId == 0 && args.ReplyText == "Goodbye"; - } - - public static bool IsNormalShutdown(RC.ShutdownEventArgs args) - { - if (args.ClassId == ConnectionCloseClassId && args.MethodId == ConnectionCloseMethodId && args.ReplyCode == ReplySuccess && args.ReplyText == "OK") - { - return true; - } - - return args.Initiator == RC.ShutdownInitiator.Application && args.ClassId == 0 && args.MethodId == 0 && args.ReplyText == "Goodbye"; - } - - public static bool IsPassiveDeclarationChannelClose(Exception exception) - { - RC.ShutdownEventArgs cause = null; - - switch (exception) - { - case ShutdownSignalException shutdownSignalException: - cause = shutdownSignalException.Args; - break; - case ProtocolException protocolException: - cause = protocolException.ShutdownReason; - break; - } - - if (cause != null) - { - return (cause.ClassId == ExchangeClassId || cause.ClassId == QueueClassId) && cause.MethodId == DeclareMethodId && cause.ReplyCode == NotFound; - } - - return false; - } - - public static bool IsMismatchedQueueArgs(Exception exception) - { - Exception cause = exception; - RC.ShutdownEventArgs args = null; - - while (cause != null && args == null) - { - if (cause is ShutdownSignalException exception1) - { - args = exception1.Args; - } - - if (cause is OperationInterruptedException exception2) - { - args = exception2.ShutdownReason; - } - - cause = cause.InnerException; - } - - if (args == null) - { - return false; - } - - return IsMismatchedQueueArgs(args); - } - - public static bool IsMismatchedQueueArgs(RC.ShutdownEventArgs args) - { - return args.ClassId == QueueClassId && args.MethodId == DeclareMethodId && args.ReplyCode == PreconditionFailed; - } - - public static int GetMaxFrame(IConnectionFactory connectionFactory) - { - try - { - return (int)connectionFactory.CreateConnection().Connection.FrameMax; - } - catch (Exception) - { - // Ignore - } - - return -1; - } - - internal static bool IsExchangeDeclarationFailure(RabbitIOException e) - { - Exception cause = e; - RC.ShutdownEventArgs args = null; - - while (cause != null && args == null) - { - if (cause is OperationInterruptedException exception) - { - args = exception.ShutdownReason; - } - - cause = cause.InnerException; - } - - if (args == null) - { - return false; - } - - return args.ClassId == ExchangeClassId && args.MethodId == DeclareMethodId && args.ReplyCode == CommandInvalid; - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/SimpleConnection.cs b/src/Messaging/src/RabbitMQ/Connection/SimpleConnection.cs deleted file mode 100644 index 24972f5388..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/SimpleConnection.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using RabbitMQ.Client.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class SimpleConnection : IConnection, RC.NetworkConnection -{ - private readonly int _closeTimeout; - - public int LocalPort => Connection.LocalPort; - - public RC.IConnection Connection { get; } - - public bool IsOpen => Connection.IsOpen; - - public string Address => Connection.Endpoint.HostName; - - public int Port => Connection.Endpoint.Port; - - public int RemotePort => Connection.Endpoint.Port; - - public SimpleConnection(RC.IConnection connection, int closeTimeout, ILogger logger = null) - { - Connection = connection; - _closeTimeout = closeTimeout; - } - - public RC.IModel CreateChannel(bool transactional = false) - { - try - { - RC.IModel result = Connection.CreateModel(); - - if (result == null) - { - throw new RabbitResourceNotAvailableException("The channelMax limit is reached. Try later."); - } - - if (transactional) - { - result.TxSelect(); - } - - return result; - } - catch (Exception e) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - public void Close() - { - try - { - // let the physical close time out if necessary - Connection.Close(_closeTimeout); - } - catch (AlreadyClosedException) - { - // Ignore - } - catch (Exception e) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - Connection?.Dispose(); - } - } - - public void AddBlockedListener(IBlockedListener listener) - { - Connection.ConnectionBlocked += listener.HandleBlocked; - Connection.ConnectionUnblocked += listener.HandleUnblocked; - } - - public bool RemoveBlockedListener(IBlockedListener listener) - { - Connection.ConnectionBlocked -= listener.HandleBlocked; - Connection.ConnectionUnblocked -= listener.HandleUnblocked; - return true; - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/SimpleResourceHolder.cs b/src/Messaging/src/RabbitMQ/Connection/SimpleResourceHolder.cs deleted file mode 100644 index 84e1390fee..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/SimpleResourceHolder.cs +++ /dev/null @@ -1,190 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.ObjectModel; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public static class SimpleResourceHolder -{ - private const string ForKey = "] for key ["; - - private const string BoundToThread = "] bound to thread ["; - - private static readonly AsyncLocal> Resources = new(); - - private static readonly AsyncLocal>> Stack = new(); - - private static readonly Dictionary Empty = new(); - - public static IDictionary GetResources() - { - Dictionary map = Resources.Value; - - if (map != null) - { - return new ReadOnlyDictionary(map); - } - - return Empty; - } - - public static bool Has(object key, ILogger logger = null) - { - object value = DoGet(key); - return value != null; - } - - public static object Get(object key, ILogger logger = null) - { - object value = DoGet(key); - - if (value != null) - { - logger?.LogTrace("Retrieved value [{value}]] for key [{key}] bound to thread [{thread}]", value, key, Thread.CurrentThread.ManagedThreadId); - } - - return value; - } - - public static void Bind(object key, object value, ILogger logger = null) - { - ArgumentGuard.NotNull(value); - - Dictionary map = Resources.Value; - - // set ThreadLocal Map if none found - if (map == null) - { - map = new Dictionary(); - Resources.Value = map; - } - - map.TryGetValue(key, out object oldValue); - map[key] = value; - - if (oldValue != null) - { - throw new InvalidOperationException($"Already value [{oldValue}{ForKey}{key}{BoundToThread}{Thread.CurrentThread.ManagedThreadId}]"); - } - - logger?.LogTrace("Bound value [{value}] for key [{key}] to thread [{thread}]", value, key, Thread.CurrentThread.ManagedThreadId); - } - - public static void Push(object key, object value, ILogger logger = null) - { - object currentValue = Get(key); - - if (currentValue == null) - { - Bind(key, value); - } - else - { - Dictionary> dict = Stack.Value; - - if (dict == null) - { - dict = new Dictionary>(); - Stack.Value = dict; - } - - if (!dict.TryGetValue(key, out Stack stack)) - { - stack = new Stack(); - dict.Add(key, stack); - } - - stack.Push(currentValue); - Unbind(key); - Bind(key, value); - } - } - - public static object Pop(object key, ILogger logger = null) - { - object popped = Unbind(key); - Dictionary> dict = Stack.Value; - - if (dict != null) - { - dict.TryGetValue(key, out Stack deque); - - if (deque != null && deque.Count > 0) - { - object previousValue = deque.Pop(); - - if (previousValue != null) - { - Bind(key, previousValue); - } - - if (deque.Count == 0) - { - Stack.Value = null; - } - } - } - - return popped; - } - - public static object Unbind(object key, ILogger logger = null) - { - object value = UnbindIfPossible(key); - - if (value == null) - { - throw new InvalidOperationException($"No value for key [{key}{BoundToThread}{Thread.CurrentThread.ManagedThreadId}]"); - } - - return value; - } - - public static object UnbindIfPossible(object key, ILogger logger = null) - { - Dictionary map = Resources.Value; - - if (map == null) - { - return null; - } - - map.Remove(key, out object value); - - // Remove entire ThreadLocal if empty... - if (map.Count == 0) - { - Resources.Value = null; - } - - if (value != null) - { - logger?.LogTrace("Removed value [{value}] for key [{key}] from thread [{thread}]", value, key, Thread.CurrentThread.ManagedThreadId); - } - - return value; - } - - public static void Clear() - { - Resources.Value = null; - Stack.Value = null; - } - - private static object DoGet(object actualKey) - { - Dictionary map = Resources.Value; - - if (map == null) - { - return null; - } - - map.TryGetValue(actualKey, out object result); - return result; - } -} diff --git a/src/Messaging/src/RabbitMQ/Connection/SimpleRoutingConnectionFactory.cs b/src/Messaging/src/RabbitMQ/Connection/SimpleRoutingConnectionFactory.cs deleted file mode 100644 index 4e715e099d..0000000000 --- a/src/Messaging/src/RabbitMQ/Connection/SimpleRoutingConnectionFactory.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Connection; - -public class SimpleRoutingConnectionFactory : AbstractRoutingConnectionFactory -{ - public override string ServiceName { get; set; } = "SimpleRoutingConnectionFactory"; - - public override object DetermineCurrentLookupKey() - { - return SimpleResourceHolder.Get(this); - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/AcknowledgeMode.cs b/src/Messaging/src/RabbitMQ/Core/AcknowledgeMode.cs deleted file mode 100644 index 4af229adc5..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/AcknowledgeMode.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public enum AcknowledgeMode -{ - /// - /// No acks. - /// - None, - - /// - /// Manual acks - user must ack/nack via a channel aware listener. - /// - Manual, - - /// - /// The container will issue the ack/nack based on whether the listener returns normally, or throws an exception. - /// - Auto -} diff --git a/src/Messaging/src/RabbitMQ/Core/AcknowledgeModeExtensions.cs b/src/Messaging/src/RabbitMQ/Core/AcknowledgeModeExtensions.cs deleted file mode 100644 index 83651225a4..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/AcknowledgeModeExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public static class AcknowledgeModeExtensions -{ - public static bool IsTransactionAllowed(this AcknowledgeMode mode) - { - return mode == AcknowledgeMode.Auto || mode == AcknowledgeMode.Manual; - } - - public static bool IsAutoAck(this AcknowledgeMode mode) - { - return mode == AcknowledgeMode.None; - } - - public static bool IsManual(this AcknowledgeMode mode) - { - return mode == AcknowledgeMode.Manual; - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/Address.cs b/src/Messaging/src/RabbitMQ/Core/Address.cs deleted file mode 100644 index 6ed08fed54..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/Address.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using System.Text.RegularExpressions; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class Address -{ - private const string AddressPattern = "^(?:.*://)?([^/]*)/?(.*)$"; - public const string AmqRabbitMQReplyTo = "amq.rabbitmq.reply-to"; - - public string ExchangeName { get; } - - public string RoutingKey { get; } - - public Address(string exchangeName, string routingKey) - { - ExchangeName = exchangeName; - RoutingKey = routingKey; - } - - public Address(string address) - { - if (string.IsNullOrEmpty(address)) - { - ExchangeName = string.Empty; - RoutingKey = string.Empty; - } - else if (address.StartsWith(AmqRabbitMQReplyTo, StringComparison.Ordinal)) - { - RoutingKey = address; - ExchangeName = string.Empty; - } - else if (address.LastIndexOf('/') <= 0) - { - var regex = new Regex("/"); - RoutingKey = regex.Replace(address, string.Empty, 1); - ExchangeName = string.Empty; - } - else - { - Match matchFound = Regex.Match(address, AddressPattern); - - if (matchFound.Success) - { - GroupCollection groups = matchFound.Groups; - ExchangeName = groups[1].Value; - RoutingKey = groups[2].Value; - } - else - { - ExchangeName = string.Empty; - RoutingKey = address; - } - } - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not Address other || GetType() != obj.GetType()) - { - return false; - } - - return ExchangeName == other.ExchangeName && RoutingKey == other.RoutingKey; - } - - public override int GetHashCode() - { - return HashCode.Combine(ExchangeName, RoutingKey); - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(ExchangeName).Append('/'); - - if (!string.IsNullOrEmpty(RoutingKey)) - { - sb.Append(RoutingKey); - } - - return sb.ToString(); - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/Base64UrlNamingStrategy.cs b/src/Messaging/src/RabbitMQ/Core/Base64UrlNamingStrategy.cs deleted file mode 100644 index d1d49272f7..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/Base64UrlNamingStrategy.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class Base64UrlNamingStrategy : INamingStrategy -{ - public static readonly Base64UrlNamingStrategy Default = new(); - - public string Prefix { get; } - - public Base64UrlNamingStrategy() - : this("spring.gen-") - { - } - - public Base64UrlNamingStrategy(string prefix) - { - ArgumentGuard.NotNull(prefix); - - Prefix = prefix; - } - - public string GenerateName() - { - var uuid = Guid.NewGuid(); - string converted = Convert.ToBase64String(uuid.ToByteArray()).Replace('+', '-').Replace('/', '_').Replace("=", string.Empty, StringComparison.Ordinal); - return Prefix + converted; - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/BatchingRabbitTemplate.cs b/src/Messaging/src/RabbitMQ/Core/BatchingRabbitTemplate.cs deleted file mode 100644 index 242ca00e0b..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/BatchingRabbitTemplate.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class BatchingRabbitTemplate : RabbitTemplate -{ - private readonly IBatchingStrategy _batchingStrategy; - private readonly object _batchLock = new(); - private CancellationTokenSource _cancellationTokenSource; - private Task _scheduledTask; - - public BatchingRabbitTemplate(IOptionsMonitor optionsMonitor, IConnectionFactory connectionFactory, ISmartMessageConverter messageConverter, - IBatchingStrategy batchingStrategy, ILogger logger = null) - : base(optionsMonitor, connectionFactory, messageConverter, logger) - { - _batchingStrategy = batchingStrategy; - } - - public BatchingRabbitTemplate(RabbitOptions options, IConnectionFactory connectionFactory, ISmartMessageConverter messageConverter, - IBatchingStrategy batchingStrategy, ILogger logger = null) - : base(options, connectionFactory, messageConverter, logger) - { - _batchingStrategy = batchingStrategy; - } - - public BatchingRabbitTemplate(IOptionsMonitor optionsMonitor, IConnectionFactory connectionFactory, IBatchingStrategy batchingStrategy, - ILogger logger = null) - : base(optionsMonitor, connectionFactory, logger) - { - _batchingStrategy = batchingStrategy; - } - - public BatchingRabbitTemplate(RabbitOptions options, IConnectionFactory connectionFactory, IBatchingStrategy batchingStrategy, ILogger logger = null) - : base(options, connectionFactory, logger) - { - _batchingStrategy = batchingStrategy; - } - - public BatchingRabbitTemplate(IConnectionFactory connectionFactory, IBatchingStrategy batchingStrategy) - : base(connectionFactory) - { - _batchingStrategy = batchingStrategy; - } - - public BatchingRabbitTemplate(IBatchingStrategy batchingStrategy) - { - _batchingStrategy = batchingStrategy; - } - - public override void Send(string exchange, string routingKey, IMessage message, CorrelationData correlationData) - { - lock (_batchLock) - { - if (correlationData != null) - { - Logger?.LogDebug("Cannot use batching with correlation data"); - base.Send(exchange, routingKey, message, correlationData); - } - else - { - MessageBatch? batch = _batchingStrategy.AddToBatch(exchange, routingKey, message); - - if (batch != null) - { - if (_scheduledTask != null) - { - _cancellationTokenSource.Cancel(false); - _scheduledTask = null; - } - - base.Send(batch.Value.Exchange, batch.Value.RoutingKey, batch.Value.Message, null); - } - - DateTime? next = _batchingStrategy.NextRelease(); - - if (next != null && _scheduledTask == null) - { - _cancellationTokenSource = new CancellationTokenSource(); - TimeSpan delay = next.Value - DateTime.UtcNow; - _scheduledTask = Task.Run(() => ReleaseBatchesAsync(delay, _cancellationTokenSource.Token), _cancellationTokenSource.Token); - } - } - } - } - - public void Flush() - { - FlushAsync(default).GetAwaiter().GetResult(); - } - - public async Task FlushAsync(CancellationToken cancellationToken) - { - await ReleaseBatchesAsync(TimeSpan.Zero, cancellationToken); - } - - private async Task ReleaseBatchesAsync(TimeSpan delay, CancellationToken cancellationToken) - { - await Task.Delay(delay, cancellationToken); - - if (cancellationToken.IsCancellationRequested) - { - return; - } - - lock (_batchLock) - { - foreach (MessageBatch batch in _batchingStrategy.ReleaseBatches()) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - base.Send(batch.Exchange, batch.RoutingKey, batch.Message, null); - } - - _scheduledTask = null; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/DeclarationExceptionEvent.cs b/src/Messaging/src/RabbitMQ/Core/DeclarationExceptionEvent.cs deleted file mode 100644 index a78c4ca2df..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/DeclarationExceptionEvent.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Configuration; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class DeclarationExceptionEvent : RabbitAdminEvent -{ - public IDeclarable Declarable { get; } - - public Exception Exception { get; } - - public DeclarationExceptionEvent(object source, IDeclarable declarable, Exception exception) - : base(source) - { - Declarable = declarable; - Exception = exception; - } - - public override string ToString() - { - return $"DeclarationExceptionEvent [declarable={Declarable}, throwable={Exception}, source={Source}]"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/Envelope.cs b/src/Messaging/src/RabbitMQ/Core/Envelope.cs deleted file mode 100644 index 51075decc0..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/Envelope.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class Envelope -{ - public ulong DeliveryTag { get; } - - public bool Redeliver { get; } - - public string Exchange { get; } - - public string RoutingKey { get; } - - public Envelope(ulong deliveryTag, bool redeliver, string exchange, string routingKey) - { - DeliveryTag = deliveryTag; - Redeliver = redeliver; - Exchange = exchange; - RoutingKey = routingKey; - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/GuidNamingStrategy.cs b/src/Messaging/src/RabbitMQ/Core/GuidNamingStrategy.cs deleted file mode 100644 index 0859a8c785..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/GuidNamingStrategy.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class GuidNamingStrategy : INamingStrategy -{ - public static readonly GuidNamingStrategy Default = new(); - - public string GenerateName() - { - return Guid.NewGuid().ToString(); - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/IChannelCallback.cs b/src/Messaging/src/RabbitMQ/Core/IChannelCallback.cs deleted file mode 100644 index ca1dd3b842..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/IChannelCallback.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public interface IChannelCallback -{ - T DoInRabbit(RC.IModel channel); -} diff --git a/src/Messaging/src/RabbitMQ/Core/IConditionalExceptionLogger.cs b/src/Messaging/src/RabbitMQ/Core/IConditionalExceptionLogger.cs deleted file mode 100644 index a2425499d3..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/IConditionalExceptionLogger.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public interface IConditionalExceptionLogger -{ - void Log(ILogger logger, string message, object cause); -} diff --git a/src/Messaging/src/RabbitMQ/Core/IConsumerTagStrategy.cs b/src/Messaging/src/RabbitMQ/Core/IConsumerTagStrategy.cs deleted file mode 100644 index c2f4f318f8..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/IConsumerTagStrategy.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public interface IConsumerTagStrategy : IServiceNameAware -{ - string CreateConsumerTag(string queue); -} diff --git a/src/Messaging/src/RabbitMQ/Core/ICorrelationDataPostProcessor.cs b/src/Messaging/src/RabbitMQ/Core/ICorrelationDataPostProcessor.cs deleted file mode 100644 index aeadb67ee7..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/ICorrelationDataPostProcessor.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Connection; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public interface ICorrelationDataPostProcessor -{ - CorrelationData PostProcess(IMessage message, CorrelationData correlationData); -} diff --git a/src/Messaging/src/RabbitMQ/Core/IMessagePostProcessor.cs b/src/Messaging/src/RabbitMQ/Core/IMessagePostProcessor.cs deleted file mode 100644 index 414f619a45..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/IMessagePostProcessor.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Connection; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public interface IMessagePostProcessor : Messaging.Core.IMessagePostProcessor -{ - IMessage PostProcessMessage(IMessage message, CorrelationData correlation); -} diff --git a/src/Messaging/src/RabbitMQ/Core/INamingStrategy.cs b/src/Messaging/src/RabbitMQ/Core/INamingStrategy.cs deleted file mode 100644 index f58b3ba6e4..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/INamingStrategy.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public interface INamingStrategy -{ - string GenerateName(); -} diff --git a/src/Messaging/src/RabbitMQ/Core/IRabbitAdmin.cs b/src/Messaging/src/RabbitMQ/Core/IRabbitAdmin.cs deleted file mode 100644 index 9a3bf39816..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/IRabbitAdmin.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; -using Steeltoe.Messaging.RabbitMQ.Configuration; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public interface IRabbitAdmin : IServiceNameAware -{ - void DeclareExchange(IExchange exchange); - - bool DeleteExchange(string exchangeName); - - IQueue DeclareQueue(); - - string DeclareQueue(IQueue queue); - - bool DeleteQueue(string queueName); - - void DeleteQueue(string queueName, bool unused, bool empty); - - void PurgeQueue(string queueName, bool noWait); - - uint PurgeQueue(string queueName); - - void DeclareBinding(IBinding binding); - - void RemoveBinding(IBinding binding); - - Dictionary GetQueueProperties(string queueName); - - QueueInformation GetQueueInfo(string queueName); - - void Initialize(); -} diff --git a/src/Messaging/src/RabbitMQ/Core/IRabbitTemplate.cs b/src/Messaging/src/RabbitMQ/Core/IRabbitTemplate.cs deleted file mode 100644 index 1dca1f8126..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/IRabbitTemplate.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client.Events; -using Steeltoe.Common.Services; -using Steeltoe.Messaging.RabbitMQ.Connection; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public interface IRabbitTemplate : IServiceNameAware -{ - IConnectionFactory ConnectionFactory { get; } - - T Execute(Func channelCallback); - - T Invoke(Func operationsCallback) - { - return Invoke(operationsCallback, null, null); - } - - T Invoke(Func operationsCallback, Action acks, Action nacks); - - bool WaitForConfirms(int timeoutInMilliseconds); - - void WaitForConfirmsOrDie(int timeoutInMilliseconds); - - void Send(string exchange, string routingKey, IMessage message); - - void Send(string exchange, string routingKey, IMessage message, CorrelationData correlationData); - - Task SendAsync(string exchange, string routingKey, IMessage message, CancellationToken cancellationToken = default); - - Task SendAsync(string exchange, string routingKey, IMessage message, CorrelationData correlationData, CancellationToken cancellationToken = default); - - void ConvertAndSend(string exchange, string routingKey, object message, CorrelationData correlationData); - - void ConvertAndSend(object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData); - - void ConvertAndSend(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData); - - void ConvertAndSend(string exchange, string routingKey, object message); - - void ConvertAndSend(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor); - - T ConvertSendAndReceive(object message, CorrelationData correlationData); - - T ConvertSendAndReceive(string exchange, string routingKey, object message, CorrelationData correlationData); - - T ConvertSendAndReceive(object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData); - - T ConvertSendAndReceive(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData); - - T ConvertSendAndReceive(string exchange, string routingKey, object message); - - IMessage Receive(int timeoutInMilliseconds); - - IMessage Receive(string queueName, int timeoutInMilliseconds); - - T ReceiveAndConvert(int timeoutMillis); - - T ReceiveAndConvert(string queueName, int timeoutInMilliseconds); - - bool ReceiveAndReply(Func callback); - - bool ReceiveAndReply(string queueName, Func callback); - - bool ReceiveAndReply(Func callback, string replyExchange, string replyRoutingKey); - - bool ReceiveAndReply(string queueName, Func callback, string replyExchange, string replyRoutingKey); - - bool ReceiveAndReply(Func callback, Func replyToAddressCallback); - - bool ReceiveAndReply(string queueName, Func callback, Func replyToAddressCallback); - - IMessage SendAndReceive(string exchange, string routingKey, IMessage message); - - public interface IOperationsCallback - { - T DoInRabbit(IRabbitTemplate operations); - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/MessageDeliveryMode.cs b/src/Messaging/src/RabbitMQ/Core/MessageDeliveryMode.cs deleted file mode 100644 index e1dcb834ef..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/MessageDeliveryMode.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.ComponentModel; -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -[TypeConverter(typeof(SnakeCaseAllCapsEnumMemberTypeConverter))] -public enum MessageDeliveryMode -{ - NonPersistent = 1, - Persistent = 2 -} diff --git a/src/Messaging/src/RabbitMQ/Core/RabbitAdmin.cs b/src/Messaging/src/RabbitMQ/Core/RabbitAdmin.cs deleted file mode 100644 index de13c1032c..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/RabbitAdmin.cs +++ /dev/null @@ -1,724 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using RabbitMQ.Client.Exceptions; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Retry; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using static Steeltoe.Messaging.RabbitMQ.Connection.CachingConnectionFactory; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class RabbitAdmin : IRabbitAdmin, IConnectionListener -{ - private const int DeclareMaxAttempts = 5; - private const int DeclareInitialRetryInterval = 1000; - private const int DeclareMaxRetryInterval = 5000; - private const double DeclareRetryMultiplier = 2.0d; - private const string DelayedMessageExchange = "x-delayed-message"; - public const string DefaultServiceName = "rabbitAdmin"; - - public const string QueueName = "QUEUE_NAME"; - public const string QueueMessageCount = "QUEUE_MESSAGE_COUNT"; - public const string QueueConsumerCount = "QUEUE_CONSUMER_COUNT"; - - private readonly object _lifecycleMonitor = new(); - private readonly ILogger _logger; - private int _initializing; - - public IApplicationContext ApplicationContext { get; set; } - - public string ServiceName { get; set; } = DefaultServiceName; - - public IConnectionFactory ConnectionFactory { get; set; } - - public RabbitTemplate RabbitTemplate { get; } - - public bool AutoStartup { get; set; } - - public bool IgnoreDeclarationExceptions { get; set; } - - public DeclarationExceptionEvent LastDeclarationExceptionEvent { get; private set; } - - public bool ExplicitDeclarationsOnly { get; set; } - - public bool IsAutoStartup { get; set; } = true; - - public bool IsRunning { get; private set; } - - public RetryTemplate RetryTemplate { get; set; } - - public bool RetryDisabled { get; set; } - - [ActivatorUtilitiesConstructor] - public RabbitAdmin(IApplicationContext applicationContext, IConnectionFactory connectionFactory, ILogger logger = null) - { - ArgumentGuard.NotNull(connectionFactory); - - _logger = logger; - ApplicationContext = applicationContext; - ConnectionFactory = connectionFactory; - RabbitTemplate = new RabbitTemplate(connectionFactory, logger); - DoInitialize(); - } - - public RabbitAdmin(IConnectionFactory connectionFactory, ILogger logger = null) - : this(null, connectionFactory, logger) - { - } - - public RabbitAdmin(RabbitTemplate template, ILogger logger = null) - { - ArgumentGuard.NotNull(template); - - _logger = logger; - RabbitTemplate = template; - - ConnectionFactory = template.ConnectionFactory ?? - throw new ArgumentException($"{nameof(template.ConnectionFactory)} in {nameof(template)} must not be null.", nameof(template)); - - DoInitialize(); - } - - public void DeclareBinding(IBinding binding) - { - try - { - RabbitTemplate.Execute(channel => - { - DeclareBindings(channel, binding); - return null; - }); - } - catch (RabbitException e) - { - LogOrRethrowDeclarationException(binding, "binding", e); - } - } - - public void DeclareExchange(IExchange exchange) - { - try - { - RabbitTemplate.Execute(channel => - { - DeclareExchanges(channel, exchange); - return null; - }); - } - catch (RabbitException e) - { - LogOrRethrowDeclarationException(exchange, "exchange", e); - } - } - - public IQueue DeclareQueue() - { - try - { - RC.QueueDeclareOk declareOk = RabbitTemplate.Execute(channel => RC.IModelExensions.QueueDeclare(channel)); - return new Queue(declareOk.QueueName, false, true, true); - } - catch (RabbitException e) - { - LogOrRethrowDeclarationException(null, "queue", e); - return null; - } - } - - public string DeclareQueue(IQueue queue) - { - try - { - return RabbitTemplate.Execute(channel => - { - List declared = DeclareQueues(channel, queue); - return declared.Count > 0 ? declared[0].QueueName : null; - }); - } - catch (RabbitException e) - { - LogOrRethrowDeclarationException(queue, "queue", e); - return null; - } - } - - public bool DeleteExchange(string exchangeName) - { - return RabbitTemplate.Execute(channel => - { - if (IsDeletingDefaultExchange(exchangeName)) - { - return true; - } - - try - { - channel.ExchangeDelete(exchangeName, false); - } - catch (RabbitMQClientException e) - { - _logger?.LogError(e, "Exception while issuing ExchangeDelete"); - return false; - } - - return true; - }); - } - - public bool DeleteQueue(string queueName) - { - return RabbitTemplate.Execute(channel => - { - try - { - RC.IModelExensions.QueueDelete(channel, queueName); - return true; - } - catch (RabbitMQClientException e) - { - _logger?.LogError(e, "Exception while issuing QueueDelete"); - return false; - } - }); - } - - public void DeleteQueue(string queueName, bool unused, bool empty) - { - RabbitTemplate.Execute(channel => - { - channel.QueueDelete(queueName, unused, empty); - return null; - }); - } - - public QueueInformation GetQueueInfo(string queueName) - { - ArgumentGuard.NotNullOrEmpty(queueName); - - if (queueName.Length > 255) - { - return null; - } - - return RabbitTemplate.Execute(channel => - { - try - { - RC.QueueDeclareOk declareOk = channel.QueueDeclarePassive(queueName); - return new QueueInformation(declareOk.QueueName, declareOk.MessageCount, declareOk.ConsumerCount); - } - catch (RabbitMQClientException e) - { - _logger?.LogError(e, "Exception while fetching Queue properties for '{queueName}'", queueName); - - try - { - if (channel is IChannelProxy proxy) - { - proxy.TargetChannel.Close(); - } - } - catch (Exception ex) - { - _logger?.LogError(ex, "Exception while closing {channel}", channel); - } - - return null; - } - catch (Exception e) - { - _logger?.LogError(e, "Queue '{queueName}' does not exist", queueName); - return null; - } - }); - } - - public Dictionary GetQueueProperties(string queueName) - { - QueueInformation queueInfo = GetQueueInfo(queueName); - - if (queueInfo != null) - { - var props = new Dictionary - { - { QueueName, queueInfo.Name }, - { QueueMessageCount, queueInfo.MessageCount }, - { QueueConsumerCount, queueInfo.ConsumerCount } - }; - - return props; - } - - return null; - } - - public void PurgeQueue(string queueName, bool noWait) - { - if (noWait) - { - Task.Run(() => PurgeQueue(queueName)); - } - else - { - PurgeQueue(queueName); - } - } - - public uint PurgeQueue(string queueName) - { - return RabbitTemplate.Execute(channel => - { - uint queuePurged = channel.QueuePurge(queueName); - _logger?.LogDebug("Purged queue: {queueName} : {result}", queueName, queuePurged); - return queuePurged; - }); - } - - public void RemoveBinding(IBinding binding) - { - RabbitTemplate.Execute(channel => - { - if (binding.IsDestinationQueue) - { - if (IsRemovingImplicitQueueBinding(binding)) - { - return null; - } - - channel.QueueUnbind(binding.Destination, binding.Exchange, binding.RoutingKey, binding.Arguments); - } - else - { - channel.ExchangeUnbind(binding.Destination, binding.Exchange, binding.RoutingKey, binding.Arguments); - } - - return null; - }); - } - - public void OnCreate(IConnection connection) - { - _logger?.LogDebug("OnCreate for connection: {connection}", connection); - - if (Interlocked.CompareExchange(ref _initializing, 1, 0) != 0) - { - return; - } - - try - { - /* - * ...but it is possible for this to happen twice in the same ConnectionFactory (if more than - * one concurrent Connection is allowed). It's idempotent, so no big deal (a bit of network - * chatter). In fact it might even be a good thing: exclusive queues only make sense if they are - * declared for every connection. If anyone has a problem with it: use auto-startup="false". - */ - if (RetryTemplate != null && !RetryDisabled) - { - RetryTemplate.Execute(c => - { - _logger?.LogTrace($"Rabbit Admin::Initialize(). Context: {c}"); - Initialize(); - }); - } - else - { - Initialize(); - } - } - finally - { - Interlocked.CompareExchange(ref _initializing, 0, 1); - } - } - - public void OnClose(IConnection connection) - { - _logger?.LogDebug("OnClose for connection: {connection}", connection); - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - _logger?.LogDebug("OnShutDown for connection: {args}", args); - } - - public void Initialize() - { - _logger?.LogDebug("Initializing declarations"); - - if (ApplicationContext == null) - { - _logger?.LogDebug("No ApplicationContext has been set, cannot auto-declare Exchanges, Queues, and Bindings"); - return; - } - - List contextExchanges = ApplicationContext.GetServices().ToList(); - List contextQueues = ApplicationContext.GetServices().ToList(); - List contextBindings = ApplicationContext.GetServices().ToList(); - List customizers = ApplicationContext.GetServices().ToList(); - - ProcessDeclarables(contextExchanges, contextQueues, contextBindings); - - List exchanges = FilterDeclarables(contextExchanges, customizers); - List queues = FilterDeclarables(contextQueues, customizers); - List bindings = FilterDeclarables(contextBindings, customizers); - - foreach (IExchange exchange in exchanges) - { - if (!exchange.IsDurable || exchange.IsAutoDelete) - { - _logger?.LogInformation( - "Auto-declaring a non-durable or auto-delete Exchange ({exchange}), durable:{durable}, auto-delete:{autoDelete}. " + - "It will be deleted by the broker if it shuts down, and can be redeclared by closing and reopening the connection.", exchange.ExchangeName, - exchange.IsDurable, exchange.IsAutoDelete); - } - } - - foreach (IQueue queue in queues) - { - if (!queue.IsDurable || queue.IsAutoDelete || queue.IsExclusive) - { - _logger?.LogInformation( - "Auto-declaring a non-durable, auto-delete, or exclusive Queue ({queueName}) durable:{durable}, auto-delete:{autoDelete}, exclusive:{exclusive}." + - "It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.", - queue.QueueName, queue.IsDurable, queue.IsAutoDelete, queue.IsExclusive); - } - } - - if (exchanges.Count == 0 && queues.Count == 0 && bindings.Count == 0) - { - _logger?.LogDebug("Nothing to declare"); - return; - } - - RabbitTemplate.Execute(channel => - { - DeclareExchanges(channel, exchanges.ToArray()); - DeclareQueues(channel, queues.ToArray()); - DeclareBindings(channel, bindings.ToArray()); - return null; - }); - - _logger?.LogDebug("Declarations finished"); - } - - private void ProcessDeclarables(List contextExchanges, List contextQueues, List contextBindings) - { - IEnumerable declarables = ApplicationContext.GetServices(); - - foreach (Declarables declarable in declarables) - { - foreach (IDeclarable d in declarable.DeclarableList) - { - switch (d) - { - case IExchange exD: - contextExchanges.Add(exD); - break; - case IQueue qD: - contextQueues.Add(qD); - break; - case IBinding bD: - contextBindings.Add(bD); - break; - } - } - } - } - - private List FilterDeclarables(IEnumerable declarables, IEnumerable customizers) - where T : IDeclarable - { - var results = new List(); - int customizerCounts = customizers.Count(); - - foreach (T dec in declarables) - { - if (ShouldDeclare(dec)) - { - if (customizerCounts == 0) - { - results.Add(dec); - } - else - { - IDeclarable customized = dec; - - foreach (IDeclarableCustomizer customizer in customizers) - { - customized = customizer.Apply(customized); - } - - if (customized != null) - { - results.Add((T)customized); - } - } - } - } - - return results; - } - - private bool ShouldDeclare(T declarable) - where T : IDeclarable - { - if (!declarable.ShouldDeclare) - { - return false; - } - - if (declarable.DeclaringAdmins.Count == 0 && !ExplicitDeclarationsOnly) - { - return true; - } - - if (declarable.DeclaringAdmins.Contains(this)) - { - return true; - } - - return ServiceName != null && declarable.DeclaringAdmins.Contains(ServiceName); - } - - private void DeclareExchanges(RC.IModel channel, params IExchange[] exchanges) - { - foreach (IExchange exchange in exchanges) - { - _logger?.LogDebug("Declaring exchange '{exchange}'", exchange.ExchangeName); - - if (!IsDeclaringDefaultExchange(exchange)) - { - try - { - if (exchange.IsDelayed) - { - Dictionary arguments = exchange.Arguments; - arguments = arguments == null ? new Dictionary() : new Dictionary(arguments); - - arguments["x-delayed-type"] = exchange.Type; - - channel.ExchangeDeclare(exchange.ExchangeName, DelayedMessageExchange, exchange.IsDurable, exchange.IsAutoDelete, arguments); - } - else - { - channel.ExchangeDeclare(exchange.ExchangeName, exchange.Type, exchange.IsDurable, exchange.IsAutoDelete, exchange.Arguments); - } - } - catch (Exception e) - { - LogOrRethrowDeclarationException(exchange, "exchange", e); - } - } - } - } - - private List DeclareQueues(RC.IModel channel, params IQueue[] queues) - { - var declareOks = new List(queues.Length); - - foreach (IQueue queue in queues) - { - if (!queue.QueueName.StartsWith("amq.", StringComparison.Ordinal)) - { - _logger?.LogDebug("Declaring Queue '{queueName}'", queue.QueueName); - - if (queue.QueueName.Length > 255) - { - throw new ArgumentException("Queue names cannot be longer than 255 characters.", nameof(queues)); - } - - try - { - try - { - RC.QueueDeclareOk declareOk = - channel.QueueDeclare(queue.QueueName, queue.IsDurable, queue.IsExclusive, queue.IsAutoDelete, queue.Arguments); - - if (!string.IsNullOrEmpty(declareOk.QueueName)) - { - queue.ActualName = declareOk.QueueName; - } - - declareOks.Add(declareOk); - } - catch (ArgumentException) - { - CloseChannelAfterIllegalArg(channel, queue); - throw; - } - } - catch (Exception e) - { - LogOrRethrowDeclarationException(queue, "queue", e); - } - } - else - { - _logger?.LogDebug("Queue with name: {queueName} that starts with 'amq.' cannot be declared.", queue.QueueName); - } - } - - return declareOks; - } - - private void CloseChannelAfterIllegalArg(RC.IModel channel, IQueue queue) - { - _logger?.LogError("Exception while declaring queue'{queueName}'", queue.QueueName); - - try - { - if (channel is IChannelProxy proxy) - { - proxy.TargetChannel.Close(); - } - } - catch (Exception exception) - { - _logger?.LogError(exception, "Failed to close {channel} after illegal argument", channel); - } - } - - private void DeclareBindings(RC.IModel channel, params IBinding[] bindings) - { - foreach (IBinding binding in bindings) - { - _logger?.LogDebug("Binding destination [{destination} ({type})] to exchange [{exchange}] with routing key [{routingKey}]", binding.Destination, - binding.Type, binding.Exchange, binding.RoutingKey); - - try - { - if (binding.IsDestinationQueue) - { - if (!IsDeclaringImplicitQueueBinding(binding)) - { - channel.QueueBind(binding.Destination, binding.Exchange, binding.RoutingKey, binding.Arguments); - } - } - else - { - channel.ExchangeBind(binding.Destination, binding.Exchange, binding.RoutingKey, binding.Arguments); - } - } - catch (Exception e) - { - LogOrRethrowDeclarationException(binding, "binding", e); - } - } - } - - private void LogOrRethrowDeclarationException(IDeclarable element, string elementType, Exception exception) - { - PublishDeclarationExceptionEvent(element, exception); - - if (IgnoreDeclarationExceptions || (element != null && element.IgnoreDeclarationExceptions)) - { - string elementText = element == null ? "broker-generated" : element.ToString(); - - _logger?.LogDebug(exception, "Failed to declare {elementType}: {elementText}, continuing...", elementType, elementText); - - Exception cause = exception.InnerException ?? exception; - - _logger?.LogWarning(exception, "Failed to declare {elementType}: {element}, continuing... {cause}", elementType, elementText, cause); - } - else - { - throw exception; - } - } - - private void PublishDeclarationExceptionEvent(IDeclarable element, Exception exception) - { - var ev = new DeclarationExceptionEvent(this, element, exception); - LastDeclarationExceptionEvent = ev; - } - - private bool IsDeclaringDefaultExchange(IExchange exchange) - { - if (IsDefaultExchange(exchange.ExchangeName)) - { - _logger?.LogDebug("Default exchange is pre-declared by server."); - return true; - } - - return false; - } - - private bool IsDeletingDefaultExchange(string exchangeName) - { - if (IsDefaultExchange(exchangeName)) - { - _logger?.LogDebug("Default exchange cannot be deleted."); - return true; - } - - return false; - } - - private bool IsDefaultExchange(string exchangeName) - { - return exchangeName == string.Empty; - } - - private bool IsDeclaringImplicitQueueBinding(IBinding binding) - { - if (IsImplicitQueueBinding(binding)) - { - _logger?.LogDebug("The default exchange is implicitly bound to every queue, with a routing key equal to the queue name."); - return true; - } - - return false; - } - - private bool IsRemovingImplicitQueueBinding(IBinding binding) - { - if (IsImplicitQueueBinding(binding)) - { - _logger?.LogDebug("Cannot remove implicit default exchange binding to queue."); - return true; - } - - return false; - } - - private bool IsImplicitQueueBinding(IBinding binding) - { - return IsDefaultExchange(binding.Exchange) && binding.Destination == binding.RoutingKey; - } - - private void DoInitialize() - { - lock (_lifecycleMonitor) - { - if (IsRunning || !IsAutoStartup) - { - return; - } - - if (RetryTemplate == null && !RetryDisabled) - { - RetryTemplate = new PollyRetryTemplate(new Dictionary(), DeclareMaxAttempts, true, DeclareInitialRetryInterval, - DeclareMaxRetryInterval, DeclareRetryMultiplier, _logger); - } - - if (ConnectionFactory is CachingConnectionFactory factory && factory.CacheMode == CachingMode.Connection) - { - _logger?.LogWarning("RabbitAdmin auto declaration is not supported with CacheMode.CONNECTION"); - return; - } - - ConnectionFactory.AddConnectionListener(this); - IsRunning = true; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/RabbitAdminEvent.cs b/src/Messaging/src/RabbitMQ/Core/RabbitAdminEvent.cs deleted file mode 100644 index d3bd33ff88..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/RabbitAdminEvent.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class RabbitAdminEvent -{ - public object Source { get; } - - protected RabbitAdminEvent(object source) - { - Source = source; - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/RabbitDestination.cs b/src/Messaging/src/RabbitMQ/Core/RabbitDestination.cs deleted file mode 100644 index 89220a0581..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/RabbitDestination.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class RabbitDestination -{ - public string QueueName { get; } - - public string RoutingKey => QueueName; - - public string ExchangeName { get; } - - public RabbitDestination(string queueName) - { - QueueName = queueName; - } - - public RabbitDestination(string exchangeName, string routingKey) - { - ExchangeName = exchangeName; - QueueName = routingKey; - } - - private static (string ExchangeName, string RoutingKey) ParseDestination(string destination) - { - string[] split = destination.Split('/'); - - if (split.Length >= 2) - { - return (split[0], split[1]); - } - - return (string.Empty, split[0]); - } - - public static implicit operator string(RabbitDestination destination) - { - if (string.IsNullOrEmpty(destination.ExchangeName)) - { - return destination.QueueName; - } - - return $"{destination.ExchangeName}/{destination.QueueName}"; - } - - public static implicit operator RabbitDestination(string destination) - { - if (string.IsNullOrEmpty(destination)) - { - return new RabbitDestination(string.Empty, string.Empty); - } - - (string ExchangeName, string RoutingKey) result = ParseDestination(destination); - return new RabbitDestination(result.ExchangeName, result.RoutingKey); - } -} diff --git a/src/Messaging/src/RabbitMQ/Core/RabbitTemplate.cs b/src/Messaging/src/RabbitMQ/Core/RabbitTemplate.cs deleted file mode 100644 index a6f34beb22..0000000000 --- a/src/Messaging/src/RabbitMQ/Core/RabbitTemplate.cs +++ /dev/null @@ -1,2798 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Globalization; -using System.Text; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using RabbitMQ.Client.Events; -using RabbitMQ.Client.Impl; -using Steeltoe.Common; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Retry; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.Support; -using RC = RabbitMQ.Client; -using SimpleMessageConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter.SimpleMessageConverter; - -namespace Steeltoe.Messaging.RabbitMQ.Core; - -public class RabbitTemplate - : AbstractMessagingTemplate, IRabbitTemplate, IMessageListener, IListenerContainerAware, IPublisherCallbackChannel.IListener, IDisposable -{ - private const string ReturnCorrelationKey = "spring_request_return_correlation"; - private const string DefaultExchange = ""; - private const string DefaultRoutingKey = ""; - private const int DefaultReplyTimeout = 5000; - private const int DefaultConsumeTimeout = 10000; - public const string DefaultServiceName = "rabbitTemplate"; - - private static readonly SpelExpressionParser Parser = new(); - - private readonly RabbitOptions _options; - - internal readonly object Lock = new(); - internal readonly ConcurrentDictionary PublisherConfirmChannels = new(); - internal readonly ConcurrentDictionary ReplyHolder = new(); - internal readonly Dictionary DirectReplyToContainers = new(); - internal readonly AsyncLocal DedicatedChannels = new(); - internal readonly IOptionsMonitor OptionsMonitor; - - protected readonly ILogger Logger; - private int _activeTemplateCallbacks; - private int _messageTagProvider; - private int _containerInstance; - private bool _isListener; - private bool? _confirmsOrReturnsCapable; - private bool _publisherConfirms; - private string _replyAddress; - internal bool EvaluatedFastReplyTo; - internal bool UsingFastReplyTo; - - protected internal RabbitOptions Options - { - get - { - if (OptionsMonitor != null) - { - return OptionsMonitor.CurrentValue; - } - - return _options; - } - } - - public virtual IConnectionFactory ConnectionFactory { get; set; } - - public virtual bool IsChannelTransacted { get; set; } - - public virtual string RoutingKey - { - get => DefaultSendDestination.RoutingKey; - set => DefaultSendDestination = new RabbitDestination(DefaultSendDestination.ExchangeName, value); - } - - public virtual string Exchange - { - get => DefaultSendDestination.ExchangeName; - set => DefaultSendDestination = new RabbitDestination(value, DefaultSendDestination.RoutingKey); - } - - public virtual string DefaultReceiveQueue - { - get => DefaultReceiveDestination?.QueueName; - set => DefaultReceiveDestination = new RabbitDestination(value); - } - - public virtual AcknowledgeMode ContainerAckMode { get; set; } - - public virtual Encoding Encoding { get; set; } = EncodingUtils.Utf8; - - public virtual string ReplyAddress - { - get => _replyAddress; - set - { - EvaluatedFastReplyTo = false; - _replyAddress = value; - } - } - - public virtual int ReceiveTimeout { get; set; } - - public virtual int ReplyTimeout { get; set; } = DefaultReplyTimeout; - - public virtual IMessageHeadersConverter MessagePropertiesConverter { get; set; } = new DefaultMessageHeadersConverter(); - - public virtual IConfirmCallback ConfirmCallback { get; set; } - - public virtual IReturnCallback ReturnCallback { get; set; } - - public virtual bool Mandatory - { - get => MandatoryExpression.GetValue(); - set => MandatoryExpression = new ValueExpression(value); - } - - public virtual IExpression MandatoryExpression { get; set; } = new ValueExpression(false); - - public virtual string MandatoryExpressionString - { - get => MandatoryExpression?.ToString(); - set - { - ArgumentGuard.NotNull(value); - - MandatoryExpression = Parser.ParseExpression(value); - } - } - - public virtual IExpression SendConnectionFactorySelectorExpression { get; set; } - - public virtual IExpression ReceiveConnectionFactorySelectorExpression { get; set; } - - public virtual string CorrelationKey { get; set; } - - public virtual IEvaluationContext EvaluationContext { get; set; } = new StandardEvaluationContext(); - - public virtual IRetryOperation RetryTemplate { get; set; } - - public virtual IRecoveryCallback RecoveryCallback { get; set; } - - public virtual IList BeforePublishPostProcessors { get; internal set; } - - public virtual IList AfterReceivePostProcessors { get; internal set; } - - public virtual ICorrelationDataPostProcessor CorrelationDataPostProcessor { get; set; } - - public virtual bool UseTemporaryReplyQueues { get; set; } - - public virtual bool UseDirectReplyToContainer { get; set; } = true; - - public virtual IExpression UserIdExpression { get; set; } - - public virtual string UserIdExpressionString - { - get => UserIdExpression?.ToString(); - set => UserIdExpression = Parser.ParseExpression(value); - } - - public virtual string ServiceName { get; set; } = DefaultServiceName; - - public virtual bool UserCorrelationId { get; set; } - - public virtual bool UsePublisherConnection { get; set; } - - public virtual bool NoLocalReplyConsumer { get; set; } - - public virtual IErrorHandler ReplyErrorHandler { get; set; } - - public virtual bool IsRunning - { - get - { - lock (DirectReplyToContainers) - { - return DirectReplyToContainers.Values.Any(c => c.IsRunning); - } - } - } - - public virtual string Uuid { get; } = Guid.NewGuid().ToString(); - - public virtual bool IsConfirmListener => ConfirmCallback != null; - - public virtual bool IsReturnListener => true; - - [ActivatorUtilitiesConstructor] - public RabbitTemplate(IOptionsMonitor optionsMonitor, IConnectionFactory connectionFactory, ISmartMessageConverter messageConverter, - ILogger logger = null) - { - OptionsMonitor = optionsMonitor; - ConnectionFactory = connectionFactory; - MessageConverter = messageConverter ?? new SimpleMessageConverter(); - Logger = logger; - Configure(Options); - } - - public RabbitTemplate(RabbitOptions options, IConnectionFactory connectionFactory, ISmartMessageConverter messageConverter, ILogger logger = null) - { - _options = options; - ConnectionFactory = connectionFactory; - MessageConverter = messageConverter ?? new SimpleMessageConverter(); - Logger = logger; - Configure(Options); - } - - public RabbitTemplate(IOptionsMonitor optionsMonitor, IConnectionFactory connectionFactory, ILogger logger = null) - { - OptionsMonitor = optionsMonitor; - ConnectionFactory = connectionFactory; - MessageConverter = new SimpleMessageConverter(); - Logger = logger; - Configure(Options); - } - - public RabbitTemplate(RabbitOptions options, IConnectionFactory connectionFactory, ILogger logger = null) - { - _options = options; - ConnectionFactory = connectionFactory; - MessageConverter = new SimpleMessageConverter(); - Logger = logger; - Configure(Options); - } - - public RabbitTemplate(IConnectionFactory connectionFactory, ILogger logger = null) - { - ConnectionFactory = connectionFactory; - MessageConverter = new SimpleMessageConverter(); - DefaultSendDestination = $"{string.Empty}/{string.Empty}"; - DefaultReceiveDestination = null; - Logger = logger; - } - - public RabbitTemplate(ILogger logger = null) - { - MessageConverter = new SimpleMessageConverter(); - DefaultSendDestination = $"{string.Empty}/{string.Empty}"; - DefaultReceiveDestination = null; - Logger = logger; - } - - public virtual void SetBeforePublishPostProcessors(params IMessagePostProcessor[] beforePublishPostProcessors) - { - ArgumentGuard.NotNull(beforePublishPostProcessors); - ArgumentGuard.ElementsNotNull(beforePublishPostProcessors); - - var newList = new List(beforePublishPostProcessors); - MessagePostProcessorUtils.Sort(newList); - BeforePublishPostProcessors = newList; - } - - public virtual void AddBeforePublishPostProcessors(params IMessagePostProcessor[] beforePublishPostProcessors) - { - ArgumentGuard.NotNull(beforePublishPostProcessors); - - IList existing = BeforePublishPostProcessors; - var newList = new List(beforePublishPostProcessors); - - if (existing != null) - { - newList.AddRange(existing); - } - - MessagePostProcessorUtils.Sort(newList); - BeforePublishPostProcessors = newList; - } - - public virtual bool RemoveBeforePublishPostProcessor(IMessagePostProcessor beforePublishPostProcessor) - { - ArgumentGuard.NotNull(beforePublishPostProcessor); - - IList existing = BeforePublishPostProcessors; - - if (existing != null && existing.Contains(beforePublishPostProcessor)) - { - var copy = new List(existing); - bool result = copy.Remove(beforePublishPostProcessor); - BeforePublishPostProcessors = copy; - return result; - } - - return false; - } - - public virtual void SetAfterReceivePostProcessors(params IMessagePostProcessor[] afterReceivePostProcessors) - { - ArgumentGuard.NotNull(afterReceivePostProcessors); - ArgumentGuard.ElementsNotNull(afterReceivePostProcessors); - - var newList = new List(afterReceivePostProcessors); - MessagePostProcessorUtils.Sort(newList); - AfterReceivePostProcessors = newList; - } - - public virtual void AddAfterReceivePostProcessors(params IMessagePostProcessor[] afterReceivePostProcessors) - { - ArgumentGuard.NotNull(afterReceivePostProcessors); - - IList existing = AfterReceivePostProcessors; - var newList = new List(afterReceivePostProcessors); - - if (existing != null) - { - newList.AddRange(existing); - } - - MessagePostProcessorUtils.Sort(newList); - AfterReceivePostProcessors = newList; - } - - public virtual bool RemoveAfterReceivePostProcessor(IMessagePostProcessor afterReceivePostProcessor) - { - ArgumentGuard.NotNull(afterReceivePostProcessor); - - IList existing = AfterReceivePostProcessors; - - if (existing != null && existing.Contains(afterReceivePostProcessor)) - { - var copy = new List(existing); - bool result = copy.Remove(afterReceivePostProcessor); - AfterReceivePostProcessors = copy; - return result; - } - - return false; - } - - public virtual void HandleConfirm(PendingConfirm pendingConfirm, bool ack) - { - if (ConfirmCallback != null) - { - ConfirmCallback.Confirm(pendingConfirm.CorrelationInfo, ack, pendingConfirm.Cause); - } - } - - public virtual void HandleReturn(int replyCode, string replyText, string exchange, string routingKey, RC.IBasicProperties properties, byte[] body) - { - IReturnCallback callback = ReturnCallback; - - if (callback == null) - { - IMessageHeaders messageProperties = MessagePropertiesConverter.ToMessageHeaders(properties, null, Encoding); - string messageTagHeader = messageProperties.Get(ReturnCorrelationKey); - - if (messageTagHeader != null) - { - string messageTag = messageTagHeader; - - if (ReplyHolder.TryGetValue(messageTag, out PendingReply pendingReply)) - { - callback = new PendingReplyReturn(pendingReply); - } - else - { - Logger?.LogWarning("Returned request message but caller has timed out"); - } - } - else - { - Logger?.LogWarning("Returned message but no callback available"); - } - } - - if (callback != null) - { - properties.Headers.Remove(PublisherCallbackChannel.ReturnListenerCorrelationKey); - IMessageHeaders messageProperties = MessagePropertiesConverter.ToMessageHeaders(properties, null, Encoding); - IMessage returnedMessage = Message.Create(body, messageProperties); - callback.ReturnedMessage(returnedMessage, replyCode, replyText, exchange, routingKey); - } - } - - public virtual void Revoke(RC.IModel channel) - { - PublisherConfirmChannels.Remove(channel, out _); - Logger?.LogDebug("Removed publisher confirm channel: {channel} from map, size now {size}", channel, PublisherConfirmChannels.Count); - } - - public virtual void OnMessageBatch(List messages) - { - throw new NotSupportedException("This listener does not support message batches"); - } - - public virtual void OnMessage(IMessage message) - { - Logger?.LogTrace("Message received {message}", message); - object messageTag; - - if (CorrelationKey == null) - { - // using standard correlationId property - messageTag = message.Headers.CorrelationId(); - } - else - { - messageTag = message.Headers.Get(CorrelationKey); - } - - if (messageTag == null) - { - throw new RabbitRejectAndDoNotRequeueException("No correlation header in reply"); - } - - if (!ReplyHolder.TryGetValue((string)messageTag, out PendingReply pendingReply)) - { - Logger?.LogWarning("Reply received after timeout for {tag}", messageTag); - throw new RabbitRejectAndDoNotRequeueException("Reply received after timeout"); - } - - RestoreProperties(message, pendingReply); - pendingReply.Reply(message); - } - - public virtual List GetExpectedQueueNames() - { - _isListener = true; - List replyQueue = null; - - if (ReplyAddress == null || ReplyAddress == Address.AmqRabbitMQReplyTo) - { - throw new InvalidOperationException("A listener container must not be provided when using direct reply-to"); - } - - var address = new Address(ReplyAddress); - - if (address.ExchangeName == string.Empty) - { - replyQueue = new List - { - address.RoutingKey - }; - } - else - { - Logger?.LogInformation("Cannot verify reply queue because 'replyAddress' is not a simple queue name: {replyAddress}", ReplyAddress); - } - - return replyQueue; - } - - public virtual void Send(string routingKey, IMessage message) - { - Send(GetDefaultExchange(), routingKey, message); - } - - public virtual void Send(string exchange, string routingKey, IMessage message) - { - Send(exchange, routingKey, message, null); - } - - public virtual void Send(string exchange, string routingKey, IMessage message, CorrelationData correlationData) - { - bool mandatory = (ReturnCallback != null || (correlationData != null && !string.IsNullOrEmpty(correlationData.Id))) && - MandatoryExpression.GetValue(EvaluationContext, message); - - Execute(channel => - { - DoSend(channel, exchange, routingKey, message, mandatory, correlationData, default); - return null; - }, ObtainTargetConnectionFactory(SendConnectionFactorySelectorExpression, message)); - } - - public virtual Task SendAsync(string routingKey, IMessage message, CancellationToken cancellationToken = default) - { - return SendAsync(GetDefaultExchange(), routingKey, message, cancellationToken); - } - - public virtual Task SendAsync(string exchange, string routingKey, IMessage message, CancellationToken cancellationToken = default) - { - return SendAsync(exchange, routingKey, message, null, cancellationToken); - } - - public virtual Task SendAsync(string exchange, string routingKey, IMessage message, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - bool mandatory = (ReturnCallback != null || (correlationData != null && !string.IsNullOrEmpty(correlationData.Id))) && - MandatoryExpression.GetValue(EvaluationContext, message); - - return Task.Run(() => Execute(channel => - { - DoSend(channel, exchange, routingKey, message, mandatory, correlationData, cancellationToken); - return null; - }, ObtainTargetConnectionFactory(SendConnectionFactorySelectorExpression, message)), cancellationToken); - } - - public virtual void ConvertAndSend(object message, IMessagePostProcessor messagePostProcessor) - { - ConvertAndSend(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, null); - } - - public virtual void ConvertAndSend(object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData) - { - ConvertAndSend(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, correlationData); - } - - public virtual void ConvertAndSend(string routingKey, object message) - { - ConvertAndSend(GetDefaultExchange(), routingKey, message, null, null); - } - - public virtual void ConvertAndSend(string routingKey, object message, CorrelationData correlationData) - { - ConvertAndSend(GetDefaultExchange(), routingKey, message, null, correlationData); - } - - public virtual void ConvertAndSend(string routingKey, object message, IMessagePostProcessor messagePostProcessor) - { - ConvertAndSend(GetDefaultExchange(), routingKey, message, messagePostProcessor, null); - } - - public virtual void ConvertAndSend(string routingKey, object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData) - { - ConvertAndSend(GetDefaultExchange(), routingKey, message, messagePostProcessor, correlationData); - } - - public virtual void ConvertAndSend(string exchange, string routingKey, object message) - { - ConvertAndSend(exchange, routingKey, message, (CorrelationData)null); - } - - public virtual void ConvertAndSend(string exchange, string routingKey, object message, CorrelationData correlationData) - { - ConvertAndSend(exchange, routingKey, message, null, correlationData); - } - - public virtual void ConvertAndSend(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor) - { - ConvertAndSend(exchange, routingKey, message, messagePostProcessor, null); - } - - public virtual void ConvertAndSend(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData) - { - IMessage messageToSend = ConvertMessageIfNecessary(message); - - if (messagePostProcessor != null) - { - messageToSend = messagePostProcessor.PostProcessMessage(messageToSend, correlationData); - } - - Send(exchange, routingKey, messageToSend, correlationData); - } - - public virtual Task ConvertAndSendAsync(object message, IMessagePostProcessor messagePostProcessor, CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, correlationData, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string routingKey, object message, CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(GetDefaultExchange(), routingKey, message, null, null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string routingKey, object message, CorrelationData correlationData, CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(GetDefaultExchange(), routingKey, message, null, correlationData, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(GetDefaultExchange(), routingKey, message, messagePostProcessor, null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string routingKey, object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(GetDefaultExchange(), routingKey, message, messagePostProcessor, correlationData, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string exchange, string routingKey, object message, CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(exchange, routingKey, message, null, null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string exchange, string routingKey, object message, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(exchange, routingKey, message, null, correlationData, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CancellationToken cancellationToken = default) - { - return ConvertAndSendAsync(exchange, routingKey, message, messagePostProcessor, null, cancellationToken); - } - - public virtual Task ConvertAndSendAsync(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData, CancellationToken cancellationToken = default) - { - IMessage messageToSend = ConvertMessageIfNecessary(message); - - if (messagePostProcessor != null) - { - messageToSend = messagePostProcessor.PostProcessMessage(messageToSend, correlationData); - } - - return SendAsync(exchange, routingKey, messageToSend, correlationData, cancellationToken); - } - - public virtual Task ReceiveAsync(string queueName, CancellationToken cancellationToken = default) - { - return Task.Run(() => DoReceive(queueName, ReceiveTimeout, cancellationToken), cancellationToken); - } - - public virtual Task ReceiveAsync(int timeoutMillis, CancellationToken cancellationToken = default) - { - return Task.Run(() => DoReceive(GetRequiredQueue(), timeoutMillis, cancellationToken), cancellationToken); - } - - public virtual Task ReceiveAsync(string queueName, int timeoutMillis, CancellationToken cancellationToken = default) - { - return Task.Run(() => DoReceive(queueName, timeoutMillis, cancellationToken), cancellationToken); - } - - public virtual IMessage Receive(int timeoutInMilliseconds) - { - return Receive(GetRequiredQueue(), timeoutInMilliseconds); - } - - public virtual IMessage Receive(string queueName) - { - return Receive(queueName, ReceiveTimeout); - } - - public virtual IMessage Receive(string queueName, int timeoutInMilliseconds) - { - if (timeoutInMilliseconds == 0) - { - return DoReceiveNoWait(queueName); - } - - return DoReceive(queueName, timeoutInMilliseconds, default); - } - - public virtual T ReceiveAndConvert(int timeoutMillis) - { - return (T)ReceiveAndConvert(GetRequiredQueue(), timeoutMillis, typeof(T)); - } - - public virtual T ReceiveAndConvert(string queueName) - { - return (T)ReceiveAndConvert(queueName, ReceiveTimeout, typeof(T)); - } - - public virtual T ReceiveAndConvert(string queueName, int timeoutInMilliseconds) - { - return (T)ReceiveAndConvert(queueName, timeoutInMilliseconds, typeof(T)); - } - - public virtual object ReceiveAndConvert(Type type) - { - return ReceiveAndConvert(GetRequiredQueue(), ReceiveTimeout, type); - } - - public virtual object ReceiveAndConvert(string queueName, Type type) - { - return ReceiveAndConvert(queueName, ReceiveTimeout, type); - } - - public virtual object ReceiveAndConvert(int timeoutMillis, Type type) - { - return ReceiveAndConvert(GetRequiredQueue(), timeoutMillis, type); - } - - public virtual object ReceiveAndConvert(string queueName, int timeoutInMilliseconds, Type type) - { - return DoReceiveAndConvert(queueName, timeoutInMilliseconds, type); - } - - public virtual Task ReceiveAndConvertAsync(int timeoutMillis, CancellationToken cancellationToken = default) - { - return ReceiveAndConvertAsync(GetRequiredQueue(), timeoutMillis, cancellationToken); - } - - public virtual Task ReceiveAndConvertAsync(string queueName, CancellationToken cancellationToken = default) - { - return ReceiveAndConvertAsync(queueName, ReceiveTimeout, cancellationToken); - } - - public virtual Task ReceiveAndConvertAsync(string queueName, int timeoutMillis, CancellationToken cancellationToken = default) - { - return Task.Run(() => (T)DoReceiveAndConvert(queueName, timeoutMillis, typeof(T), cancellationToken), cancellationToken); - } - - public virtual Task ReceiveAndConvertAsync(Type type, CancellationToken cancellation = default) - { - return ReceiveAndConvertAsync(GetRequiredQueue(), ReceiveTimeout, type, cancellation); - } - - public virtual Task ReceiveAndConvertAsync(string queueName, Type type, CancellationToken cancellationToken = default) - { - return ReceiveAndConvertAsync(queueName, ReceiveTimeout, type, cancellationToken); - } - - public virtual Task ReceiveAndConvertAsync(int timeoutMillis, Type type, CancellationToken cancellationToken = default) - { - return ReceiveAndConvertAsync(GetRequiredQueue(), timeoutMillis, type, cancellationToken); - } - - public virtual Task ReceiveAndConvertAsync(string queueName, int timeoutMillis, Type type, CancellationToken cancellationToken = default) - { - return Task.Run(() => DoReceiveAndConvert(queueName, timeoutMillis, type, cancellationToken), cancellationToken); - } - - public virtual bool ReceiveAndReply(Func callback) - { - return ReceiveAndReply(GetRequiredQueue(), callback); - } - - public virtual bool ReceiveAndReply(string queueName, Func callback) - { - return ReceiveAndReply(queueName, callback, (request, _) => GetReplyToAddress(request)); - } - - public virtual bool ReceiveAndReply(Func callback, string replyExchange, string replyRoutingKey) - { - return ReceiveAndReply(GetRequiredQueue(), callback, replyExchange, replyRoutingKey); - } - - public virtual bool ReceiveAndReply(string queueName, Func callback, string replyExchange, string replyRoutingKey) - { - return ReceiveAndReply(queueName, callback, (_, _) => new Address(replyExchange, replyRoutingKey)); - } - - public virtual bool ReceiveAndReply(Func callback, Func replyToAddressCallback) - { - return ReceiveAndReply(GetRequiredQueue(), callback, replyToAddressCallback); - } - - public virtual bool ReceiveAndReply(string queueName, Func callback, - Func replyToAddressCallback) - { - return DoReceiveAndReply(queueName, callback, replyToAddressCallback); - } - - public virtual IMessage SendAndReceive(IMessage message, CorrelationData correlationData) - { - return DoSendAndReceive(GetDefaultExchange(), GetDefaultRoutingKey(), message, correlationData, default); - } - - public virtual IMessage SendAndReceive(string routingKey, IMessage message) - { - return DoSendAndReceive(GetDefaultExchange(), routingKey, message, null, default); - } - - public virtual IMessage SendAndReceive(string routingKey, IMessage message, CorrelationData correlationData) - { - return DoSendAndReceive(GetDefaultExchange(), routingKey, message, correlationData, default); - } - - public virtual IMessage SendAndReceive(string exchange, string routingKey, IMessage message) - { - return DoSendAndReceive(exchange, routingKey, message, null, default); - } - - public virtual IMessage SendAndReceive(string exchange, string routingKey, IMessage message, CorrelationData correlationData) - { - return DoSendAndReceive(exchange, routingKey, message, correlationData, default); - } - - public virtual Task SendAndReceiveAsync(IMessage message, CorrelationData correlationData, CancellationToken cancellationToken = default) - { - return SendAndReceiveAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, correlationData, cancellationToken); - } - - public virtual Task SendAndReceiveAsync(string routingKey, IMessage message, CancellationToken cancellationToken = default) - { - return SendAndReceiveAsync(GetDefaultExchange(), routingKey, message, null, cancellationToken); - } - - public virtual Task SendAndReceiveAsync(string routingKey, IMessage message, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - return SendAndReceiveAsync(GetDefaultExchange(), routingKey, message, null, cancellationToken); - } - - public virtual Task SendAndReceiveAsync(string exchange, string routingKey, IMessage message, CancellationToken cancellationToken = default) - { - return SendAndReceiveAsync(exchange, routingKey, message, null, cancellationToken); - } - - public virtual Task SendAndReceiveAsync(string exchange, string routingKey, IMessage message, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - return Task.Run(() => DoSendAndReceive(exchange, routingKey, message, correlationData, cancellationToken), cancellationToken); - } - - public virtual T ConvertSendAndReceive(object message, CorrelationData correlationData) - { - return (T)ConvertSendAndReceiveAsType(GetDefaultExchange(), GetDefaultRoutingKey(), message, null, correlationData, typeof(T)); - } - - public virtual T ConvertSendAndReceive(object message, IMessagePostProcessor messagePostProcessor) - { - return (T)ConvertSendAndReceiveAsType(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, null, typeof(T)); - } - - public virtual T ConvertSendAndReceive(object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData) - { - return (T)ConvertSendAndReceiveAsType(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, correlationData, typeof(T)); - } - - public virtual T ConvertSendAndReceive(string routingKey, object message) - { - return (T)ConvertSendAndReceiveAsType(GetDefaultExchange(), routingKey, message, null, null, typeof(T)); - } - - public virtual T ConvertSendAndReceive(string routingKey, object message, CorrelationData correlationData) - { - return (T)ConvertSendAndReceiveAsType(GetDefaultExchange(), routingKey, message, null, correlationData, typeof(T)); - } - - public virtual T ConvertSendAndReceive(string routingKey, object message, IMessagePostProcessor messagePostProcessor) - { - return (T)ConvertSendAndReceiveAsType(GetDefaultExchange(), routingKey, message, messagePostProcessor, null, typeof(T)); - } - - public virtual T ConvertSendAndReceive(string routingKey, object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData) - { - return (T)ConvertSendAndReceiveAsType(GetDefaultExchange(), routingKey, message, messagePostProcessor, correlationData, typeof(T)); - } - - public virtual T ConvertSendAndReceive(string exchange, string routingKey, object message) - { - return (T)ConvertSendAndReceiveAsType(exchange, routingKey, message, null, null, typeof(T)); - } - - public virtual T ConvertSendAndReceive(string exchange, string routingKey, object message, CorrelationData correlationData) - { - return (T)ConvertSendAndReceiveAsType(exchange, routingKey, message, null, correlationData, typeof(T)); - } - - public virtual T ConvertSendAndReceive(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor) - { - return (T)ConvertSendAndReceiveAsType(exchange, routingKey, message, messagePostProcessor, null, typeof(T)); - } - - public virtual T ConvertSendAndReceive(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData) - { - return (T)ConvertSendAndReceiveAsType(exchange, routingKey, message, messagePostProcessor, correlationData, typeof(T)); - } - - public virtual Task ConvertSendAndReceiveAsync(object message, CorrelationData correlationData, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, null, correlationData, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(object message, IMessagePostProcessor messagePostProcessor, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, null, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, correlationData, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string routingKey, object message, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(GetDefaultExchange(), routingKey, message, null, null, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string routingKey, object message, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(GetDefaultExchange(), routingKey, message, null, correlationData, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(GetDefaultExchange(), routingKey, message, messagePostProcessor, null, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(GetDefaultExchange(), routingKey, message, messagePostProcessor, correlationData, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string exchange, string routingKey, object message, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(exchange, routingKey, message, null, null, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string exchange, string routingKey, object message, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(exchange, routingKey, message, null, correlationData, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsync(exchange, routingKey, message, messagePostProcessor, null, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsync(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData, CancellationToken cancellationToken = default) - { - return Task.Run(() => (T)ConvertSendAndReceiveAsType(exchange, routingKey, message, messagePostProcessor, correlationData, typeof(T)), - cancellationToken); - } - - public virtual object ConvertSendAndReceiveAsType(object message, Type type) - { - return ConvertSendAndReceiveAsType(GetDefaultExchange(), GetDefaultRoutingKey(), message, null, null, type); - } - - public virtual object ConvertSendAndReceiveAsType(object message, CorrelationData correlationData, Type type) - { - return ConvertSendAndReceiveAsType(GetDefaultExchange(), GetDefaultRoutingKey(), message, null, correlationData, type); - } - - public virtual object ConvertSendAndReceiveAsType(object message, IMessagePostProcessor messagePostProcessor, Type type) - { - return ConvertSendAndReceiveAsType(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, null, type); - } - - public virtual object ConvertSendAndReceiveAsType(object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData, Type type) - { - return ConvertSendAndReceiveAsType(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, correlationData, type); - } - - public virtual object ConvertSendAndReceiveAsType(string routingKey, object message, Type type) - { - return ConvertSendAndReceiveAsType(GetDefaultExchange(), routingKey, message, null, null, type); - } - - public virtual object ConvertSendAndReceiveAsType(string routingKey, object message, CorrelationData correlationData, Type type) - { - return ConvertSendAndReceiveAsType(GetDefaultExchange(), routingKey, message, null, correlationData, type); - } - - public virtual object ConvertSendAndReceiveAsType(string routingKey, object message, IMessagePostProcessor messagePostProcessor, Type type) - { - return ConvertSendAndReceiveAsType(GetDefaultExchange(), routingKey, message, messagePostProcessor, null, type); - } - - public virtual object ConvertSendAndReceiveAsType(string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData, Type type) - { - return ConvertSendAndReceiveAsType(GetDefaultExchange(), routingKey, message, messagePostProcessor, correlationData, type); - } - - public virtual object ConvertSendAndReceiveAsType(string exchange, string routingKey, object message, Type type) - { - return ConvertSendAndReceiveAsType(exchange, routingKey, message, null, null, type); - } - - public virtual object ConvertSendAndReceiveAsType(string exchange, string routingKey, object message, CorrelationData correlationData, Type type) - { - return ConvertSendAndReceiveAsType(exchange, routingKey, message, null, correlationData, type); - } - - public virtual object ConvertSendAndReceiveAsType(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, Type type) - { - return ConvertSendAndReceiveAsType(exchange, routingKey, message, messagePostProcessor, null, type); - } - - public virtual object ConvertSendAndReceiveAsType(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData, Type type) - { - IMessage replyMessage = ConvertSendAndReceiveRaw(exchange, routingKey, message, messagePostProcessor, correlationData); - - if (replyMessage == null) - { - return default; - } - - object value = GetRequiredSmartMessageConverter().FromMessage(replyMessage, type); - - return value is Exception exception && ThrowReceivedExceptions ? throw exception : value; - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(object message, Type type, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, null, null, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(object message, CorrelationData correlationData, Type type, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, null, correlationData, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(object message, IMessagePostProcessor messagePostProcessor, Type type, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, null, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(object message, IMessagePostProcessor messagePostProcessor, CorrelationData correlationData, - Type type, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(GetDefaultExchange(), GetDefaultRoutingKey(), message, messagePostProcessor, correlationData, type, - cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(string routingKey, object message, Type type, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(GetDefaultExchange(), routingKey, message, null, null, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(string routingKey, object message, CorrelationData correlationData, Type type, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(GetDefaultExchange(), routingKey, message, null, correlationData, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(string routingKey, object message, IMessagePostProcessor messagePostProcessor, Type type, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(GetDefaultExchange(), routingKey, message, messagePostProcessor, null, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData, Type type, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(GetDefaultExchange(), routingKey, message, messagePostProcessor, correlationData, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(string exchange, string routingKey, object message, Type type, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(exchange, routingKey, message, null, null, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(string exchange, string routingKey, object message, CorrelationData correlationData, Type type, - CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(exchange, routingKey, message, null, correlationData, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - Type type, CancellationToken cancellationToken = default) - { - return ConvertSendAndReceiveAsTypeAsync(exchange, routingKey, message, messagePostProcessor, null, type, cancellationToken); - } - - public virtual Task ConvertSendAndReceiveAsTypeAsync(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData, Type type, CancellationToken cancellationToken = default) - { - return Task.Run(() => - { - IMessage replyMessage = ConvertSendAndReceiveRaw(exchange, routingKey, message, messagePostProcessor, correlationData); - - if (replyMessage == null) - { - return default; - } - - object value = GetRequiredSmartMessageConverter().FromMessage(replyMessage, type); - - return value switch - { - Exception exception when ThrowReceivedExceptions => throw exception, - _ => value - }; - }, cancellationToken); - } - - public virtual void CorrelationConvertAndSend(object message, CorrelationData correlationData) - { - ConvertAndSend(GetDefaultExchange(), GetDefaultRoutingKey(), message, null, correlationData); - } - - public virtual ICollection GetUnconfirmed(long age) - { - var unconfirmed = new HashSet(); - long cutoffTime = DateTimeOffset.Now.ToUnixTimeMilliseconds() - age; - - foreach (RC.IModel channel in PublisherConfirmChannels.Keys) - { - if (channel is IPublisherCallbackChannel pubCallbackChan) - { - IList confirms = pubCallbackChan.Expire(this, cutoffTime); - - foreach (PendingConfirm confirm in confirms) - { - unconfirmed.Add(confirm.CorrelationInfo); - } - } - } - - return unconfirmed.Count > 0 ? unconfirmed : null; - } - - public virtual int GetUnconfirmedCount() - { - return PublisherConfirmChannels.Keys.Select(m => - { - if (m is IPublisherCallbackChannel pubCallbackChan) - { - return pubCallbackChan.GetPendingConfirmsCount(this); - } - - return 0; - }).Sum(); - } - - public virtual void Execute(Action action) - { - _ = Execute(channel => - { - action(channel); - return null; - }, ConnectionFactory); - } - - public virtual T Execute(Func channelCallback) - { - return Execute(channelCallback, ConnectionFactory); - } - - public virtual void AddListener(RC.IModel channel) - { - if (channel is IPublisherCallbackChannel publisherCallbackChannel) - { - RC.IModel key = channel is IChannelProxy proxy ? proxy.TargetChannel : channel; - - if (PublisherConfirmChannels.TryAdd(key, this)) - { - publisherCallbackChannel.AddListener(this); - Logger?.LogDebug("Added publisher confirm channel: {channel} to map, size now {size}", channel, PublisherConfirmChannels.Count); - } - } - else - { - throw new InvalidOperationException("Channel does not support confirms or returns; is the connection factory configured for confirms or returns?"); - } - } - - public virtual T Invoke(Func operationsCallback) - { - return Invoke(operationsCallback, null, null); - } - - public virtual T Invoke(Func operationsCallback, Action acks, Action nacks) - { - RC.IModel currentChannel = DedicatedChannels.Value; - - if (currentChannel != null) - { - throw new InvalidOperationException($"Nested invoke() calls are not supported; channel '{currentChannel}' is already associated with this thread"); - } - - Interlocked.Increment(ref _activeTemplateCallbacks); - RabbitResourceHolder resourceHolder = null; - IConnection connection = null; - RC.IModel channel; - IConnectionFactory connectionFactory = ConnectionFactory; - - if (IsChannelTransacted) - { - resourceHolder = ConnectionFactoryUtils.GetTransactionalResourceHolder(connectionFactory, true, UsePublisherConnection); - channel = resourceHolder.GetChannel(); - - if (channel == null) - { - ConnectionFactoryUtils.ReleaseResources(resourceHolder); - throw new InvalidOperationException("Resource holder returned a null channel"); - } - } - else - { - if (UsePublisherConnection && connectionFactory.PublisherConnectionFactory != null) - { - connectionFactory = connectionFactory.PublisherConnectionFactory; - } - - connection = connectionFactory.CreateConnection(); - - if (connection == null) - { - throw new InvalidOperationException("Connection factory returned a null connection"); - } - - try - { - channel = connection.CreateChannel(); - - if (channel == null) - { - throw new InvalidOperationException("Connection returned a null channel"); - } - - if (!connectionFactory.IsPublisherConfirms) - { - RabbitUtils.SetPhysicalCloseRequired(channel, true); - } - - DedicatedChannels.Value = channel; - } - catch (Exception e) - { - Logger?.LogError(e, "Exception thrown while creating channel"); - RabbitUtils.CloseConnection(connection); - throw; - } - } - - ConfirmListener listener = AddConfirmListener(acks, nacks, channel); - - try - { - return operationsCallback(this); - } - finally - { - CleanUpAfterAction(resourceHolder, connection, channel, listener); - } - } - - public virtual bool WaitForConfirms(int timeoutInMilliseconds) - { - RC.IModel channel = DedicatedChannels.Value; - - if (channel == null) - { - throw new InvalidOperationException("This operation is only available within the scope of an invoke operation"); - } - - try - { - return channel.WaitForConfirms(TimeSpan.FromMilliseconds(timeoutInMilliseconds)); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception thrown during WaitForConfirms"); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - public virtual void WaitForConfirmsOrDie(int timeoutInMilliseconds) - { - RC.IModel channel = DedicatedChannels.Value; - - if (channel == null) - { - throw new InvalidOperationException("This operation is only available within the scope of an invoke operation"); - } - - try - { - channel.WaitForConfirmsOrDie(TimeSpan.FromMilliseconds(timeoutInMilliseconds)); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception thrown during WaitForConfirmsOrDie"); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - public virtual void DetermineConfirmsReturnsCapability(IConnectionFactory connectionFactory) - { - _publisherConfirms = connectionFactory.IsPublisherConfirms; - _confirmsOrReturnsCapable = _publisherConfirms || connectionFactory.IsPublisherReturns; - } - - public virtual bool IsMandatoryFor(IMessage message) - { - return MandatoryExpression.GetValue(EvaluationContext, message); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - StopAsync().GetAwaiter().GetResult(); - } - } - - public virtual async Task StartAsync() - { - await DoStartAsync(); - } - - public virtual async Task StopAsync() - { - var tasks = new List(); - - lock (DirectReplyToContainers) - { - foreach (DirectReplyToMessageListenerContainer c in DirectReplyToContainers.Values) - { - if (c.IsRunning) - { - Task task = c.StopAsync(); - tasks.Add(task); - } - } - - DirectReplyToContainers.Clear(); - } - - await Task.WhenAll(tasks); - await DoStopAsync(); - } - - protected internal virtual IMessage ConvertMessageIfNecessary(object message) - { - if (message is IMessage byteArrayMessage) - { - return byteArrayMessage; - } - - return GetRequiredMessageConverter().ToMessage(message, new MessageHeaders()); - } - - protected internal virtual IMessage DoSendAndReceiveWithTemporary(string exchange, string routingKey, IMessage message, CorrelationData correlationData, - CancellationToken cancellationToken) - { - return Execute(channel => - { - if (message.Headers.ReplyTo() != null) - { - throw new InvalidOperationException( - $"Send-and-receive methods can only be used if the message does not already have a {nameof(RabbitMessageHeaders.ReplyTo)} property."); - } - - cancellationToken.ThrowIfCancellationRequested(); - - var pendingReply = new PendingReply(); - string messageTag = Interlocked.Increment(ref _messageTagProvider).ToString(CultureInfo.InvariantCulture); - ReplyHolder.TryAdd(messageTag, pendingReply); - string replyTo; - - if (UsingFastReplyTo) - { - replyTo = Address.AmqRabbitMQReplyTo; - } - else - { - RC.QueueDeclareOk queueDeclaration = RC.IModelExensions.QueueDeclare(channel); - replyTo = queueDeclaration.QueueName; - } - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ReplyTo = replyTo; - - string consumerTag = Guid.NewGuid().ToString(); - - var consumer = new DoSendAndReceiveTemplateConsumer(this, channel, pendingReply); - - channel.ModelShutdown += (_, args) => - { - if (!RabbitUtils.IsNormalChannelClose(args)) - { - var exception = new ShutdownSignalException(args); - pendingReply.CompleteExceptionally(exception); - } - }; - - channel.BasicConsume(replyTo, true, consumerTag, NoLocalReplyConsumer, true, null, consumer); - IMessage reply = null; - - try - { - reply = ExchangeMessages(exchange, routingKey, message, correlationData, channel, pendingReply, messageTag, cancellationToken); - } - finally - { - ReplyHolder.TryRemove(messageTag, out _); - - if (channel.IsOpen) - { - CancelConsumerQuietly(channel, consumer); - } - } - - return reply; - }, ObtainTargetConnectionFactory(SendConnectionFactorySelectorExpression, message)); - } - - protected virtual object DoReceiveAndConvert(string queueName, int timeoutMillis, Type type, CancellationToken cancellationToken = default) - { - IMessage response = timeoutMillis == 0 ? DoReceiveNoWait(queueName) : DoReceive(queueName, timeoutMillis, cancellationToken); - - if (response != null) - { - return GetRequiredSmartMessageConverter().FromMessage(response, type); - } - - return default; - } - - protected virtual IMessage DoReceive(string queueName, int timeoutMillis, CancellationToken cancellationToken) - { - IMessage message = Execute(channel => - { - Delivery delivery = ConsumeDelivery(channel, queueName, timeoutMillis, cancellationToken); - - if (delivery == null) - { - return null; - } - - if (IsChannelLocallyTransacted(channel)) - { - channel.BasicAck(delivery.Envelope.DeliveryTag, false); - channel.TxCommit(); - } - else if (IsChannelTransacted) - { - ConnectionFactoryUtils.RegisterDeliveryTag(ConnectionFactory, channel, delivery.Envelope.DeliveryTag); - } - else - { - channel.BasicAck(delivery.Envelope.DeliveryTag, false); - } - - return BuildMessageFromDelivery(delivery); - }); - - LogReceived(message); - return message; - } - - protected override IMessage DoReceive(RabbitDestination destination) - { - if (ReceiveTimeout == 0) - { - return DoReceiveNoWait(destination.QueueName); - } - - return DoReceive(destination.QueueName, ReceiveTimeout, default); - } - - protected override Task DoReceiveAsync(RabbitDestination destination, CancellationToken cancellationToken) - { - return ReceiveAsync(destination.QueueName, cancellationToken); - } - - protected override Task DoSendAsync(RabbitDestination destination, IMessage message, CancellationToken cancellationToken) - { - return SendAsync(destination.ExchangeName, destination.RoutingKey, message, cancellationToken); - } - - protected override Task DoSendAndReceiveAsync(RabbitDestination destination, IMessage requestMessage, - CancellationToken cancellationToken = default) - { - return SendAndReceiveAsync(destination.ExchangeName, destination.RoutingKey, requestMessage, cancellationToken); - } - - protected override IMessage DoSendAndReceive(RabbitDestination destination, IMessage requestMessage) - { - return DoSendAndReceive(destination.ExchangeName, destination.RoutingKey, requestMessage, null, default); - } - - protected virtual IMessage DoSendAndReceive(string exchange, string routingKey, IMessage message, CorrelationData correlationData, - CancellationToken cancellationToken) - { - if (!EvaluatedFastReplyTo) - { - lock (Lock) - { - if (!EvaluatedFastReplyTo) - { - EvaluateFastReplyTo(); - } - } - } - - if (UsingFastReplyTo && UseDirectReplyToContainer) - { - return DoSendAndReceiveWithDirect(exchange, routingKey, message, correlationData, cancellationToken); - } - - if (ReplyAddress == null || UsingFastReplyTo) - { - return DoSendAndReceiveWithTemporary(exchange, routingKey, message, correlationData, cancellationToken); - } - - return DoSendAndReceiveWithFixed(exchange, routingKey, message, correlationData, cancellationToken); - } - - protected virtual IMessage DoSendAndReceiveWithFixed(string exchange, string routingKey, IMessage message, CorrelationData correlationData, - CancellationToken cancellationToken) - { - if (!_isListener) - { - throw new InvalidOperationException($"RabbitTemplate is not configured as MessageListener - cannot use a 'replyAddress': {ReplyAddress}"); - } - - return Execute(channel => DoSendAndReceiveAsListener(exchange, routingKey, message, correlationData, channel, cancellationToken), - ObtainTargetConnectionFactory(SendConnectionFactorySelectorExpression, message)); - } - - protected virtual IMessage DoSendAndReceiveWithDirect(string exchange, string routingKey, IMessage message, CorrelationData correlationData, - CancellationToken cancellationToken = default) - { - IConnectionFactory connectionFactory = ObtainTargetConnectionFactory(SendConnectionFactorySelectorExpression, message); - - if (UsePublisherConnection && connectionFactory.PublisherConnectionFactory != null) - { - connectionFactory = connectionFactory.PublisherConnectionFactory; - } - - if (!DirectReplyToContainers.TryGetValue(connectionFactory, out DirectReplyToMessageListenerContainer container)) - { - lock (DirectReplyToContainers) - { - if (!DirectReplyToContainers.TryGetValue(connectionFactory, out container)) - { - container = new DirectReplyToMessageListenerContainer(null, connectionFactory); - container.MessageListener = this; - container.ServiceName = $"{ServiceName}#{Interlocked.Increment(ref _containerInstance)}"; - - if (AfterReceivePostProcessors != null) - { - container.SetAfterReceivePostProcessors(AfterReceivePostProcessors.ToArray()); - } - - container.NoLocal = NoLocalReplyConsumer; - - if (ReplyErrorHandler != null) - { - container.ErrorHandler = ReplyErrorHandler; - } - - container.StartAsync().GetAwaiter().GetResult(); - DirectReplyToContainers.TryAdd(connectionFactory, container); - _replyAddress = Address.AmqRabbitMQReplyTo; - } - } - } - - DirectReplyToMessageListenerContainer.ChannelHolder channelHolder = container.GetChannelHolder(); - - try - { - RC.IModel channel = channelHolder.Channel; - - if (_confirmsOrReturnsCapable.HasValue && _confirmsOrReturnsCapable.Value) - { - AddListener(channel); - } - - return DoSendAndReceiveAsListener(exchange, routingKey, message, correlationData, channel, cancellationToken); - } - catch (Exception e) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - finally - { - container.ReleaseConsumerFor(channelHolder, false, null); - } - } - - protected virtual IMessage DoReceiveNoWait(string queueName, CancellationToken cancellationToken = default) - { - IMessage message = Execute(channel => - { - if (!cancellationToken.IsCancellationRequested) - { - RC.BasicGetResult response = channel.BasicGet(queueName, !IsChannelTransacted); - - // Response can be null is the case that there is no message on the queue. - if (response != null) - { - ulong deliveryTag = response.DeliveryTag; - - if (IsChannelLocallyTransacted(channel)) - { - channel.BasicAck(deliveryTag, false); - channel.TxCommit(); - } - else if (IsChannelTransacted) - { - // Not locally transacted but it is transacted so it - // could be synchronized with an external transaction - ConnectionFactoryUtils.RegisterDeliveryTag(ConnectionFactory, channel, deliveryTag); - } - - return BuildMessageFromResponse(response); - } - } - - return null; - }, ObtainTargetConnectionFactory(ReceiveConnectionFactorySelectorExpression, queueName)); - - LogReceived(message); - return message; - } - - protected virtual void DoSend(RC.IModel channel, string exchangeArg, string routingKeyArg, IMessage message, bool mandatory, - CorrelationData correlationData, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - string exchange = exchangeArg; - string rKey = routingKeyArg; - exchange ??= GetDefaultExchange(); - rKey ??= GetDefaultRoutingKey(); - - Logger?.LogTrace("Original message to publish: {message}", message); - - IMessage messageToUse = message; - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(messageToUse); - - if (mandatory) - { - accessor.SetHeader(PublisherCallbackChannel.ReturnListenerCorrelationKey, Uuid); - } - - if (BeforePublishPostProcessors != null) - { - IList processors = BeforePublishPostProcessors; - - foreach (IMessagePostProcessor processor in processors) - { - messageToUse = processor.PostProcessMessage(messageToUse, correlationData); - } - } - - SetupConfirm(channel, messageToUse, correlationData); - - if (UserIdExpression != null && accessor.UserId == null) - { - string userId = UserIdExpression.GetValue(EvaluationContext, messageToUse); - - if (userId != null) - { - accessor.UserId = userId; - } - } - - Logger?.LogDebug("Publishing message [{message}] on exchange [{exchange}], routingKey = [{routingKey}]", messageToUse, exchange, rKey); - SendToRabbit(channel, exchange, rKey, mandatory, messageToUse); - - // Check if commit needed - if (IsChannelLocallyTransacted(channel)) - { - // Transacted channel created by this template -> commit. - RabbitUtils.CommitIfNecessary(channel); - } - } - - protected override void DoSend(RabbitDestination destination, IMessage message) - { - Send(destination.ExchangeName, destination.RoutingKey, message, null); - } - - protected virtual void SendToRabbit(RC.IModel channel, string exchange, string routingKey, bool mandatory, IMessage message) - { - if (message.Payload is not byte[] body) - { - throw new InvalidOperationException("Unable to publish IMessage, payload must be a byte[]"); - } - - RC.IBasicProperties convertedMessageProperties = channel.CreateBasicProperties(); - MessagePropertiesConverter.FromMessageHeaders(message.Headers, convertedMessageProperties, Encoding); - channel.BasicPublish(exchange, routingKey, mandatory, convertedMessageProperties, body); - } - - protected virtual bool IsChannelLocallyTransacted(RC.IModel channel) - { - return IsChannelTransacted && !ConnectionFactoryUtils.IsChannelTransactional(channel, ConnectionFactory); - } - - protected virtual IConnection CreateConnection() - { - return ConnectionFactory.CreateConnection(); - } - - protected virtual RabbitResourceHolder GetTransactionalResourceHolder() - { - return ConnectionFactoryUtils.GetTransactionalResourceHolder(ConnectionFactory, IsChannelTransacted); - } - - protected virtual Exception ConvertRabbitAccessException(Exception ex) - { - return RabbitExceptionTranslator.ConvertRabbitAccessException(ex); - } - - protected IMessage ConvertSendAndReceiveRaw(string exchange, string routingKey, object message, IMessagePostProcessor messagePostProcessor, - CorrelationData correlationData) - { - IMessage requestMessage = ConvertMessageIfNecessary(message); - - if (messagePostProcessor != null) - { - requestMessage = messagePostProcessor.PostProcessMessage(requestMessage, correlationData); - } - - return DoSendAndReceive(exchange, routingKey, requestMessage, correlationData, default); - } - - protected virtual string GetDefaultExchange() - { - if (DefaultSendDestination != null) - { - return DefaultSendDestination.ExchangeName; - } - - return DefaultExchange; - } - - protected virtual string GetDefaultRoutingKey() - { - if (DefaultSendDestination != null) - { - return DefaultSendDestination.RoutingKey; - } - - return DefaultRoutingKey; - } - - protected virtual bool UseDirectReplyTo() - { - if (UseTemporaryReplyQueues) - { - if (ReplyAddress != null) - { - Logger?.LogWarning("'useTemporaryReplyQueues' is ignored when a 'replyAddress' is provided"); - } - else - { - return false; - } - } - - if (ReplyAddress == null || ReplyAddress == Address.AmqRabbitMQReplyTo) - { - try - { - return Execute(channel => - { - channel.QueueDeclarePassive(Address.AmqRabbitMQReplyTo); - return true; - }); - } - catch (RabbitException ex) when (ex is RabbitConnectException || ex is RabbitIOException) - { - if (ShouldRethrow(ex)) - { - throw; - } - } - } - - return false; - } - - protected virtual void ReplyTimedOut(string correlationId) - { - } - - protected virtual Task DoStartAsync() - { - return Task.CompletedTask; - } - - protected virtual Task DoStopAsync() - { - return Task.CompletedTask; - } - - private void Configure(RabbitOptions options) - { - if (options == null) - { - return; - } - - RabbitOptions.TemplateOptions templateOptions = options.Template; - Mandatory = templateOptions.Mandatory || options.PublisherReturns; - - if (templateOptions.Retry.Enabled) - { - RetryTemplate = new PollyRetryTemplate(new Dictionary(), templateOptions.Retry.MaxAttempts, true, - (int)templateOptions.Retry.InitialInterval.TotalMilliseconds, (int)templateOptions.Retry.MaxInterval.TotalMilliseconds, - templateOptions.Retry.Multiplier, Logger); - } - - if (templateOptions.ReceiveTimeout.HasValue) - { - int asMillis = (int)templateOptions.ReceiveTimeout.Value.TotalMilliseconds; - ReceiveTimeout = asMillis; - } - - if (templateOptions.ReplyTimeout.HasValue) - { - int asMillis = (int)templateOptions.ReplyTimeout.Value.TotalMilliseconds; - ReplyTimeout = asMillis; - } - - DefaultSendDestination = $"{templateOptions.Exchange}/{templateOptions.RoutingKey}"; - DefaultReceiveDestination = templateOptions.DefaultReceiveQueue; - } - - private void RestoreProperties(IMessage message, PendingReply pendingReply) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - - if (!UserCorrelationId) - { - // Restore the inbound correlation data - string savedCorrelation = pendingReply.SavedCorrelation; - - if (CorrelationKey == null) - { - accessor.CorrelationId = savedCorrelation; - } - else - { - if (savedCorrelation != null) - { - accessor.SetHeader(CorrelationKey, savedCorrelation); - } - else - { - accessor.RemoveHeader(CorrelationKey); - } - } - } - - // Restore any inbound replyTo - string savedReplyTo = pendingReply.SavedReplyTo; - accessor.ReplyTo = savedReplyTo; - - if (savedReplyTo != null) - { - Logger?.LogDebug("Restored replyTo to: {replyTo} ", savedReplyTo); - } - } - - private IMessage BuildMessageFromDelivery(Delivery delivery) - { - return BuildMessage(delivery.Envelope, delivery.Properties, delivery.Body, null); - } - - private IMessage BuildMessageFromResponse(RC.BasicGetResult response) - { - return BuildMessage(new Envelope(response.DeliveryTag, response.Redelivered, response.Exchange, response.RoutingKey), response.BasicProperties, - response.Body, response.MessageCount); - } - - private IMessage BuildMessage(Envelope envelope, RC.IBasicProperties properties, byte[] body, uint? msgCount) - { - IMessageHeaders messageProps = MessagePropertiesConverter.ToMessageHeaders(properties, envelope, Encoding); - - if (msgCount.HasValue) - { - var accessor = MessageHeaderAccessor.GetAccessor(messageProps); - accessor.MessageCount = msgCount.Value; - } - - IMessage message = Message.Create(body, messageProps); - - if (AfterReceivePostProcessors != null) - { - IList processors = AfterReceivePostProcessors; - IMessage postProcessed = message; - - foreach (IMessagePostProcessor processor in processors) - { - postProcessed = processor.PostProcessMessage(postProcessed); - } - - message = postProcessed; - } - - return message; - } - - private IMessageConverter GetRequiredMessageConverter() - { - IMessageConverter converter = MessageConverter; - - if (converter == null) - { - throw new RabbitIllegalStateException("No 'messageConverter' specified. Check configuration of RabbitTemplate."); - } - - return converter; - } - - private ISmartMessageConverter GetRequiredSmartMessageConverter() - { - return GetRequiredMessageConverter() as ISmartMessageConverter ?? - throw new RabbitIllegalStateException("template's message converter must be a SmartMessageConverter"); - } - - private string GetRequiredQueue() - { - RabbitDestination name = DefaultReceiveDestination; - - if (name == null) - { - throw new RabbitIllegalStateException("No 'queue' specified. Check configuration of RabbitTemplate."); - } - - return name; - } - - private Address GetReplyToAddress(IMessage request) - { - Address replyTo = request.Headers.ReplyToAddress(); - - if (replyTo == null) - { - string exchange = GetDefaultExchange(); - string routingKey = GetDefaultRoutingKey(); - - if (exchange == null) - { - throw new RabbitException( - "Cannot determine ReplyTo message property value: Request message does not contain reply-to property, and no default Exchange was set."); - } - - replyTo = new Address(exchange, routingKey); - } - - return replyTo; - } - - private void SetupConfirm(RC.IModel channel, IMessage message, CorrelationData correlationDataArg) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - - if ((_publisherConfirms || ConfirmCallback != null) && channel is IPublisherCallbackChannel callbackChannel) - { - IPublisherCallbackChannel publisherCallbackChannel = callbackChannel; - - CorrelationData correlationData = CorrelationDataPostProcessor != null - ? CorrelationDataPostProcessor.PostProcess(message, correlationDataArg) - : correlationDataArg; - - ulong nextPublishSeqNo = channel.NextPublishSeqNo; - accessor.PublishSequenceNumber = nextPublishSeqNo; - - publisherCallbackChannel.AddPendingConfirm(this, nextPublishSeqNo, - new PendingConfirm(correlationData, DateTimeOffset.Now.ToUnixTimeMilliseconds())); - - if (correlationData != null && !string.IsNullOrEmpty(correlationData.Id)) - { - accessor.SetHeader(PublisherCallbackChannel.ReturnedMessageCorrelationKey, correlationData.Id); - } - } - else if (channel is IChannelProxy proxy && proxy.IsConfirmSelected) - { - ulong nextPublishSeqNo = channel.NextPublishSeqNo; - accessor.PublishSequenceNumber = nextPublishSeqNo; - } - } - - private IMessage DoSendAndReceiveAsListener(string exchange, string routingKey, IMessage message, CorrelationData correlationData, RC.IModel channel, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var pendingReply = new PendingReply(); - string messageTag = Interlocked.Increment(ref _messageTagProvider).ToString(CultureInfo.InvariantCulture); - - if (UserCorrelationId) - { - object correlationId = CorrelationKey != null ? message.Headers.Get(CorrelationKey) : message.Headers.CorrelationId(); - - if (correlationId == null) - { - ReplyHolder[messageTag] = pendingReply; - } - else - { - ReplyHolder[(string)correlationId] = pendingReply; - } - } - else - { - ReplyHolder[messageTag] = pendingReply; - } - - SaveAndSetProperties(message, pendingReply, messageTag); - - Logger?.LogDebug("Sending message with tag {tag}", messageTag); - IMessage reply = null; - - try - { - reply = ExchangeMessages(exchange, routingKey, message, correlationData, channel, pendingReply, messageTag, cancellationToken); - - if (reply != null && AfterReceivePostProcessors != null) - { - IList processors = AfterReceivePostProcessors; - IMessage postProcessed = reply; - - foreach (IMessagePostProcessor processor in processors) - { - postProcessed = processor.PostProcessMessage(postProcessed); - } - - reply = postProcessed; - } - } - finally - { - ReplyHolder.TryRemove(messageTag, out _); - } - - return reply; - } - - private void SaveAndSetProperties(IMessage message, PendingReply pendingReply, string messageTag) - { - // Save any existing replyTo and correlation data - string savedReplyTo = message.Headers.ReplyTo(); - pendingReply.SavedReplyTo = savedReplyTo; - - if (!string.IsNullOrEmpty(savedReplyTo)) - { - Logger?.LogDebug("Replacing replyTo header: {savedReplyTo} in favor of template's configured reply-queue: {replyAddress}", savedReplyTo, - ReplyAddress); - } - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ReplyTo = ReplyAddress; - - if (!UserCorrelationId) - { - object savedCorrelation = null; - - if (CorrelationKey == null) - { - // using standard correlationId property - string correlationId = accessor.CorrelationId; - - if (correlationId != null) - { - savedCorrelation = correlationId; - } - } - else - { - savedCorrelation = accessor.GetHeader(CorrelationKey); - } - - pendingReply.SavedCorrelation = (string)savedCorrelation; - - if (CorrelationKey == null) - { - // using standard correlationId property - accessor.CorrelationId = messageTag; - } - else - { - accessor.SetHeader(CorrelationKey, messageTag); - } - } - } - - private IMessage ExchangeMessages(string exchange, string routingKey, IMessage message, CorrelationData correlationData, RC.IModel channel, - PendingReply pendingReply, string messageTag, CancellationToken cancellationToken) - { - IMessage reply; - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - bool mandatory = IsMandatoryFor(message); - - if (mandatory && ReturnCallback == null) - { - accessor.SetHeader(ReturnCorrelationKey, messageTag); - } - - DoSend(channel, exchange, routingKey, message, mandatory, correlationData, cancellationToken); - - reply = ReplyTimeout < 0 ? pendingReply.Get() : pendingReply.Get(ReplyTimeout); - Logger?.LogDebug("Reply: {reply} ", reply); - - if (reply == null) - { - ReplyTimedOut(accessor.CorrelationId); - } - - return reply; - } - - private void CancelConsumerQuietly(RC.IModel channel, RC.DefaultBasicConsumer consumer) - { - RabbitUtils.Cancel(channel, consumer.ConsumerTag); - } - - private bool DoReceiveAndReply(string queueName, Func callback, Func replyToAddressCallback) - { - bool result = Execute(channel => - { - IMessage receiveMessage = ReceiveForReply(queueName, channel, default); - - if (receiveMessage != null) - { - return SendReply(callback, replyToAddressCallback, channel, receiveMessage); - } - - return false; - }, ObtainTargetConnectionFactory(ReceiveConnectionFactorySelectorExpression, queueName)); - - return result; - } - - private IMessage ReceiveForReply(string queueName, RC.IModel channel, CancellationToken cancellationToken) - { - bool channelTransacted = IsChannelTransacted; - bool channelLocallyTransacted = IsChannelLocallyTransacted(channel); - IMessage receiveMessage = null; - - if (ReceiveTimeout == 0) - { - RC.BasicGetResult response = channel.BasicGet(queueName, !channelTransacted); - - // Response can be null in the case that there is no message on the queue. - if (response != null) - { - ulong deliveryTag1 = response.DeliveryTag; - - if (channelLocallyTransacted) - { - channel.BasicAck(deliveryTag1, false); - } - else if (channelTransacted) - { - // Not locally transacted but it is transacted so it could be - // synchronized with an external transaction - ConnectionFactoryUtils.RegisterDeliveryTag(ConnectionFactory, channel, deliveryTag1); - } - - receiveMessage = BuildMessageFromResponse(response); - } - } - else - { - Delivery delivery = ConsumeDelivery(channel, queueName, ReceiveTimeout, cancellationToken); - - if (delivery != null) - { - ulong deliveryTag2 = delivery.Envelope.DeliveryTag; - - if (channelTransacted && !channelLocallyTransacted) - { - // Not locally transacted but it is transacted so it could be - // synchronized with an external transaction - ConnectionFactoryUtils.RegisterDeliveryTag(ConnectionFactory, channel, deliveryTag2); - } - else - { - channel.BasicAck(deliveryTag2, false); - } - - receiveMessage = BuildMessageFromDelivery(delivery); - } - } - - LogReceived(receiveMessage); - return receiveMessage; - } - - private Delivery ConsumeDelivery(RC.IModel channel, string queueName, int timeoutMillis, CancellationToken cancellationToken) - { - Delivery delivery = null; - Exception exception = null; - var future = new TaskCompletionSource(); - - RC.DefaultBasicConsumer consumer = null; - - try - { - int consumeTimeout = timeoutMillis < 0 ? DefaultConsumeTimeout : timeoutMillis; - consumer = CreateConsumer(queueName, channel, future, consumeTimeout, cancellationToken); - - if (timeoutMillis < 0) - { - delivery = future.Task.Result; - } - else - { - if (future.Task.Wait(TimeSpan.FromMilliseconds(timeoutMillis))) - { - delivery = future.Task.Result; - } - else - { - RabbitUtils.SetPhysicalCloseRequired(channel, true); - } - } - - cancellationToken.ThrowIfCancellationRequested(); - } - catch (AggregateException e) - { - Exception cause = e.InnerExceptions.FirstOrDefault(); - - Logger?.LogError(cause, "Consumer {consumer} failed to receive message", consumer); - exception = RabbitExceptionTranslator.ConvertRabbitAccessException(cause); - throw exception; - } - finally - { - if (consumer != null && exception is not ConsumerCancelledException && channel.IsOpen) - { - CancelConsumerQuietly(channel, consumer); - } - } - - return delivery; - } - - private void LogReceived(IMessage message) - { - if (message == null) - { - Logger?.LogDebug("Received no message"); - } - else - { - Logger?.LogDebug("Received: {message}", message); - } - } - - private bool SendReply(Func receiveAndReplyCallback, Func replyToAddressCallback, - RC.IModel channel, IMessage receiveMessage) - { - object message = receiveMessage; - - if (message is not TReceive) - { - message = GetRequiredMessageConverter().FromMessage(receiveMessage, typeof(TReceive)); - } - - if (message is not TReceive messageAsTReceive) - { - throw new InvalidOperationException($"'receiveAndReplyCallback' can't handle received object of type '{message.GetType()}'."); - } - - object reply = receiveAndReplyCallback(messageAsTReceive); - - if (reply is TReply) - { - DoSendReply(replyToAddressCallback, channel, receiveMessage, (TReply)reply); - } - else if (IsChannelLocallyTransacted(channel)) - { - channel.TxCommit(); - } - - return true; - } - - private void DoSendReply(Func replyToAddressCallback, RC.IModel channel, IMessage receiveMessage, TReply reply) - { - Address replyTo = replyToAddressCallback(receiveMessage, reply); - - IMessage replyMessage = ConvertMessageIfNecessary(reply); - - RabbitHeaderAccessor receiveMessageAccessor = RabbitHeaderAccessor.GetMutableAccessor(receiveMessage); - RabbitHeaderAccessor replyMessageAccessor = RabbitHeaderAccessor.GetMutableAccessor(replyMessage); - - object correlation = CorrelationKey == null ? receiveMessageAccessor.CorrelationId : receiveMessageAccessor.GetHeader(CorrelationKey); - - if (CorrelationKey == null || correlation == null) - { - // using standard correlationId property - if (correlation == null) - { - string messageId = receiveMessageAccessor.MessageId; - - if (messageId != null) - { - correlation = messageId; - } - } - - replyMessageAccessor.CorrelationId = (string)correlation; - } - else - { - replyMessageAccessor.SetHeader(CorrelationKey, correlation); - } - - // 'doSend()' takes care of 'channel.txCommit()'. - DoSend(channel, replyTo.ExchangeName, replyTo.RoutingKey, replyMessage, ReturnCallback != null && IsMandatoryFor(replyMessage), null, default); - } - - private RC.DefaultBasicConsumer CreateConsumer(string queueName, RC.IModel channel, TaskCompletionSource future, int timeoutMillis, - CancellationToken cancellationToken) - { - channel.BasicQos(0, 1, false); - var latch = new CountdownEvent(1); - var consumer = new DefaultTemplateConsumer(channel, latch, future, queueName, cancellationToken); - RC.IModelExensions.BasicConsume(channel, queueName, false, consumer); - - // Waiting for consumeOK, if latch hasn't signaled, then consumeOK response never hit - if (!latch.Wait(TimeSpan.FromMilliseconds(timeoutMillis))) - { - if (channel is IChannelProxy asProxy) - { - asProxy.TargetChannel.Close(); - } - - future.TrySetException( - new ConsumeOkNotReceivedException($"Blocking receive, consumer failed to consume within ms: {timeoutMillis} for consumer {consumer}")); - } - - return consumer; - } - - private IConnectionFactory ObtainTargetConnectionFactory(IExpression expression, object rootObject) - { - if (ConnectionFactory is AbstractRoutingConnectionFactory routingConnectionFactory) - { - if (expression == null) - { - return routingConnectionFactory.DetermineTargetConnectionFactory(); - } - - object lookupKey = rootObject != null - ? SendConnectionFactorySelectorExpression.GetValue(EvaluationContext, rootObject) - : SendConnectionFactorySelectorExpression.GetValue(EvaluationContext); - - if (lookupKey != null) - { - IConnectionFactory connectionFactory = routingConnectionFactory.GetTargetConnectionFactory(lookupKey); - - if (connectionFactory != null) - { - return connectionFactory; - } - - if (!routingConnectionFactory.LenientFallback) - { - throw new InvalidOperationException($"Cannot determine target ConnectionFactory for lookup key [{lookupKey}]"); - } - } - } - - return ConnectionFactory; - } - - private T Execute(Func action, IConnectionFactory connectionFactory) - { - if (RetryTemplate != null) - { - try - { - return RetryTemplate.Execute(_ => DoExecute(action, connectionFactory), (IRecoveryCallback)RecoveryCallback); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception executing DoExecute in retry"); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - return DoExecute(action, connectionFactory); - } - - private T DoExecute(Func channelCallback, IConnectionFactory connectionFactory) - { - ArgumentGuard.NotNull(channelCallback); - - RC.IModel channel = null; - bool invokeScope = false; - - // No need to check the thread local if we know that no invokes are in process - if (_activeTemplateCallbacks > 0) - { - channel = DedicatedChannels.Value; - } - - RabbitResourceHolder resourceHolder = null; - IConnection connection = null; - - if (channel == null) - { - if (IsChannelTransacted) - { - resourceHolder = ConnectionFactoryUtils.GetTransactionalResourceHolder(connectionFactory, true, UsePublisherConnection); - channel = resourceHolder.GetChannel(); - - if (channel == null) - { - ConnectionFactoryUtils.ReleaseResources(resourceHolder); - throw new InvalidOperationException("Resource holder returned a null channel"); - } - } - else - { - connection = ConnectionFactoryUtils.CreateConnection(connectionFactory, UsePublisherConnection); - - if (connection == null) - { - throw new InvalidOperationException("Connection factory returned a null connection"); - } - - try - { - channel = connection.CreateChannel(); - - if (channel == null) - { - throw new InvalidOperationException("Connection returned a null channel"); - } - } - catch (Exception e) - { - Logger?.LogError(e, "Exception while creating channel"); - RabbitUtils.CloseConnection(connection); - throw; - } - } - } - else - { - invokeScope = true; - } - - try - { - return InvokeAction(channelCallback, connectionFactory, channel); - } - catch (Exception ex) - { - if (IsChannelLocallyTransacted(channel) && resourceHolder != null) - { - resourceHolder.RollbackAll(); - } - - throw ConvertRabbitAccessException(ex); - } - finally - { - CleanUpAfterAction(channel, invokeScope, resourceHolder, connection); - } - } - - private T InvokeAction(Func channelCallback, IConnectionFactory connectionFactory, RC.IModel channel) - { - if (!_confirmsOrReturnsCapable.HasValue) - { - DetermineConfirmsReturnsCapability(connectionFactory); - } - - if (_confirmsOrReturnsCapable.Value) - { - AddListener(channel); - } - - Logger?.LogDebug("Executing callback on RabbitMQ Channel: {channel}", channel); - return channelCallback(channel); - } - - private ConfirmListener AddConfirmListener(Action acks, Action nacks, RC.IModel channel) - { - if (acks == null || nacks == null || channel is not IChannelProxy { IsConfirmSelected: true }) - { - return null; - } - - return new ConfirmListener(acks, nacks, channel); - } - - private void CleanUpAfterAction(RC.IModel channel, bool invokeScope, RabbitResourceHolder resourceHolder, IConnection connection) - { - if (!invokeScope) - { - if (resourceHolder != null) - { - ConnectionFactoryUtils.ReleaseResources(resourceHolder); - } - else - { - RabbitUtils.CloseChannel(channel); - RabbitUtils.CloseConnection(connection); - } - } - } - - private void CleanUpAfterAction(RabbitResourceHolder resourceHolder, IConnection connection, RC.IModel channel, ConfirmListener listener) - { - if (listener != null) - { - listener.Remove(); - } - - Interlocked.Decrement(ref _activeTemplateCallbacks); - DedicatedChannels.Value = null; - - if (resourceHolder != null) - { - ConnectionFactoryUtils.ReleaseResources(resourceHolder); - } - else - { - RabbitUtils.CloseChannel(channel); - RabbitUtils.CloseConnection(connection); - } - } - - private bool ShouldRethrow(RabbitException ex) - { - Exception cause = ex; - - while (cause != null && cause is not ShutdownSignalException && cause is not ProtocolException) - { - cause = cause.InnerException; - } - - if (cause != null && RabbitUtils.IsPassiveDeclarationChannelClose(cause)) - { - Logger?.LogWarning(ex, "Broker does not support fast replies via 'amq.rabbitmq.reply-to', temporary queues will be used: {cause}.", cause.Message); - _replyAddress = null; - return false; - } - - if (ex != null) - { - Logger?.LogDebug(ex, "IO error, deferring directReplyTo detection"); - } - - return true; - } - - private void EvaluateFastReplyTo() - { - UsingFastReplyTo = UseDirectReplyTo(); - EvaluatedFastReplyTo = true; - } - - protected internal sealed class PendingReply - { - private readonly TaskCompletionSource _future = new(); - - public string SavedReplyTo { get; set; } - - public string SavedCorrelation { get; set; } - - public IMessage Get() - { - try - { - return _future.Task.Result; - } - catch (Exception e) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e.InnerException); - } - } - - public IMessage Get(int timeout) - { - try - { - if (_future.Task.Wait(TimeSpan.FromMilliseconds(timeout))) - { - return _future.Task.Result; - } - - return null; - } - catch (Exception e) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e.InnerException); - } - } - - public void Reply(IMessage message) - { - _future.TrySetResult(message); - } - - public void Returned(RabbitMessageReturnedException e) - { - CompleteExceptionally(e); - } - - public void CompleteExceptionally(Exception exception) - { - _future.TrySetException(exception); - } - } - - protected class DoSendAndReceiveTemplateConsumer : AbstractTemplateConsumer - { - private readonly RabbitTemplate _template; - private readonly PendingReply _pendingReply; - - public DoSendAndReceiveTemplateConsumer(RabbitTemplate template, RC.IModel channel, PendingReply pendingReply) - : base(channel) - { - _template = template; - _pendingReply = pendingReply; - } - - public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - RC.IBasicProperties properties, byte[] body) - { - IMessageHeaders messageProperties = - _template.MessagePropertiesConverter.ToMessageHeaders(properties, new Envelope(deliveryTag, redelivered, exchange, routingKey), - _template.Encoding); - - IMessage reply = Message.Create(body, messageProperties); - _template.Logger?.LogTrace("Message received {reply}", reply); - - if (_template.AfterReceivePostProcessors != null) - { - IList processors = _template.AfterReceivePostProcessors; - IMessage postProcessed = reply; - - foreach (IMessagePostProcessor processor in processors) - { - postProcessed = processor.PostProcessMessage(postProcessed); - } - } - - _pendingReply.Reply(reply); - } - - public override void HandleModelShutdown(object model, RC.ShutdownEventArgs reason) - { - base.HandleModelShutdown(model, reason); - - if (!RabbitUtils.IsNormalChannelClose(reason)) - { - _pendingReply.CompleteExceptionally(new ShutdownSignalException(reason)); - } - else - { - _pendingReply.Reply(null); - } - } - } - - protected class DefaultTemplateConsumer : AbstractTemplateConsumer - { - private readonly CountdownEvent _latch; - private readonly TaskCompletionSource _completionSource; - private readonly string _queueName; - - public DefaultTemplateConsumer(RC.IModel channel, CountdownEvent latch, TaskCompletionSource completionSource, string queueName, - CancellationToken cancellationToken) - : base(channel) - { - _latch = latch; - _completionSource = completionSource; - _queueName = queueName; - - cancellationToken.Register(() => - { - Signal(); - _completionSource.TrySetCanceled(); - channel.BasicCancel(ConsumerTag); - }); - } - - public override void HandleBasicCancel(string consumerTag) - { - _completionSource.TrySetException(new ConsumerCancelledException()); - Signal(); - } - - public override void HandleBasicConsumeOk(string consumerTag) - { - Signal(); - base.HandleBasicConsumeOk(consumerTag); - } - - public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - RC.IBasicProperties properties, byte[] body) - { - base.HandleBasicDeliver(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body); - - _completionSource.TrySetResult( - new Delivery(consumerTag, new Envelope(deliveryTag, redelivered, exchange, routingKey), properties, body, _queueName)); - - Signal(); - } - - public override void HandleModelShutdown(object model, RC.ShutdownEventArgs reason) - { - base.HandleModelShutdown(model, reason); - - if (!RabbitUtils.IsNormalChannelClose(reason)) - { - _completionSource.TrySetException(new ShutdownSignalException(reason)); - } - - Signal(); - } - - private void Signal() - { - if (!_latch.IsSet) - { - _latch.Signal(); - } - } - } - - protected abstract class AbstractTemplateConsumer : RC.DefaultBasicConsumer - { - protected AbstractTemplateConsumer(RC.IModel channel) - : base(channel) - { - } - - public override string ToString() - { - return $"TemplateConsumer [channel={Model}, consumerTag={ConsumerTag}]"; - } - } - - protected class ConfirmListener - { - private readonly Action _acks; - private readonly Action _nacks; - private readonly RC.IModel _channel; - - public ConfirmListener(Action acks, Action nacks, RC.IModel channel) - { - _channel = channel; - _acks = acks; - _nacks = nacks; - - _channel.BasicAcks += Channel_BasicAcks; - _channel.BasicNacks += Channel_BasicNacks; - } - - public virtual void Remove() - { - _channel.BasicAcks -= Channel_BasicAcks; - _channel.BasicNacks -= Channel_BasicNacks; - } - - private void Channel_BasicNacks(object sender, BasicNackEventArgs args) - { - _nacks(sender, args); - } - - private void Channel_BasicAcks(object sender, BasicAckEventArgs args) - { - _acks(sender, args); - } - } - - public interface IConfirmCallback - { - void Confirm(CorrelationData correlationData, bool ack, string cause); - } - - private sealed class PendingReplyReturn : IReturnCallback - { - private readonly PendingReply _pendingReply; - - public PendingReplyReturn(PendingReply pendingReply) - { - _pendingReply = pendingReply; - } - - public void ReturnedMessage(IMessage message, int replyCode, string replyText, string exchange, string routingKey) - { - _pendingReply.Returned(new RabbitMessageReturnedException("Message returned", message, replyCode, replyText, exchange, routingKey)); - } - } - - public interface IReturnCallback - { - void ReturnedMessage(IMessage message, int replyCode, string replyText, string exchange, string routingKey); - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/ImmediateAcknowledgeException.cs b/src/Messaging/src/RabbitMQ/Exceptions/ImmediateAcknowledgeException.cs deleted file mode 100644 index 317a78793e..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/ImmediateAcknowledgeException.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class ImmediateAcknowledgeException : RabbitException -{ - public ImmediateAcknowledgeException(string message) - : base(message) - { - } - - public ImmediateAcknowledgeException(Exception innerException) - : base(innerException) - { - } - - public ImmediateAcknowledgeException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/ImmediateRequeueException.cs b/src/Messaging/src/RabbitMQ/Exceptions/ImmediateRequeueException.cs deleted file mode 100644 index 784757cb77..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/ImmediateRequeueException.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class ImmediateRequeueException : RabbitException -{ - public ImmediateRequeueException(string message) - : base(message) - { - } - - public ImmediateRequeueException(Exception innerException) - : base(innerException) - { - } - - public ImmediateRequeueException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitApplicationContextClosedException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitApplicationContextClosedException.cs deleted file mode 100644 index b8d96f4238..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitApplicationContextClosedException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitApplicationContextClosedException : RabbitException -{ - public RabbitApplicationContextClosedException(string message) - : base(message) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitAuthenticationException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitAuthenticationException.cs deleted file mode 100644 index d38d8de6e3..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitAuthenticationException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitAuthenticationException : RabbitException -{ - public RabbitAuthenticationException(Exception innerException) - : base(innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitClientException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitClientException.cs deleted file mode 100644 index 7c93b67670..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitClientException.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitClientException : RabbitException -{ - public RabbitClientException(RabbitMQClientException innerException) - : base(innerException) - { - } - - public RabbitClientException(string message, RabbitMQClientException innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitConnectException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitConnectException.cs deleted file mode 100644 index 80b605f268..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitConnectException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitConnectException : RabbitException -{ - public RabbitConnectException(Exception innerException) - : base(innerException) - { - } - - public RabbitConnectException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitException.cs deleted file mode 100644 index 9011263eb7..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitException.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitException : Exception -{ - public RabbitException(string message) - : base(message) - { - } - - public RabbitException(Exception innerException) - : base(null, innerException) - { - } - - public RabbitException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitIOException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitIOException.cs deleted file mode 100644 index 18492eae42..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitIOException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitIOException : RabbitException -{ - public RabbitIOException(Exception innerException) - : base(innerException) - { - } - - public RabbitIOException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitIllegalStateException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitIllegalStateException.cs deleted file mode 100644 index ea2ff62518..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitIllegalStateException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitIllegalStateException : RabbitException -{ - public RabbitIllegalStateException(string message) - : base(message) - { - } - - public RabbitIllegalStateException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitMessageReturnedException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitMessageReturnedException.cs deleted file mode 100644 index 823afadc05..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitMessageReturnedException.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitMessageReturnedException : RabbitException -{ - public IMessage ReturnedMessage { get; } - public int ReplyCode { get; } - public string ReplyText { get; } - public string Exchange { get; } - public string RoutingKey { get; } - - public RabbitMessageReturnedException(string message, IMessage returnedMessage, int replyCode, string replyText, string exchange, string routingKey) - : base(message) - { - ReturnedMessage = returnedMessage; - ReplyCode = replyCode; - ReplyText = replyText; - Exchange = exchange; - RoutingKey = routingKey; - } - - public override string ToString() - { - return - $"AmqpMessageReturnedException: {Message}[returnedMessage={ReturnedMessage}, replyCode={ReplyCode}, replyText={ReplyText}, exchange={Exchange}, routingKey={RoutingKey}]"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitRejectAndDoNotRequeueException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitRejectAndDoNotRequeueException.cs deleted file mode 100644 index 2f69a8f84d..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitRejectAndDoNotRequeueException.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitRejectAndDoNotRequeueException : RabbitException -{ - public bool IsRejectManual { get; } - - public RabbitRejectAndDoNotRequeueException(string message) - : this(message, false, null) - { - } - - public RabbitRejectAndDoNotRequeueException(Exception innerException) - : this(null, false, innerException) - { - } - - public RabbitRejectAndDoNotRequeueException(string message, Exception innerException) - : this(message, false, innerException) - { - } - - public RabbitRejectAndDoNotRequeueException(string message, bool rejectManual, Exception innerException) - : base(message, innerException) - { - IsRejectManual = rejectManual; - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitRemoteException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitRemoteException.cs deleted file mode 100644 index 86c65e68e5..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitRemoteException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitRemoteException : RabbitException -{ - public RabbitRemoteException(Exception innerException) - : base(innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitResourceNotAvailableException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitResourceNotAvailableException.cs deleted file mode 100644 index d2ec3f1c27..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitResourceNotAvailableException.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitResourceNotAvailableException : RabbitException -{ - public RabbitResourceNotAvailableException(string message) - : base(message) - { - } - - public RabbitResourceNotAvailableException(Exception innerException) - : base(innerException) - { - } - - public RabbitResourceNotAvailableException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitTimeoutException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitTimeoutException.cs deleted file mode 100644 index 3c974d1905..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitTimeoutException.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitTimeoutException : RabbitException -{ - public RabbitTimeoutException(string message) - : base(message) - { - } - - public RabbitTimeoutException(Exception innerException) - : base(innerException) - { - } - - public RabbitTimeoutException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitUncategorizedException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitUncategorizedException.cs deleted file mode 100644 index 6ae1347250..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitUncategorizedException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitUncategorizedException : RabbitException -{ - public RabbitUncategorizedException(Exception innerException) - : base(innerException) - { - } - - public RabbitUncategorizedException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Exceptions/RabbitUnsupportedEncodingException.cs b/src/Messaging/src/RabbitMQ/Exceptions/RabbitUnsupportedEncodingException.cs deleted file mode 100644 index d22874efa0..0000000000 --- a/src/Messaging/src/RabbitMQ/Exceptions/RabbitUnsupportedEncodingException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Exceptions; - -public class RabbitUnsupportedEncodingException : RabbitException -{ - public RabbitUnsupportedEncodingException(Exception innerException) - : base(innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Extensions/ApplicationContextExtensions.cs b/src/Messaging/src/RabbitMQ/Extensions/ApplicationContextExtensions.cs deleted file mode 100644 index 1ca2d55d79..0000000000 --- a/src/Messaging/src/RabbitMQ/Extensions/ApplicationContextExtensions.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Extensions; - -public static class ApplicationContextExtensions -{ - public static RabbitTemplate GetRabbitTemplate(this IApplicationContext context, string name = null) - { - return context.ServiceProvider.GetRabbitTemplate(name); - } - - public static IRabbitAdmin GetRabbitAdmin(this IApplicationContext context, string name = null) - { - return context.ServiceProvider.GetRabbitAdmin(name); - } - - public static IQueue GetRabbitQueue(this IApplicationContext context, string name) - { - return context.ServiceProvider.GetRabbitQueue(name); - } - - public static IExchange GetRabbitExchange(this IApplicationContext context, string name) - { - return context.ServiceProvider.GetRabbitExchange(name); - } - - public static IBinding GetRabbitBinding(this IApplicationContext context, string name) - { - return context.ServiceProvider.GetRabbitBinding(name); - } - - public static IEnumerable GetRabbitQueues(this IApplicationContext context) - { - return context.ServiceProvider.GetRabbitQueues(); - } - - public static IEnumerable GetRabbitExchanges(this IApplicationContext context) - { - return context.ServiceProvider.GetRabbitExchanges(); - } - - public static IEnumerable GetRabbitBindings(this IApplicationContext context) - { - return context.ServiceProvider.GetRabbitBindings(); - } -} diff --git a/src/Messaging/src/RabbitMQ/Extensions/MessageBuilderExtensions.cs b/src/Messaging/src/RabbitMQ/Extensions/MessageBuilderExtensions.cs deleted file mode 100644 index a7fdd25aa1..0000000000 --- a/src/Messaging/src/RabbitMQ/Extensions/MessageBuilderExtensions.cs +++ /dev/null @@ -1,251 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.RabbitMQ.Extensions; - -public static class MessageBuilderExtensions -{ - public static AbstractMessageBuilder SetTimestamp(this AbstractMessageBuilder builder, long timestamp) - { - builder.SetHeader(MessageHeaders.TimestampName, timestamp); - return builder; - } - - public static AbstractMessageBuilder SetMessageId(this AbstractMessageBuilder builder, string messageId) - { - builder.SetHeader(MessageHeaders.IdName, messageId); - return builder; - } - - public static AbstractMessageBuilder SetUserId(this AbstractMessageBuilder builder, string userId) - { - builder.SetHeader(RabbitMessageHeaders.UserId, userId); - return builder; - } - - public static AbstractMessageBuilder SetAppId(this AbstractMessageBuilder builder, string appId) - { - builder.SetHeader(RabbitMessageHeaders.AppId, appId); - return builder; - } - - public static AbstractMessageBuilder SetClusterId(this AbstractMessageBuilder builder, string clusterId) - { - builder.SetHeader(RabbitMessageHeaders.ClusterId, clusterId); - return builder; - } - - public static AbstractMessageBuilder SetType(this AbstractMessageBuilder builder, string type) - { - builder.SetHeader(RabbitMessageHeaders.Type, type); - return builder; - } - - public static AbstractMessageBuilder SetCorrelationId(this AbstractMessageBuilder builder, string correlationId) - { - builder.SetHeader(RabbitMessageHeaders.CorrelationId, correlationId); - return builder; - } - - public static AbstractMessageBuilder SetReplyTo(this AbstractMessageBuilder builder, string replyTo) - { - builder.SetHeader(RabbitMessageHeaders.ReplyTo, replyTo); - return builder; - } - - public static AbstractMessageBuilder SetReplyToAddress(this AbstractMessageBuilder builder, Address replyTo) - { - builder.SetHeader(RabbitMessageHeaders.ReplyTo, replyTo.ToString()); - return builder; - } - - public static AbstractMessageBuilder SetContentType(this AbstractMessageBuilder builder, string contentType) - { - builder.SetHeader(RabbitMessageHeaders.ContentType, contentType); - return builder; - } - - public static AbstractMessageBuilder SetContentEncoding(this AbstractMessageBuilder builder, string contentEncoding) - { - builder.SetHeader(RabbitMessageHeaders.ContentEncoding, contentEncoding); - return builder; - } - - public static AbstractMessageBuilder SetContentLength(this AbstractMessageBuilder builder, long contentLength) - { - builder.SetHeader(RabbitMessageHeaders.ContentLength, contentLength); - return builder; - } - - public static AbstractMessageBuilder SetDeliveryMode(this AbstractMessageBuilder builder, MessageDeliveryMode deliveryMode) - { - builder.SetHeader(RabbitMessageHeaders.DeliveryMode, deliveryMode); - return builder; - } - - public static AbstractMessageBuilder SetExpiration(this AbstractMessageBuilder builder, string expiration) - { - builder.SetHeader(RabbitMessageHeaders.Expiration, expiration); - return builder; - } - - public static AbstractMessageBuilder SetPriority(this AbstractMessageBuilder builder, int priority) - { - builder.SetHeader(RabbitMessageHeaders.Priority, priority); - return builder; - } - - public static AbstractMessageBuilder SetReceivedExchange(this AbstractMessageBuilder builder, string receivedExchange) - { - builder.SetHeader(RabbitMessageHeaders.ReceivedExchange, receivedExchange); - return builder; - } - - public static AbstractMessageBuilder SetReceivedRoutingKey(this AbstractMessageBuilder builder, string receivedRoutingKey) - { - builder.SetHeader(RabbitMessageHeaders.ReceivedRoutingKey, receivedRoutingKey); - return builder; - } - - public static AbstractMessageBuilder SetRedelivered(this AbstractMessageBuilder builder, bool redelivered) - { - builder.SetHeader(RabbitMessageHeaders.Redelivered, redelivered); - return builder; - } - - public static AbstractMessageBuilder SetDeliveryTag(this AbstractMessageBuilder builder, ulong deliveryTag) - { - builder.SetHeader(RabbitMessageHeaders.DeliveryTag, deliveryTag); - return builder; - } - - public static AbstractMessageBuilder SetMessageCount(this AbstractMessageBuilder builder, uint messageCount) - { - builder.SetHeader(RabbitMessageHeaders.MessageCount, messageCount); - return builder; - } - - public static AbstractMessageBuilder SetTimestampIfAbsent(this AbstractMessageBuilder builder, long timestamp) - { - builder.SetHeaderIfAbsent(MessageHeaders.TimestampName, timestamp); - return builder; - } - - public static AbstractMessageBuilder SetMessageIdIfAbsent(this AbstractMessageBuilder builder, string messageId) - { - builder.SetHeaderIfAbsent(MessageHeaders.IdName, messageId); - return builder; - } - - public static AbstractMessageBuilder SetUserIdIfAbsent(this AbstractMessageBuilder builder, string userId) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.UserId, userId); - return builder; - } - - public static AbstractMessageBuilder SetAppIdIfAbsent(this AbstractMessageBuilder builder, string appId) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.AppId, appId); - return builder; - } - - public static AbstractMessageBuilder SetClusterIdIfAbsent(this AbstractMessageBuilder builder, string clusterId) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.ClusterId, clusterId); - return builder; - } - - public static AbstractMessageBuilder SetTypeIfAbsent(this AbstractMessageBuilder builder, string type) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.Type, type); - return builder; - } - - public static AbstractMessageBuilder SetCorrelationIdIfAbsent(this AbstractMessageBuilder builder, string correlationId) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.CorrelationId, correlationId); - return builder; - } - - public static AbstractMessageBuilder SetReplyToIfAbsent(this AbstractMessageBuilder builder, string replyTo) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.ReplyTo, replyTo); - return builder; - } - - public static AbstractMessageBuilder SetReplyToAddressIfAbsent(this AbstractMessageBuilder builder, Address replyTo) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.ReplyTo, replyTo.ToString()); - return builder; - } - - public static AbstractMessageBuilder SetContentTypeIfAbsent(this AbstractMessageBuilder builder, string contentType) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.ContentType, contentType); - return builder; - } - - public static AbstractMessageBuilder SetContentEncodingIfAbsent(this AbstractMessageBuilder builder, string contentEncoding) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.ContentEncoding, contentEncoding); - return builder; - } - - public static AbstractMessageBuilder SetContentLengthIfAbsent(this AbstractMessageBuilder builder, long contentLength) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.ContentLength, contentLength); - return builder; - } - - public static AbstractMessageBuilder SetDeliveryModeIfAbsent(this AbstractMessageBuilder builder, MessageDeliveryMode deliveryMode) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.DeliveryMode, deliveryMode); - return builder; - } - - public static AbstractMessageBuilder SetExpirationIfAbsent(this AbstractMessageBuilder builder, string expiration) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.Expiration, expiration); - return builder; - } - - public static AbstractMessageBuilder SetPriorityIfAbsent(this AbstractMessageBuilder builder, int priority) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.Priority, priority); - return builder; - } - - public static AbstractMessageBuilder SetReceivedExchangeIfAbsent(this AbstractMessageBuilder builder, string receivedExchange) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.ReceivedExchange, receivedExchange); - return builder; - } - - public static AbstractMessageBuilder SetReceivedRoutingKeyIfAbsent(this AbstractMessageBuilder builder, string receivedRoutingKey) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.ReceivedRoutingKey, receivedRoutingKey); - return builder; - } - - public static AbstractMessageBuilder SetRedeliveredIfAbsent(this AbstractMessageBuilder builder, bool redelivered) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.Redelivered, redelivered); - return builder; - } - - public static AbstractMessageBuilder SetDeliveryTagIfAbsent(this AbstractMessageBuilder builder, ulong deliveryTag) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.DeliveryTag, deliveryTag); - return builder; - } - - public static AbstractMessageBuilder SetMessageCountIfAbsent(this AbstractMessageBuilder builder, uint messageCount) - { - builder.SetHeaderIfAbsent(RabbitMessageHeaders.MessageCount, messageCount); - return builder; - } -} diff --git a/src/Messaging/src/RabbitMQ/Extensions/MessageHeaderExtensions.cs b/src/Messaging/src/RabbitMQ/Extensions/MessageHeaderExtensions.cs deleted file mode 100644 index 22303f569f..0000000000 --- a/src/Messaging/src/RabbitMQ/Extensions/MessageHeaderExtensions.cs +++ /dev/null @@ -1,215 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Extensions; - -public static class MessageHeaderExtensions -{ - public static string AppId(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.AppId); - } - - public static string ClusterId(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ClusterId); - } - - public static string ConsumerQueue(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ConsumerQueue); - } - - public static string ConsumerTag(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ConsumerTag); - } - - public static string ContentEncoding(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ContentEncoding); - } - - public static long? ContentLength(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ContentLength); - } - - public static bool IsContentLengthSet(this IMessageHeaders headers) - { - long? len = ContentLength(headers); - - if (len.HasValue) - { - return true; - } - - return false; - } - - public static string ContentType(this IMessageHeaders headers) - { - object contentType = headers.Get(RabbitMessageHeaders.ContentType); - return contentType?.ToString(); - } - - public static string CorrelationId(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.CorrelationId); - } - - public static int? Delay(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.XDelay); - } - - public static MessageDeliveryMode? DeliveryMode(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.DeliveryMode); - } - - public static ulong? DeliveryTag(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.DeliveryTag); - } - - public static bool IsDeliveryTagSet(this IMessageHeaders headers) - { - ulong? result = DeliveryTag(headers); - - if (result.HasValue) - { - return true; - } - - return false; - } - - public static string Expiration(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.Expiration); - } - - public static Type InferredArgumentType(this IMessageHeaders headers) - { - return headers.Get(MessageHeaders.InferredArgumentType); - } - - public static uint? MessageCount(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.MessageCount); - } - - public static string MessageId(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.MessageId); - } - - public static int? Priority(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.Priority); - } - - public static ulong? PublishSequenceNumber(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.PublishSequenceNumber); - } - - public static int? ReceivedDelay(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ReceivedDelay); - } - - public static MessageDeliveryMode? ReceivedDeliveryMode(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ReceivedDeliveryMode); - } - - public static string ReceivedExchange(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ReceivedExchange); - } - - public static string ReceivedRoutingKey(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ReceivedRoutingKey); - } - - public static string ReceivedUserId(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ReceivedUserId); - } - - public static bool? Redelivered(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.Redelivered); - } - - public static string ReplyTo(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.ReplyTo); - } - - public static Address ReplyToAddress(this IMessageHeaders headers) - { - string results = headers.ReplyTo(); - - if (results != null) - { - return new Address(results); - } - - return null; - } - - public static object Target(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.Target); - } - - public static MethodInfo TargetMethod(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.TargetMethod); - } - - public static long? Timestamp(this IMessageHeaders headers) - { - return headers.Get(MessageHeaders.TimestampName); - } - - public static string Type(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.Type); - } - - public static string UserId(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.UserId); - } - - public static bool? FinalRetryForMessageWithNoId(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.FinalRetryForMessageWithNoId); - } - - public static bool IsFinalRetryForMessageWithNoId(this IMessageHeaders headers) - { - bool? result = FinalRetryForMessageWithNoId(headers); - - if (result.HasValue) - { - return result.Value; - } - - return false; - } - - public static bool? LastInBatch(this IMessageHeaders headers) - { - return headers.Get(RabbitMessageHeaders.LastInBatch); - } -} diff --git a/src/Messaging/src/RabbitMQ/Extensions/RabbitListenerExtensions.cs b/src/Messaging/src/RabbitMQ/Extensions/RabbitListenerExtensions.cs deleted file mode 100644 index a34f3a884f..0000000000 --- a/src/Messaging/src/RabbitMQ/Extensions/RabbitListenerExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common; -using Steeltoe.Messaging.RabbitMQ.Configuration; - -namespace Steeltoe.Messaging.RabbitMQ.Extensions; - -public static class RabbitListenerExtensions -{ - public static IServiceCollection AddRabbitListeners(this IServiceCollection services, IConfiguration configuration, params Type[] listenerServices) - { - ArgumentGuard.NotNull(services); - - foreach (Type t in listenerServices) - { - var metadata = RabbitListenerMetadata.BuildMetadata(services, t); - - if (metadata != null) - { - services.AddSingleton(metadata); - } - - RabbitListenerDeclareAttributeProcessor.ProcessDeclareAttributes(services, configuration, t); - } - - return services; - } - - public static IServiceCollection AddRabbitListeners(this IServiceCollection services, IConfiguration configuration = null) - where T : class - { - return services.AddRabbitListeners(configuration, typeof(T)); - } -} diff --git a/src/Messaging/src/RabbitMQ/Extensions/RabbitServicesExtensions.cs b/src/Messaging/src/RabbitMQ/Extensions/RabbitServicesExtensions.cs deleted file mode 100644 index 213ee10ef5..0000000000 --- a/src/Messaging/src/RabbitMQ/Extensions/RabbitServicesExtensions.cs +++ /dev/null @@ -1,740 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Connectors.RabbitMQ; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Hosting; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using static Steeltoe.Common.Contexts.AbstractApplicationContext; -using RC = RabbitMQ.Client; -using SimpleMessageConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter.SimpleMessageConverter; - -namespace Steeltoe.Messaging.RabbitMQ.Extensions; - -public static class RabbitServicesExtensions -{ - public static IServiceCollection AddRabbitTemplate(this IServiceCollection services) - { - return services.AddRabbitTemplate(RabbitTemplate.DefaultServiceName); - } - - public static IServiceCollection AddRabbitTemplate(this IServiceCollection services, Action configure) - { - return services.AddRabbitTemplate(RabbitTemplate.DefaultServiceName, configure); - } - - public static IServiceCollection AddRabbitTemplate(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - services.AddSingleton(p => - { - var template = ActivatorUtilities.CreateInstance(p, typeof(RabbitTemplate)) as RabbitTemplate; - template.ServiceName = serviceName; - - if (configure != null) - { - configure(p, template); - } - - return template; - }); - - services.AddSingleton(p => - { - return p.GetServices().SingleOrDefault(t => t.ServiceName == serviceName); - }); - - return services; - } - - public static IServiceCollection AddRabbitAdmin(this IServiceCollection services) - { - return services.AddRabbitAdmin(RabbitAdmin.DefaultServiceName); - } - - public static IServiceCollection AddRabbitAdmin(this IServiceCollection services, Action configure) - { - return services.AddRabbitAdmin(RabbitAdmin.DefaultServiceName, configure); - } - - public static IServiceCollection AddRabbitAdmin(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - services.AddSingleton(p => - { - var admin = ActivatorUtilities.CreateInstance(p, typeof(RabbitAdmin)) as RabbitAdmin; - admin.ServiceName = serviceName; - - if (configure != null) - { - configure(p, admin); - } - - return admin; - }); - - services.AddSingleton(p => - { - return p.GetServices().SingleOrDefault(t => t.ServiceName == serviceName); - }); - - return services; - } - - public static IServiceCollection AddRabbitQueues(this IServiceCollection services, params IQueue[] queues) - { - foreach (IQueue q in queues) - { - services.AddRabbitQueue(q); - } - - return services; - } - - public static IServiceCollection AddRabbitQueue(this IServiceCollection services, IQueue queue) - { - services.RegisterService(queue.ServiceName, typeof(IQueue)); - return services.AddSingleton(queue); - } - - public static IServiceCollection AddRabbitQueue(this IServiceCollection services, Func factory) - { - services.AddSingleton(factory); - - return services; - } - - public static IServiceCollection AddRabbitQueue(this IServiceCollection services, string queueName, Action configure = null) - { - services.RegisterService(queueName, typeof(IQueue)); - - services.AddSingleton(p => - { - var queue = new Queue(queueName); - - if (configure != null) - { - configure(p, queue); - } - - return queue; - }); - - return services; - } - - public static IServiceCollection AddRabbitExchanges(this IServiceCollection services, params IExchange[] exchanges) - { - foreach (IExchange e in exchanges) - { - services.AddRabbitExchange(e); - } - - return services; - } - - public static IServiceCollection AddRabbitExchange(this IServiceCollection services, Func factory) - { - services.AddSingleton(factory); - - return services; - } - - public static IServiceCollection AddRabbitExchange(this IServiceCollection services, IExchange exchange) - { - return services.AddSingleton(exchange); - } - - public static IServiceCollection AddRabbitExchange(this IServiceCollection services, string exchangeName, string exchangeType, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(exchangeName); - ArgumentGuard.NotNullOrEmpty(exchangeType); - - services.AddSingleton(p => - { - IExchange exchange = ExchangeBuilder.Create(exchangeName, exchangeType); - - if (configure != null) - { - configure(p, exchange); - } - - return exchange; - }); - - return services; - } - - public static IServiceCollection AddRabbitBindings(this IServiceCollection services, params IBinding[] bindings) - { - foreach (IBinding b in bindings) - { - services.AddRabbitBinding(b); - } - - return services; - } - - public static IServiceCollection AddRabbitBinding(this IServiceCollection services, Func factory) - { - services.AddSingleton(factory); - - return services; - } - - public static IServiceCollection AddRabbitBinding(this IServiceCollection services, IBinding binding) - { - services.RegisterService(binding.ServiceName, typeof(IBinding)); - services.AddSingleton(binding); - return services; - } - - public static IServiceCollection AddRabbitBinding(this IServiceCollection services, string bindingName, Binding.DestinationType bindingType, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(bindingName); - - services.RegisterService(bindingName, typeof(IBinding)); - - services.AddSingleton(p => - { - IBinding binding = BindingBuilder.Create(bindingName, bindingType); - - if (configure != null) - { - configure(p, binding); - } - - return binding; - }); - - return services; - } - - public static IServiceCollection ConfigureRabbitOptions(this IServiceCollection services, IConfiguration configuration) - { - services.AddOptions().Bind(configuration.GetSection(RabbitOptions.Prefix)).Configure((options, provider) => - { - RC.ConnectionFactory connectionFactory = TryCreateRabbitConnectionFactory(provider, string.Empty); - - if (connectionFactory is not null) - { - options.Addresses = $"{connectionFactory.UserName}:{connectionFactory.Password}@{connectionFactory.HostName}:{connectionFactory.Port}"; - options.VirtualHost = connectionFactory.VirtualHost; - options.Host = connectionFactory.HostName; - options.Username = connectionFactory.UserName; - options.Password = connectionFactory.Password; - } - }); - - return services; - } - - public static IServiceCollection AddRabbitServices(this IServiceCollection services, bool useJsonMessageConverter = false) - { - ArgumentGuard.NotNull(services); - - services.AddRabbitHostingServices(); - - services.AddRabbitConnectionFactory(); - - if (useJsonMessageConverter) - { - services.AddRabbitJsonMessageConverter(); - } - else - { - services.AddRabbitDefaultMessageConverter(); - } - - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerContainerFactory(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - services.AddRabbitListenerAttributeProcessor(); - return services; - } - - public static IServiceCollection AddRabbitHostingServices(this IServiceCollection services) - { - services.AddOptions(); - services.AddHostedService(); - services.TryAddSingleton(); - - services.TryAddSingleton(p => - { - var context = new GenericApplicationContext(p.GetRequiredService(), p.GetService(), - p.GetServices()); - - context.ServiceExpressionResolver = new StandardServiceExpressionResolver(); - return context; - }); - - return services; - } - - public static IServiceCollection AddRabbitListenerAttributeProcessor(this IServiceCollection services, - Action configure = null) - { - return services.AddRabbitListenerAttributeProcessor(RabbitListenerAttributeProcessor.DefaultServiceName, configure); - } - - public static IServiceCollection AddRabbitListenerAttributeProcessor(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - services.AddRabbitListenerAttributeProcessor(serviceName, configure); - return services; - } - - public static IServiceCollection AddRabbitListenerAttributeProcessor(this IServiceCollection services, - Action configure) - where TProcessor : IRabbitListenerAttributeProcessor - { - return services.AddRabbitListenerAttributeProcessor(null, configure); - } - - public static IServiceCollection AddRabbitListenerAttributeProcessor(this IServiceCollection services, string serviceName = null, - Action configure = null) - where TProcessor : IRabbitListenerAttributeProcessor - { - services.TryAddSingleton(p => - { - var instance = (TProcessor)ActivatorUtilities.GetServiceOrCreateInstance(p, typeof(TProcessor)); - - if (!string.IsNullOrEmpty(serviceName)) - { - instance.ServiceName = serviceName; - } - - if (configure != null) - { - configure(p, instance); - } - - return instance; - }); - - return services; - } - - public static IServiceCollection AddRabbitListenerEndpointRegistrar(this IServiceCollection services, - Action configure = null) - { - services.AddRabbitListenerEndpointRegistrar(RabbitListenerEndpointRegistrar.DefaultServiceName, configure); - return services; - } - - public static IServiceCollection AddRabbitListenerEndpointRegistrar(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - services.AddRabbitListenerEndpointRegistrar(serviceName, configure); - return services; - } - - public static IServiceCollection AddRabbitListenerEndpointRegistrar(this IServiceCollection services, - Action configure) - where TRegistrar : IRabbitListenerEndpointRegistrar - { - return services.AddRabbitListenerEndpointRegistrar(null, configure); - } - - public static IServiceCollection AddRabbitListenerEndpointRegistrar(this IServiceCollection services, string serviceName = null, - Action configure = null) - where TRegistrar : IRabbitListenerEndpointRegistrar - { - services.TryAddSingleton(p => - { - var instance = (TRegistrar)ActivatorUtilities.GetServiceOrCreateInstance(p, typeof(TRegistrar)); - - if (!string.IsNullOrEmpty(serviceName)) - { - instance.ServiceName = serviceName; - } - - if (configure != null) - { - configure(p, instance); - } - - return instance; - }); - - return services; - } - - public static IServiceCollection AddRabbitListenerEndpointRegistry(this IServiceCollection services, - Action configure = null) - { - services.AddRabbitListenerEndpointRegistry(RabbitListenerEndpointRegistry.DefaultServiceName, configure); - return services; - } - - public static IServiceCollection AddRabbitListenerEndpointRegistry(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - services.AddRabbitListenerEndpointRegistry(serviceName, configure); - return services; - } - - public static IServiceCollection AddRabbitListenerEndpointRegistry(this IServiceCollection services, - Action configure) - where TRegistry : IRabbitListenerEndpointRegistry - { - return services.AddRabbitListenerEndpointRegistry(null, configure); - } - - public static IServiceCollection AddRabbitListenerEndpointRegistry(this IServiceCollection services, string serviceName = null, - Action configure = null) - where TRegistry : IRabbitListenerEndpointRegistry - { - services.TryAddSingleton(p => - { - var instance = (TRegistry)ActivatorUtilities.GetServiceOrCreateInstance(p, typeof(TRegistry)); - - if (!string.IsNullOrEmpty(serviceName)) - { - instance.ServiceName = serviceName; - } - - if (configure != null) - { - configure(p, instance); - } - - return instance; - }); - - services.AddSingleton(p => - { - var instance = p.GetRequiredService() as ILifecycle; - return instance; - }); - - return services; - } - - public static IServiceCollection AddRabbitListenerContainerFactory(this IServiceCollection services, - Action configure = null) - { - return services.AddRabbitListenerContainerFactory(DirectRabbitListenerContainerFactory.DefaultServiceName, - configure); - } - - public static IServiceCollection AddRabbitListenerContainerFactory(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - return services.AddRabbitListenerContainerFactory(serviceName, configure); - } - - public static IServiceCollection AddRabbitListenerContainerFactory(this IServiceCollection services, Action configure) - where TFactory : IRabbitListenerContainerFactory - { - return services.AddRabbitListenerContainerFactory(null, configure); - } - - public static IServiceCollection AddRabbitListenerContainerFactory(this IServiceCollection services, string serviceName = null, - Action configure = null) - where TFactory : IRabbitListenerContainerFactory - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - services.AddSingleton(p => - { - var instance = (TFactory)ActivatorUtilities.CreateInstance(p, typeof(TFactory)); - - if (!string.IsNullOrEmpty(serviceName)) - { - instance.ServiceName = serviceName; - } - - if (configure != null) - { - configure(p, instance); - } - - return instance; - }); - - return services; - } - - public static IServiceCollection AddRabbitConnectionFactory(this IServiceCollection services, - Action configure = null) - { - return services.AddRabbitConnectionFactory(CachingConnectionFactory.DefaultServiceName, configure); - } - - public static IServiceCollection AddRabbitConnectionFactory(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - return services.AddRabbitConnectionFactory(serviceName, configure); - } - - public static IServiceCollection AddRabbitConnectionFactory(this IServiceCollection services, Action configure) - where TFactory : IConnectionFactory - { - return services.AddRabbitConnectionFactory(null, configure); - } - - public static IServiceCollection AddRabbitConnectionFactory(this IServiceCollection services, string serviceName = null, - Action configure = null) - where TFactory : IConnectionFactory - { - services.AddSingleton(provider => - { - RC.ConnectionFactory rabbitConnectionFactory = TryCreateRabbitConnectionFactory(provider, serviceName); - - IConnectionFactory instance = rabbitConnectionFactory is not null && typeof(TFactory) == typeof(CachingConnectionFactory) - ? new CachingConnectionFactory(rabbitConnectionFactory) - : (TFactory)ActivatorUtilities.GetServiceOrCreateInstance(provider, typeof(TFactory)); - - if (!string.IsNullOrEmpty(serviceName)) - { - instance.ServiceName = serviceName; - } - - if (configure != null) - { - configure(provider, (TFactory)instance); - } - - return instance; - }); - - return services; - } - - private static RC.ConnectionFactory TryCreateRabbitConnectionFactory(IServiceProvider serviceProvider, string serviceName) - { - var optionsMonitor = serviceProvider.GetRequiredService>(); - RabbitMQOptions options = optionsMonitor.Get(serviceName); - - if (!string.IsNullOrEmpty(options.ConnectionString)) - { - var rabbitConnectionFactory = new RC.ConnectionFactory - { - Uri = new Uri(options.ConnectionString) - }; - - return rabbitConnectionFactory; - } - - return null; - } - - public static IServiceCollection AddRabbitJsonMessageConverter(this IServiceCollection services, - Action configure = null) - { - return services.AddRabbitMessageConverter(JsonMessageConverter.DefaultServiceName, configure); - } - - public static IServiceCollection AddRabbitJsonMessageConverter(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - return services.AddRabbitMessageConverter(serviceName, configure); - } - - public static IServiceCollection AddRabbitDefaultMessageConverter(this IServiceCollection services, - Action configure = null) - { - return services.AddRabbitMessageConverter(Converter.SimpleMessageConverter.DefaultServiceName, configure); - } - - public static IServiceCollection AddRabbitDefaultMessageConverter(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - return services.AddRabbitMessageConverter(serviceName, configure); - } - - public static IServiceCollection AddRabbitMessageConverter(this IServiceCollection services, Action configure) - where TConverter : ISmartMessageConverter - { - return services.AddRabbitMessageConverter(null, configure); - } - - public static IServiceCollection AddRabbitMessageConverter(this IServiceCollection services, string serviceName = null, - Action configure = null) - where TConverter : ISmartMessageConverter - { - services.TryAddSingleton(p => - { - var instance = (TConverter)ActivatorUtilities.GetServiceOrCreateInstance(p, typeof(TConverter)); - - if (!string.IsNullOrEmpty(serviceName)) - { - instance.ServiceName = serviceName; - } - - if (configure != null) - { - configure(p, instance); - } - - return instance; - }); - - return services; - } - - public static IServiceCollection AddRabbitMessageHandlerMethodFactory(this IServiceCollection services, - Action configure = null) - { - return services.AddRabbitMessageHandlerMethodFactory(RabbitMessageHandlerMethodFactory.DefaultServiceName, - configure); - } - - public static IServiceCollection AddRabbitMessageHandlerMethodFactory(this IServiceCollection services, string serviceName, - Action configure = null) - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - return services.AddRabbitMessageHandlerMethodFactory(serviceName, configure); - } - - public static IServiceCollection AddRabbitMessageHandlerMethodFactory(this IServiceCollection services, - Action configure) - where TFactory : IMessageHandlerMethodFactory - { - return services.AddRabbitMessageHandlerMethodFactory(null, configure); - } - - public static IServiceCollection AddRabbitMessageHandlerMethodFactory(this IServiceCollection services, string serviceName = null, - Action configure = null) - where TFactory : IMessageHandlerMethodFactory - { - services.TryAddSingleton(p => - { - var instance = (TFactory)ActivatorUtilities.GetServiceOrCreateInstance(p, typeof(TFactory)); - - if (!string.IsNullOrEmpty(serviceName)) - { - instance.ServiceName = serviceName; - } - - if (configure != null) - { - configure(p, instance); - } - - instance.Initialize(); - - return instance; - }); - - return services; - } - - public static IServiceCollection AddRabbitDirectListenerContainer(this IServiceCollection services, - Func factory) - { - return services.AddRabbitListenerContainer(factory); - } - - public static IServiceCollection AddRabbitDirectListenerContainer(this IServiceCollection services, string serviceName = null, - Action configure = null) - { - return services.AddRabbitListenerContainer(serviceName, configure); - } - - public static IServiceCollection AddRabbitListenerContainer(this IServiceCollection services, Func factory) - where TContainer : AbstractMessageListenerContainer - { - services.AddSingleton(factory); - - return services; - } - - public static IServiceCollection AddRabbitListenerContainer(this IServiceCollection services, string serviceName = null, - Action configure = null) - where TContainer : AbstractMessageListenerContainer - { - services.AddSingleton(p => - { - var instance = (TContainer)ActivatorUtilities.CreateInstance(p, typeof(TContainer)); - - if (!string.IsNullOrEmpty(serviceName)) - { - instance.ServiceName = serviceName; - } - - if (configure != null) - { - configure(p, instance); - } - - instance.Initialize(); - - return instance; - }); - - return services; - } - - public static IServiceCollection AddRabbitListenerErrorHandler(this IServiceCollection services, string serviceName, - Func factory) - where THandler : IRabbitListenerErrorHandler - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - services.RegisterService(serviceName, typeof(IRabbitListenerErrorHandler)); - - services.AddSingleton(p => - { - THandler result = factory(p); - result.ServiceName = serviceName; - return result; - }); - - return services; - } - - public static IServiceCollection AddRabbitListenerErrorHandler(this IServiceCollection services, string serviceName) - where THandler : IRabbitListenerErrorHandler - { - ArgumentGuard.NotNullOrEmpty(serviceName); - - services.RegisterService(serviceName, typeof(IRabbitListenerErrorHandler)); - - services.AddSingleton(p => - { - var instance = (THandler)ActivatorUtilities.CreateInstance(p, typeof(THandler)); - instance.ServiceName = serviceName; - return instance; - }); - - return services; - } -} diff --git a/src/Messaging/src/RabbitMQ/Extensions/ServiceProviderExtensions.cs b/src/Messaging/src/RabbitMQ/Extensions/ServiceProviderExtensions.cs deleted file mode 100644 index f7cad459e4..0000000000 --- a/src/Messaging/src/RabbitMQ/Extensions/ServiceProviderExtensions.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Extensions; - -public static class ServiceProviderExtensions -{ - public static RabbitTemplate GetRabbitTemplate(this IServiceProvider provider, string name = null) - { - string serviceName = name ?? RabbitTemplate.DefaultServiceName; - return provider.GetServices().SingleOrDefault(t => t.ServiceName == serviceName); - } - - public static RabbitAdmin GetRabbitAdmin(this IServiceProvider provider, string name = null) - { - string serviceName = name ?? RabbitAdmin.DefaultServiceName; - return provider.GetServices().SingleOrDefault(t => t.ServiceName == serviceName); - } - - public static IQueue GetRabbitQueue(this IServiceProvider provider, string name) - { - return provider.GetServices().SingleOrDefault(t => t.ServiceName == name); - } - - public static IExchange GetRabbitExchange(this IServiceProvider provider, string name) - { - return provider.GetServices().SingleOrDefault(t => t.ServiceName == name); - } - - public static IBinding GetRabbitBinding(this IServiceProvider provider, string name) - { - return provider.GetServices().SingleOrDefault(t => t.ServiceName == name); - } - - public static IEnumerable GetRabbitQueues(this IServiceProvider provider) - { - return provider.GetServices(); - } - - public static IEnumerable GetRabbitExchanges(this IServiceProvider provider) - { - return provider.GetServices(); - } - - public static IEnumerable GetRabbitBindings(this IServiceProvider provider) - { - return provider.GetServices(); - } - - public static IApplicationContext GetApplicationContext(this IServiceProvider provider) - { - return provider.GetService(); - } - - public static IConnectionFactory GetRabbitConnectionFactory(this IServiceProvider provider, string factoryName = null) - { - string serviceName = factoryName ?? CachingConnectionFactory.DefaultServiceName; - return provider.GetServices().SingleOrDefault(t => t.ServiceName == serviceName); - } -} diff --git a/src/Messaging/src/RabbitMQ/Hosting/RabbitHostService.cs b/src/Messaging/src/RabbitMQ/Hosting/RabbitHostService.cs deleted file mode 100644 index bc9c6e3899..0000000000 --- a/src/Messaging/src/RabbitMQ/Hosting/RabbitHostService.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Hosting; - -public class RabbitHostService : IHostedService -{ - private readonly IApplicationContext _applicationContext; - private readonly ILogger _logger; - - public RabbitHostService(IApplicationContext applicationContext, ILogger logger = null) - { - _applicationContext = applicationContext; - _logger = logger; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - _logger?.LogInformation("Starting RabbitHostService ..."); - - // Ensure any admins in the container get initialized - IEnumerable admins = _applicationContext.GetServices(); - - if (admins == null || !admins.Any()) - { - _logger?.LogInformation("Found no IRabbitAdmin services to initialize"); - } - - // Allow for processing of RabbitListenerAttributes - var listenerProcessor = _applicationContext.GetService(); - - if (listenerProcessor == null) - { - _logger?.LogInformation("Found no IRabbitListenerAttributeProcessor services to initialize"); - } - else - { - listenerProcessor.Initialize(); - } - - // Ensure any RabbitContainers get started - var processor = _applicationContext.GetService(); - - if (processor != null) - { - await processor.OnRefreshAsync(); - } - else - { - _logger?.LogInformation("Found no ILifecycleProcessor service to initialize"); - } - } - - public Task StopAsync(CancellationToken cancellationToken) - { - _logger?.LogInformation("Stopping RabbitHostService ..."); - return Task.CompletedTask; - } -} diff --git a/src/Messaging/src/RabbitMQ/Hosting/RabbitMQHost.cs b/src/Messaging/src/RabbitMQ/Hosting/RabbitMQHost.cs deleted file mode 100644 index 42cc6f9c4c..0000000000 --- a/src/Messaging/src/RabbitMQ/Hosting/RabbitMQHost.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Steeltoe.Common.Lifecycle; - -namespace Steeltoe.Messaging.RabbitMQ.Hosting; - -public sealed class RabbitMQHost : IHost -{ - private readonly IHost _host; - - public IServiceProvider Services => _host.Services; - - public RabbitMQHost(IHost host) - { - _host = host; - } - - public static IHostBuilder CreateDefaultBuilder() - { - return new RabbitMQHostBuilder(Host.CreateDefaultBuilder()); - } - - public static IHostBuilder CreateDefaultBuilder(string[] args) - { - return new RabbitMQHostBuilder(Host.CreateDefaultBuilder(args)); - } - - public void Dispose() - { - _host?.Dispose(); - } - - public async Task StartAsync(CancellationToken cancellationToken = default) - { - using (IServiceScope scope = _host.Services.CreateScope()) - { - var lifecycleProcessor = scope.ServiceProvider.GetRequiredService(); - await lifecycleProcessor.OnRefreshAsync(); - } - - await _host.StartAsync(cancellationToken); - } - - public async Task StopAsync(CancellationToken cancellationToken = default) - { - using (IServiceScope scope = _host.Services.CreateScope()) - { - var lifecycleProcessor = scope.ServiceProvider.GetRequiredService(); - await lifecycleProcessor.StopAsync(); - } - - await _host.StopAsync(cancellationToken); - } -} diff --git a/src/Messaging/src/RabbitMQ/Hosting/RabbitMQHostBuilder.cs b/src/Messaging/src/RabbitMQ/Hosting/RabbitMQHostBuilder.cs deleted file mode 100644 index 6794e59aa6..0000000000 --- a/src/Messaging/src/RabbitMQ/Hosting/RabbitMQHostBuilder.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Steeltoe.Configuration.SpringBoot; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Hosting; - -public class RabbitMQHostBuilder : IHostBuilder -{ - private readonly IHostBuilder _hostbuilder; - - public IDictionary Properties => _hostbuilder.Properties; - - public RabbitMQHostBuilder(IHostBuilder hostbuilder) - { - _hostbuilder = hostbuilder; - - _hostbuilder.ConfigureAppConfiguration(configBuilder => - { - configBuilder.AddSpringBootFromEnvironmentVariable(); - }).ConfigureServices((hostBuilderContext, services) => - { - IConfigurationSection rabbitConfigSection = hostBuilderContext.Configuration.GetSection(RabbitOptions.Prefix); - services.Configure(rabbitConfigSection); - - services.AddRabbitServices(); - services.AddRabbitAdmin(); - services.AddRabbitTemplate(); - }); - } - - public IHost Build() - { - IHost host = _hostbuilder.Build(); - - return new RabbitMQHost(host); - } - - public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) - { - _hostbuilder.ConfigureAppConfiguration(configureDelegate); - - return this; - } - - public IHostBuilder ConfigureContainer(Action configureDelegate) - { - _hostbuilder.ConfigureContainer(configureDelegate); - - return this; - } - - public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) - { - _hostbuilder.ConfigureHostConfiguration(configureDelegate); - - return this; - } - - public IHostBuilder ConfigureServices(Action configureDelegate) - { - _hostbuilder.ConfigureServices(configureDelegate); - - return this; - } - - public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) - { - _hostbuilder.UseServiceProviderFactory(factory); - - return this; - } - - public IHostBuilder UseServiceProviderFactory(Func> factory) - { - _hostbuilder.UseServiceProviderFactory(factory); - - return this; - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/AbstractMessageListenerContainer.cs b/src/Messaging/src/RabbitMQ/Listener/AbstractMessageListenerContainer.cs deleted file mode 100644 index f95f0a8a4f..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/AbstractMessageListenerContainer.cs +++ /dev/null @@ -1,1113 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Retry; -using Steeltoe.Common.Transaction; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener.Support; -using Steeltoe.Messaging.RabbitMQ.Support; -using R = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public abstract class AbstractMessageListenerContainer : IMessageListenerContainer -{ - public const int DefaultFailedDeclarationRetryInterval = 5000; - public const long DefaultShutdownTimeout = 5000; - public const int DefaultRecoveryInterval = 5000; - public const bool DefaultDebatchingEnabled = true; - public const int DefaultPrefetchCount = 250; - - protected readonly object ConsumersMonitor = new(); - protected readonly object Lock = new(); - protected readonly object LifecycleMonitor = new(); - protected readonly ILogger Logger; - protected readonly ILoggerFactory LoggerFactory; - - private string _listenerId; - private IConnectionFactory _connectionFactory; - - protected int recoveryInterval = DefaultRecoveryInterval; - - private List Queues { get; set; } = new(); - - protected virtual bool IsChannelLocallyTransacted => IsChannelTransacted && TransactionManager == null; - - protected virtual long LastReceive { get; private set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - protected virtual bool ForceCloseChannel { get; set; } = true; - public IRecoveryCallback Recoverer { get; set; } - - public virtual IConnectionFactory ConnectionFactory - { - get - { - _connectionFactory ??= ApplicationContext.GetService(); - return _connectionFactory; - } - set => _connectionFactory = value; - } - - public virtual bool IsChannelTransacted { get; set; } - - public IApplicationContext ApplicationContext { get; set; } - - public virtual AcknowledgeMode AcknowledgeMode { get; set; } = AcknowledgeMode.Auto; - - public virtual string ServiceName { get; set; } - - public virtual bool ExposeListenerChannel { get; set; } = true; - - public virtual IMessageListener MessageListener { get; set; } - - public virtual IErrorHandler ErrorHandler { get; set; } - - public virtual bool IsDeBatchingEnabled { get; set; } = DefaultDebatchingEnabled; - - public virtual IList AfterReceivePostProcessors { get; private set; } - - public virtual bool IsAutoStartup { get; set; } = true; - - public virtual int Phase { get; set; } = int.MaxValue; - - public virtual string LookupKeyQualifier { get; set; } = string.Empty; - - public virtual string ListenerId - { - get => _listenerId ?? ServiceName; - set => _listenerId = value; - } - - public virtual IConsumerTagStrategy ConsumerTagStrategy { get; set; } - - public virtual Dictionary ConsumerArguments { get; set; } = new(); - - public virtual bool Exclusive { get; set; } - - public virtual bool NoLocal { get; set; } - - public virtual bool DefaultRequeueRejected { get; set; } = true; - - public virtual int PrefetchCount { get; set; } = DefaultPrefetchCount; - - public virtual long ShutdownTimeout { get; set; } = DefaultShutdownTimeout; - - public virtual long IdleEventInterval { get; set; } - - public virtual int RecoveryInterval - { - get => recoveryInterval; - set - { - recoveryInterval = value; - RecoveryBackOff = new FixedBackOff(recoveryInterval, FixedBackOff.UnlimitedAttempts); - } - } - - public IBackOff RecoveryBackOff { get; set; } = new FixedBackOff(DefaultRecoveryInterval, FixedBackOff.UnlimitedAttempts); - - public virtual IMessageHeadersConverter MessageHeadersConverter { get; set; } - - public virtual IRabbitAdmin RabbitAdmin { get; set; } - - public virtual bool MissingQueuesFatal { get; set; } = true; - - public virtual bool MismatchedQueuesFatal { get; set; } - - public virtual bool PossibleAuthenticationFailureFatal { get; set; } = true; - - public virtual bool AutoDeclare { get; set; } = true; - - public virtual long FailedDeclarationRetryInterval { get; set; } = DefaultFailedDeclarationRetryInterval; - - public virtual bool StatefulRetryFatalWithNullMessageId { get; set; } = true; - - public virtual IConditionalExceptionLogger ExclusiveConsumerExceptionLogger { get; set; } - - public virtual bool AlwaysRequeueWithTxManagerRollback { get; set; } - - public virtual IBatchingStrategy BatchingStrategy { get; set; } - - public virtual bool IsRunning { get; private set; } - - public virtual bool IsActive { get; private set; } - - public virtual bool IsLazyLoad { get; private set; } - - public virtual bool Initialized { get; private set; } - - public RetryTemplate RetryTemplate { get; set; } - - public virtual IPlatformTransactionManager TransactionManager { get; set; } - - public virtual ITransactionAttribute TransactionAttribute { get; set; } - - protected AbstractMessageListenerContainer(IApplicationContext applicationContext, IConnectionFactory connectionFactory, string name = null, - ILoggerFactory loggerFactory = null) - { - LoggerFactory = loggerFactory; - Logger = LoggerFactory?.CreateLogger(GetType()); - ApplicationContext = applicationContext; - ConnectionFactory = connectionFactory; - ErrorHandler = new ConditionalRejectingErrorHandler(Logger); - MessageHeadersConverter = new DefaultMessageHeadersConverter(Logger); - ExclusiveConsumerExceptionLogger = new DefaultExclusiveConsumerLogger(); - BatchingStrategy = new SimpleBatchingStrategy(0, 0, 0L); - TransactionAttribute = new DefaultTransactionAttribute(); - ServiceName = name ?? $"{GetType().Name}@{GetHashCode()}"; - } - - public virtual void SetQueueNames(params string[] queueNames) - { - ArgumentGuard.NotNull(queueNames); - ArgumentGuard.ElementsNotNull(queueNames); - - IQueue[] queues = queueNames.Select(name => new Queue(name)).Cast().ToArray(); - SetQueues(queues); - } - - public virtual string[] GetQueueNames() - { - return QueuesToNames().ToArray(); - } - - public virtual void SetQueues(params IQueue[] queues) - { - ArgumentGuard.NotNull(queues); - ArgumentGuard.ElementsNotNull(queues); - - if (IsRunning && Array.Exists(queues, queue => queue.QueueName == string.Empty)) - { - throw new InvalidOperationException("Cannot add broker-named queues dynamically."); - } - - Queues = queues.ToList(); - } - - public virtual void AddQueueNames(params string[] queueNames) - { - ArgumentGuard.NotNull(queueNames); - ArgumentGuard.ElementsNotNull(queueNames); - - IQueue[] queues = queueNames.Select(name => new Queue(name)).Cast().ToArray(); - AddQueues(queues); - } - - public virtual void AddQueues(params IQueue[] queues) - { - ArgumentGuard.NotNull(queues); - ArgumentGuard.ElementsNotNull(queues); - - if (IsRunning && Array.Exists(queues, queue => queue.QueueName == string.Empty)) - { - throw new InvalidOperationException("Cannot add broker-named queues dynamically."); - } - - var newQueues = new List(Queues); - newQueues.AddRange(queues); - Queues = newQueues; - } - - public virtual bool RemoveQueueNames(params string[] queueNames) - { - ArgumentGuard.NotNull(queueNames); - ArgumentGuard.ElementsNotNull(queueNames); - - HashSet toRemove = queueNames.ToHashSet(); - var copy = new List(Queues); - List filtered = copy.Where(q => !toRemove.Contains(q.ActualName)).ToList(); - Queues = filtered; - return filtered.Count != copy.Count; - } - - public virtual void RemoveQueues(params IQueue[] queues) - { - ArgumentGuard.NotNull(queues); - ArgumentGuard.ElementsNotNull(queues); - - string[] toRemove = queues.Select(queue => queue.ActualName).ToArray(); - RemoveQueueNames(toRemove); - } - - public virtual void SetAfterReceivePostProcessors(params IMessagePostProcessor[] afterReceivePostProcessors) - { - ArgumentGuard.NotNull(afterReceivePostProcessors); - ArgumentGuard.ElementsNotNull(afterReceivePostProcessors); - - List asList = afterReceivePostProcessors.ToList(); - AfterReceivePostProcessors = MessagePostProcessorUtils.Sort(asList); - } - - public virtual void AddAfterReceivePostProcessors(params IMessagePostProcessor[] afterReceivePostProcessors) - { - ArgumentGuard.NotNull(afterReceivePostProcessors); - ArgumentGuard.ElementsNotNull(afterReceivePostProcessors); - - IList current = AfterReceivePostProcessors ?? new List(); - - List asList = afterReceivePostProcessors.ToList(); - asList.AddRange(current); - AfterReceivePostProcessors = MessagePostProcessorUtils.Sort(asList); - } - - public virtual bool RemoveAfterReceivePostProcessor(IMessagePostProcessor afterReceivePostProcessor) - { - ArgumentGuard.NotNull(afterReceivePostProcessor); - - IList current = AfterReceivePostProcessors; - - if (current != null && current.Contains(afterReceivePostProcessor)) - { - var copy = new List(current); - copy.Remove(afterReceivePostProcessor); - AfterReceivePostProcessors = copy; - return true; - } - - return false; - } - - public virtual void SetupMessageListener(IMessageListener messageListener) - { - MessageListener = messageListener; - } - - public virtual IConnectionFactory ResolveConnectionFactory() - { - IConnectionFactory connectionFactory = ConnectionFactory; - - if (connectionFactory is IRoutingConnectionFactory routingFactory) - { - IConnectionFactory targetConnectionFactory = routingFactory.GetTargetConnectionFactory(GetRoutingLookupKey()); - - if (targetConnectionFactory != null) - { - return targetConnectionFactory; - } - } - - return connectionFactory; - } - - public virtual void Initialize() - { - ValidateConfiguration(); - - try - { - lock (LifecycleMonitor) - { - Monitor.PulseAll(LifecycleMonitor); - } - - DoInitialize(); - - if (!ExposeListenerChannel && TransactionManager != null) - { - Logger?.LogWarning("exposeListenerChannel=false is ignored when using a TransactionManager"); - } - - if (TransactionManager != null && !IsChannelTransacted) - { - Logger?.LogDebug("The 'channelTransacted' is coerced to 'true', when 'transactionManager' is provided"); - IsChannelTransacted = true; - } - - if (MessageListener != null) - { - MessageListener.ContainerAckMode = AcknowledgeMode; - } - - Initialized = true; - } - catch (Exception e) - { - Logger?.LogError(e, "Error initializing listener container"); - throw ConvertRabbitAccessException(e); - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - Shutdown(); - } - } - - public virtual void Shutdown() - { - lock (LifecycleMonitor) - { - if (!IsActive) - { - Logger?.LogInformation("Shutdown ignored - container is not active already"); - return; - } - - IsActive = false; - Monitor.PulseAll(LifecycleMonitor); - } - - Logger?.LogDebug("Shutting down RabbitMQ listener container"); - - // Shut down the invokers. - try - { - DoShutdown(); - } - catch (Exception ex) - { - Logger?.LogError(ex, "DoShutdown error"); - throw ConvertRabbitAccessException(ex); - } - finally - { - lock (LifecycleMonitor) - { - IsRunning = false; - Monitor.PulseAll(LifecycleMonitor); - } - } - } - - public virtual Task StartAsync() - { - if (IsRunning) - { - return Task.CompletedTask; - } - - if (!Initialized) - { - lock (LifecycleMonitor) - { - if (!Initialized) - { - Initialize(); - } - } - } - - try - { - Logger?.LogDebug("Starting RabbitMQ listener container {name}", ServiceName); - ConfigureAdminIfNeeded(); - CheckMismatchedQueues(); - DoStart(); - } - catch (Exception ex) - { - Logger?.LogError(ex, "Error Starting RabbitMQ listener container {name}", ServiceName); - throw ConvertRabbitAccessException(ex); - } - finally - { - IsLazyLoad = false; - } - - return Task.CompletedTask; - } - - public virtual Task StopAsync() - { - try - { - DoStop(); - } - catch (Exception ex) - { - Logger?.LogError(ex, "Error stopping RabbitMQ listener container {name}", ServiceName); - throw ConvertRabbitAccessException(ex); - } - finally - { - lock (LifecycleMonitor) - { - IsRunning = false; - Monitor.PulseAll(LifecycleMonitor); - } - } - - return Task.CompletedTask; - } - - public virtual async Task StopAsync(Action callback) - { - try - { - await StopAsync(); - } - finally - { - callback(); - } - } - - public virtual void LazyLoad() - { - if (MismatchedQueuesFatal) - { - if (MissingQueuesFatal) - { - Logger?.LogWarning("'mismatchedQueuesFatal' and 'missingQueuesFatal' are ignored during the initial start(), for lazily loaded containers"); - } - else - { - Logger?.LogWarning("'mismatchedQueuesFatal' is ignored during the initial start(), for lazily loaded containers"); - } - } - else if (MissingQueuesFatal) - { - Logger?.LogWarning("'missingQueuesFatal' is ignored during the initial start(), for lazily loaded containers"); - } - - IsLazyLoad = true; - } - - protected virtual IConnection CreateConnection() - { - return ConnectionFactory.CreateConnection(); - } - - protected virtual RabbitResourceHolder GetTransactionalResourceHolder() - { - return ConnectionFactoryUtils.GetTransactionalResourceHolder(ConnectionFactory, IsChannelTransacted); - } - - protected virtual Exception ConvertRabbitAccessException(Exception ex) - { - return RabbitExceptionTranslator.ConvertRabbitAccessException(ex); - } - - protected virtual void RedeclareElementsIfNecessary() - { - lock (Lock) - { - IRabbitAdmin admin = RabbitAdmin; - - if (!IsLazyLoad && admin != null && AutoDeclare) - { - try - { - AttemptDeclarations(admin); - } - catch (Exception e) - { - if (RabbitUtils.IsMismatchedQueueArgs(e) && MismatchedQueuesFatal) - { - throw new FatalListenerStartupException("Mismatched queues", e); - } - - Logger?.LogError(e, "Failed to check/redeclare auto-delete queue(s). Container: {name}", ServiceName); - } - } - } - } - - protected virtual void InvokeErrorHandler(Exception ex) - { - if (ErrorHandler != null) - { - try - { - ErrorHandler.HandleError(ex); - } - catch (Exception e) - { - Logger?.LogError(e, "Execution of Rabbit message listener failed, and the error handler threw an exception. Container: {name}", ServiceName); - throw; - } - } - else - { - Logger?.LogWarning(ex, "Execution of Rabbit message listener failed, and no ErrorHandler has been set. Container: {name}", ServiceName); - } - } - - protected virtual void ExecuteListener(R.IModel channel, IMessage message) - { - if (!IsRunning) - { - Logger?.LogWarning("Rejecting received message(s) because the listener container {name} has been stopped {message}", ServiceName, message); - throw new MessageRejectedWhileStoppingException(); - } - - try - { - DoExecuteListener(channel, message); - } - catch (Exception ex) - { - CheckStatefulRetry(ex, message); - HandleListenerException(ex); - throw; - } - } - - protected virtual void ActualInvokeListener(R.IModel channel, List data) - { - IMessageListener listener = MessageListener; - - if (listener is IChannelAwareMessageListener chanListener) - { - DoInvokeListener(chanListener, channel, data); - } - else if (listener != null) - { - bool bindChannel = ExposeListenerChannel && IsChannelLocallyTransacted; - - if (bindChannel) - { - var resourceHolder = new RabbitResourceHolder(channel, false, LoggerFactory?.CreateLogger()) - { - SynchronizedWithTransaction = true - }; - - TransactionSynchronizationManager.BindResource(ConnectionFactory, resourceHolder); - } - - try - { - DoInvokeListener(listener, data); - } - finally - { - if (bindChannel) - { - // unbind if we bound - TransactionSynchronizationManager.UnbindResource(ConnectionFactory); - } - } - } - else - { - throw new FatalListenerExecutionException("No message listener specified - see property 'messageListener'"); - } - } - - protected virtual void ActualInvokeListener(R.IModel channel, IMessage message) - { - IMessageListener listener = MessageListener; - - if (listener is IChannelAwareMessageListener chanListener) - { - DoInvokeListener(chanListener, channel, message); - } - else if (listener != null) - { - bool bindChannel = ExposeListenerChannel && IsChannelLocallyTransacted; - - if (bindChannel) - { - var resourceHolder = new RabbitResourceHolder(channel, false, LoggerFactory?.CreateLogger()) - { - SynchronizedWithTransaction = true - }; - - TransactionSynchronizationManager.BindResource(ConnectionFactory, resourceHolder); - } - - try - { - DoInvokeListener(listener, message); - } - finally - { - if (bindChannel) - { - // unbind if we bound - TransactionSynchronizationManager.UnbindResource(ConnectionFactory); - } - } - } - else - { - throw new FatalListenerExecutionException("No message listener specified - see property 'messageListener'"); - } - } - - protected virtual void DoInvokeListener(IChannelAwareMessageListener listener, R.IModel channel, List data) - { - RabbitResourceHolder resourceHolder = null; - R.IModel channelToUse = channel; - bool boundHere = false; - - try - { - (boundHere, channelToUse, resourceHolder) = HandleChannelAwareTransaction(channel); - - // Actually invoke the message listener... - try - { - listener.OnMessageBatch(data, channelToUse); - } - catch (Exception e) - { - throw WrapToListenerExecutionFailedExceptionIfNeeded(e, data); - } - } - finally - { - CleanUpAfterInvoke(resourceHolder, channelToUse, boundHere); - } - } - - protected virtual void DoInvokeListener(IChannelAwareMessageListener listener, R.IModel channel, IMessage message) - { - RabbitResourceHolder resourceHolder = null; - R.IModel channelToUse = channel; - bool boundHere = false; - - try - { - (boundHere, channelToUse, resourceHolder) = HandleChannelAwareTransaction(channel); - - // Actually invoke the message listener... - try - { - listener.OnMessage(message, channelToUse); - } - catch (Exception e) - { - throw WrapToListenerExecutionFailedExceptionIfNeeded(e, message); - } - } - finally - { - CleanUpAfterInvoke(resourceHolder, channelToUse, boundHere); - } - } - - protected virtual void DoInvokeListener(IMessageListener listener, List data) - { - try - { - listener.OnMessageBatch(data); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception in OnMessage call. Container: {name}", ServiceName); - throw WrapToListenerExecutionFailedExceptionIfNeeded(e, data); - } - } - - protected virtual void DoInvokeListener(IMessageListener listener, IMessage message) - { - try - { - listener.OnMessage(message); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception in OnMessage call. Container: {name}", ServiceName); - throw WrapToListenerExecutionFailedExceptionIfNeeded(e, message); - } - } - - protected virtual (bool BoundHere, R.IModel ChannelToUse, RabbitResourceHolder ResourceHolder) HandleChannelAwareTransaction(R.IModel channel) - { - RabbitResourceHolder resourceHolder = null; - R.IModel channelToUse = channel; - bool boundHere = false; - - if (!ExposeListenerChannel) - { - // We need to expose a separate Channel. - resourceHolder = GetTransactionalResourceHolder(); - channelToUse = resourceHolder.GetChannel(); - - /* - * If there is a real transaction, the resource will have been bound; otherwise - * we need to bind it temporarily here. Any work done on this channel - * will be committed in the finally block. - */ - if (IsChannelLocallyTransacted && !TransactionSynchronizationManager.IsActualTransactionActive()) - { - resourceHolder.SynchronizedWithTransaction = true; - TransactionSynchronizationManager.BindResource(ConnectionFactory, resourceHolder); - boundHere = true; - } - } - else - { - // if locally transacted, bind the current channel to make it available to RabbitTemplate - if (IsChannelLocallyTransacted) - { - var localResourceHolder = new RabbitResourceHolder(channelToUse, false, LoggerFactory?.CreateLogger()) - { - SynchronizedWithTransaction = true - }; - - TransactionSynchronizationManager.BindResource(ConnectionFactory, localResourceHolder); - boundHere = true; - } - } - - return (boundHere, channelToUse, resourceHolder); - } - - protected virtual ListenerExecutionFailedException WrapToListenerExecutionFailedExceptionIfNeeded(Exception exception, List data) - { - if (exception is not ListenerExecutionFailedException listenerException) - { - return new ListenerExecutionFailedException("Listener threw exception", exception, data.ToArray()); - } - - return listenerException; - } - - protected virtual ListenerExecutionFailedException WrapToListenerExecutionFailedExceptionIfNeeded(Exception exception, IMessage message) - { - if (exception is not ListenerExecutionFailedException listenerException) - { - return new ListenerExecutionFailedException("Listener threw exception", exception, message); - } - - return listenerException; - } - - protected virtual void HandleListenerException(Exception exception) - { - if (IsActive) - { - // Regular case: failed while active. - // Invoke ErrorHandler if available. - InvokeErrorHandler(exception); - } - else - { - // Rare case: listener thread failed after container shutdown. - // Log at debug level, to avoid spamming the shutdown log. - Logger?.LogDebug(exception, "Listener exception after container shutdown. Container: {name}", ServiceName); - } - } - - protected virtual void UpdateLastReceive() - { - if (IdleEventInterval > 0) - { - LastReceive = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - } - } - - protected virtual void ConfigureAdminIfNeeded() - { - if (RabbitAdmin == null && ApplicationContext != null) - { - IEnumerable admins = ApplicationContext.GetServices(); - - if (admins.Count() == 1) - { - RabbitAdmin = admins.Single(); - } - else - { - if (AutoDeclare || MismatchedQueuesFatal) - { - Logger?.LogDebug( - "For 'autoDeclare' and 'mismatchedQueuesFatal' to work, there must be exactly one " + - "RabbitAdmin in the context or you must inject one into this container; found: {count}" + " for container {container}", admins.Count(), - ToString()); - } - - if (MismatchedQueuesFatal) - { - throw new InvalidOperationException("When 'mismatchedQueuesFatal' is 'true', there must be exactly " + - $"one RabbitAdmin in the context or you must inject one into this container; found: {admins.Count()} " + - $" for container {ToString()}"); - } - } - } - } - - protected virtual void CheckMismatchedQueues() - { - if (MismatchedQueuesFatal && RabbitAdmin != null) - { - try - { - RabbitAdmin.Initialize(); - } - catch (RabbitConnectException e) - { - Logger?.LogInformation(e, "Broker not available; cannot check queue declarations. Container: {name}", ServiceName); - } - catch (RabbitIOException e) - { - if (RabbitUtils.IsMismatchedQueueArgs(e)) - { - throw new FatalListenerStartupException("Mismatched queues", e); - } - - Logger?.LogInformation(e, "Failed to get connection during Start()"); - } - } - else - { - try - { - IConnection connection = ConnectionFactory.CreateConnection(); - - if (connection != null) - { - connection.Close(); - } - } - catch (Exception e) - { - Logger?.LogInformation(e, "Broker not available; cannot force queue declarations during start"); - } - } - } - - protected abstract void DoInitialize(); - - protected abstract void DoShutdown(); - - protected virtual void DoStart() - { - // Reschedule paused tasks, if any. - lock (LifecycleMonitor) - { - IsActive = true; - IsRunning = true; - Monitor.PulseAll(LifecycleMonitor); - } - } - - protected virtual void DoStop() - { - Shutdown(); - } - - protected virtual void ValidateConfiguration() - { - if (!(ExposeListenerChannel || !AcknowledgeMode.IsManual())) - { - throw new InvalidOperationException("You cannot acknowledge messages manually if the channel is not exposed to the listener " + - "(please check your configuration and set exposeListenerChannel=true or " + "acknowledgeMode!=MANUAL)"); - } - - if (IsChannelTransacted && AcknowledgeMode.IsAutoAck()) - { - throw new InvalidOperationException("The acknowledgeMode is NONE (autoack in Rabbit terms) which is not consistent with having a " + - "transactional channel. Either use a different AcknowledgeMode or make sure " + "channelTransacted=false"); - } - } - - protected virtual IRoutingConnectionFactory GetRoutingConnectionFactory() - { - return ConnectionFactory as IRoutingConnectionFactory; - } - - protected virtual string GetRoutingLookupKey() - { - return ConnectionFactory is IRoutingConnectionFactory ? LookupKeyQualifier + GetQueuesAsListString() : null; - } - - protected virtual void CheckMessageListener(object listener) - { - if (listener is not IMessageListener) - { - throw new ArgumentException($"Message listener needs to be of type [{nameof(IMessageListener)}] or [{nameof(IChannelAwareMessageListener)}]", - nameof(listener)); - } - } - - protected virtual ISet GetQueueNamesAsSet() - { - return new HashSet(QueuesToNames()); - } - - protected virtual Dictionary GetQueueNamesToQueues() - { - return Queues.ToDictionary(q => q.ActualName); - } - - protected virtual bool CauseChainHasImmediateAcknowledgeRabbitException(Exception exception) - { - Exception cause = exception.InnerException; - - while (cause != null) - { - if (cause is ImmediateAcknowledgeException) - { - return true; - } - - if (cause is RabbitRejectAndDoNotRequeueException) - { - return false; - } - - cause = cause.InnerException; - } - - return false; - } - - protected virtual void PrepareHolderForRollback(RabbitResourceHolder resourceHolder, Exception exception) - { - if (resourceHolder != null) - { - resourceHolder.RequeueOnRollback = AlwaysRequeueWithTxManagerRollback || ContainerUtils.ShouldRequeue(DefaultRequeueRejected, exception, Logger); - } - } - - private void AttemptDeclarations(IRabbitAdmin admin) - { - ISet queueNames = GetQueueNamesAsSet(); - IEnumerable queueBeans = ApplicationContext.GetServices(); - - foreach (IQueue entry in queueBeans) - { - if (MismatchedQueuesFatal || (queueNames.Contains(entry.QueueName) && admin.GetQueueProperties(entry.QueueName) == null)) - { - Logger?.LogDebug("Redeclaring context exchanges, queues, bindings. Container: {name}", ServiceName); - admin.Initialize(); - break; - } - } - } - - private void CheckStatefulRetry(Exception ex, IMessage message) - { - if (message.Headers.IsFinalRetryForMessageWithNoId()) - { - if (StatefulRetryFatalWithNullMessageId) - { - throw new FatalListenerExecutionException($"Illegal null id in {nameof(message)}. Failed to manage retry for: {message}", ex); - } - - throw new ListenerExecutionFailedException("Cannot retry message more than once without an ID", - new RabbitRejectAndDoNotRequeueException("Not retryable; rejecting and not requeuing", ex), message); - } - } - - private void CleanUpAfterInvoke(RabbitResourceHolder resourceHolder, R.IModel channelToUse, bool boundHere) - { - if (resourceHolder != null && boundHere) - { - // so the channel exposed (because exposeListenerChannel is false) will be closed - resourceHolder.SynchronizedWithTransaction = false; - } - - ConnectionFactoryUtils.ReleaseResources(resourceHolder); - - if (boundHere) - { - // unbind if we bound - TransactionSynchronizationManager.UnbindResource(ConnectionFactory); - - if (!ExposeListenerChannel && IsChannelLocallyTransacted) - { - /* - * commit the temporary channel we exposed; the consumer's channel - * will be committed later. Note that when exposing a different channel - * when there's no transaction manager, the exposed channel is committed - * on each message, and not based on txSize. - */ - RabbitUtils.CommitIfNecessary(channelToUse, Logger); - } - } - } - - private void DoExecuteListener(R.IModel channel, IMessage message) - { - if (AfterReceivePostProcessors != null) - { - IMessage postProcessed = message; - - foreach (IMessagePostProcessor processor in AfterReceivePostProcessors) - { - postProcessed = processor.PostProcessMessage(postProcessed); - - if (postProcessed == null) - { - throw new ImmediateAcknowledgeException("Message Post Processor returned 'null', discarding message"); - } - } - - message = postProcessed as IMessage; - - if (message == null) - { - throw new InvalidOperationException("AfterReceivePostProcessors failed to return a IMessage"); - } - } - - if (IsDeBatchingEnabled && BatchingStrategy.CanDebatch(message.Headers)) - { - BatchingStrategy.DeBatch(message, fragment => InvokeListener(channel, fragment)); - } - else - { - InvokeListener(channel, message); - } - } - - private void InvokeListener(R.IModel channel, IMessage message) - { - if (RetryTemplate != null) - { - if (Recoverer != null) - { - RetryTemplate.Execute(context => ActualInvokeListener(channel, message), Recoverer); - } - else - { - RetryTemplate.Execute(context => ActualInvokeListener(channel, message)); - } - } - else - { - ActualInvokeListener(channel, message); - } - } - - private string GetQueuesAsListString() - { - var sb = new StringBuilder("["); - List queues = Queues; - - foreach (IQueue q in queues) - { - sb.Append(q.QueueName); - sb.Append(','); - } - - return $"{sb.ToString(0, sb.Length - 1)}]"; - } - - private List QueuesToNames() - { - return Queues.Select(q => q.ActualName).ToList(); - } - - private sealed class DefaultExclusiveConsumerLogger : IConditionalExceptionLogger - { - public void Log(ILogger logger, string message, object cause) - { - logger.LogError("Unexpected invocation of {type}, with {message}:{cause}", GetType(), message, cause); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/AbstractRabbitListenerEndpoint.cs b/src/Messaging/src/RabbitMQ/Listener/AbstractRabbitListenerEndpoint.cs deleted file mode 100644 index 054206de8a..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/AbstractRabbitListenerEndpoint.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public abstract class AbstractRabbitListenerEndpoint : IRabbitListenerEndpoint -{ - protected readonly ILogger Logger; - protected readonly ILoggerFactory LoggerFactory; - private IApplicationContext _applicationContext; - - protected IServiceExpressionResolver Resolver { get; set; } - - protected IServiceResolver ServiceResolver { get; set; } - - protected IServiceExpressionContext ExpressionContext { get; set; } - - public IApplicationContext ApplicationContext - { - get => _applicationContext; - set - { - _applicationContext = value; - - if (_applicationContext != null) - { - Resolver = _applicationContext.ServiceExpressionResolver; - ExpressionContext = new ServiceExpressionContext(_applicationContext); - ServiceResolver = new ServiceFactoryResolver(_applicationContext); - } - } - } - - public string Id { get; set; } - - public List Queues { get; } = new(); - - public List QueueNames { get; } = new(); - - public bool Exclusive { get; set; } - - public int? Priority { get; set; } - - public int? Concurrency { get; set; } - - public IRabbitAdmin Admin { get; set; } - - public bool? AutoStartup { get; set; } - - public ISmartMessageConverter MessageConverter { get; set; } - - public bool BatchListener { get; set; } - - public IBatchingStrategy BatchingStrategy { get; set; } - - public AcknowledgeMode? AckMode { get; set; } - - public IReplyPostProcessor ReplyPostProcessor { get; set; } - - public string Group { get; set; } - - protected AbstractRabbitListenerEndpoint(IApplicationContext applicationContext, ILoggerFactory loggerFactory = null) - { - ApplicationContext = applicationContext; - LoggerFactory = loggerFactory; - Logger = loggerFactory?.CreateLogger(GetType()); - - if (applicationContext != null) - { - Resolver = applicationContext.ServiceExpressionResolver; - ExpressionContext = new ServiceExpressionContext(applicationContext); - ServiceResolver = new ServiceFactoryResolver(applicationContext); - } - } - - public void SetQueues(params IQueue[] queues) - { - ArgumentGuard.NotNull(queues); - - Queues.Clear(); - Queues.AddRange(queues); - } - - public void SetQueueNames(params string[] queueNames) - { - ArgumentGuard.NotNull(queueNames); - - QueueNames.Clear(); - QueueNames.AddRange(queueNames); - } - - public void SetupListenerContainer(IMessageListenerContainer listenerContainer) - { - var container = (AbstractMessageListenerContainer)listenerContainer; - - bool queuesEmpty = Queues.Count == 0; - bool queueNamesEmpty = QueueNames.Count == 0; - - if (!queuesEmpty && !queueNamesEmpty) - { - throw new InvalidOperationException($"Queues or queue names must be provided but not both for {this}"); - } - - if (queuesEmpty) - { - List names = QueueNames; - container.SetQueueNames(names.ToArray()); - } - else - { - List instances = Queues; - container.SetQueues(instances.ToArray()); - } - - container.Exclusive = Exclusive; - - if (Priority.HasValue) - { - var args = new Dictionary - { - { "x-priority", Priority.Value } - }; - - container.ConsumerArguments = args; - } - - if (Admin != null) - { - container.RabbitAdmin = Admin; - } - - SetupMessageListener(listenerContainer); - } - - public override string ToString() - { - return GetEndpointDescription().ToString(); - } - - protected abstract IMessageListener CreateMessageListener(IMessageListenerContainer container); - - protected virtual StringBuilder GetEndpointDescription() - { - var result = new StringBuilder(); - - return result.Append(GetType().Name).Append('[').Append(Id).Append("] queues=").Append(Queues).Append("' | queueNames='").Append(QueueNames) - .Append("' | exclusive='").Append(Exclusive).Append("' | priority='").Append(Priority).Append("' | admin='").Append(Admin).Append('\''); - } - - private void SetupMessageListener(IMessageListenerContainer container) - { - IMessageListener messageListener = CreateMessageListener(container); - - if (messageListener == null) - { - throw new InvalidOperationException($"Endpoint [{this}] must provide a non null message listener"); - } - - container.SetupMessageListener(messageListener); - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/AbstractMessageListenerAdapter.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/AbstractMessageListenerAdapter.cs deleted file mode 100644 index a75026e1d1..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/AbstractMessageListenerAdapter.cs +++ /dev/null @@ -1,441 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Expression.Internal.Spring.Common; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Retry; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener.Support; -using Steeltoe.Messaging.RabbitMQ.Support; -using RC = RabbitMQ.Client; -using SimpleMessageConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter.SimpleMessageConverter; - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public abstract class AbstractMessageListenerAdapter : IChannelAwareMessageListener -{ - private const string DefaultEncoding = "UTF-8"; - - private static readonly SpelExpressionParser Parser = new(); - private static readonly IParserContext ParserContext = new TemplateParserContext("!{", "}"); - protected readonly ILogger Logger; - - public IApplicationContext ApplicationContext { get; set; } - - public virtual string Encoding { get; set; } = DefaultEncoding; - - public virtual string ResponseRoutingKey { get; set; } = string.Empty; - - public virtual string ResponseExchange { get; set; } - - public virtual Address ResponseAddress { get; set; } - - public virtual bool MandatoryPublish { get; set; } - - public virtual ISmartMessageConverter MessageConverter { get; set; } - - public virtual List BeforeSendReplyPostProcessors { get; private set; } - - public virtual RetryTemplate RetryTemplate { get; set; } - - public virtual IRecoveryCallback RecoveryCallback { get; set; } - - public virtual bool DefaultRequeueRejected { get; set; } = true; - - public virtual AcknowledgeMode ContainerAckMode { get; set; } - - public virtual bool IsManualAck => ContainerAckMode == AcknowledgeMode.Manual; - - public virtual StandardEvaluationContext EvalContext { get; set; } = new(); - - public virtual IMessageHeadersConverter MessagePropertiesConverter { get; set; } = new DefaultMessageHeadersConverter(); - - public virtual IExpression ResponseExpression { get; set; } - - public virtual IReplyPostProcessor ReplyPostProcessor { get; set; } - - protected AbstractMessageListenerAdapter(IApplicationContext context, ILogger logger = null) - { - Logger = logger; - MessageConverter = new SimpleMessageConverter(); - ApplicationContext = context; - } - - public virtual void SetResponseAddress(string defaultReplyTo) - { - if (defaultReplyTo.StartsWith(ParserContext.ExpressionPrefix, StringComparison.Ordinal)) - { - ResponseExpression = Parser.ParseExpression(defaultReplyTo, ParserContext); - } - else - { - ResponseAddress = new Address(defaultReplyTo); - } - } - - public void SetServiceResolver(IServiceResolver serviceResolver) - { - EvalContext.ServiceResolver = serviceResolver; - EvalContext.TypeConverter = new StandardTypeConverter(); - EvalContext.AddPropertyAccessor(new DictionaryAccessor()); - } - - public virtual void SetBeforeSendReplyPostProcessors(params IMessagePostProcessor[] beforeSendReplyPostProcessors) - { - ArgumentGuard.NotNull(beforeSendReplyPostProcessors); - ArgumentGuard.ElementsNotNull(beforeSendReplyPostProcessors); - - BeforeSendReplyPostProcessors = new List(beforeSendReplyPostProcessors); - } - - public abstract void OnMessage(IMessage message, RC.IModel channel); - - public virtual void OnMessage(IMessage message) - { - throw new InvalidOperationException("Should never be called for a ChannelAwareMessageListener"); - } - - public virtual void OnMessageBatch(List messages, RC.IModel channel) - { - throw new NotSupportedException("This listener does not support message batches"); - } - - public virtual void OnMessageBatch(List messages) - { - throw new NotSupportedException("This listener does not support message batches"); - } - - protected internal virtual IMessage BuildMessage(RC.IModel channel, object result, Type genericType) - { - ISmartMessageConverter converter = MessageConverter; - - if (converter != null && result is not IMessage) - { - result = converter.ToMessage(result, new MessageHeaders(), genericType); - } - - if (result is not IMessage byteArrayMessage) - { - throw new MessageConversionException($"No MessageConverter specified - cannot handle message [{result}]"); - } - - return byteArrayMessage; - } - - protected virtual void HandleListenerException(Exception exception) - { - Logger?.LogError(exception, "Listener execution failed"); - } - - protected virtual object ExtractMessage(IMessage message) - { - ISmartMessageConverter converter = MessageConverter; - - if (converter != null) - { - return converter.FromMessage(message, null); - } - - return message; - } - - protected virtual void HandleResult(InvocationResult resultArg, IMessage request, RC.IModel channel) - { - HandleResult(resultArg, request, channel, null); - } - - protected virtual void HandleResult(InvocationResult resultArg, IMessage request, RC.IModel channel, object source) - { - if (channel != null) - { - if (resultArg.ReturnValue is Task asTask) - { - if (!IsManualAck) - { - Logger?.LogWarning("Container AcknowledgeMode must be MANUAL for a Future return type; " + - "otherwise the container will ack the message immediately"); - } - - asTask.ContinueWith(t => - { - if (t.IsCompletedSuccessfully) - { - AsyncSuccess(resultArg, request, channel, source, GetReturnValue(t)); - BasicAck(request, channel); - } - else - { - AsyncFailure(request, channel, t.Exception); - } - }); - } - else - { - DoHandleResult(resultArg, request, channel, source); - } - } - else - { - Logger?.LogWarning("Listener method returned result [{result}]: not generating response message for it because no Rabbit Channel given", resultArg); - } - } - - protected virtual void DoHandleResult(InvocationResult resultArg, IMessage request, RC.IModel channel, object source) - { - Logger?.LogDebug("Listener method returned result [{result}] - generating response message for it", resultArg); - - try - { - IMessage response = BuildMessage(channel, resultArg.ReturnValue, resultArg.ReturnType); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(response); - accessor.Target = resultArg.Instance; - accessor.TargetMethod = resultArg.Method; - PostProcessResponse(request, response); - Address replyTo = GetReplyToAddress(request, source, resultArg); - SendResponse(channel, replyTo, response); - } - catch (Exception ex) - { - throw new ReplyFailureException($"Failed to send reply with payload '{resultArg}'", ex); - } - } - - protected virtual string GetReceivedExchange(IMessage request) - { - return request.Headers.ReceivedExchange(); - } - - protected virtual void PostProcessResponse(IMessage request, IMessage response) - { - string correlation = request.Headers.CorrelationId(); - - if (correlation == null) - { - string messageId = request.Headers.MessageId(); - - if (messageId != null) - { - correlation = messageId; - } - } - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(response); - accessor.CorrelationId = correlation; - } - - protected virtual Address GetReplyToAddress(IMessage request, object source, InvocationResult result) - { - Address replyTo = request.Headers.ReplyToAddress(); - - if (replyTo == null) - { - if (ResponseAddress == null && ResponseExchange != null) - { - ResponseAddress = new Address(ResponseExchange, ResponseRoutingKey); - } - - if (result.SendTo != null) - { - replyTo = EvaluateReplyTo(request, source, result.ReturnValue, result.SendTo); - } - else if (request.Headers.ReplyTo() != null) - { - return new Address(request.Headers.ReplyTo()); - } - else if (ResponseExpression != null) - { - replyTo = EvaluateReplyTo(request, source, result.ReturnValue, ResponseExpression); - } - else if (ResponseAddress == null) - { - throw new RabbitException("Cannot determine ReplyTo message property value: " + "Request message does not contain reply-to property, " + - "and no default response Exchange was set."); - } - else - { - replyTo = ResponseAddress; - } - } - - return replyTo; - } - - protected void SendResponse(RC.IModel channel, Address replyTo, IMessage messageIn) - { - IMessage message = messageIn; - - if (BeforeSendReplyPostProcessors != null) - { - List processors = BeforeSendReplyPostProcessors; - IMessage postProcessed = message; - - foreach (IMessagePostProcessor postProcessor in processors) - { - postProcessed = postProcessor.PostProcessMessage(postProcessed); - } - - message = postProcessed as IMessage; - - if (message == null) - { - throw new InvalidOperationException("A BeforeSendReplyPostProcessors failed to return IMessage"); - } - } - - PostProcessChannel(channel, message); - - try - { - Logger?.LogDebug("Publishing response to exchange = [{exchange}], routingKey = [{routingKey}]", replyTo.ExchangeName, replyTo.RoutingKey); - - if (RetryTemplate == null) - { - DoPublish(channel, replyTo, message); - } - else - { - IMessage messageToSend = message; - - RetryTemplate.Execute(_ => - { - DoPublish(channel, replyTo, messageToSend); - return null; - }, ctx => - { - if (RecoveryCallback != null) - { - ctx.SetAttribute(SendRetryContextAccessor.Message, messageToSend); - ctx.SetAttribute(SendRetryContextAccessor.Address, replyTo); - RecoveryCallback.Recover(ctx); - return null; - } - - throw RabbitExceptionTranslator.ConvertRabbitAccessException(ctx.LastException); - }); - } - } - catch (Exception ex) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(ex); - } - } - - protected virtual void DoPublish(RC.IModel channel, Address replyTo, IMessage message) - { - RC.IBasicProperties props = channel.CreateBasicProperties(); - MessagePropertiesConverter.FromMessageHeaders(message.Headers, props, EncodingUtils.GetEncoding(Encoding)); - channel.BasicPublish(replyTo.ExchangeName, replyTo.RoutingKey, MandatoryPublish, props, message.Payload); - } - - protected virtual void PostProcessChannel(RC.IModel channel, IMessage response) - { - } - - private static object GetReturnValue(Task task) - { - Type taskType = task.GetType(); - - if (!taskType.IsGenericType) - { - return null; - } - - PropertyInfo property = taskType.GetProperty("Result"); - return property.GetValue(task); - } - - private Address EvaluateReplyTo(IMessage request, object source, object result, IExpression expression) - { - object value = expression.GetValue(EvalContext, new ReplyExpressionRoot(request, source, result)); - - Address replyTo = value switch - { - not string and not Address => throw new InvalidOperationException($"Response expression must be of type {nameof(String)} or {nameof(Address)}."), - string stringValue => new Address(stringValue), - _ => (Address)value - }; - - return replyTo; - } - - private void AsyncSuccess(InvocationResult resultArg, IMessage request, RC.IModel channel, object source, object deferredResult) - { - if (deferredResult == null) - { - Logger?.LogDebug("Async result is null, ignoring"); - } - else - { - Type returnType = resultArg.ReturnType; - - if (returnType != null) - { - Type[] actualTypeArguments = returnType.ContainsGenericParameters ? returnType.GetGenericArguments() : Array.Empty(); - - if (actualTypeArguments.Length > 0) - { - returnType = actualTypeArguments[0]; - } - } - - DoHandleResult(new InvocationResult(deferredResult, resultArg.SendTo, returnType, resultArg.Instance, resultArg.Method), request, channel, source); - } - } - - private void BasicAck(IMessage request, RC.IModel channel) - { - ulong? tag = request.Headers.DeliveryTag(); - ulong deliveryTag = tag ?? 0; - - try - { - channel.BasicAck(deliveryTag, false); - } - catch (Exception e) - { - Logger?.LogError(e, "Failed to ack message"); - } - } - - private void AsyncFailure(IMessage request, RC.IModel channel, Exception exception) - { - Logger?.LogError(exception, "Async method was completed with an exception for {request} ", request); - - try - { - channel.BasicNack(request.Headers.DeliveryTag().Value, false, ContainerUtils.ShouldRequeue(DefaultRequeueRejected, exception, Logger)); - } - catch (Exception e) - { - Logger?.LogError(e, "Failed to nack message"); - } - } - - protected class ReplyExpressionRoot - { - public IMessage Request { get; } - - public object Source { get; } - - public object Result { get; } - - public ReplyExpressionRoot(IMessage request, object source, object result) - { - Request = request; - Source = source; - Result = result; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/BatchMessagingMessageListenerAdapter.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/BatchMessagingMessageListenerAdapter.cs deleted file mode 100644 index 840e291588..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/BatchMessagingMessageListenerAdapter.cs +++ /dev/null @@ -1,137 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Reflection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.Support; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public class BatchMessagingMessageListenerAdapter : MessagingMessageListenerAdapter, IChannelAwareBatchMessageListener -{ - private IBatchingStrategy BatchingStrategy { get; } - - public BatchMessagingMessageListenerAdapter(IApplicationContext context, object bean, MethodInfo method, bool returnExceptions, - IRabbitListenerErrorHandler errorHandler, IBatchingStrategy batchingStrategy, ILogger logger = null) - : base(context, bean, method, returnExceptions, errorHandler, true, logger) - { - BatchingStrategy = batchingStrategy ?? new SimpleBatchingStrategy(0, 0, 0L); - } - - public override void OnMessageBatch(List messages, RC.IModel channel) - { - IMessage converted = null; - - if (IsMessageByteArrayList) - { - var list = new List>(); - - foreach (IMessage m in messages) - { - list.Add((IMessage)m); - } - - converted = Message.Create(list); - } - else - { - if (IsMessageList) - { - IList messagingMessages = CreateMessageList(InferredArgumentType); - - foreach (IMessage message in messages) - { - messagingMessages.Add(ToMessagingMessage(message)); - } - - converted = Message.Create(messagingMessages); - } - else - { - IList payloads = CreateList(InferredArgumentType); - - foreach (IMessage message in messages) - { - PreProcessMessage(message); - object convertedObject = MessageConverter.FromMessage(message, InferredArgumentType); - - if (convertedObject == null) - { - throw new MessageConversionException("Message converter returned null"); - } - - payloads.Add(convertedObject); - } - - converted = Message.Create(payloads); - } - } - - try - { - InvokeHandlerAndProcessResult(null, channel, converted); - } - catch (Exception e) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - protected IList CreateMessageList(Type type) - { - Type messageType = typeof(IMessage<>).MakeGenericType(type); - Type listType = typeof(List<>).MakeGenericType(messageType); - return (IList)Activator.CreateInstance(listType); - } - - protected IList CreateList(Type type) - { - Type listType = typeof(List<>).MakeGenericType(type); - return (IList)Activator.CreateInstance(listType); - } - - protected IMessage ToMessagingMessage(IMessage amqpMessage) - { - if (BatchingStrategy.CanDebatch(amqpMessage.Headers)) - { - var list = new List(); - - BatchingStrategy.DeBatch(amqpMessage, _ => - { - object convertedObject = MessageConverter.FromMessage(amqpMessage, null); - - if (convertedObject == null) - { - throw new MessageConversionException("Message converter returned null"); - } - - list.Add(convertedObject); - }); - - return RabbitMessageBuilder.WithPayload(list).CopyHeaders(amqpMessage.Headers).Build(); - } - - PreProcessMessage(amqpMessage); - IMessageHeaders headers = amqpMessage.Headers; - object convertedObject = MessageConverter.FromMessage(amqpMessage, InferredArgumentType); - - if (convertedObject == null) - { - throw new MessageConversionException("Message converter returned null"); - } - - AbstractMessageBuilder builder = convertedObject is IMessage message1 - ? RabbitMessageBuilder.FromMessage(message1) - : RabbitMessageBuilder.WithPayload(convertedObject); - - IMessage message = builder.CopyHeadersIfAbsent(headers).Build(); - return message; - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/DelegatingInvocableHandler.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/DelegatingInvocableHandler.cs deleted file mode 100644 index 68d99a36a0..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/DelegatingInvocableHandler.cs +++ /dev/null @@ -1,255 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Reflection; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Expression.Internal.Spring.Common; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.RabbitMQ.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public class DelegatingInvocableHandler -{ - private static readonly SpelExpressionParser Parser = new(); - private static readonly IParserContext ParserContext = new TemplateParserContext("!{", "}"); - - private readonly Dictionary _handlerSendTo = new(); - private readonly ConcurrentDictionary _cachedHandlers = new(); - - public List Handlers { get; } - - public IInvocableHandlerMethod DefaultHandler { get; } - - public object Bean { get; } - - public IServiceExpressionResolver Resolver { get; } - - public IServiceExpressionContext ServiceExpressionContext { get; } - - public bool HasDefaultHandler => DefaultHandler != null; - - public DelegatingInvocableHandler(List handlers, object bean, IServiceExpressionResolver resolver, - IServiceExpressionContext context) - : this(handlers, null, bean, resolver, context) - { - } - - public DelegatingInvocableHandler(List handlers, IInvocableHandlerMethod defaultHandler, object bean, - IServiceExpressionResolver resolver, IServiceExpressionContext context) - { - Handlers = new List(handlers); - DefaultHandler = defaultHandler; - Bean = bean; - Resolver = resolver; - ServiceExpressionContext = context; - } - - public InvocationResult Invoke(IMessage message, params object[] providedArgs) - { - Type payloadClass = message.Payload.GetType(); - IInvocableHandlerMethod handler = GetHandlerForPayload(payloadClass); - object result = handler.Invoke(message, providedArgs); - - if (!message.Headers.TryGetValue(RabbitMessageHeaders.ReplyTo, out _) && _handlerSendTo.TryGetValue(handler, out IExpression replyTo)) - { - return new InvocationResult(result, replyTo, handler.Method.ReturnType, handler.Handler, handler.Method); - } - - return new InvocationResult(result, null, handler.Method.ReturnType, handler.Handler, handler.Method); - } - - public string GetMethodNameFor(object payload) - { - IInvocableHandlerMethod handlerForPayload = null; - - try - { - handlerForPayload = GetHandlerForPayload(payload.GetType()); - } - catch (Exception) - { - // Ignore - } - - return handlerForPayload == null ? "no match" : handlerForPayload.Method.ToString(); - } - - public MethodInfo GetMethodFor(object payload) - { - return GetHandlerForPayload(payload.GetType()).Method; - } - - public InvocationResult GetInvocationResultFor(object result, object inboundPayload) - { - IInvocableHandlerMethod handler = FindHandlerForPayload(inboundPayload.GetType()); - - if (handler != null) - { - _handlerSendTo.TryGetValue(handler, out IExpression sendTo); - return new InvocationResult(result, sendTo, handler.Method.ReturnType, handler.Handler, handler.Method); - } - - return null; - } - - protected IInvocableHandlerMethod GetHandlerForPayload(Type payloadType) - { - if (!_cachedHandlers.TryGetValue(payloadType, out IInvocableHandlerMethod handler)) - { - handler = FindHandlerForPayload(payloadType); - - if (handler == null) - { - throw new RabbitException($"No method found for {payloadType}"); - } - - _cachedHandlers.TryAdd(payloadType, handler); - SetupReplyTo(handler); - } - - return handler; - } - - protected virtual IInvocableHandlerMethod FindHandlerForPayload(Type payloadType) - { - IInvocableHandlerMethod result = null; - - foreach (IInvocableHandlerMethod handler in Handlers) - { - if (MatchHandlerMethod(payloadType, handler)) - { - if (result != null) - { - bool resultIsDefault = result.Equals(DefaultHandler); - - if (!handler.Equals(DefaultHandler) && !resultIsDefault) - { - throw new RabbitException($"Ambiguous methods for payload type: {payloadType}: {result.Method.Name} and {handler.Method.Name}"); - } - - if (!resultIsDefault) - { - continue; // otherwise replace the result with the actual match - } - } - - result = handler; - } - } - - return result ?? DefaultHandler; - } - - protected bool MatchHandlerMethod(Type payloadType, IInvocableHandlerMethod handler) - { - MethodInfo method = handler.Method; - ParameterInfo[] parameters = method.GetParameters(); - Attribute[][] parameterAnnotations = GetParameterAnnotations(method); - - // Single param; no annotation or not @Header - if (parameterAnnotations.Length == 1 && - (parameterAnnotations[0].Length == 0 || !Array.Exists(parameterAnnotations[0], attr => attr is HeaderAttribute)) && - parameters[0].ParameterType.IsAssignableFrom(payloadType)) - { - return true; - } - - bool foundCandidate = false; - - for (int i = 0; i < parameterAnnotations.Length; i++) - { - if ((parameterAnnotations[i].Length == 0 || !Array.Exists(parameterAnnotations[i], attr => attr is HeaderAttribute)) && - parameters[i].ParameterType.IsAssignableFrom(payloadType)) - { - if (foundCandidate) - { - throw new RabbitException($"Ambiguous payload parameter for {method}"); - } - - foundCandidate = true; - } - } - - return foundCandidate; - } - - private Attribute[][] GetParameterAnnotations(MethodInfo method) - { - ParameterInfo[] parameters = method.GetParameters(); - var attributes = new Attribute[parameters.Length][]; - int index = 0; - - foreach (ParameterInfo parameter in parameters) - { - attributes[index++] = parameter.GetCustomAttributes().ToArray(); - } - - return attributes; - } - - private void SetupReplyTo(IInvocableHandlerMethod handler) - { - string replyTo = null; - MethodInfo method = handler.Method; - - if (method != null) - { - var ann = method.GetCustomAttribute(); - replyTo = ExtractSendTo(method.ToString(), ann); - } - - if (replyTo == null) - { - var ann = Bean.GetType().GetCustomAttribute(); - replyTo = ExtractSendTo(Bean.GetType().Name, ann); - } - - if (replyTo != null) - { - _handlerSendTo[handler] = Parser.ParseExpression(replyTo, ParserContext); - } - } - - private string ExtractSendTo(string element, SendToAttribute ann) - { - string replyTo = null; - - if (ann != null) - { - string[] destinations = ann.Destinations; - - if (destinations.Length > 1) - { - throw new InvalidOperationException($"Invalid SendToAttribute on '{element}' only one destination must be set"); - } - - replyTo = destinations.Length == 1 ? Resolve(destinations[0]) : null; - } - - return replyTo; - } - - private string Resolve(string value) - { - if (Resolver != null) - { - string resolvedValue = ServiceExpressionContext.ApplicationContext.ResolveEmbeddedValue(value); - object newValue = Resolver.Evaluate(resolvedValue, ServiceExpressionContext); - - if (newValue is not string sValue) - { - throw new InvalidOperationException("Invalid SendToAttribute expression"); - } - - return sValue; - } - - return value; - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/HandlerAdapter.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/HandlerAdapter.cs deleted file mode 100644 index c7b549edba..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/HandlerAdapter.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public class HandlerAdapter -{ - public IInvocableHandlerMethod InvokerHandlerMethod { get; } - - public DelegatingInvocableHandler DelegatingHandler { get; } - - public object Instance - { - get - { - if (InvokerHandlerMethod != null) - { - return InvokerHandlerMethod.Handler; - } - - return DelegatingHandler.Bean; - } - } - - public HandlerAdapter(IInvocableHandlerMethod invokerHandlerMethod) - { - InvokerHandlerMethod = invokerHandlerMethod; - DelegatingHandler = null; - } - - public HandlerAdapter(DelegatingInvocableHandler delegatingHandler) - { - InvokerHandlerMethod = null; - DelegatingHandler = delegatingHandler; - } - - public InvocationResult Invoke(IMessage message, params object[] providedArgs) - { - if (InvokerHandlerMethod != null) - { - return new InvocationResult(InvokerHandlerMethod.Invoke(message, providedArgs), null, InvokerHandlerMethod.Method.ReturnType, - InvokerHandlerMethod.Handler, InvokerHandlerMethod.Method); - } - - if (DelegatingHandler.HasDefaultHandler) - { - // Needed to avoid returning raw Message which matches Object - object[] args = new object[providedArgs.Length + 1]; - args[0] = message.Payload; - Array.Copy(providedArgs, 0, args, 1, providedArgs.Length); - return DelegatingHandler.Invoke(message, args); - } - - return DelegatingHandler.Invoke(message, providedArgs); - } - - public string GetMethodAsString(object payload) - { - if (InvokerHandlerMethod != null) - { - return InvokerHandlerMethod.Method.ToString(); - } - - return DelegatingHandler.GetMethodNameFor(payload); - } - - public MethodInfo GetMethodFor(object payload) - { - if (InvokerHandlerMethod != null) - { - return InvokerHandlerMethod.Method; - } - - return DelegatingHandler.GetMethodFor(payload); - } - - public Type GetReturnTypeFor(object payload) - { - if (InvokerHandlerMethod != null) - { - return InvokerHandlerMethod.Method.ReturnType; - } - - return DelegatingHandler.GetMethodFor(payload).ReturnType; - } - - public InvocationResult GetInvocationResultFor(object result, object inboundPayload) - { - if (InvokerHandlerMethod != null) - { - return new InvocationResult(result, null, InvokerHandlerMethod.Method.ReturnType, InvokerHandlerMethod.Handler, InvokerHandlerMethod.Method); - } - - return DelegatingHandler.GetInvocationResultFor(result, inboundPayload); - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/IReplyPostProcessor.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/IReplyPostProcessor.cs deleted file mode 100644 index 91b3bcf420..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/IReplyPostProcessor.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public interface IReplyPostProcessor : IServiceNameAware -{ - IMessage Apply(IMessage arg1, IMessage arg2); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/IReplyingMessageListener.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/IReplyingMessageListener.cs deleted file mode 100644 index e965f335de..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/IReplyingMessageListener.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public interface IReplyingMessageListener -{ - TResult HandleMessage(TMessage t); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/InvocationResult.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/InvocationResult.cs deleted file mode 100644 index fe640751d8..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/InvocationResult.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Expression.Internal; - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public class InvocationResult -{ - public object ReturnValue { get; } - - public IExpression SendTo { get; } - - public Type ReturnType { get; } - - public object Instance { get; } - - public MethodInfo Method { get; } - - public InvocationResult(object result, IExpression sendTo, Type returnType, object instance, MethodInfo method) - { - ReturnValue = result; - SendTo = sendTo; - ReturnType = returnType; - Instance = instance; - Method = method; - } - - public override string ToString() - { - return - $"InvocationResult [returnValue={ReturnValue}{(SendTo != null ? $", sendTo={SendTo}" : string.Empty)}, returnType={ReturnType}, instance={Instance}, method={Method}]"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/MessageListenerAdapter.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/MessageListenerAdapter.cs deleted file mode 100644 index b588145f37..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/MessageListenerAdapter.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using RC = RabbitMQ.Client; -using SteeltoeMethodInvoker = Steeltoe.Common.Util.MethodInvoker; - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public class MessageListenerAdapter : AbstractMessageListenerAdapter -{ - public const string OriginalDefaultListenerMethod = "HandleMessage"; - private readonly Dictionary _queueOrTagToMethodName = new(); - - public string DefaultListenerMethod { get; set; } = OriginalDefaultListenerMethod; - - public object Instance { get; set; } - - public MessageListenerAdapter(IApplicationContext context, ILogger logger = null) - : base(context, logger) - { - Instance = this; - } - - public MessageListenerAdapter(IApplicationContext context, object @delegate, ILogger logger = null) - : base(context, logger) - { - ArgumentGuard.NotNull(@delegate); - - Instance = @delegate; - } - - public MessageListenerAdapter(IApplicationContext context, object @delegate, ISmartMessageConverter messageConverter, ILogger logger = null) - : base(context, logger) - { - ArgumentGuard.NotNull(@delegate); - - Instance = @delegate; - MessageConverter = messageConverter; - } - - public MessageListenerAdapter(IApplicationContext context, object @delegate, string defaultListenerMethod, ILogger logger = null) - : this(context, @delegate, logger) - { - DefaultListenerMethod = defaultListenerMethod; - } - - public void SetQueueOrTagToMethodName(Dictionary queueOrTagToMethodName) - { - foreach (KeyValuePair entry in queueOrTagToMethodName) - { - _queueOrTagToMethodName[entry.Key] = entry.Value; - } - } - - public void AddQueueOrTagToMethodName(string queueOrTag, string methodName) - { - _queueOrTagToMethodName[queueOrTag] = methodName; - } - - public string RemoveQueueOrTagToMethodName(string queueOrTag) - { - _queueOrTagToMethodName.Remove(queueOrTag, out string previous); - return previous; - } - - public override void OnMessage(IMessage message, RC.IModel channel) - { - // Check whether the delegate is a IMessageListener impl itself. - // In that case, the adapter will simply act as a pass-through. - object delegateListener = Instance; - - if (delegateListener != this) - { - switch (delegateListener) - { - case IChannelAwareMessageListener channelAwareMessageListener: - channelAwareMessageListener.OnMessage(message, channel); - return; - case IMessageListener messageListener: - messageListener.OnMessage(message); - return; - } - } - - // Regular case: find a handler method reflectively. - object convertedMessage = ExtractMessage(message); - string methodName = GetListenerMethodName(message, convertedMessage); - - if (methodName == null) - { - throw new InvalidOperationException("No default listener method specified: " + - "Either specify a non-null value for the 'DefaultListenerMethod' property or " + "override the 'GetListenerMethodName' method."); - } - - // Invoke the handler method with appropriate arguments. - object[] listenerArguments = BuildListenerArguments(convertedMessage, channel, message); - object result = InvokeListenerMethod(methodName, listenerArguments, message); - - if (result != null) - { - HandleResult(new InvocationResult(result, null, null, null, null), message, channel); - } - else - { - Logger?.LogTrace("No result object given - no result to handle"); - } - } - - protected virtual object[] BuildListenerArguments(object extractedMessage, RC.IModel channel, IMessage message) - { - return BuildListenerArguments(extractedMessage); - } - - protected virtual object[] BuildListenerArguments(object extractedMessage) - { - return new[] - { - extractedMessage - }; - } - - protected virtual string GetListenerMethodName(IMessage originalMessage, object extractedMessage) - { - if (_queueOrTagToMethodName.Count > 0) - { - IMessageHeaders props = originalMessage.Headers; - - if (!_queueOrTagToMethodName.TryGetValue(props.ConsumerQueue(), out string methodName)) - { - _queueOrTagToMethodName.TryGetValue(props.ConsumerTag(), out methodName); - } - - if (methodName != null) - { - return methodName; - } - } - - return DefaultListenerMethod; - } - - protected virtual object InvokeListenerMethod(string methodName, object[] arguments, IMessage originalMessage) - { - try - { - var methodInvoker = new SteeltoeMethodInvoker(); - methodInvoker.SetTargetObject(Instance); - methodInvoker.TargetMethod = methodName; - methodInvoker.SetArguments(arguments); - methodInvoker.Prepare(); - return methodInvoker.Invoke(); - } - catch (TargetInvocationException ex) - { - Exception targetEx = ex.InnerException; - - throw new ListenerExecutionFailedException($"Listener method '{methodName}' threw exception", targetEx, originalMessage); - } - catch (Exception ex) - { - var arrayClass = new List(); - - if (arguments != null) - { - foreach (object argument in arguments) - { - if (argument != null) - { - arrayClass.Add(argument.GetType().ToString()); - } - } - } - - throw new ListenerExecutionFailedException( - $"Failed to invoke target method '{methodName}' with argument type = [{string.Join(",", arrayClass)}], value = [{NullSafeToString(arguments)}]", - ex, originalMessage); - } - } - - private string NullSafeToString(object[] values) - { - var sb = new StringBuilder(); - - foreach (object v in values) - { - sb.Append(v); - sb.Append(','); - } - - return sb.ToString(0, sb.Length - 1); - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/MessagingMessageListenerAdapter.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/MessagingMessageListenerAdapter.cs deleted file mode 100644 index c83131e8e1..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/MessagingMessageListenerAdapter.cs +++ /dev/null @@ -1,319 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.Support; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public class MessagingMessageListenerAdapter : AbstractMessageListenerAdapter -{ - public virtual object Instance { get; } - - public virtual MethodInfo Method { get; } - - public bool IsBatch { get; } - - public bool IsMessageList { get; set; } - - public bool IsMessageByteArrayList { get; set; } - - public Type InferredArgumentType { get; set; } - - public bool ReturnExceptions { get; } - - public IRabbitListenerErrorHandler ErrorHandler { get; } - - public HandlerAdapter HandlerAdapter { get; set; } - - public MessagingMessageListenerAdapter(IApplicationContext context, ILogger logger = null) - : this(context, null, null, logger) - { - } - - public MessagingMessageListenerAdapter(IApplicationContext context, object instance, MethodInfo method, ILogger logger = null) - : this(context, instance, method, false, null, logger) - { - } - - public MessagingMessageListenerAdapter(IApplicationContext context, object instance, MethodInfo method, bool returnExceptions, - IRabbitListenerErrorHandler errorHandler, ILogger logger = null) - : this(context, instance, method, returnExceptions, errorHandler, false, logger) - { - } - - protected MessagingMessageListenerAdapter(IApplicationContext context, object instance, MethodInfo method, bool returnExceptions, - IRabbitListenerErrorHandler errorHandler, bool batch, ILogger logger = null) - : base(context, logger) - { - Instance = instance; - Method = method; - IsBatch = batch; - ReturnExceptions = returnExceptions; - ErrorHandler = errorHandler; - InferredArgumentType = DetermineInferredType(); - } - - public override void OnMessage(IMessage message, RC.IModel channel) - { - PreProcessMessage(message); - IMessageHeaders headers = message.Headers; - object convertedObject = MessageConverter.FromMessage(message, InferredArgumentType); - - if (convertedObject == null) - { - throw new MessageConversionException("Message converter returned null"); - } - - AbstractMessageBuilder builder = convertedObject is IMessage message1 - ? RabbitMessageBuilder.FromMessage(message1) - : RabbitMessageBuilder.WithPayload(convertedObject); - - IMessage newMessage = builder.CopyHeadersIfAbsent(headers).Build(); - InvokeHandlerAndProcessResult(message, channel, newMessage); - } - - protected internal override IMessage BuildMessage(RC.IModel channel, object result, Type genericType) - { - ISmartMessageConverter converter = MessageConverter; - - if (converter != null) - { - if (result is IMessage asMessage) - { - result = converter.ToMessage(asMessage.Payload, asMessage.Headers, genericType); - } - else - { - result = converter.ToMessage(result, new MessageHeaders(), genericType); - } - } - - if (result is not IMessage mResult) - { - throw new MessageConversionException($"No MessageConverter specified - cannot handle message [{result}]"); - } - - return mResult; - } - - protected void InvokeHandlerAndProcessResult(IMessage amqpMessage, RC.IModel channel, IMessage message) - { - Logger?.LogDebug("Processing [{message}]", message); - InvocationResult result = null; - - try - { - if (Method == null) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.TargetMethod = HandlerAdapter.GetMethodFor(message.Payload); - } - - result = InvokeHandler(amqpMessage, channel, message); - - if (result.ReturnValue != null) - { - HandleResult(result, amqpMessage, channel, message); - } - else - { - Logger?.LogTrace("No result object given - no result to handle"); - } - } - catch (ListenerExecutionFailedException e) - { - if (ErrorHandler != null) - { - try - { - IMessage messageWithChannel = RabbitMessageBuilder.FromMessage(message).SetHeader(RabbitMessageHeaders.Channel, channel).Build(); - object errorResult = ErrorHandler.HandleError(amqpMessage, messageWithChannel, e); - - if (errorResult != null) - { - HandleResult(HandlerAdapter.GetInvocationResultFor(errorResult, message.Payload), amqpMessage, channel, message); - } - else - { - Logger?.LogTrace(e, "Error handler returned no result"); - } - } - catch (Exception ex) - { - ReturnOrThrow(amqpMessage, channel, message, ex, ex); - } - } - else - { - ReturnOrThrow(amqpMessage, channel, message, e.InnerException, e); - } - } - } - - protected void PreProcessMessage(IMessage message) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - - if (Instance != null) - { - accessor.Target = Instance; - } - - if (Method != null) - { - accessor.TargetMethod = Method; - - if (InferredArgumentType != null) - { - accessor.InferredArgumentType = InferredArgumentType; - } - } - } - - private InvocationResult InvokeHandler(IMessage amqpMessage, RC.IModel channel, IMessage message) - { - try - { - return HandlerAdapter.Invoke(message, channel); - } - catch (MessagingException ex) - { - throw new ListenerExecutionFailedException( - CreateMessagingErrorMessage("Listener method could not be invoked with the incoming message", message.Payload), ex, amqpMessage); - } - catch (Exception ex) - { - throw new ListenerExecutionFailedException($"Listener method '{HandlerAdapter.GetMethodAsString(message.Payload)}' threw exception", ex, - amqpMessage); - } - } - - private string CreateMessagingErrorMessage(string description, object payload) - { - return $"{description}\nEndpoint handler details:\nMethod [{HandlerAdapter.GetMethodAsString(payload)}]\nBean [{HandlerAdapter.Instance}]"; - } - - private void ReturnOrThrow(IMessage amqpMessage, RC.IModel channel, IMessage message, Exception exceptionToReturn, Exception exceptionToThrow) - { - if (!ReturnExceptions) - { - throw exceptionToThrow; - } - - try - { - HandleResult( - new InvocationResult(exceptionToReturn, null, HandlerAdapter.GetReturnTypeFor(message.Payload), HandlerAdapter.Instance, - HandlerAdapter.GetMethodFor(message.Payload)), amqpMessage, channel, message); - } - catch (ReplyFailureException) - { - if (HandlerAdapter.GetReturnTypeFor(message.Payload) == typeof(void)) - { - throw exceptionToThrow; - } - - throw; - } - } - - private Type DetermineInferredType() - { - if (Method == null) - { - return null; - } - - Type genericParameterType = null; - - foreach (ParameterInfo methodParameter in Method.GetParameters()) - { - /* - * We're looking for a single non-annotated parameter, or one annotated with @Payload. - * We ignore parameters with type Message because they are not involved with conversion. - */ - if (IsEligibleParameter(methodParameter) && (methodParameter.GetCustomAttributes(false).Length == 0 || - methodParameter.GetCustomAttribute(typeof(PayloadAttribute)) != null)) - { - if (genericParameterType == null) - { - genericParameterType = ExtractGenericParameterTypFromMethodParameter(methodParameter); - } - else - { - Logger?.LogDebug("Ambiguous parameters for target payload for method {method}; no inferred type header added", Method); - return null; - } - } - } - - return genericParameterType; - } - - // Don't consider parameter types that are available after conversion. - // Message, Message and Channel. - private bool IsEligibleParameter(ParameterInfo methodParameter) - { - Type parameterType = methodParameter.ParameterType; - - if (parameterType == typeof(RC.IModel)) - { - return false; - } - - if (parameterType.IsGenericType) - { - Type typeDef = parameterType.GetGenericTypeDefinition(); - - if (typeDef == typeof(IMessage<>)) - { - return true; - } - } - - return parameterType != typeof(IMessage); // could be Message without a generic type - } - - private Type ExtractGenericParameterTypFromMethodParameter(ParameterInfo methodParameter) - { - Type parameterType = methodParameter.ParameterType; - - if (parameterType.IsGenericType) - { - Type typeDef = parameterType.GetGenericTypeDefinition(); - - if (typeDef == typeof(IMessage<>)) - { - parameterType = parameterType.GetGenericArguments()[0]; - } - else if (IsBatch && typeDef == typeof(List<>)) - { - Type paramType = parameterType.GetGenericArguments()[0]; - bool messageHasGeneric = paramType.IsGenericType && paramType.GetGenericTypeDefinition() == typeof(IMessage<>); - IsMessageList = paramType == typeof(IMessage) || messageHasGeneric; - IsMessageByteArrayList = paramType == typeof(IMessage); - - if (messageHasGeneric) - { - parameterType = paramType.GetGenericArguments()[0]; - } - else - { - // when decoding batch messages we convert to the List's generic type - parameterType = paramType; - } - } - } - - return parameterType; - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Adapters/ReplyFailureException.cs b/src/Messaging/src/RabbitMQ/Listener/Adapters/ReplyFailureException.cs deleted file mode 100644 index 55500a68aa..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Adapters/ReplyFailureException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -public class ReplyFailureException : Exception -{ - public ReplyFailureException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/BlockingQueueConsumer.cs b/src/Messaging/src/RabbitMQ/Listener/BlockingQueueConsumer.cs deleted file mode 100644 index cd5c665878..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/BlockingQueueConsumer.cs +++ /dev/null @@ -1,763 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Runtime.CompilerServices; -using Microsoft.Extensions.Logging; -using RabbitMQ.Client.Exceptions; -using Steeltoe.Common.Transaction; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener.Support; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Util; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class BlockingQueueConsumer -{ - private const int DefaultDeclarationRetries = 3; - private const int DefaultRetryDeclarationInterval = 60000; - - private ConcurrentDictionary Consumers { get; } = new(); - - protected bool HasDelivery => Queue.Count != 0; - - protected bool Cancelled => - Cancel.Value || (AbortStarted > 0 && AbortStarted + ShutdownTimeout > DateTimeOffset.Now.ToUnixTimeMilliseconds()) || !ActiveObjectCounter.IsActive; - - public ILogger Logger { get; } - - public IMessageHeadersConverter MessageHeadersConverter { get; set; } - - public BlockingCollection Queue { get; } - - public ILoggerFactory LoggerFactory { get; } - - public IConnectionFactory ConnectionFactory { get; } - - public ActiveObjectCounter ActiveObjectCounter { get; } - - public bool Transactional { get; } - - public ushort PrefetchCount { get; } - - public List Queues { get; } - - public AcknowledgeMode AcknowledgeMode { get; } - - public RC.IModel Channel { get; internal set; } - - public bool Exclusive { get; } - - public bool NoLocal { get; } - - public AtomicBoolean Cancel { get; } = new(false); - - public bool DefaultRequeueRejected { get; } - - public Dictionary ConsumerArgs { get; } = new(); - - public RC.ShutdownEventArgs Shutdown { get; private set; } - - public HashSet DeliveryTags { get; internal set; } = new(); - - public long AbortStarted { get; private set; } - - public bool NormalCancel { get; set; } - - public bool Declaring { get; set; } - - public int ShutdownTimeout { get; set; } - - public int DeclarationRetries { get; set; } = DefaultDeclarationRetries; - - public int FailedDeclarationRetryInterval { get; set; } = AbstractMessageListenerContainer.DefaultFailedDeclarationRetryInterval; - - public int RetryDeclarationInterval { get; set; } = DefaultRetryDeclarationInterval; - - public IConsumerTagStrategy TagStrategy { get; set; } - - public IBackOffExecution BackOffExecution { get; set; } - - public bool LocallyTransacted { get; set; } - - public int QueueCount => Queues.Count; - - public HashSet MissingQueues => new(); - - public long LastRetryDeclaration { get; set; } - - public RabbitResourceHolder ResourceHolder { get; set; } - - public BlockingQueueConsumer(IConnectionFactory connectionFactory, IMessageHeadersConverter messagePropertiesConverter, - ActiveObjectCounter activeObjectCounter, AcknowledgeMode acknowledgeMode, bool transactional, ushort prefetchCount, - ILoggerFactory loggerFactory, params string[] queues) - : this(connectionFactory, messagePropertiesConverter, activeObjectCounter, acknowledgeMode, transactional, prefetchCount, true, loggerFactory, queues) - { - } - - public BlockingQueueConsumer(IConnectionFactory connectionFactory, IMessageHeadersConverter messagePropertiesConverter, - ActiveObjectCounter activeObjectCounter, AcknowledgeMode acknowledgeMode, bool transactional, ushort prefetchCount, - bool defaultRequeueRejected, ILoggerFactory loggerFactory, params string[] queues) - : this(connectionFactory, messagePropertiesConverter, activeObjectCounter, acknowledgeMode, transactional, prefetchCount, defaultRequeueRejected, null, - loggerFactory, queues) - { - } - - public BlockingQueueConsumer(IConnectionFactory connectionFactory, IMessageHeadersConverter messagePropertiesConverter, - ActiveObjectCounter activeObjectCounter, AcknowledgeMode acknowledgeMode, bool transactional, ushort prefetchCount, - bool defaultRequeueRejected, Dictionary consumerArgs, ILoggerFactory loggerFactory, params string[] queues) - : this(connectionFactory, messagePropertiesConverter, activeObjectCounter, acknowledgeMode, transactional, prefetchCount, defaultRequeueRejected, - consumerArgs, false, loggerFactory, queues) - { - } - - public BlockingQueueConsumer(IConnectionFactory connectionFactory, IMessageHeadersConverter messagePropertiesConverter, - ActiveObjectCounter activeObjectCounter, AcknowledgeMode acknowledgeMode, bool transactional, ushort prefetchCount, - bool defaultRequeueRejected, Dictionary consumerArgs, bool exclusive, ILoggerFactory loggerFactory, params string[] queues) - : this(connectionFactory, messagePropertiesConverter, activeObjectCounter, acknowledgeMode, transactional, prefetchCount, defaultRequeueRejected, - consumerArgs, false, exclusive, loggerFactory, queues) - { - } - - public BlockingQueueConsumer(IConnectionFactory connectionFactory, IMessageHeadersConverter messagePropertiesConverter, - ActiveObjectCounter activeObjectCounter, AcknowledgeMode acknowledgeMode, bool transactional, ushort prefetchCount, - bool defaultRequeueRejected, Dictionary consumerArgs, bool noLocal, bool exclusive, ILoggerFactory loggerFactory, - params string[] queues) - { - ConnectionFactory = connectionFactory; - MessageHeadersConverter = messagePropertiesConverter; - ActiveObjectCounter = activeObjectCounter; - AcknowledgeMode = acknowledgeMode; - Transactional = transactional; - PrefetchCount = prefetchCount; - DefaultRequeueRejected = defaultRequeueRejected; - - if (consumerArgs != null && consumerArgs.Count > 0) - { - foreach (KeyValuePair arg in consumerArgs) - { - ConsumerArgs.Add(arg.Key, arg.Value); - } - } - - NoLocal = noLocal; - Exclusive = exclusive; - Queues = queues.ToList(); - Queue = new BlockingCollection(prefetchCount); - LoggerFactory = loggerFactory; - Logger = loggerFactory?.CreateLogger(); - } - - public List GetConsumerTags() - { - return Consumers.Values.Select(c => c.ConsumerTag).Where(tag => tag != null).ToList(); - } - - public void ClearDeliveryTags() - { - DeliveryTags.Clear(); - } - - public IMessage NextMessage() - { - Logger?.LogTrace("Retrieving delivery for: {consumer}", this); - return Handle(Queue.Take()); - } - - public IMessage NextMessage(int timeout) - { - Logger?.LogTrace("Retrieving delivery for: {consumer}", this); - CheckShutdown(); - - if (MissingQueues.Count > 0) - { - CheckMissingQueues(); - } - - Queue.TryTake(out Delivery item, timeout); - IMessage message = Handle(item); - - if (message == null && Cancel.Value) - { - throw new ConsumerCancelledException(); - } - - return message; - } - - public void Start() - { - Logger?.LogDebug("Starting consumer {consumer}", this); - - try - { - ResourceHolder = ConnectionFactoryUtils.GetTransactionalResourceHolder(ConnectionFactory, Transactional); - Channel = ResourceHolder.GetChannel(); - } - catch (RabbitAuthenticationException e) - { - throw new FatalListenerStartupException("Authentication failure", e); - } - - DeliveryTags.Clear(); - ActiveObjectCounter.Add(this); - PassiveDeclarations(); - SetQosAndCreateConsumers(); - } - - public void Stop() - { - if (AbortStarted == 0) - { - AbortStarted = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - } - - if (!Cancelled) - { - try - { - RabbitUtils.CloseMessageConsumer(Channel, GetConsumerTags(), Transactional); - } - catch (Exception e) - { - Logger?.LogDebug(e, "Error closing consumer: {consumer}", this); - } - } - - Logger?.LogDebug("Closing Rabbit Channel : {channel}", Channel); - RabbitUtils.SetPhysicalCloseRequired(Channel, true); - ConnectionFactoryUtils.ReleaseResources(ResourceHolder); - DeliveryTags.Clear(); - _ = Consumers.TakeWhile(_ => Consumers.Count > 0); - _ = Queue.TakeWhile(_ => Queue.Count > 0); - } - - public void RollbackOnExceptionIfNecessary(Exception ex) - { - bool ackRequired = !AcknowledgeMode.IsAutoAck() && (!AcknowledgeMode.IsManual() || ContainerUtils.IsRejectManual(ex)); - - try - { - if (Transactional) - { - Logger?.LogDebug(ex, "Initiating transaction rollback on application exception"); - RabbitUtils.RollbackIfNecessary(Channel); - } - - if (ackRequired) - { - if (DeliveryTags.Count > 0) - { - ulong deliveryTag = DeliveryTags.Max(); - Channel.BasicNack(deliveryTag, true, ContainerUtils.ShouldRequeue(DefaultRequeueRejected, ex, Logger)); - } - - if (Transactional) - { - // Need to commit the reject (=nack) - RabbitUtils.CommitIfNecessary(Channel); - } - } - } - catch (Exception e) - { - Logger?.LogError(ex, "Application exception overridden by rollback exception"); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - finally - { - DeliveryTags.Clear(); - } - } - - public bool CommitIfNecessary(bool localTx) - { - if (DeliveryTags.Count == 0) - { - return false; - } - - bool isLocallyTransacted = localTx || (Transactional && TransactionSynchronizationManager.GetResource(ConnectionFactory) == null); - - try - { - bool ackRequired = !AcknowledgeMode.IsAutoAck() && !AcknowledgeMode.IsManual(); - - if (ackRequired && (!Transactional || isLocallyTransacted)) - { - ulong deliveryTag = new List(DeliveryTags)[DeliveryTags.Count - 1]; - Channel.BasicAck(deliveryTag, true); - } - - if (isLocallyTransacted) - { - // For manual acks we still need to commit - RabbitUtils.CommitIfNecessary(Channel); - } - } - finally - { - DeliveryTags.Clear(); - } - - return true; - } - - public override string ToString() - { - return - $"Consumer@{RuntimeHelpers.GetHashCode(this)}: tags=[{string.Join(',', GetConsumerTags())}], channel={Channel}, acknowledgeMode={AcknowledgeMode} local queue size={Queue.Count}"; - } - - internal List CurrentConsumers() - { - return Consumers.Values.ToList(); - } - - protected void BasicCancel() - { - BasicCancel(false); - } - - protected void BasicCancel(bool expected) - { - NormalCancel = expected; - - GetConsumerTags().ForEach(consumerTag => - { - if (Channel.IsOpen) - { - RabbitUtils.Cancel(Channel, consumerTag); - } - }); - - Cancel.Value = true; - AbortStarted = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - } - - private void PassiveDeclarations() - { - // mirrored queue might be being moved - int passiveDeclareRetries = DeclarationRetries; - Declaring = true; - - do - { - if (Cancelled) - { - break; - } - - try - { - AttemptPassiveDeclarations(); - - if (passiveDeclareRetries < DeclarationRetries) - { - Logger?.LogInformation("Queue declaration succeeded after retrying"); - } - - passiveDeclareRetries = 0; - } - catch (DeclarationException e) - { - HandleDeclarationException(passiveDeclareRetries, e); - } - } - while (passiveDeclareRetries-- > 0 && !Cancelled); - - Declaring = false; - } - - private void SetQosAndCreateConsumers() - { - if (!AcknowledgeMode.IsAutoAck() && !Cancelled) - { - // Set basicQos before calling basicConsume (otherwise if we are not acking the broker - // will send blocks of 100 messages) - try - { - Channel.BasicQos(0, PrefetchCount, true); - } - catch (Exception e) - { - ActiveObjectCounter.Release(this); - throw new RabbitIOException(e); - } - } - - try - { - if (!Cancelled) - { - foreach (string queueName in Queues) - { - if (!MissingQueues.Contains(queueName)) - { - ConsumeFromQueue(queueName); - } - } - } - } - catch (Exception e) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e); - } - } - - private void HandleDeclarationException(int passiveDeclareRetries, DeclarationException e) - { - if (passiveDeclareRetries > 0 && Channel.IsOpen) - { - Logger?.LogWarning(e, "Queue declaration failed; retries left={retries}", passiveDeclareRetries); - - try - { - Thread.Sleep(FailedDeclarationRetryInterval); - } - catch (Exception e1) - { - Declaring = false; - ActiveObjectCounter.Release(this); - throw RabbitExceptionTranslator.ConvertRabbitAccessException(e1); - } - } - else if (e.FailedQueues.Count < Queues.Count) - { - Logger?.LogWarning(e, "Not all queues are available; only listening on those that are - configured: {queues}; not available: {notAvailable}", - string.Join(',', Queues), string.Join(',', e.FailedQueues)); - - lock (MissingQueues) - { - foreach (string q in e.FailedQueues) - { - MissingQueues.Add(q); - } - } - - LastRetryDeclaration = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - } - else - { - Declaring = false; - ActiveObjectCounter.Release(this); - - throw new QueuesNotAvailableException( - "Cannot prepare queue for listener. Either the queue doesn't exist or the broker will not allow us to use it.", e); - } - } - - private void ConsumeFromQueue(string queue) - { - var consumer = new InternalConsumer(this, Channel, queue); - - string consumerTag = Channel.BasicConsume(queue, AcknowledgeMode.IsAutoAck(), TagStrategy != null ? TagStrategy.CreateConsumerTag(queue) : string.Empty, - NoLocal, Exclusive, ConsumerArgs, consumer); - - if (consumerTag != null) - { - Logger?.LogDebug("Started on queue '{queue}' with tag {consumerTag} : {consumer}", queue, consumerTag, this); - } - else - { - Logger?.LogError("Null consumer tag received for queue: {queue} ", queue); - } - } - - private void AttemptPassiveDeclarations() - { - DeclarationException failures = null; - - foreach (string queueName in Queues) - { - try - { - try - { - Channel.QueueDeclarePassive(queueName); - } - catch (WireFormattingException e) - { - try - { - if (Channel is IChannelProxy proxy) - { - proxy.TargetChannel.Close(); - } - } - catch (TimeoutException) - { - // Intentionally left empty. - } - - throw new FatalListenerStartupException("Illegal Argument on Queue Declaration", e); - } - } - catch (RabbitMQClientException e) - { - Logger?.LogWarning(e, "Failed to declare queue: {name} ", queueName); - - if (!Channel.IsOpen) - { - throw new RabbitIOException(e); - } - - failures ??= new DeclarationException(e); - failures.AddFailedQueue(queueName); - } - } - - // Rule suppressed due to Sonar bug: https://github.com/SonarSource/sonar-dotnet/issues/8140 -#pragma warning disable S2583 // Conditionally executed code should be reachable - if (failures != null) -#pragma warning restore S2583 // Conditionally executed code should be reachable - { - throw failures; - } - } - - private void CheckMissingQueues() - { - long now = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - if (now - RetryDeclarationInterval > LastRetryDeclaration) - { - lock (MissingQueues) - { - var toRemove = new List(); - Exception error = null; - - foreach (string queueToCheck in MissingQueues) - { - bool available = true; - const IConnection connection = null; - RC.IModel channelForCheck = null; - - try - { - channelForCheck = ConnectionFactory.CreateConnection().CreateChannel(); - channelForCheck.QueueDeclarePassive(queueToCheck); - Logger?.LogInformation("Queue '{queue}' is now available", queueToCheck); - } - catch (Exception e) - { - available = false; - Logger?.LogWarning(e, "Queue '{queue}' is not available", queueToCheck); - } - finally - { - RabbitUtils.CloseChannel(channelForCheck); - RabbitUtils.CloseConnection(connection); - } - - if (available) - { - try - { - ConsumeFromQueue(queueToCheck); - toRemove.Add(queueToCheck); - } - catch (Exception e) - { - error = e; - break; - } - } - } - - if (toRemove.Count > 0) - { - foreach (string remove in toRemove) - { - MissingQueues.Remove(remove); - } - } - - if (error != null) - { - throw RabbitExceptionTranslator.ConvertRabbitAccessException(error); - } - } - - LastRetryDeclaration = now; - } - } - - private void CheckShutdown() - { - if (Shutdown != null) - { - throw new ShutdownSignalException(Shutdown); - } - } - - private IMessage Handle(Delivery delivery) - { - if (delivery == null && Shutdown != null) - { - throw new ShutdownSignalException(Shutdown); - } - - if (delivery == null) - { - return null; - } - - byte[] body = delivery.Body; - IMessageHeaders messageProperties = MessageHeadersConverter.ToMessageHeaders(delivery.Properties, delivery.Envelope, EncodingUtils.Utf8); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(messageProperties); - accessor.ConsumerTag = delivery.ConsumerTag; - accessor.ConsumerQueue = delivery.Queue; - IMessage message = Message.Create(body, accessor.MessageHeaders); - Logger?.LogDebug("Received message: {message}", message); - - if (messageProperties.DeliveryTag() != null) - { - DeliveryTags.Add(messageProperties.DeliveryTag().Value); - } - - if (Transactional && !LocallyTransacted) - { - ConnectionFactoryUtils.RegisterDeliveryTag(ConnectionFactory, Channel, delivery.Envelope.DeliveryTag); - } - - return message; - } - - private sealed class InternalConsumer : RC.DefaultBasicConsumer - { - public BlockingQueueConsumer Consumer { get; } - - public string QueueName { get; } - - public ILogger Logger { get; } - - public bool Canceled { get; set; } - - public InternalConsumer(BlockingQueueConsumer consumer, RC.IModel channel, string queue, ILogger logger = null) - : base(channel) - { - Consumer = consumer; - QueueName = queue; - Logger = logger; - } - - public override void HandleBasicConsumeOk(string consumerTag) - { - base.HandleBasicConsumeOk(consumerTag); - ConsumerTag = consumerTag; - Logger?.LogDebug("ConsumeOK: {consumer} {consumerTag}", Consumer, consumerTag); - Consumer.Consumers.TryAdd(QueueName, this); - } - - public override void HandleModelShutdown(object model, RC.ShutdownEventArgs reason) - { - base.HandleModelShutdown(model, reason); - - Logger?.LogDebug("Received shutdown signal for consumer tag: {tag} reason: {reason}", ConsumerTag, reason.ReplyText); - Consumer.Shutdown = reason; - Consumer.DeliveryTags.Clear(); - Consumer.ActiveObjectCounter.Release(Consumer); - } - - public override void HandleBasicCancel(string consumerTag) - { - Logger?.LogWarning("Cancel received for {consumerTag} : {queueName} : {consumer}", consumerTag, QueueName, this); - Consumer.Consumers.Remove(QueueName, out _); - - if (Consumer.Consumers.Count != 0) - { - Consumer.BasicCancel(false); - } - else - { - Consumer.Cancel.Value = true; - } - } - - public override void HandleBasicCancelOk(string consumerTag) - { - Logger?.LogDebug("Received CancelOk for {consumerTag} : {queueName} : {consumer}", consumerTag, QueueName, this); - Canceled = true; - } - - public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - RC.IBasicProperties properties, byte[] body) - { - Logger?.LogDebug("Storing delivery for consumer tag: {tag} with deliveryTag: {deliveryTag} for consumer: {consumer}", ConsumerTag, deliveryTag, - this); - - try - { - var delivery = new Delivery(consumerTag, new Envelope(deliveryTag, redelivered, exchange, routingKey), properties, body, QueueName); - - if (Consumer.AbortStarted > 0) - { - if (!Consumer.Queue.TryAdd(delivery, Consumer.ShutdownTimeout)) - { - RabbitUtils.SetPhysicalCloseRequired(Model, true); - _ = Consumer.Queue.TakeWhile(_ => Consumer.Queue.Count > 0); - - if (!Canceled) - { - RabbitUtils.Cancel(Model, consumerTag); - } - - try - { - Model.Close(); - } - catch (Exception) - { - // Intentionally left empty. - } - } - } - else - { - Consumer.Queue.TryAdd(delivery); - } - } - catch (Exception e) - { - Logger?.LogWarning(e, "Unexpected exception during delivery"); - } - } - - public override string ToString() - { - return $"InternalConsumer{{queue='{QueueName}', consumerTag='{ConsumerTag}'}}"; - } - } - - private sealed class DeclarationException : RabbitException - { - private const string MessageText = "Failed to declare queue(s):"; - - public List FailedQueues { get; } = new(); - public override string Message => base.Message + string.Join(',', FailedQueues); - - public DeclarationException() - : base(MessageText) - { - } - - public DeclarationException(Exception innerException) - : base(MessageText, innerException) - { - } - - public void AddFailedQueue(string queue) - { - FailedQueues.Add(queue); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/ConditionalRejectingErrorHandler.cs b/src/Messaging/src/RabbitMQ/Listener/ConditionalRejectingErrorHandler.cs deleted file mode 100644 index fdd9d53a78..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/ConditionalRejectingErrorHandler.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class ConditionalRejectingErrorHandler : IErrorHandler -{ - public const string DefaultServiceName = nameof(ConditionalRejectingErrorHandler); - - private readonly ILogger _logger; - private readonly IFatalExceptionStrategy _exceptionStrategy; - - public virtual bool DiscardFatalErrorsWithXDeath { get; set; } = true; - - public virtual bool RejectManual { get; set; } = true; - - public string ServiceName { get; set; } = DefaultServiceName; - - public ConditionalRejectingErrorHandler(ILogger logger = null) - { - _logger = logger; - _exceptionStrategy = new DefaultExceptionStrategy(logger); - } - - public ConditionalRejectingErrorHandler(IFatalExceptionStrategy exceptionStrategy, ILogger logger = null) - { - _logger = logger; - _exceptionStrategy = exceptionStrategy; - } - - public virtual bool HandleError(Exception exception) - { - _logger?.LogWarning(exception, "Execution of Rabbit message listener failed."); - - if (!CauseChainContainsRabbitRejectAndDoNotRequeueException(exception) && _exceptionStrategy.IsFatal(exception)) - { - if (DiscardFatalErrorsWithXDeath && exception is ListenerExecutionFailedException listenerException) - { - IMessage failed = listenerException.FailedMessage; - - if (failed != null) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(failed); - List> xDeath = accessor.GetXDeathHeader(); - - if (xDeath != null && xDeath.Count > 0) - { - _logger?.LogError( - "x-death header detected on a message with a fatal exception; " + "perhaps requeued from a DLQ? - discarding: {failedMessage} ", - failed); - - throw new ImmediateAcknowledgeException("Fatal and x-death present"); - } - } - } - - throw new RabbitRejectAndDoNotRequeueException("Error Handler converted exception to fatal", RejectManual, exception); - } - - return true; - } - - protected virtual bool CauseChainContainsRabbitRejectAndDoNotRequeueException(Exception exception) - { - Exception cause = exception.InnerException; - - while (cause != null) - { - if (cause is RabbitRejectAndDoNotRequeueException) - { - return true; - } - - cause = cause.InnerException; - } - - return false; - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/DefaultExceptionStrategy.cs b/src/Messaging/src/RabbitMQ/Listener/DefaultExceptionStrategy.cs deleted file mode 100644 index e5de93bf66..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/DefaultExceptionStrategy.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class DefaultExceptionStrategy : IFatalExceptionStrategy -{ - private readonly ILogger _logger; - - public DefaultExceptionStrategy(ILogger logger = null) - { - _logger = logger; - } - - public bool IsFatal(Exception exception) - { - Exception cause = exception.InnerException; - - while (cause is MessagingException && cause is not MessageConversionException && cause is not MethodArgumentResolutionException) - { - cause = cause.InnerException; - } - - switch (exception) - { - case ListenerExecutionFailedException listenerExecution when IsCauseFatal(cause): - _logger?.LogWarning( - "Fatal message conversion error; message rejected; it will be dropped or routed to a dead letter exchange, if so configured: {message}", - listenerExecution.FailedMessage); - - return true; - default: - return false; - } - } - - protected virtual bool IsUserCauseFatal(Exception exception) - { - return false; - } - - private bool IsCauseFatal(Exception exception) - { - return exception switch - { - MessageConversionException => true, - MethodArgumentResolutionException => true, - MissingMethodException => true, - InvalidCastException => true, - _ => IsUserCauseFatal(exception) - }; - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/DirectMessageListenerContainer.cs b/src/Messaging/src/RabbitMQ/Listener/DirectMessageListenerContainer.cs deleted file mode 100644 index 47cacdc708..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/DirectMessageListenerContainer.cs +++ /dev/null @@ -1,1211 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Transaction; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener.Support; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Transaction; -using Steeltoe.Messaging.RabbitMQ.Util; -using Steeltoe.Messaging.Support; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class DirectMessageListenerContainer : AbstractMessageListenerContainer -{ - protected const int StartWaitTime = 60; - protected const int DefaultMonitorInterval = 10_000; - protected const int DefaultAckTimeout = 20_000; - - protected internal readonly List Consumers = new(); - protected internal readonly Dictionary> ConsumersByQueue = new(); - protected internal readonly ActiveObjectCounter CancellationLock = new(); - protected internal readonly List ConsumersToRestart = new(); - - private int _consumersPerQueue = 1; - private volatile bool _started; - private volatile bool _aborted; - private volatile bool _hasStopped; - private long _lastAlertAt; - private Task _consumerMonitorTask; - private CancellationTokenSource _consumerMonitorCancellationToken; - internal CountdownEvent StartedLatch = new(1); - - public virtual int ConsumersPerQueue - { - get => _consumersPerQueue; - set - { - if (IsRunning) - { - AdjustConsumers(value); - } - - _consumersPerQueue = value; - } - } - - public override bool Exclusive - { - get => base.Exclusive; - set - { - if (value && ConsumersPerQueue != 1) - { - throw new ArgumentException("When the consumer is exclusive, the number of consumers per queue must be 1.", nameof(value)); - } - - base.Exclusive = value; - } - } - - public virtual long MonitorInterval { get; set; } = DefaultMonitorInterval; - - public virtual int MessagesPerAck { get; set; } - - public virtual long AckTimeout { get; set; } = DefaultAckTimeout; - - public virtual long LastRestartAttempt { get; private set; } - - public DirectMessageListenerContainer(string name = null, ILoggerFactory loggerFactory = null) - : this(null, null, name, loggerFactory) - { - } - - public DirectMessageListenerContainer(IApplicationContext applicationContext, string name = null, ILoggerFactory loggerFactory = null) - : this(applicationContext, null, name, loggerFactory) - { - } - - public DirectMessageListenerContainer(IApplicationContext applicationContext, IConnectionFactory connectionFactory, string name = null, - ILoggerFactory loggerFactory = null) - : base(applicationContext, connectionFactory, name, loggerFactory) - { - MissingQueuesFatal = false; - } - - public override void SetQueueNames(params string[] queueNames) - { - RemoveQueues(queueNames.AsEnumerable()); - base.RemoveQueueNames(queueNames); - base.SetQueueNames(queueNames); - UpdateQueues(); - } - - public override void AddQueueNames(params string[] queueNames) - { - ArgumentGuard.NotNull(queueNames); - ArgumentGuard.ElementsNotNull(queueNames); - - try - { - AddQueues(queueNames); - } - catch (Exception e) - { - Logger?.LogError(e, "Failed to add queue names"); - throw; - } - - base.AddQueueNames(queueNames); - } - - public override void AddQueues(params IQueue[] queues) - { - ArgumentGuard.NotNull(queues); - ArgumentGuard.ElementsNotNull(queues); - - try - { - IEnumerable queueNames = queues.Select(queue => queue.QueueName); - AddQueues(queueNames); - } - catch (Exception e) - { - Logger?.LogError(e, "Failed to add queues"); - throw; - } - - base.AddQueues(queues); - } - - public override bool RemoveQueueNames(params string[] queueNames) - { - RemoveQueues(queueNames.AsEnumerable()); - return base.RemoveQueueNames(queueNames); - } - - public override void RemoveQueues(params IQueue[] queues) - { - RemoveQueues(queues.Select(q => q.ActualName)); - base.RemoveQueues(queues); - } - - protected virtual int FindIdleConsumer() - { - return 0; - } - - protected override void DoInitialize() - { - if (MessagesPerAck > 0 && IsChannelTransacted) - { - throw new InvalidOperationException("'messagesPerAck' is not allowed with transactions."); - } - } - - protected override void DoStart() - { - if (!_started) - { - ActualStart(); - } - } - - protected virtual void ActualStart() - { - _aborted = false; - _hasStopped = false; - - if (PrefetchCount < MessagesPerAck) - { - PrefetchCount = MessagesPerAck; - } - - base.DoStart(); - CheckListenerContainerAware(); - string[] queueNames = GetQueueNames(); - CheckMissingQueues(queueNames); - - if (IdleEventInterval > 0 && MonitorInterval > IdleEventInterval) - { - MonitorInterval = IdleEventInterval / 2; - } - - if (FailedDeclarationRetryInterval < MonitorInterval) - { - MonitorInterval = FailedDeclarationRetryInterval; - } - - Dictionary namesToQueues = GetQueueNamesToQueues(); - LastRestartAttempt = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - StartMonitor(IdleEventInterval, namesToQueues); - - if (queueNames.Length > 0) - { - DoRedeclareElementsIfNecessary(); - Task.Run(() => StartConsumers(queueNames)); - } - else - { - _started = true; - StartedLatch.Signal(); - } - - Logger?.LogInformation("Container initialized for queues: {queues}", string.Join(',', queueNames)); - } - - protected virtual void DoRedeclareElementsIfNecessary() - { - string routingLookupKey = GetRoutingLookupKey(); - - if (routingLookupKey != null) - { - SimpleResourceHolder.Push(GetRoutingConnectionFactory(), routingLookupKey); - } - - try - { - RedeclareElementsIfNecessary(); - } - catch (FatalListenerStartupException fe) - { - Logger?.LogError(fe, "Fatal exception while redeclare elements"); - throw; - } - catch (Exception e) - { - Logger?.LogError(e, "Failed to redeclare elements"); - } - finally - { - if (routingLookupKey != null) - { - SimpleResourceHolder.Pop(GetRoutingConnectionFactory()); - } - } - } - - protected override void DoShutdown() - { - List canceledConsumers = null; - bool waitForConsumers = false; - - lock (ConsumersMonitor) - { - if (_started || _aborted) - { - // Copy in the same order to avoid ConcurrentModificationException during remove in the - // cancelConsumer(). - canceledConsumers = new List(Consumers); - ActualShutDown(canceledConsumers); - waitForConsumers = true; - } - } - - if (waitForConsumers) - { - try - { - if (CancellationLock.Wait(TimeSpan.FromMilliseconds(ShutdownTimeout))) - { - Logger?.LogInformation("Successfully waited for consumers to finish."); - } - else - { - Logger?.LogInformation("Consumers not finished."); - - if (ForceCloseChannel) - { - canceledConsumers.ForEach(consumer => - { - string eventMessage = $"Closing channel for unresponsive consumer: {consumer} "; - Logger?.LogWarning(eventMessage); - consumer.CancelConsumer(eventMessage); - }); - } - } - } - catch (Exception e) - { - Logger?.LogWarning(e, "Interrupted waiting for consumers. Continuing with shutdown."); - } - finally - { - StartedLatch = new CountdownEvent(1); - _started = false; - _aborted = false; - _hasStopped = true; - } - } - } - - protected virtual void ProcessMonitorTask() - { - // Override if needed - } - - protected virtual void ConsumerRemoved(SimpleConsumer consumer) - { - // Override if needed - } - - private void CheckListenerContainerAware() - { - if (MessageListener is IListenerContainerAware listenerAware) - { - List expectedQueueNames = listenerAware.GetExpectedQueueNames(); - string[] queueNames = GetQueueNames(); - - if (expectedQueueNames.Count != queueNames.Length) - { - throw new InvalidOperationException("Listener expects queues that the container is not listening on"); - } - - if (!Array.Exists(queueNames, expectedQueueNames.Contains)) - { - throw new InvalidOperationException("Listener expects queues that the container is not listening on"); - } - } - } - - private void CheckMissingQueues(string[] queueNames) - { - if (MissingQueuesFatal) - { - IRabbitAdmin checkAdmin = RabbitAdmin; - - if (checkAdmin == null) - { - checkAdmin = new RabbitAdmin(ApplicationContext, ConnectionFactory, LoggerFactory?.CreateLogger()); - RabbitAdmin = checkAdmin; - } - - foreach (string queue in queueNames) - { - Dictionary queueProperties = checkAdmin.GetQueueProperties(queue); - - if (queueProperties == null && MissingQueuesFatal) - { - throw new InvalidOperationException("At least one of the configured queues is missing"); - } - } - } - } - - private void ActualShutDown(List consumers) - { - Logger?.LogDebug("Shutting down"); - consumers.ForEach(CancelConsumer); - Consumers.Clear(); - ConsumersByQueue.Clear(); - Logger?.LogDebug("All consumers canceled"); - - if (_consumerMonitorTask != null) - { - _consumerMonitorCancellationToken.Cancel(); - _consumerMonitorTask = null; - } - } - - private void ConsumeFromQueue(string queue) - { - ConsumersByQueue.TryGetValue(queue, out List list); - - // Possible race with setConsumersPerQueue and the task launched by start() - if (list == null || list.Count == 0) - { - for (int i = 0; i < _consumersPerQueue; i++) - { - DoConsumeFromQueue(queue); - } - } - } - - private void DoConsumeFromQueue(string queue) - { - if (!IsActive) - { - Logger?.LogDebug("Consume from queue {queueName} ignore, container stopping", queue); - return; - } - - string routingLookupKey = GetRoutingLookupKey(); - - if (routingLookupKey != null) - { - SimpleResourceHolder.Push(GetRoutingConnectionFactory(), routingLookupKey); - } - - IConnection connection = null; - - try - { - connection = ConnectionFactory.CreateConnection(); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception while CreateConnection"); - AddConsumerToRestart(new SimpleConsumer(this, null, null, queue, LoggerFactory?.CreateLogger())); - throw; - } - finally - { - if (routingLookupKey != null) - { - SimpleResourceHolder.Pop(GetRoutingConnectionFactory()); - } - } - - SimpleConsumer consumer = Consume(queue, connection); - - lock (ConsumersMonitor) - { - if (consumer != null) - { - CancellationLock.Add(consumer); - Consumers.Add(consumer); - ConsumersByQueue.TryGetValue(queue, out List list); - - if (list == null) - { - list = new List(); - ConsumersByQueue.Add(queue, list); - } - - list.Add(consumer); - Logger?.LogInformation("{consumer} started", consumer); - } - } - } - - private SimpleConsumer Consume(string queue, IConnection connection) - { - RC.IModel channel = null; - SimpleConsumer consumer = null; - - try - { - channel = connection.CreateChannel(IsChannelTransacted); - channel.BasicQos(0, (ushort)PrefetchCount, false); - consumer = new SimpleConsumer(this, connection, channel, queue, LoggerFactory?.CreateLogger()); - channel.QueueDeclarePassive(queue); - - consumer.ConsumerTag = channel.BasicConsume(queue, AcknowledgeMode.IsAutoAck(), - ConsumerTagStrategy != null ? ConsumerTagStrategy.CreateConsumerTag(queue) : string.Empty, NoLocal, Exclusive, ConsumerArguments, consumer); - } - catch (Exception e) - { - RabbitUtils.CloseChannel(channel, Logger); - RabbitUtils.CloseConnection(connection, Logger); - - consumer = HandleConsumeException(queue, consumer, e); - } - - return consumer; - } - - private SimpleConsumer HandleConsumeException(string queue, SimpleConsumer consumerArg, Exception e) - { - SimpleConsumer consumer = consumerArg; - - Logger?.LogWarning(e, $"basicConsume failed, scheduling consumer {consumer?.ToString() ?? $"for queue {queue}"} for restart"); - - if (consumer == null) - { - AddConsumerToRestart(new SimpleConsumer(this, null, null, queue, LoggerFactory?.CreateLogger())); - } - else - { - AddConsumerToRestart(consumer); - consumer = null; - } - - return consumer; - } - - private void StartMonitor(long idleEventInterval, Dictionary namesToQueues) - { - _consumerMonitorCancellationToken = new CancellationTokenSource(); - - _consumerMonitorTask = Task.Run(async () => - { - bool shouldShutdown = false; - - while (!_consumerMonitorCancellationToken.Token.IsCancellationRequested && !shouldShutdown) - { - long now = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - CheckIdle(idleEventInterval, now); - CheckConsumers(now); - - if (LastRestartAttempt + FailedDeclarationRetryInterval < now) - { - lock (ConsumersMonitor) - { - var restartableConsumers = new List(ConsumersToRestart); - ConsumersToRestart.Clear(); - - if (_started) - { - if (restartableConsumers.Count > 0) - { - try - { - DoRedeclareElementsIfNecessary(); - } - catch (FatalListenerStartupException) - { - shouldShutdown = true; - } - } - - if (!shouldShutdown) - { - foreach (SimpleConsumer consumer in restartableConsumers) - { - if (!ConsumersByQueue.ContainsKey(consumer.Queue)) - { - Logger?.LogDebug("Skipping restart of consumer {consumer}", consumer); - continue; - } - - Logger?.LogDebug("Attempting to restart consumer {consumer}", consumer); - - if (!RestartConsumer(namesToQueues, restartableConsumers, consumer)) - { - break; - } - } - - LastRestartAttempt = now; - } - } - } - } - - ProcessMonitorTask(); - - if (shouldShutdown) - { - Shutdown(); - } - else - { - await Task.Delay(TimeSpan.FromMilliseconds(MonitorInterval), _consumerMonitorCancellationToken.Token); - } - } - }, _consumerMonitorCancellationToken.Token); - } - - private void CheckIdle(long idleEventInterval, long now) - { - if (idleEventInterval > 0 && now - LastReceive > idleEventInterval && now - _lastAlertAt > idleEventInterval) - { - _lastAlertAt = now; - } - } - - private void CancelConsumer(SimpleConsumer consumer) - { - try - { - Logger?.LogDebug("Canceling {consumer}", consumer); - - lock (consumer) - { - consumer.Canceled = true; - - if (MessagesPerAck > 1) - { - try - { - consumer.AckIfNecessary(0L); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception while sending delayed ack"); - } - } - } - - RabbitUtils.Cancel(consumer.Model, consumer.ConsumerTag, Logger); - } - finally - { - Consumers.Remove(consumer); - ConsumerRemoved(consumer); - } - } - - private void CheckConsumers(long now) - { - List consumersToCancel; - - lock (ConsumersMonitor) - { - consumersToCancel = Consumers.Where(consumer => - { - bool open = consumer.Model.IsOpen && !consumer.AckFailed && !consumer.TargetChanged; - - if (open && MessagesPerAck > 1) - { - try - { - consumer.AckIfNecessary(now); - } - catch (Exception e) - { - Logger?.LogError(e, "Exception while sending delayed ack"); - } - } - - return !open; - }).ToList(); - } - - consumersToCancel.ForEach(consumer => - { - try - { - RabbitUtils.CloseMessageConsumer(consumer.Model, new List - { - consumer.ConsumerTag - }, IsChannelTransacted, Logger); - } - catch (Exception e) - { - Logger?.LogDebug(e, "Error closing consumer {consumer} ", consumer); - } - - Logger?.LogError("Consumer {consumer} canceled - channel closed ", consumer); - consumer.CancelConsumer($"Consumer {consumer} channel closed"); - }); - } - - private bool RestartConsumer(Dictionary namesToQueues, List restartableConsumers, SimpleConsumer consumerArg) - { - SimpleConsumer consumer = consumerArg; - namesToQueues.TryGetValue(consumer.Queue, out IQueue queue); - - if (queue != null && string.IsNullOrEmpty(queue.QueueName)) - { - // check to see if a broker-declared queue name has changed - string actualName = queue.ActualName; - - if (!string.IsNullOrEmpty(actualName)) - { - namesToQueues.Remove(consumer.Queue); - namesToQueues[actualName] = queue; - consumer = new SimpleConsumer(this, null, null, actualName, LoggerFactory?.CreateLogger()); - } - } - - try - { - DoConsumeFromQueue(consumer.Queue); - return true; - } - catch (Exception e) - { - Logger?.LogError(e, "Cannot connect to server"); - - ConsumersToRestart.AddRange(restartableConsumers); - Logger?.LogTrace(e, "After restart exception, consumers to restart now: {consumersToRestart}", ConsumersToRestart.Count); - return false; - } - } - - private void StartConsumers(string[] queueNames) - { - lock (ConsumersMonitor) - { - if (_hasStopped) - { - // container stopped before we got the lock - Logger?.LogDebug("Consumer start aborted - container stopping"); - } - else - { - IBackOffExecution backOffExecution = RecoveryBackOff.Start(); - - while (!_started && IsRunning) - { - CancellationLock.Reset(); - - try - { - foreach (string queue in queueNames) - { - ConsumeFromQueue(queue); - } - } - catch (Exception e) when (e is RabbitConnectException || e is RabbitIOException) - { - int nextBackOff = backOffExecution.NextBackOff(); - - if (nextBackOff < 0) - { - _aborted = true; - Shutdown(); - Logger?.LogError(e, "Failed to start container - fatal error or backOffs exhausted"); - Task.Run(StopAsync); - break; - } - - Logger?.LogError(e, "Error creating consumer; retrying in {nextBackOff}", nextBackOff); - DoShutdown(); - - try - { - Thread.Sleep(nextBackOff); - } - catch (Exception e1) - { - Logger?.LogError(e1, "Exception while in backoff, {nextBackOff}", nextBackOff); - } - - // initialization failed; try again having rested for backOff-interval - continue; - } - - _started = true; - StartedLatch.Signal(); - } - } - } - } - - private void CheckStartState() - { - if (!IsRunning) - { - try - { - if (!StartedLatch.Wait(TimeSpan.FromSeconds(StartWaitTime))) - { - throw new InvalidOperationException("Container is not started - cannot adjust queues"); - } - } - catch (Exception e) - { - Logger?.LogError(e, "Exception thrown while waiting for container start"); - throw; - } - } - } - - private void UpdateQueues() - { - if (IsRunning) - { - lock (ConsumersMonitor) - { - CheckStartState(); - ISet current = GetQueueNamesAsSet(); - - foreach (string name in current) - { - ConsumeFromQueue(name); - } - } - } - } - - private void AddQueues(IEnumerable names) - { - if (IsRunning) - { - lock (ConsumersMonitor) - { - CheckStartState(); - ISet current = GetQueueNamesAsSet(); - - foreach (string name in names) - { - if (current.Contains(name)) - { - Logger?.LogWarning("Queue {name} is already configured for this container: {container}, ignoring add", name, this); - } - else - { - ConsumeFromQueue(name); - } - } - } - } - } - - private void RemoveQueues(IEnumerable queueNames) - { - if (IsRunning) - { - lock (ConsumersMonitor) - { - CheckStartState(); - - foreach (string name in queueNames) - { - if (name != null && ConsumersByQueue.TryGetValue(name, out List consumers)) - { - foreach (SimpleConsumer consumer in consumers) - { - CancelConsumer(consumer); - } - } - } - } - } - } - - private void AdjustConsumers(int newCount) - { - lock (ConsumersMonitor) - { - CheckStartState(); - ConsumersToRestart.Clear(); - - foreach (string queue in GetQueueNames()) - { - ConsumersByQueue.TryGetValue(queue, out List consumers); - - while (consumers == null || consumers.Count < newCount) - { - DoConsumeFromQueue(queue); - ConsumersByQueue.TryGetValue(queue, out consumers); - } - - if (consumers.Count > newCount) - { - int delta = consumers.Count - newCount; - - for (int i = 0; i < delta; i++) - { - int index = FindIdleConsumer(); - - if (index >= 0) - { - SimpleConsumer consumer = consumers[index]; - consumers.RemoveAt(index); - - if (consumer != null) - { - CancelConsumer(consumer); - } - } - } - } - } - } - } - - private void AddConsumerToRestart(SimpleConsumer consumer) - { - if (_started) - { - ConsumersToRestart.Add(consumer); - Logger?.LogTrace("Consumers to restart now: {count}", ConsumersToRestart.Count); - } - } - - protected internal sealed class SimpleConsumer : RC.DefaultBasicConsumer - { - private readonly DirectMessageListenerContainer _container; - private readonly IConnection _connection; - private readonly RC.IModel _targetChannel; - private readonly ILogger _logger; - private readonly object _lock = new(); - - public string Queue { get; internal set; } - - public int Epoch { get; internal set; } - - public bool Canceled { get; internal set; } - - public bool AckFailed { get; internal set; } - - public bool AckRequired { get; internal set; } - - public int MessagesPerAck { get; internal set; } - - public IPlatformTransactionManager TransactionManager { get; internal set; } - - public ITransactionAttribute TransactionAttribute { get; internal set; } - - public TransactionTemplate TransactionTemplate { get; internal set; } - - public IConnectionFactory ConnectionFactory { get; internal set; } - - public bool IsRabbitTxManager { get; internal set; } - - public ulong LatestDeferredDeliveryTag { get; internal set; } - - public int PendingAcks { get; internal set; } - - public long LastAck { get; internal set; } - - public long AckTimeout { get; internal set; } - - public bool TargetChanged => _targetChannel != null && !_targetChannel.Equals(((IChannelProxy)Model).TargetChannel); - - public SimpleConsumer(DirectMessageListenerContainer container, IConnection connection, RC.IModel channel, string queue, ILogger logger = null) - : base(channel) - { - _container = container; - _connection = connection; - Queue = queue; - AckRequired = !_container.AcknowledgeMode.IsAutoAck() && !_container.AcknowledgeMode.IsManual(); - _targetChannel = channel is IChannelProxy proxy ? proxy.TargetChannel : null; - - _logger = logger; - TransactionManager = _container.TransactionManager; - TransactionAttribute = _container.TransactionAttribute; - IsRabbitTxManager = TransactionManager is RabbitTransactionManager; - ConnectionFactory = _container.ConnectionFactory; - MessagesPerAck = _container.MessagesPerAck; - LastAck = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - AckTimeout = _container.AckTimeout; - } - - public int IncrementAndGetEpoch() - { - Epoch++; - return Epoch; - } - - public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - RC.IBasicProperties properties, byte[] body) - { - var envelope = new Envelope(deliveryTag, redelivered, exchange, routingKey); - IMessageHeaders messageHeaders = _container.MessageHeadersConverter.ToMessageHeaders(properties, envelope, EncodingUtils.Utf8); - var headerAccessor = MessageHeaderAccessor.GetAccessor(messageHeaders); - headerAccessor.ConsumerTag = consumerTag; - headerAccessor.ConsumerQueue = Queue; - IMessage message = Message.Create(body, headerAccessor.MessageHeaders); - _logger?.LogDebug("Received {message} {consumer}", message, this); - _container.UpdateLastReceive(); - - if (TransactionManager != null) - { - try - { - ExecuteListenerInTransaction(message, deliveryTag); - } - catch (Exception) - { - // Intentionally left empty. - } - finally - { - if (IsRabbitTxManager) - { - ConsumerChannelRegistry.UnRegisterConsumerChannel(); - } - } - } - else - { - try - { - CallExecuteListener(message, deliveryTag); - } - catch (Exception) - { - // Intentionally left empty. - } - } - } - - public override void HandleBasicConsumeOk(string consumerTag) - { - base.HandleBasicConsumeOk(consumerTag); - _logger?.LogDebug("New {consumer} consumeOk", this); - } - - public override void HandleBasicCancelOk(string consumerTag) - { - _logger?.LogDebug("CancelOk {consumer}", this); - FinalizeConsumer(); - } - - public override void HandleBasicCancel(string consumerTag) - { - _logger?.LogError("Consumer canceled - queue deleted? {consumerTag}, {consumer}", consumerTag, this); - CancelConsumer($"Consumer {this} canceled"); - } - - public override string ToString() - { - return $"SimpleConsumer [queue={Queue}, consumerTag={ConsumerTag} identity={GetHashCode()}]"; - } - - internal void CancelConsumer(string eventMessage) - { - lock (_container.ConsumersMonitor) - { - if (_container.ConsumersByQueue.TryGetValue(Queue, out List list)) - { - list.Remove(this); - } - - _container.Consumers.Remove(this); - _container.AddConsumerToRestart(this); - } - - FinalizeConsumer(); - } - - internal void AckIfNecessary(long now) - { - lock (_lock) - { - if (PendingAcks >= MessagesPerAck || (PendingAcks > 0 && (now - LastAck > AckTimeout || Canceled))) - { - SendAck(now); - } - } - } - - internal void SendAck(long now) - { - lock (_lock) - { - Model.BasicAck(LatestDeferredDeliveryTag, true); - LastAck = now; - PendingAcks = 0; - } - } - - private void ExecuteListenerInTransaction(IMessage message, ulong deliveryTag) - { - if (IsRabbitTxManager) - { - ConsumerChannelRegistry.RegisterConsumerChannel(Model, ConnectionFactory); - } - - TransactionTemplate ??= new TransactionTemplate(TransactionManager, TransactionAttribute, _logger); - - TransactionTemplate.Execute(_ => - { - RabbitResourceHolder resourceHolder = ConnectionFactoryUtils.BindResourceToTransaction( - new RabbitResourceHolder(Model, false, _container.LoggerFactory?.CreateLogger()), ConnectionFactory, true); - - if (resourceHolder != null) - { - resourceHolder.AddDeliveryTag(Model, deliveryTag); - } - - // unbound in ResourceHolderSynchronization.beforeCompletion() - try - { - CallExecuteListener(message, deliveryTag); - } - catch (Exception e1) - { - _container.PrepareHolderForRollback(resourceHolder, e1); - throw; - } - - return null; - }); - } - - private void CallExecuteListener(IMessage message, ulong deliveryTag) - { - bool channelLocallyTransacted = _container.IsChannelLocallyTransacted; - - try - { - _container.ExecuteListener(Model, message); - HandleAck(deliveryTag, channelLocallyTransacted); - } - catch (ImmediateAcknowledgeException e) - { - _logger?.LogDebug(e, "User requested ack for failed delivery '{tag}'", deliveryTag); - HandleAck(deliveryTag, channelLocallyTransacted); - } - catch (Exception e) - { - if (_container.CauseChainHasImmediateAcknowledgeRabbitException(e)) - { - _logger?.LogDebug(e, "User requested ack for failed delivery: {tag}", deliveryTag); - HandleAck(deliveryTag, channelLocallyTransacted); - } - else - { - _logger?.LogError(e, "Failed to invoke listener"); - - if (TransactionManager != null) - { - if (TransactionAttribute.RollbackOn(e)) - { - var resourceHolder = (RabbitResourceHolder)TransactionSynchronizationManager.GetResource(ConnectionFactory); - - if (resourceHolder == null) - { - /* - * If we don't actually have a transaction, we have to roll back - * manually. See prepareHolderForRollback(). - */ - Rollback(deliveryTag, e); - } - - throw; // encompassing transaction will handle the rollback. - } - - _logger?.LogDebug(e, "No rollback"); - } - else - { - Rollback(deliveryTag, e); - - // no need to rethrow e - we'd ignore it anyway, not throw to client - } - } - } - } - - private void HandleAck(ulong deliveryTag, bool channelLocallyTransacted) - { - /* - * If we have a TX Manager, but no TX, act like we are locally transacted. - */ - bool isLocallyTransacted = channelLocallyTransacted || - (_container.IsChannelTransacted && TransactionSynchronizationManager.GetResource(ConnectionFactory) == null); - - try - { - if (AckRequired) - { - if (MessagesPerAck > 1) - { - lock (_lock) - { - LatestDeferredDeliveryTag = deliveryTag; - PendingAcks++; - AckIfNecessary(LastAck); - } - } - else if (!_container.IsChannelTransacted || isLocallyTransacted) - { - Model.BasicAck(deliveryTag, false); - } - } - - if (isLocallyTransacted) - { - RabbitUtils.CommitIfNecessary(Model); - } - } - catch (Exception e) - { - AckFailed = true; - _logger?.LogError(e, "Error acking"); - } - } - - private void Rollback(ulong deliveryTag, Exception e) - { - if (_container.IsChannelTransacted) - { - RabbitUtils.RollbackIfNecessary(Model); - } - - if (AckRequired || ContainerUtils.IsRejectManual(e)) - { - try - { - if (MessagesPerAck > 1) - { - lock (_lock) - { - if (PendingAcks > 0) - { - SendAck(DateTimeOffset.Now.ToUnixTimeMilliseconds()); - } - } - } - - Model.BasicNack(deliveryTag, true, ContainerUtils.ShouldRequeue(_container.DefaultRequeueRejected, e, _logger)); - } - catch (Exception e1) - { - _logger?.LogError(e1, "Failed to nack message"); - } - } - - if (_container.IsChannelTransacted) - { - RabbitUtils.CommitIfNecessary(Model); - } - } - - private void FinalizeConsumer() - { - RabbitUtils.SetPhysicalCloseRequired(Model, true); - RabbitUtils.CloseChannel(Model); - RabbitUtils.CloseConnection(_connection); - _container.CancellationLock.Release(this); - _container.ConsumerRemoved(this); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/DirectReplyToMessageListenerContainer.cs b/src/Messaging/src/RabbitMQ/Listener/DirectReplyToMessageListenerContainer.cs deleted file mode 100644 index bd046ba399..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/DirectReplyToMessageListenerContainer.cs +++ /dev/null @@ -1,295 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class DirectReplyToMessageListenerContainer : DirectMessageListenerContainer -{ - private const int DefaultIdle = 60000; - internal readonly ConcurrentDictionary InUseConsumerChannels = new(); - internal readonly ConcurrentDictionary WhenUsed = new(); - private int _consumerCount; - - public override int ConsumersPerQueue - { -#pragma warning disable S4275 // Getters and setters should access the expected fields - get -#pragma warning restore S4275 // Getters and setters should access the expected fields - { - return base.ConsumersPerQueue; - } - set - { - throw new NotSupportedException(); - } - } - - public override IMessageListener MessageListener - { - get => base.MessageListener; - set => base.MessageListener = new ChannelAwareMessageListener(this, value); - } - - public DirectReplyToMessageListenerContainer(string name = null, ILoggerFactory loggerFactory = null) - : this(null, null, name, loggerFactory) - { - } - - public DirectReplyToMessageListenerContainer(IApplicationContext applicationContext, string name = null, ILoggerFactory loggerFactory = null) - : this(applicationContext, null, name, loggerFactory) - { - } - - public DirectReplyToMessageListenerContainer(IApplicationContext applicationContext, IConnectionFactory connectionFactory, string name = null, - ILoggerFactory loggerFactory = null) - : base(applicationContext, connectionFactory, name, loggerFactory) - { - base.SetQueueNames(Address.AmqRabbitMQReplyTo); - AcknowledgeMode = AcknowledgeMode.None; - base.ConsumersPerQueue = 0; - IdleEventInterval = DefaultIdle; - } - - public override void SetQueueNames(params string[] queueNames) - { - throw new NotSupportedException(); - } - - public override void AddQueueNames(params string[] queueNames) - { - throw new NotSupportedException(); - } - - public override bool RemoveQueueNames(params string[] queueNames) - { - throw new NotSupportedException(); - } - - public ChannelHolder GetChannelHolder() - { - lock (ConsumersMonitor) - { - ChannelHolder channelHolder = null; - - while (channelHolder == null) - { - if (!IsRunning) - { - throw new InvalidOperationException("Direct reply-to container is not running"); - } - - foreach (SimpleConsumer consumer in Consumers) - { - RC.IModel candidate = consumer.Model; - - if (candidate.IsOpen && InUseConsumerChannels.TryAdd(candidate, consumer)) - { - channelHolder = new ChannelHolder(candidate, consumer.IncrementAndGetEpoch()); - WhenUsed[consumer] = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - break; - } - } - - if (channelHolder == null) - { - _consumerCount++; - base.ConsumersPerQueue = _consumerCount; - } - } - - return channelHolder; - } - } - - public void ReleaseConsumerFor(ChannelHolder channelHolder, bool cancelConsumer, string message) - { - lock (ConsumersMonitor) - { - InUseConsumerChannels.TryGetValue(channelHolder.Channel, out SimpleConsumer consumer); - - if (consumer != null && consumer.Epoch == channelHolder.ConsumerEpoch) - { - InUseConsumerChannels.Remove(channelHolder.Channel, out _); - - if (cancelConsumer) - { - if (message == null) - { - throw new InvalidOperationException("A 'message' is required when 'cancelConsumer' is 'true'."); - } - - consumer.CancelConsumer($"Consumer {this} canceled due to {message}"); - } - } - } - } - - internal void SetChannelAwareMessageListener(IChannelAwareMessageListener listener) - { - base.MessageListener = listener; - } - - protected override void DoStart() - { - if (!IsRunning) - { - _consumerCount = 0; - base.ConsumersPerQueue = 0; - base.DoStart(); - } - } - - protected override void ProcessMonitorTask() - { - long now = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - lock (ConsumersMonitor) - { - long reduce = 0; - - foreach (SimpleConsumer c in Consumers) - { - if (WhenUsed.TryGetValue(c, out long howLong) && !InUseConsumerChannels.Values.Contains(c) && howLong < now - IdleEventInterval) - { - reduce++; - } - } - - if (reduce > 0) - { - Logger?.LogDebug("Reducing idle consumes by {reduce}", reduce); - _consumerCount = (int)Math.Max(0, _consumerCount - reduce); - base.ConsumersPerQueue = _consumerCount; - } - } - } - - protected override int FindIdleConsumer() - { - for (int i = 0; i < Consumers.Count; i++) - { - if (!InUseConsumerChannels.Values.Contains(Consumers[i])) - { - return i; - } - } - - return -1; - } - - protected override void ConsumerRemoved(SimpleConsumer consumer) - { - InUseConsumerChannels.Remove(consumer.Model, out _); - WhenUsed.Remove(consumer, out _); - } - - public class ChannelHolder - { - public RC.IModel Channel { get; } - - public int ConsumerEpoch { get; } - - public ChannelHolder(RC.IModel channel, int consumerEpoch) - { - Channel = channel; - ConsumerEpoch = consumerEpoch; - } - - public override string ToString() - { - return $"ChannelHolder [channel={Channel}, consumerEpoch={ConsumerEpoch}]"; - } - } - - private sealed class ChannelAwareMessageListener : IChannelAwareMessageListener - { - private readonly DirectReplyToMessageListenerContainer _container; - - private readonly IMessageListener _listener; - - public AcknowledgeMode ContainerAckMode - { - get => AcknowledgeMode.None; - set - { - // Do nothing - } - } - - public ChannelAwareMessageListener(DirectReplyToMessageListenerContainer container, IMessageListener listener) - { - _container = container; - _listener = listener; - } - - public void OnMessage(IMessage message, RC.IModel channel) - { - if (_listener is IChannelAwareMessageListener chanAwareListener) - { - try - { - chanAwareListener.OnMessage(message, channel); - } - finally - { - _container.InUseConsumerChannels.Remove(channel, out _); - } - } - else - { - try - { - _listener.OnMessage(message); - } - finally - { - _container.InUseConsumerChannels.Remove(channel, out _); - } - } - } - - public void OnMessage(IMessage message) - { - throw new InvalidOperationException("Should never be called for a ChannelAwareMessageListener"); - } - - public void OnMessageBatch(List messages, RC.IModel channel) - { - if (_listener is IChannelAwareMessageListener chanAwareListener) - { - try - { - chanAwareListener.OnMessageBatch(messages, channel); - } - finally - { - _container.InUseConsumerChannels.Remove(channel, out _); - } - } - else - { - try - { - _listener.OnMessageBatch(messages); - } - finally - { - _container.InUseConsumerChannels.Remove(channel, out _); - } - } - } - - public void OnMessageBatch(List messages) - { - throw new InvalidOperationException("Should never be called for a ChannelAwareMessageListener"); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Exceptions/FatalListenerExecutionException.cs b/src/Messaging/src/RabbitMQ/Listener/Exceptions/FatalListenerExecutionException.cs deleted file mode 100644 index 726b362c91..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Exceptions/FatalListenerExecutionException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; - -public class FatalListenerExecutionException : Exception -{ - public FatalListenerExecutionException(string message) - : base(message) - { - } - - public FatalListenerExecutionException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Exceptions/FatalListenerStartupException.cs b/src/Messaging/src/RabbitMQ/Listener/Exceptions/FatalListenerStartupException.cs deleted file mode 100644 index a227f234a3..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Exceptions/FatalListenerStartupException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; - -public class FatalListenerStartupException : Exception -{ - public FatalListenerStartupException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Exceptions/ListenerExecutionFailedException.cs b/src/Messaging/src/RabbitMQ/Listener/Exceptions/ListenerExecutionFailedException.cs deleted file mode 100644 index 6f3475a25b..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Exceptions/ListenerExecutionFailedException.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; - -public class ListenerExecutionFailedException : Exception -{ - public IMessage FailedMessage - { - get - { - if (FailedMessages.Count > 0) - { - return FailedMessages[0]; - } - - return null; - } - } - - public List FailedMessages { get; } = new(); - - public ListenerExecutionFailedException(string message, Exception innerException, params IMessage[] failedMessages) - : base(message, innerException) - { - FailedMessages.AddRange(failedMessages); - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Exceptions/QueuesNotAvailableException.cs b/src/Messaging/src/RabbitMQ/Listener/Exceptions/QueuesNotAvailableException.cs deleted file mode 100644 index e31add2978..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Exceptions/QueuesNotAvailableException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; - -public class QueuesNotAvailableException : FatalListenerStartupException -{ - public QueuesNotAvailableException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IChannelAwareBatchMessageListener.cs b/src/Messaging/src/RabbitMQ/Listener/IChannelAwareBatchMessageListener.cs deleted file mode 100644 index d222dc4f0b..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IChannelAwareBatchMessageListener.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IChannelAwareBatchMessageListener : IChannelAwareMessageListener -{ - new void OnMessage(IMessage message, RC.IModel channel) - { - throw new InvalidOperationException("Should never be called by the container"); - } - - new void OnMessageBatch(List messages, RC.IModel channel); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IChannelAwareMessageListener.cs b/src/Messaging/src/RabbitMQ/Listener/IChannelAwareMessageListener.cs deleted file mode 100644 index 3b9c1df6e3..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IChannelAwareMessageListener.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IChannelAwareMessageListener : IMessageListener -{ - void OnMessage(IMessage message, RC.IModel channel); - - void OnMessageBatch(List messages, RC.IModel channel); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IFatalExceptionStrategy.cs b/src/Messaging/src/RabbitMQ/Listener/IFatalExceptionStrategy.cs deleted file mode 100644 index c3e472fa11..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IFatalExceptionStrategy.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IFatalExceptionStrategy -{ - bool IsFatal(Exception exception); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IMessageListener.cs b/src/Messaging/src/RabbitMQ/Listener/IMessageListener.cs deleted file mode 100644 index 4472c4889b..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IMessageListener.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IMessageListener -{ - AcknowledgeMode ContainerAckMode { get; set; } - - void OnMessage(IMessage message); - - void OnMessageBatch(List messages); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IMessageListenerContainer.cs b/src/Messaging/src/RabbitMQ/Listener/IMessageListenerContainer.cs deleted file mode 100644 index 02b93be0d7..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IMessageListenerContainer.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IMessageListenerContainer : ISmartLifecycle, IDisposable, IServiceNameAware -{ - /// - /// Setup the message listener to use. - /// - /// - /// the message listener. - /// - void SetupMessageListener(IMessageListener messageListener); - - /// - /// Do not check for missing or mismatched queues during startup. Used for lazily loaded message listener containers to avoid a deadlock when starting - /// such containers. Applications lazily loading containers should verify the queue configuration before loading the container bean. - /// - void LazyLoad(); - - void Initialize(); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IMessageListenerContainerCollection.cs b/src/Messaging/src/RabbitMQ/Listener/IMessageListenerContainerCollection.cs deleted file mode 100644 index 7abdbcecdd..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IMessageListenerContainerCollection.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IMessageListenerContainerCollection : IServiceNameAware -{ - string GroupName { get; } - - IList Containers { get; } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IMessagingMessageListenerAdapter.cs b/src/Messaging/src/RabbitMQ/Listener/IMessagingMessageListenerAdapter.cs deleted file mode 100644 index 2e11887eda..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IMessagingMessageListenerAdapter.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IMessagingMessageListenerAdapter -{ -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerContainerFactory.cs b/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerContainerFactory.cs deleted file mode 100644 index 503647a2bc..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerContainerFactory.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IRabbitListenerContainerFactory : IServiceNameAware -{ - IMessageListenerContainer CreateListenerContainer(IRabbitListenerEndpoint endpoint); - - IMessageListenerContainer CreateListenerContainer() - { - return CreateListenerContainer(null); - } -} - -public interface IRabbitListenerContainerFactory : IRabbitListenerContainerFactory - where TContainer : IMessageListenerContainer -{ - new TContainer CreateListenerContainer(IRabbitListenerEndpoint endpoint); - - new TContainer CreateListenerContainer() - { - return CreateListenerContainer(null); - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerEndpoint.cs b/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerEndpoint.cs deleted file mode 100644 index 0f53b985ee..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerEndpoint.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IRabbitListenerEndpoint -{ - string Id { get; set; } - - int? Concurrency { get; set; } - - bool? AutoStartup { get; set; } - - IApplicationContext ApplicationContext { get; set; } - - ISmartMessageConverter MessageConverter { get; set; } - - bool BatchListener { get; set; } - - IBatchingStrategy BatchingStrategy { get; set; } - - AcknowledgeMode? AckMode { get; set; } - - IReplyPostProcessor ReplyPostProcessor { get; set; } - - string Group { get; set; } - - void SetupListenerContainer(IMessageListenerContainer listenerContainer); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerEndpointRegistrar.cs b/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerEndpointRegistrar.cs deleted file mode 100644 index a6440e0cf3..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerEndpointRegistrar.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Services; -using Steeltoe.Messaging.Handler.Attributes.Support; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IRabbitListenerEndpointRegistrar : IServiceNameAware -{ - IRabbitListenerEndpointRegistry EndpointRegistry { get; set; } - - IMessageHandlerMethodFactory MessageHandlerMethodFactory { get; set; } - - IRabbitListenerContainerFactory ContainerFactory { get; set; } - - IApplicationContext ApplicationContext { get; set; } - - string ContainerFactoryServiceName { get; set; } - - bool StartImmediately { get; set; } - - void Initialize(); - - void RegisterEndpoint(IRabbitListenerEndpoint endpoint, IRabbitListenerContainerFactory factory); - - void RegisterEndpoint(IRabbitListenerEndpoint endpoint); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerEndpointRegistry.cs b/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerEndpointRegistry.cs deleted file mode 100644 index 4968bdb10f..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerEndpointRegistry.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Services; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IRabbitListenerEndpointRegistry : ISmartLifecycle, IDisposable, IServiceNameAware -{ - public IMessageListenerContainer GetListenerContainer(string id); - - public ISet GetListenerContainerIds(); - - public ICollection GetListenerContainers(); - - public void RegisterListenerContainer(IRabbitListenerEndpoint endpoint, IRabbitListenerContainerFactory factory); - - public void RegisterListenerContainer(IRabbitListenerEndpoint endpoint, IRabbitListenerContainerFactory factory, bool startImmediately); - - public IMessageListenerContainer UnregisterListenerContainer(string id); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerErrorHandler.cs b/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerErrorHandler.cs deleted file mode 100644 index 417397d063..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/IRabbitListenerErrorHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public interface IRabbitListenerErrorHandler : IServiceNameAware -{ - object HandleError(IMessage originalMessage, IMessage message, ListenerExecutionFailedException exception); -} diff --git a/src/Messaging/src/RabbitMQ/Listener/MessageListenerContainerCollection.cs b/src/Messaging/src/RabbitMQ/Listener/MessageListenerContainerCollection.cs deleted file mode 100644 index 949ce2da1f..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/MessageListenerContainerCollection.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class MessageListenerContainerCollection : IMessageListenerContainerCollection -{ - private readonly List _containers = new(); - - public string ServiceName { get; set; } - - public string GroupName => ServiceName; - - public IList Containers => new List(_containers); - - public MessageListenerContainerCollection(string groupName) - { - ArgumentGuard.NotNullOrEmpty(groupName); - - ServiceName = groupName; - } - - internal void AddContainer(IMessageListenerContainer messageListenerContainer) - { - _containers.Add(messageListenerContainer); - } - - internal void RemoveContainer(IMessageListenerContainer messageListenerContainer) - { - _containers.Remove(messageListenerContainer); - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/MessageRejectedWhileStoppingException.cs b/src/Messaging/src/RabbitMQ/Listener/MessageRejectedWhileStoppingException.cs deleted file mode 100644 index 63e38c4473..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/MessageRejectedWhileStoppingException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class MessageRejectedWhileStoppingException : Exception -{ - public MessageRejectedWhileStoppingException() - : base("Message listener container was stopping when a message was received") - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/MethodRabbitListenerEndpoint.cs b/src/Messaging/src/RabbitMQ/Listener/MethodRabbitListenerEndpoint.cs deleted file mode 100644 index 1440a83321..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/MethodRabbitListenerEndpoint.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class MethodRabbitListenerEndpoint : AbstractRabbitListenerEndpoint -{ - public MethodInfo Method { get; set; } - - public object Instance { get; set; } - - public IMessageHandlerMethodFactory MessageHandlerMethodFactory { get; set; } - - public bool ReturnExceptions { get; set; } - - public IRabbitListenerErrorHandler ErrorHandler { get; set; } - - public MethodRabbitListenerEndpoint(IApplicationContext applicationContext, MethodInfo method, object instance, ILoggerFactory loggerFactory = null) - : base(applicationContext, loggerFactory) - { - Method = method; - Instance = instance; - } - - protected override IMessageListener CreateMessageListener(IMessageListenerContainer container) - { - if (MessageHandlerMethodFactory == null) - { - throw new InvalidOperationException("Could not create message listener - MessageHandlerMethodFactory not set"); - } - - MessagingMessageListenerAdapter messageListener = CreateMessageListenerInstance(); - messageListener.HandlerAdapter = ConfigureListenerAdapter(messageListener); - string replyToAddress = GetDefaultReplyToAddress(); - - if (replyToAddress != null) - { - messageListener.SetResponseAddress(replyToAddress); - } - - ISmartMessageConverter messageConverter = MessageConverter; - - if (messageConverter != null) - { - messageListener.MessageConverter = messageConverter; - } - - if (ServiceResolver != null) - { - messageListener.SetServiceResolver(ServiceResolver); - } - - return messageListener; - } - - protected virtual HandlerAdapter ConfigureListenerAdapter(MessagingMessageListenerAdapter messageListener) - { - IInvocableHandlerMethod invocableHandlerMethod = MessageHandlerMethodFactory.CreateInvocableHandlerMethod(Instance, Method); - return new HandlerAdapter(invocableHandlerMethod); - } - - protected virtual MessagingMessageListenerAdapter CreateMessageListenerInstance() - { - if (BatchListener) - { - return new BatchMessagingMessageListenerAdapter(ApplicationContext, Instance, Method, ReturnExceptions, ErrorHandler, BatchingStrategy, - LoggerFactory?.CreateLogger(typeof(BatchMessagingMessageListenerAdapter))); - } - - return new MessagingMessageListenerAdapter(ApplicationContext, Instance, Method, ReturnExceptions, ErrorHandler, - LoggerFactory?.CreateLogger(typeof(MessagingMessageListenerAdapter))); - } - - protected override StringBuilder GetEndpointDescription() - { - return base.GetEndpointDescription().Append(" | bean='").Append(Instance).Append('\'').Append(" | method='").Append(Method).Append('\''); - } - - private string GetDefaultReplyToAddress() - { - MethodInfo listenerMethod = Method; - - if (listenerMethod != null) - { - var ann = listenerMethod.GetCustomAttribute(); - - if (ann != null) - { - string[] destinations = ann.Destinations; - - if (destinations.Length > 1) - { - throw new InvalidOperationException( - $"Invalid SendToAttribute on '{listenerMethod}' one destination must be set (got {string.Join(",", destinations)})"); - } - - return destinations.Length == 1 ? ResolveSendTo(destinations[0]) : string.Empty; - } - } - - return null; - } - - private string ResolveSendTo(string value) - { - if (ApplicationContext != null) - { - string resolvedValue = ExpressionContext.ApplicationContext.ResolveEmbeddedValue(value); - object result = Resolver.Evaluate(resolvedValue, ExpressionContext); - - if (result is string strResult) - { - return strResult; - } - } - - return value; - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/MultiMethodRabbitListenerEndpoint.cs b/src/Messaging/src/RabbitMQ/Listener/MultiMethodRabbitListenerEndpoint.cs deleted file mode 100644 index 4f7fe25a09..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/MultiMethodRabbitListenerEndpoint.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class MultiMethodRabbitListenerEndpoint : MethodRabbitListenerEndpoint -{ - public List Methods { get; } - - public MethodInfo DefaultMethod { get; } - - public MultiMethodRabbitListenerEndpoint(IApplicationContext applicationContext, List methods, object instance, - ILoggerFactory loggerFactory = null) - : this(applicationContext, methods, null, instance, loggerFactory) - { - } - - public MultiMethodRabbitListenerEndpoint(IApplicationContext applicationContext, List methods, MethodInfo defaultMethod, object instance, - ILoggerFactory loggerFactory = null) - : base(applicationContext, null, instance, loggerFactory) - { - Methods = methods; - DefaultMethod = defaultMethod; - } - - protected override HandlerAdapter ConfigureListenerAdapter(MessagingMessageListenerAdapter messageListener) - { - var invocableHandlerMethods = new List(); - IInvocableHandlerMethod defaultHandler = null; - - foreach (MethodInfo method in Methods) - { - IInvocableHandlerMethod handler = MessageHandlerMethodFactory.CreateInvocableHandlerMethod(Instance, method); - invocableHandlerMethods.Add(handler); - - if (method == DefaultMethod) - { - defaultHandler = handler; - } - } - - return new HandlerAdapter(new DelegatingInvocableHandler(invocableHandlerMethods, defaultHandler, Instance, Resolver, ExpressionContext)); - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/RabbitListenerEndpointRegistrar.cs b/src/Messaging/src/RabbitMQ/Listener/RabbitListenerEndpointRegistrar.cs deleted file mode 100644 index 9fc05725dc..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/RabbitListenerEndpointRegistrar.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.Handler.Attributes.Support; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class RabbitListenerEndpointRegistrar : IRabbitListenerEndpointRegistrar -{ - public const string DefaultServiceName = nameof(RabbitListenerEndpointRegistrar); - - private readonly List _endpointDescriptors = new(); - - public IRabbitListenerEndpointRegistry EndpointRegistry { get; set; } - - public IMessageHandlerMethodFactory MessageHandlerMethodFactory { get; set; } - - public IRabbitListenerContainerFactory ContainerFactory { get; set; } - - public string ContainerFactoryServiceName { get; set; } - - public IApplicationContext ApplicationContext { get; set; } - - public bool StartImmediately { get; set; } - - public string ServiceName { get; set; } = DefaultServiceName; - - public RabbitListenerEndpointRegistrar(IMessageHandlerMethodFactory messageHandlerMethodFactory = null) - { - MessageHandlerMethodFactory = messageHandlerMethodFactory; - } - - public void Initialize() - { - RegisterAllEndpoints(); - } - - public void RegisterEndpoint(IRabbitListenerEndpoint endpoint, IRabbitListenerContainerFactory factory) - { - ArgumentGuard.NotNull(endpoint); - - if (string.IsNullOrEmpty(endpoint.Id)) - { - throw new InvalidOperationException("Endpoint id must be set"); - } - - if (StartImmediately && EndpointRegistry == null) - { - throw new InvalidOperationException("No registry available"); - } - - // Factory may be null, we defer the resolution right before actually creating the container - var descriptor = new RabbitListenerEndpointDescriptor(endpoint, factory); - - lock (_endpointDescriptors) - { - if (StartImmediately) - { - // Register and start immediately - EndpointRegistry.RegisterListenerContainer(descriptor.Endpoint, ResolveContainerFactory(descriptor), true); - } - else - { - _endpointDescriptors.Add(descriptor); - } - } - } - - public void RegisterEndpoint(IRabbitListenerEndpoint endpoint) - { - RegisterEndpoint(endpoint, null); - } - - protected void RegisterAllEndpoints() - { - if (EndpointRegistry == null) - { - throw new InvalidOperationException("No registry available"); - } - - lock (_endpointDescriptors) - { - foreach (RabbitListenerEndpointDescriptor descriptor in _endpointDescriptors) - { - EndpointRegistry.RegisterListenerContainer(descriptor.Endpoint, ResolveContainerFactory(descriptor)); - } - - StartImmediately = true; // trigger immediate startup - } - } - - private IRabbitListenerContainerFactory ResolveContainerFactory(RabbitListenerEndpointDescriptor descriptor) - { - if (descriptor.ContainerFactory != null) - { - return descriptor.ContainerFactory; - } - - if (ContainerFactory != null) - { - return ContainerFactory; - } - - if (ContainerFactoryServiceName != null) - { - if (ApplicationContext == null) - { - throw new InvalidOperationException("BeanFactory must be set to obtain container factory by bean name"); - } - - ContainerFactory = ApplicationContext.GetService(ContainerFactoryServiceName); - return ContainerFactory; - } - - throw new InvalidOperationException( - $"Could not resolve the IRabbitListenerContainerFactory to use for [{descriptor.Endpoint}] no factory was given and no default is set."); - } - - private sealed class RabbitListenerEndpointDescriptor - { - public IRabbitListenerEndpoint Endpoint { get; } - - public IRabbitListenerContainerFactory ContainerFactory { get; } - - public RabbitListenerEndpointDescriptor(IRabbitListenerEndpoint endpoint, IRabbitListenerContainerFactory containerFactory) - { - Endpoint = endpoint; - ContainerFactory = containerFactory; - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/RabbitListenerEndpointRegistry.cs b/src/Messaging/src/RabbitMQ/Listener/RabbitListenerEndpointRegistry.cs deleted file mode 100644 index 9e357c4f7d..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/RabbitListenerEndpointRegistry.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class RabbitListenerEndpointRegistry : IRabbitListenerEndpointRegistry -{ - public const string DefaultServiceName = nameof(RabbitListenerEndpointRegistry); - - private readonly ILogger _logger; - private readonly ConcurrentDictionary _listenerContainers = new(); - private bool _isDisposed; - - public IApplicationContext ApplicationContext { get; set; } - - public int Phase { get; set; } = int.MaxValue; - - public bool IsAutoStartup => true; - - public bool IsRunning => GetListenerContainers().Any(listener => listener.IsRunning); - - public string ServiceName { get; set; } = DefaultServiceName; - - public RabbitListenerEndpointRegistry(IApplicationContext applicationContext, ILogger logger = null) - { - _logger = logger; - ApplicationContext = applicationContext; - } - - public IMessageListenerContainer GetListenerContainer(string id) - { - ArgumentGuard.NotNullOrEmpty(id); - - _listenerContainers.TryGetValue(id, out IMessageListenerContainer messageListenerContainer); - return messageListenerContainer; - } - - public ISet GetListenerContainerIds() - { - return new HashSet(_listenerContainers.Keys); - } - - public ICollection GetListenerContainers() - { - return new List(_listenerContainers.Values); - } - - public void RegisterListenerContainer(IRabbitListenerEndpoint endpoint, IRabbitListenerContainerFactory factory) - { - RegisterListenerContainer(endpoint, factory, false); - } - - public void RegisterListenerContainer(IRabbitListenerEndpoint endpoint, IRabbitListenerContainerFactory factory, bool startImmediately) - { - ArgumentGuard.NotNull(endpoint); - ArgumentGuard.NotNull(factory); - - if (string.IsNullOrEmpty(endpoint.Id)) - { - throw new ArgumentException($"{nameof(endpoint.Id)} in {nameof(endpoint)} must not be null or empty.", nameof(endpoint)); - } - - Task task = null; - - lock (_listenerContainers) - { - if (_listenerContainers.ContainsKey(endpoint.Id)) - { - throw new InvalidOperationException($"Another endpoint is already registered with id '{endpoint.Id}'"); - } - - IMessageListenerContainer container = CreateListenerContainer(endpoint, factory); - _listenerContainers.TryAdd(endpoint.Id, container); - - if (!string.IsNullOrEmpty(endpoint.Group) && ApplicationContext != null && - ApplicationContext.GetService(endpoint.Group) is MessageListenerContainerCollection containerCollection) - { - containerCollection.AddContainer(container); - } - - if (startImmediately && container.IsAutoStartup) - { - task = container.StartAsync(); - } - } - - task?.GetAwaiter().GetResult(); - } - - public IMessageListenerContainer UnregisterListenerContainer(string id) - { - _listenerContainers.TryRemove(id, out IMessageListenerContainer removed); - return removed; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing && !_isDisposed) - { - foreach (IMessageListenerContainer listenerContainer in _listenerContainers.Values) - { - if (listenerContainer is IDisposable disposable) - { - try - { - disposable.Dispose(); - } - catch (Exception ex) - { - _logger?.LogWarning(ex, "Failed to destroy listener container [{container}]", listenerContainer); - } - } - } - - _isDisposed = true; - } - } - - public async Task StopAsync(Action callback) - { - ICollection containers = _listenerContainers.Values; - - if (containers.Count > 0) - { - int count = containers.Count; - - Action aggCallback = () => - { - int result = Interlocked.Decrement(ref count); - - if (result == 0) - { - callback(); - } - }; - - foreach (IMessageListenerContainer listenerContainer in containers) - { - try - { - await listenerContainer.StopAsync(aggCallback); - } - catch (Exception e) - { - _logger?.LogWarning(e, "Failed to stop listener container [{container}]", listenerContainer); - } - } - } - else - { - callback(); - } - } - - public async Task StopAsync() - { - foreach (IMessageListenerContainer listenerContainer in _listenerContainers.Values) - { - try - { - await listenerContainer.StopAsync(); - } - catch (Exception e) - { - _logger?.LogWarning(e, "Failed to stop listener container [{container}]", listenerContainer); - } - } - } - - public async Task StartAsync() - { - foreach (IMessageListenerContainer listenerContainer in _listenerContainers.Values) - { - if (listenerContainer.IsAutoStartup) - { - await listenerContainer.StartAsync(); - } - } - } - - protected IMessageListenerContainer CreateListenerContainer(IRabbitListenerEndpoint endpoint, IRabbitListenerContainerFactory factory) - { - IMessageListenerContainer listenerContainer = factory.CreateListenerContainer(endpoint); - - try - { - listenerContainer.Initialize(); - } - catch (Exception ex) - { - throw new TypeInitializationException("Failed to initialize message listener container", ex); - } - - int containerPhase = listenerContainer.Phase; - - if (containerPhase < int.MaxValue) - { - // a custom phase value - if (Phase < int.MaxValue && Phase != containerPhase) - { - throw new InvalidOperationException($"Encountered phase mismatch between container factory definitions: {Phase} vs {containerPhase}"); - } - - Phase = listenerContainer.Phase; - } - - return listenerContainer; - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/RabbitMessageHandlerMethodFactory.cs b/src/Messaging/src/RabbitMQ/Listener/RabbitMessageHandlerMethodFactory.cs deleted file mode 100644 index feea31fe3c..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/RabbitMessageHandlerMethodFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class RabbitMessageHandlerMethodFactory : DefaultMessageHandlerMethodFactory -{ - public new const string DefaultServiceName = nameof(RabbitMessageHandlerMethodFactory); - - public override string ServiceName { get; set; } = DefaultServiceName; - - public Encoding Charset { get; set; } = EncodingUtils.Utf8; - - public RabbitMessageHandlerMethodFactory() - : base(new DefaultConversionService()) - { - var defService = ConversionService as DefaultConversionService; - defService.AddConverter(new BytesToStringConverter(Charset)); - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/SimpleRabbitListenerEndpoint.cs b/src/Messaging/src/RabbitMQ/Listener/SimpleRabbitListenerEndpoint.cs deleted file mode 100644 index 9634760bd0..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/SimpleRabbitListenerEndpoint.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Messaging.RabbitMQ.Listener; - -public class SimpleRabbitListenerEndpoint : AbstractRabbitListenerEndpoint -{ - public IMessageListener MessageListener { get; set; } - - public SimpleRabbitListenerEndpoint(IApplicationContext context, IMessageListener listener = null, ILoggerFactory loggerFactory = null) - : base(context, loggerFactory) - { - MessageListener = listener; - } - - protected override IMessageListener CreateMessageListener(IMessageListenerContainer container) - { - return MessageListener; - } - - protected override StringBuilder GetEndpointDescription() - { - return base.GetEndpointDescription().Append(" | messageListener='").Append(MessageListener).Append('\''); - } -} diff --git a/src/Messaging/src/RabbitMQ/Listener/Support/ContainerUtils.cs b/src/Messaging/src/RabbitMQ/Listener/Support/ContainerUtils.cs deleted file mode 100644 index 4df902ec36..0000000000 --- a/src/Messaging/src/RabbitMQ/Listener/Support/ContainerUtils.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging.RabbitMQ.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Listener.Support; - -public static class ContainerUtils -{ - public static bool ShouldRequeue(bool defaultRequeueRejected, Exception exception, ILogger logger = null) - { - bool shouldRequeue = defaultRequeueRejected || exception is MessageRejectedWhileStoppingException; - Exception e = exception; - - while (e != null) - { - if (e is RabbitRejectAndDoNotRequeueException) - { - shouldRequeue = false; - break; - } - - if (e is ImmediateRequeueException) - { - shouldRequeue = true; - break; - } - - e = e.InnerException; - } - - logger?.LogDebug(exception, "Rejecting messages (requeue={requeue})", shouldRequeue); - return shouldRequeue; - } - - public static bool IsRejectManual(Exception exception) - { - return exception is RabbitRejectAndDoNotRequeueException rejectException && rejectException.IsRejectManual; - } -} diff --git a/src/Messaging/src/RabbitMQ/Properties/AssemblyInfo.cs b/src/Messaging/src/RabbitMQ/Properties/AssemblyInfo.cs deleted file mode 100644 index ff063e4599..0000000000 --- a/src/Messaging/src/RabbitMQ/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Steeltoe.Messaging.RabbitMQ.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Integration.RabbitMQ.Test")] diff --git a/src/Messaging/src/RabbitMQ/RabbitMessageHeaders.cs b/src/Messaging/src/RabbitMQ/RabbitMessageHeaders.cs deleted file mode 100644 index 3ff5f0098e..0000000000 --- a/src/Messaging/src/RabbitMQ/RabbitMessageHeaders.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ; - -public static class RabbitMessageHeaders -{ - public const string Prefix = "amqp_"; - public const string RabbitProperty = "rabbit_"; - - // Encoded in Rabbit Message properties - public const string AppId = $"{RabbitProperty}appId"; - public const string ClusterId = $"{RabbitProperty}clusterId"; - public const string ContentEncoding = $"{RabbitProperty}contentEncoding"; - public const string CorrelationId = $"{RabbitProperty}correlationId"; - public const string Delay = $"{RabbitProperty}delay"; - public const string DeliveryMode = $"{RabbitProperty}deliveryMode"; - public const string DeliveryTag = $"{RabbitProperty}deliveryTag"; - public const string Expiration = $"{RabbitProperty}expiration"; - public const string MessageId = $"{RabbitProperty}messageId"; - public const string ReceivedExchange = $"{RabbitProperty}receivedExchange"; - public const string ReceivedRoutingKey = $"{RabbitProperty}receivedRoutingKey"; - public const string Redelivered = $"{RabbitProperty}redelivered"; - public const string ReplyTo = $"{RabbitProperty}replyTo"; - public const string Timestamp = $"{RabbitProperty}timestamp"; - public const string Type = $"{RabbitProperty}type"; - public const string UserId = $"{RabbitProperty}userId"; - public const string ReceivedDeliveryMode = $"{RabbitProperty}receivedDeliveryMode"; - public const string ReceivedUserId = $"{RabbitProperty}receivedUserId"; - public const string ReceivedDelay = $"{RabbitProperty}receivedDelay"; - public const string Priority = $"{RabbitProperty}priority"; - - public const string ContentType = MessageHeaders.ContentType; - public const string ContentLength = $"{MessageHeaders.Internal}contentLength"; - public const string MessageCount = $"{MessageHeaders.Internal}messageCount"; - public const string PublishSequenceNumber = $"{MessageHeaders.Internal}PublishSequenceNumber"; - - public const string FinalRetryForMessageWithNoId = $"{MessageHeaders.Internal}FinalRetryForMessageWithNoID"; - - // Used in RabbitMQ Integration code - public const string PublishConfirmCorrelation = $"{Prefix}_publishConfirmCorrelation"; - public const string PublishConfirm = $"{Prefix}publishConfirm"; - public const string PublishConfirmNackCause = $"{Prefix}publishConfirmNackCause"; - public const string ReturnReplyCode = $"{Prefix}returnReplyCode"; - public const string ReturnReplyText = $"{Prefix}returnReplyText"; - public const string ReturnExchange = $"{Prefix}returnExchange"; - public const string ReturnRoutingKey = $"{Prefix}returnRoutingKey"; - public const string RawMessage = $"{Prefix}raw_message"; - - // Used and found in AMQPHeaders - public const string Channel = $"{MessageHeaders.Internal}channel"; - public const string ConsumerTag = $"{MessageHeaders.Internal}consumerTag"; - public const string ConsumerQueue = $"{MessageHeaders.Internal}consumerQueue"; - - public const string LastInBatch = $"{Prefix}lastInBatch"; - public const string BatchSize = $"{Prefix}batchSize"; - - public const string Target = $"{MessageHeaders.Internal}Target"; - public const string TargetMethod = $"{MessageHeaders.Internal}TargetMethod"; - - public const string SpringBatchFormat = "springBatchFormat"; - public const string SpringReplyCorrelation = "spring_reply_correlation"; - public const string SpringReplyToStack = "spring_reply_to"; - public const string BatchFormatLengthHeader4 = "lengthHeader4"; - public const string SpringAutoDecompress = "springAutoDecompress"; - public const string XDelay = "x-delay"; - public const string XDeath = "x-death"; -} diff --git a/src/Messaging/src/RabbitMQ/Retry/IMessageRecoverer.cs b/src/Messaging/src/RabbitMQ/Retry/IMessageRecoverer.cs deleted file mode 100644 index d5ceff3705..0000000000 --- a/src/Messaging/src/RabbitMQ/Retry/IMessageRecoverer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Retry; - -public interface IMessageRecoverer -{ - void Recover(IMessage message, Exception exception); -} diff --git a/src/Messaging/src/RabbitMQ/Retry/RejectAndDoNotRequeueRecoverer.cs b/src/Messaging/src/RabbitMQ/Retry/RejectAndDoNotRequeueRecoverer.cs deleted file mode 100644 index f8283a16d3..0000000000 --- a/src/Messaging/src/RabbitMQ/Retry/RejectAndDoNotRequeueRecoverer.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Retry; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Retry; - -public class RejectAndDoNotRequeueRecoverer : IMessageRecoverer, IRecoveryCallback -{ - public void Recover(IMessage message, Exception exception) - { - throw new ListenerExecutionFailedException("Retry Policy Exhausted", new RabbitRejectAndDoNotRequeueException(exception), message); - } - - public object Recover(IRetryContext context) - { - var cause = context.LastException as ListenerExecutionFailedException; - Recover(cause.FailedMessage, cause); - return null; - } -} diff --git a/src/Messaging/src/RabbitMQ/Retry/RepublishMessageRecoverer.cs b/src/Messaging/src/RabbitMQ/Retry/RepublishMessageRecoverer.cs deleted file mode 100644 index 3bb3fb3abf..0000000000 --- a/src/Messaging/src/RabbitMQ/Retry/RepublishMessageRecoverer.cs +++ /dev/null @@ -1,185 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Support; - -namespace Steeltoe.Messaging.RabbitMQ.Retry; - -public class RepublishMessageRecoverer : IMessageRecoverer -{ - private const int EllipsisLength = 3; - private const int MaxExceptionMessageSizeInTrace = 100 - EllipsisLength; - public const string XExceptionStacktrace = "x-exception-stacktrace"; - public const string XExceptionMessage = "x-exception-message"; - public const string XOriginalExchange = "x-original-exchange"; - public const string XOriginalRoutingKey = "x-original-routingKey"; - public const int DefaultFrameMaxHeadroom = 20_000; - - private readonly ILogger _logger; - - public RabbitTemplate ErrorTemplate { get; } - - public string ErrorExchangeName { get; } - - public string ErrorRoutingKey { get; } - - public int MaxStackTraceLength { get; set; } - - public string ErrorRoutingKeyPrefix { get; set; } - - public MessageDeliveryMode DeliveryMode { get; set; } - - public int FrameMaxHeadroom { get; set; } = DefaultFrameMaxHeadroom; - - public RepublishMessageRecoverer(RabbitTemplate errorTemplate, ILogger logger = null) - : this(errorTemplate, null, null, logger) - { - } - - public RepublishMessageRecoverer(RabbitTemplate errorTemplate, string errorExchange, ILogger logger = null) - : this(errorTemplate, errorExchange, null, logger) - { - } - - public RepublishMessageRecoverer(RabbitTemplate errorTemplate, string errorExchange, string errorRoutingKey, ILogger logger = null) - { - ArgumentGuard.NotNull(errorTemplate); - - ErrorTemplate = errorTemplate; - ErrorExchangeName = errorExchange; - ErrorRoutingKey = errorRoutingKey; - MaxStackTraceLength = int.MaxValue; - _logger = logger; - } - - public void Recover(IMessage message, Exception exception) - { - RabbitHeaderAccessor headers = RabbitHeaderAccessor.GetMutableAccessor(message); - - string exceptionMessage = exception.InnerException != null ? exception.InnerException.Message : exception.Message; - List processed = ProcessStackTrace(exception, exceptionMessage); - string stackTraceAsString = processed[0]; - string truncatedExceptionMessage = processed[1]; - - if (truncatedExceptionMessage != null) - { - exceptionMessage = truncatedExceptionMessage; - } - - headers.SetHeader(XExceptionStacktrace, stackTraceAsString); - headers.SetHeader(XExceptionMessage, exceptionMessage); - headers.SetHeader(XOriginalExchange, headers.ReceivedExchange); - headers.SetHeader(XOriginalRoutingKey, headers.ReceivedRoutingKey); - Dictionary additionalHeaders = AddAdditionalHeaders(message, exception); - - if (additionalHeaders != null) - { - foreach (KeyValuePair h in additionalHeaders) - { - headers.SetHeader(h.Key, h.Value); - } - } - - headers.DeliveryMode ??= DeliveryMode; - - if (ErrorExchangeName != null) - { - string routingKey = ErrorRoutingKey ?? PrefixedOriginalRoutingKey(message); - ErrorTemplate.Send(ErrorExchangeName, routingKey, message); - - _logger?.LogWarning("Republishing failed message to exchange '{exchange}' with routing key '{routingKey}'", ErrorExchangeName, routingKey); - } - else - { - string routingKey = PrefixedOriginalRoutingKey(message); - ErrorTemplate.Send(routingKey, message); - - _logger?.LogWarning("Republishing failed message to the template's default exchange with routing key {key}", routingKey); - } - } - - protected virtual Dictionary AddAdditionalHeaders(IMessage message, Exception cause) - { - return null; - } - - private string PrefixedOriginalRoutingKey(IMessage message) - { - return ErrorRoutingKeyPrefix + message.Headers.ReceivedRoutingKey(); - } - - private List ProcessStackTrace(Exception cause, string exceptionMessage) - { - string stackTraceAsString = cause.StackTrace; - - if (MaxStackTraceLength < 0) - { - int maxStackTraceLen = RabbitUtils.GetMaxFrame(ErrorTemplate.ConnectionFactory); - - if (maxStackTraceLen > 0) - { - maxStackTraceLen -= FrameMaxHeadroom; - MaxStackTraceLength = maxStackTraceLen; - } - } - - return TruncateIfNecessary(cause, exceptionMessage, stackTraceAsString); - } - - private List TruncateIfNecessary(Exception cause, string exception, string stackTrace) - { - bool truncated = false; - string stackTraceAsString = stackTrace; - string exceptionMessage = exception; - - string truncatedExceptionMessage = exceptionMessage.Length <= MaxExceptionMessageSizeInTrace - ? exceptionMessage - : exceptionMessage.Substring(0, MaxExceptionMessageSizeInTrace) + "..."; - - if (MaxStackTraceLength > 0 && stackTraceAsString.Length + exceptionMessage.Length > MaxStackTraceLength) - { - if (exceptionMessage != truncatedExceptionMessage) - { - int start = stackTraceAsString.IndexOf(exceptionMessage, StringComparison.Ordinal); - - stackTraceAsString = stackTraceAsString.Substring(0, start) + truncatedExceptionMessage + - stackTraceAsString.Substring(start + exceptionMessage.Length); - } - - int adjustedStackTraceLen = MaxStackTraceLength - truncatedExceptionMessage.Length; - - if (adjustedStackTraceLen > 0) - { - if (stackTraceAsString.Length > adjustedStackTraceLen) - { - stackTraceAsString = stackTraceAsString.Substring(0, adjustedStackTraceLen); - - _logger?.LogWarning(cause, - "Stack trace in republished message header truncated due to frame_max limitations; consider increasing frame_max on the broker or reduce the stack trace depth"); - - truncated = true; - } - else if (stackTraceAsString.Length + exceptionMessage.Length > MaxStackTraceLength) - { - _logger?.LogWarning(cause, - "Exception message in republished message header truncated due to frame_max limitations; consider increasing frame_max on the broker or reduce the exception message size"); - - truncatedExceptionMessage = $"{exceptionMessage.Substring(0, MaxStackTraceLength - stackTraceAsString.Length - EllipsisLength)}..."; - truncated = true; - } - } - } - - return new List - { - stackTraceAsString, - truncated ? truncatedExceptionMessage : null - }; - } -} diff --git a/src/Messaging/src/RabbitMQ/Steeltoe.Messaging.RabbitMQ.csproj b/src/Messaging/src/RabbitMQ/Steeltoe.Messaging.RabbitMQ.csproj deleted file mode 100644 index 96fd57109a..0000000000 --- a/src/Messaging/src/RabbitMQ/Steeltoe.Messaging.RabbitMQ.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - net8.0;net6.0 - Steeltoe Messaging RabbitMQ - Messaging, ASPNET Core, Spring, Spring Cloud - true - - - - - - - - - - - - - - - - - - diff --git a/src/Messaging/src/RabbitMQ/Support/ConsumeOkNotReceivedException.cs b/src/Messaging/src/RabbitMQ/Support/ConsumeOkNotReceivedException.cs deleted file mode 100644 index 3adebc5fea..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/ConsumeOkNotReceivedException.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public class ConsumeOkNotReceivedException : RabbitException -{ - public ConsumeOkNotReceivedException(string message) - : base(message) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/ConsumerCancelledException.cs b/src/Messaging/src/RabbitMQ/Support/ConsumerCancelledException.cs deleted file mode 100644 index ecfc8ed562..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/ConsumerCancelledException.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public class ConsumerCancelledException : Exception -{ - public ConsumerCancelledException() - { - } - - public ConsumerCancelledException(Exception innerException) - : base(null, innerException) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/Converter/AbstractMessageConverter.cs b/src/Messaging/src/RabbitMQ/Support/Converter/AbstractMessageConverter.cs deleted file mode 100644 index 40da84a664..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/Converter/AbstractMessageConverter.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Support.Converter; - -public abstract class AbstractMessageConverter : ISmartMessageConverter -{ - protected readonly ILogger Logger; - - public bool CreateMessageIds { get; set; } - - public abstract string ServiceName { get; set; } - - protected AbstractMessageConverter(ILogger logger = null) - { - Logger = logger; - } - - public abstract object FromMessage(IMessage message, Type targetType, object conversionHint); - - public T FromMessage(IMessage message, object conversionHint) - { - return (T)FromMessage(message, typeof(T), conversionHint); - } - - public object FromMessage(IMessage message, Type targetType) - { - return FromMessage(message, targetType, null); - } - - public T FromMessage(IMessage message) - { - return (T)FromMessage(message, typeof(T), null); - } - - public IMessage ToMessage(object payload, IMessageHeaders headers) - { - return ToMessage(payload, headers, null); - } - - public IMessage ToMessage(object payload, IMessageHeaders headers, object conversionHint) - { - IMessageHeaders messageProperties = headers ?? new MessageHeaders(); - - IMessage message = CreateMessage(payload, messageProperties, conversionHint); - - if (CreateMessageIds && message.Headers.MessageId() == null) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.MessageId = Guid.NewGuid().ToString(); - } - - return message; - } - - protected abstract IMessage CreateMessage(object payload, IMessageHeaders headers, object conversionHint); - - protected virtual IMessage CreateMessage(object payload, IMessageHeaders headers) - { - return CreateMessage(payload, headers, null); - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/Converter/BytesToStringConverter.cs b/src/Messaging/src/RabbitMQ/Support/Converter/BytesToStringConverter.cs deleted file mode 100644 index 202ab1b843..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/Converter/BytesToStringConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Util; - -namespace Steeltoe.Messaging.RabbitMQ.Support.Converter; - -public class BytesToStringConverter : IGenericConverter -{ - private readonly Encoding _charset; - - public ISet<(Type SourceType, Type TargetType)> ConvertibleTypes { get; } - - public BytesToStringConverter(Encoding charset) - { - _charset = charset ?? EncodingUtils.Utf8; - - ConvertibleTypes = new HashSet<(Type SourceType, Type TargetType)> - { - (typeof(byte[]), typeof(string)) - }; - } - - public object Convert(object source, Type sourceType, Type targetType) - { - return source is not byte[] asByteArray ? null : _charset.GetString(asByteArray); - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/Converter/ContentTypeDelegatingMessageConverter.cs b/src/Messaging/src/RabbitMQ/Support/Converter/ContentTypeDelegatingMessageConverter.cs deleted file mode 100644 index 11f790521f..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/Converter/ContentTypeDelegatingMessageConverter.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Support.Converter; - -public class ContentTypeDelegatingMessageConverter : ISmartMessageConverter -{ - public const string DefaultServiceName = nameof(ContentTypeDelegatingMessageConverter); - - private readonly Dictionary _delegates; - - private readonly ISmartMessageConverter _defaultConverter; - - public string ServiceName { get; set; } = DefaultServiceName; - - public ContentTypeDelegatingMessageConverter() - : this(new Dictionary(), new SimpleMessageConverter()) - { - } - - public ContentTypeDelegatingMessageConverter(ISmartMessageConverter defaultConverter) - : this(new Dictionary(), defaultConverter) - { - } - - public ContentTypeDelegatingMessageConverter(Dictionary delegates, ISmartMessageConverter defaultConverter) - { - _delegates = delegates; - _defaultConverter = defaultConverter; - } - - public void AddDelegate(string contentType, ISmartMessageConverter messageConverter) - { - _delegates[contentType] = messageConverter; - } - - public ISmartMessageConverter RemoveDelegate(string contentType) - { - _delegates.Remove(contentType, out ISmartMessageConverter removed); - return removed; - } - - public object FromMessage(IMessage message, Type targetType, object conversionHint) - { - string contentType = message.Headers.ContentType(); - return GetConverterForContentType(contentType).FromMessage(message, targetType, conversionHint); - } - - public T FromMessage(IMessage message, object conversionHint) - { - return (T)FromMessage(message, typeof(T), null); - } - - public object FromMessage(IMessage message, Type targetType) - { - return FromMessage(message, targetType, null); - } - - public T FromMessage(IMessage message) - { - return (T)FromMessage(message, typeof(T), null); - } - - public IMessage ToMessage(object payload, IMessageHeaders headers, object conversionHint) - { - string contentType = headers.ContentType(); - return GetConverterForContentType(contentType).ToMessage(payload, headers, conversionHint); - } - - public IMessage ToMessage(object payload, IMessageHeaders headers) - { - return ToMessage(payload, headers, null); - } - - protected virtual ISmartMessageConverter GetConverterForContentType(string contentType) - { - ISmartMessageConverter d = null; - - if (contentType != null) - { - _delegates.TryGetValue(contentType, out d); - } - - d ??= _defaultConverter; - - if (d == null) - { - throw new MessageConversionException($"No delegate converter is specified for content type {contentType}"); - } - - return d; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/Converter/JsonMessageConverter.cs b/src/Messaging/src/RabbitMQ/Support/Converter/JsonMessageConverter.cs deleted file mode 100644 index e21b5ae554..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/Converter/JsonMessageConverter.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Support.Converter; - -public class JsonMessageConverter : AbstractMessageConverter -{ - public const string DefaultServiceName = nameof(JsonMessageConverter); - - public const string DefaultClassIdFieldName = "__TypeId__"; - public const string DefaultContentClassIdFieldName = "__ContentTypeId__"; - public const string DefaultKeyClassIdFieldName = "__KeyTypeId__"; - - public JsonSerializerSettings Settings { get; set; } - - public override string ServiceName { get; set; } = DefaultServiceName; - - public bool AssumeSupportedContentType { get; set; } = true; - - public MimeType SupportedContentType { get; set; } = MimeTypeUtils.ApplicationJson; - - public Encoding DefaultCharset { get; set; } = EncodingUtils.Utf8; - - public ITypeMapper TypeMapper { get; set; } = new DefaultTypeMapper(); - - public TypePrecedence Precedence - { - get => TypeMapper.Precedence; - set => TypeMapper.Precedence = value; - } - - public JsonMessageConverter(ILogger logger = null) - : base(logger) - { - var contractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy - { - ProcessDictionaryKeys = false - } - }; - - Settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = contractResolver - }; - } - - public override object FromMessage(IMessage message, Type targetType, object conversionHint) - { - object content = null; - IMessageHeaders properties = message.Headers; - - if (properties != null) - { - string contentType = properties.ContentType(); - - bool isDefault = AssumeSupportedContentType && contentType is null or RabbitHeaderAccessor.DefaultContentType; - bool isSupported = contentType != null && contentType.Contains(SupportedContentType.Subtype, StringComparison.Ordinal); - - if (isDefault || isSupported) - { - Encoding encoding = EncodingUtils.GetEncoding(properties.ContentEncoding()) ?? DefaultCharset; - - content = DoFromMessage(message, targetType, conversionHint, properties, encoding); - } - else - { - Logger?.LogWarning("Could not convert incoming message with content-type [{contentType}], '{keyword}' keyword missing.", contentType, - SupportedContentType.Subtype); - } - } - - content ??= message.Payload; - return content; - } - - protected override IMessage CreateMessage(object payload, IMessageHeaders headers, object conversionHint) - { - byte[] bytes; - - try - { - string jsonString = JsonConvert.SerializeObject(payload, Settings); - bytes = DefaultCharset.GetBytes(jsonString); - } - catch (Exception e) - { - throw new MessageConversionException("Failed to convert Message content", e); - } - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(headers); - accessor.ContentType = SupportedContentType.ToString(); - accessor.ContentEncoding = EncodingUtils.GetEncoding(DefaultCharset); - accessor.ContentLength = bytes.Length; - TypeMapper.FromType(payload.GetType(), accessor.MessageHeaders); - - IMessage message = Message.Create(bytes, headers); - return message; - } - - private object DoFromMessage(IMessage from, Type targetType, object conversionHint, IMessageHeaders headers, Encoding encoding) - { - if (from is not IMessage message) - { - throw new MessageConversionException($"Failed to convert Message content, message missing byte[] {from.GetType()}"); - } - - object content; - - try - { - if (conversionHint is ParameterInfo parameterInfo) - { - content = ConvertBytesToObject(message.Payload, encoding, parameterInfo.ParameterType); - } - else if (targetType != null) - { - content = ConvertBytesToObject(message.Payload, encoding, targetType); - } - else if (TypeMapper != null) - { - var target = TypeMapper.ToType(headers); - content = ConvertBytesToObject(message.Payload, encoding, target); - } - else - { - content = message.Payload; - } - } - catch (Exception e) - { - throw new MessageConversionException("Failed to convert Message content", e); - } - - return content; - } - - private object ConvertBytesToObject(byte[] body, Encoding encoding, Type targetClass) - { - string contentAsString = encoding.GetString(body); - return JsonConvert.DeserializeObject(contentAsString, targetClass, Settings); - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/Converter/SimpleMessageConverter.cs b/src/Messaging/src/RabbitMQ/Support/Converter/SimpleMessageConverter.cs deleted file mode 100644 index a22508a82a..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/Converter/SimpleMessageConverter.cs +++ /dev/null @@ -1,152 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.Serialization.Formatters.Binary; -using System.Text; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Support.Converter; - -public class SimpleMessageConverter : AbstractMessageConverter -{ - public const string DefaultServiceName = nameof(SimpleMessageConverter); - - public string DefaultCharset { get; set; } = "utf-8"; - - public override string ServiceName { get; set; } = DefaultServiceName; - - public SimpleMessageConverter(ILogger logger = null) - : base(logger) - { - } - - public override object FromMessage(IMessage message, Type targetType, object conversionHint) - { - if (message is not IMessage bytesMessage) - { - throw new MessageConversionException($"Failed to convert non byte[] Message content{message.GetType()}"); - } - - object content = null; - IMessageHeaders properties = bytesMessage.Headers; - - if (properties != null) - { - string contentType = properties.ContentType(); - - if (contentType != null && contentType.StartsWith("text", StringComparison.Ordinal)) - { - string encoding = properties.ContentEncoding() ?? DefaultCharset; - - try - { - Encoding enc = EncodingUtils.GetEncoding(encoding); - content = enc.GetString(bytesMessage.Payload); - } - catch (Exception e) - { - throw new MessageConversionException("failed to convert text-based Message content", e); - } - } - else if (contentType != null && contentType == MessageHeaders.ContentTypeDotNetSerializedObject) - { - try - { -#pragma warning disable SYSLIB0011 // Type or member is obsolete - var formatter = new BinaryFormatter(); - using var stream = new MemoryStream(bytesMessage.Payload); - content = formatter.Deserialize(stream); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - } - catch (Exception e) - { - throw new MessageConversionException("failed to convert serialized Message content", e); - } - } - else if (contentType != null && contentType == MessageHeaders.ContentTypeJavaSerializedObject) - { - throw new MessageConversionException($"Content type: {MessageHeaders.ContentTypeJavaSerializedObject} unsupported"); - } - } - - if (content == null) - { - Logger?.LogDebug("FromMessage() returning message payload unchanged"); - content = bytesMessage.Payload; - } - - return content; - } - - protected override IMessage CreateMessage(object payload, IMessageHeaders headers, object conversionHint) - { - byte[] bytes = null; - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(headers); - - switch (payload) - { - case byte[] v: - bytes = v; - accessor.ContentType = MessageHeaders.ContentTypeBytes; - break; - case string sPayload: - { - try - { - Encoding enc = EncodingUtils.GetEncoding(DefaultCharset); - bytes = enc.GetBytes(sPayload); - } - catch (Exception e) - { - throw new MessageConversionException("failed to convert to Message content", e); - } - - accessor.ContentType = MessageHeaders.ContentTypeTextPlain; - accessor.ContentEncoding = DefaultCharset; - break; - } - default: -#pragma warning disable SYSLIB0050 // Type or member is obsolete - if (payload != null && payload.GetType().IsSerializable) -#pragma warning restore SYSLIB0050 // Type or member is obsolete - { - bytes = SerializeObject(payload); - accessor.ContentType = MessageHeaders.ContentTypeDotNetSerializedObject; - } - - break; - } - - if (bytes == null) - { - throw new ArgumentException( - $"{nameof(SimpleMessageConverter)} only supports string, byte[] and binary serializable payloads, received: {payload?.GetType().Name}", - nameof(payload)); - } - - IMessage message = Message.Create(bytes, headers); - accessor.ContentLength = bytes.Length; - return message; - } - - private static byte[] SerializeObject(object payload) - { - try - { -#pragma warning disable SYSLIB0011 // Type or member is obsolete - var formatter = new BinaryFormatter(); - using var stream = new MemoryStream(512); - formatter.Serialize(stream, payload); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - return stream.ToArray(); - } - catch (Exception e) - { - throw new MessageConversionException("failed to convert serialized Message content", e); - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/DefaultMessageHeadersConverter.cs b/src/Messaging/src/RabbitMQ/Support/DefaultMessageHeadersConverter.cs deleted file mode 100644 index 32c5ed02c3..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/DefaultMessageHeadersConverter.cs +++ /dev/null @@ -1,324 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Text; -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public class DefaultMessageHeadersConverter : IMessageHeadersConverter -{ - private const int DefaultLongStringLimit = 1024; - private readonly ILogger _logger; - - public DefaultMessageHeadersConverter(ILogger logger = null) - : this(DefaultLongStringLimit, false) - { - _logger = logger; - } - - public DefaultMessageHeadersConverter(int longStringLimit) - : this(longStringLimit, false) - { - } - - public DefaultMessageHeadersConverter(int longStringLimit, bool convertLongLongStrings) - { - } - - public virtual void FromMessageHeaders(IMessageHeaders source, RC.IBasicProperties target, Encoding charset) - { - target.Headers = ConvertHeadersIfNecessary(source); - - if (source.Timestamp.HasValue) - { - target.Timestamp = new RC.AmqpTimestamp(source.Timestamp.Value); - } - - if (source.Id != null) - { - target.MessageId = source.Id; - } - - if (source.UserId() != null) - { - target.UserId = source.UserId(); - } - - if (source.AppId() != null) - { - target.AppId = source.AppId(); - } - - if (source.ClusterId() != null) - { - target.ClusterId = source.ClusterId(); - } - - if (source.Type() != null) - { - target.Type = source.Type(); - } - - if (source.DeliveryMode().HasValue) - { - target.DeliveryMode = (byte)source.DeliveryMode().Value; - } - else - { - target.DeliveryMode = (byte)RabbitHeaderAccessor.DefaultDeliveryMode; - } - - if (source.Expiration() != null) - { - target.Expiration = source.Expiration(); - } - - if (source.Priority().HasValue) - { - target.Priority = (byte)source.Priority().Value; - } - - if (source.ContentType() != null) - { - target.ContentType = source.ContentType(); - } - - if (source.ContentEncoding() != null) - { - target.ContentEncoding = source.ContentEncoding(); - } - - string correlationId = source.CorrelationId(); - - if (!string.IsNullOrEmpty(correlationId)) - { - target.CorrelationId = correlationId; - } - - string replyTo = source.ReplyTo(); - - if (replyTo != null) - { - target.ReplyTo = replyTo; - } - } - - public virtual IMessageHeaders ToMessageHeaders(RC.IBasicProperties source, Envelope envelope, Encoding charset) - { - var target = new RabbitHeaderAccessor(); - IDictionary headers = source.Headers; - - if (headers?.Count > 0) - { - foreach (KeyValuePair entry in headers) - { - string key = entry.Key; - - if (key == RabbitMessageHeaders.XDelay) - { - object value = entry.Value; - - if (value is int intVal) - { - target.ReceivedDelay = intVal; - } - } - else - { - target.SetHeader(key, ConvertLongStringIfNecessary(entry.Value, charset)); - } - } - } - - target.Timestamp = source.Timestamp.UnixTime; - target.MessageId = source.MessageId; - target.ReceivedUserId = source.UserId; - target.AppId = source.AppId; - target.ClusterId = source.ClusterId; - target.Type = source.Type; - int deliveryMode = source.DeliveryMode; - target.ReceivedDeliveryMode = (MessageDeliveryMode)Enum.ToObject(typeof(MessageDeliveryMode), deliveryMode); - target.DeliveryMode = null; - target.Expiration = source.Expiration; - target.Priority = source.Priority; - - target.ContentType = source.ContentType; - - target.ContentEncoding = source.ContentEncoding; - string correlationId = source.CorrelationId; - - if (!string.IsNullOrEmpty(correlationId)) - { - target.CorrelationId = correlationId; - } - - string replyTo = source.ReplyTo; - - if (replyTo != null) - { - target.ReplyTo = replyTo; - } - - if (envelope != null) - { - target.ReceivedExchange = envelope.Exchange; - target.ReceivedRoutingKey = envelope.RoutingKey; - target.Redelivered = envelope.Redeliver; - target.DeliveryTag = envelope.DeliveryTag; - } - - target.LeaveMutable = true; - return target.MessageHeaders; - } - - private IDictionary ConvertHeadersIfNecessary(IDictionary source) - { - if (source.Count == 0) - { - return source; - } - - var writableHeaders = new Dictionary(); - - foreach (KeyValuePair entry in source) - { - if (!entry.Key.StartsWith(MessageHeaders.Internal, StringComparison.Ordinal) && - !entry.Key.StartsWith(RabbitMessageHeaders.RabbitProperty, StringComparison.Ordinal) && entry.Key != MessageHeaders.IdName && - entry.Key != MessageHeaders.TimestampName) - { - writableHeaders[entry.Key] = ConvertHeaderValueIfNecessary(entry.Value); - } - } - - return writableHeaders; - } - - private object ConvertHeaderValueIfNecessary(object valueArg) - { - object value = valueArg; - - if (value == null) - { - return null; - } - - bool isValid = value switch - { - string => true, - byte[] => true, - bool => true, - byte => true, - sbyte => true, - uint => true, - int => true, - long => true, - float => true, - double => true, - decimal => true, - short => true, - RC.AmqpTimestamp => true, - IList => true, - IDictionary => true, - RC.BinaryTableValue => true, - Type => true, - _ => false - }; - - if (!isValid) - { - value = value.ToString(); - } - else if (value is byte[] v) - { - value = new RC.BinaryTableValue(v); - } - else if (value is object[] array) - { - object[] writableList = new object[array.Length]; - - for (int i = 0; i < array.Length; i++) - { - writableList[i] = ConvertHeaderValueIfNecessary(array[i]); - } - - value = writableList; - } - else if (value is IList list) - { - var writableList = new List(); - - foreach (object listValue in list) - { - writableList.Add(ConvertHeaderValueIfNecessary(listValue)); - } - - value = writableList; - } - else if (value is IDictionary originalMap) - { - var writableMap = new Dictionary(); - - foreach (DictionaryEntry entry in originalMap) - { - writableMap[entry.Key] = ConvertHeaderValueIfNecessary(entry.Value); - } - - value = writableMap; - } - else if (value is Type type) - { - value = type.ToString(); - } - - return value; - } - - private object ConvertLongStringIfNecessary(object valueArg, Encoding charset) - { - switch (valueArg) - { - case byte[] byteValue: - - try - { - return charset.GetString(byteValue); - } - catch (Exception ex) - { - _logger?.LogError(ex, ex.Message); - } - - break; - - case List listValue: - - var convertedList = new List(); - - foreach (object item in listValue) - { - convertedList.Add(ConvertLongStringIfNecessary(item, charset)); - } - - return convertedList; - - case IDictionary dictValue: - - var convertedMap = new Dictionary(); - - foreach (KeyValuePair entry in dictValue) - { - convertedMap.Add(entry.Key, ConvertLongStringIfNecessary(entry.Value, charset)); - } - - return convertedMap; - } - - return valueArg; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/Delivery.cs b/src/Messaging/src/RabbitMQ/Support/Delivery.cs deleted file mode 100644 index 5d8a6336de..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/Delivery.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Core; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public class Delivery -{ - public string ConsumerTag { get; } - - public Envelope Envelope { get; } - - public RC.IBasicProperties Properties { get; } - - public byte[] Body { get; } - - public string Queue { get; } - - public Delivery(string consumerTag, Envelope envelope, RC.IBasicProperties properties, byte[] body, string queue) - { - ConsumerTag = consumerTag; - Envelope = envelope; - Properties = properties; - Body = body; - Queue = queue; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/IListenerContainerAware.cs b/src/Messaging/src/RabbitMQ/Support/IListenerContainerAware.cs deleted file mode 100644 index 3c3c65e1be..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/IListenerContainerAware.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public interface IListenerContainerAware -{ - List GetExpectedQueueNames(); -} diff --git a/src/Messaging/src/RabbitMQ/Support/IMessageHeadersConverter.cs b/src/Messaging/src/RabbitMQ/Support/IMessageHeadersConverter.cs deleted file mode 100644 index 06f83a0902..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/IMessageHeadersConverter.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Messaging.RabbitMQ.Core; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public interface IMessageHeadersConverter -{ - IMessageHeaders ToMessageHeaders(RC.IBasicProperties source, Envelope envelope, Encoding charset); - - void FromMessageHeaders(IMessageHeaders source, RC.IBasicProperties target, Encoding charset); -} diff --git a/src/Messaging/src/RabbitMQ/Support/MessagePostProcessorUtils.cs b/src/Messaging/src/RabbitMQ/Support/MessagePostProcessorUtils.cs deleted file mode 100644 index 2ad746b1c4..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/MessagePostProcessorUtils.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Order; -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public static class MessagePostProcessorUtils -{ - public static List Sort(List processors) - { - var priorityOrdered = new List(); - var ordered = new List(); - var unOrdered = new List(); - - foreach (IMessagePostProcessor processor in processors) - { - switch (processor) - { - case IPriorityOrdered priOrdered: - priorityOrdered.Add(priOrdered); - break; - case IOrdered orderProcessor: - ordered.Add(orderProcessor); - break; - default: - unOrdered.Add(processor); - break; - } - } - - var sorted = new List(); - - priorityOrdered.Sort(OrderComparer.Instance); - sorted.AddRange(priorityOrdered.Select(o => (IMessagePostProcessor)o)); - - ordered.Sort(OrderComparer.Instance); - sorted.AddRange(ordered.Select(o => (IMessagePostProcessor)o)); - - sorted.AddRange(unOrdered); - return sorted; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/AbstractCompressingPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/AbstractCompressingPostProcessor.cs deleted file mode 100644 index 20ff62fa49..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/AbstractCompressingPostProcessor.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Order; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public abstract class AbstractCompressingPostProcessor : IMessagePostProcessor, IOrdered -{ - protected readonly ILogger Logger; - - public bool AutoDecompress { get; } - - public bool CopyHeaders { get; set; } - - public int Order { get; set; } - - protected AbstractCompressingPostProcessor(ILogger logger = null) - : this(true, logger) - { - } - - protected AbstractCompressingPostProcessor(bool autoDecompress, ILogger logger = null) - { - Logger = logger; - AutoDecompress = autoDecompress; - } - - public virtual IMessage PostProcessMessage(IMessage message) - { - try - { - var zipped = new MemoryStream(); - Stream compressor = GetCompressorStream(zipped); - var payStream = new MemoryStream((byte[])message.Payload); - payStream.CopyTo(compressor); - compressor.Close(); - - byte[] compressed = zipped.ToArray(); - - Logger?.LogTrace("Compressed {beforeLength} to {afterLength} bytes.", ((byte[])message.Payload).Length, compressed.Length); - - return CreateMessage(message, compressed); - } - catch (IOException e) - { - throw new RabbitIOException(e); - } - } - - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return PostProcessMessage(message); - } - - protected virtual IMessage CreateMessage(IMessage message, byte[] compressed) - { - RabbitHeaderAccessor headers = RabbitHeaderAccessor.GetMutableAccessor(message.Headers); - - if (CopyHeaders) - { - headers = RabbitHeaderAccessor.GetMutableAccessor(new MessageHeaders(message.Headers, message.Headers.Id, message.Headers.Timestamp)); - } - - if (AutoDecompress) - { - headers.SetHeader(RabbitMessageHeaders.SpringAutoDecompress, true); - } - - headers.ContentEncoding = message.Headers.ContentEncoding() == null ? GetEncoding() : $"{GetEncoding()}:{message.Headers.ContentEncoding()}"; - - return Message.Create(compressed, headers.ToMessageHeaders()); - } - - protected abstract string GetEncoding(); - - protected abstract Stream GetCompressorStream(Stream stream); -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/AbstractDecompressingPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/AbstractDecompressingPostProcessor.cs deleted file mode 100644 index 6ae4030efd..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/AbstractDecompressingPostProcessor.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Order; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public abstract class AbstractDecompressingPostProcessor : IMessagePostProcessor, IOrdered -{ - public int Order { get; set; } - - public bool AlwaysDecompress { get; } - - protected AbstractDecompressingPostProcessor() - : this(false) - { - } - - protected AbstractDecompressingPostProcessor(bool alwaysDecompress) - { - AlwaysDecompress = alwaysDecompress; - } - - public virtual IMessage PostProcessMessage(IMessage message) - { - bool? autoDecompress = message.Headers.Get(RabbitMessageHeaders.SpringAutoDecompress); - - if (AlwaysDecompress || (autoDecompress != null && autoDecompress.Value)) - { - try - { - var compressed = new MemoryStream((byte[])message.Payload); - Stream decompressor = GetDeCompressorStream(compressed); - var outStream = new MemoryStream(); - decompressor.CopyTo(outStream); - decompressor.Close(); - - RabbitHeaderAccessor headers = RabbitHeaderAccessor.GetMutableAccessor(message.Headers); - string encoding = headers.ContentEncoding; - int colonAt = encoding.IndexOf(':'); - - if (colonAt > 0) - { - encoding = encoding.Substring(0, colonAt); - } - - if (GetEncoding() != encoding) - { - throw new InvalidOperationException($"Content encoding must be:{GetEncoding()}, was:{encoding}"); - } - - headers.ContentEncoding = colonAt < 0 ? null : headers.ContentEncoding.Substring(colonAt + 1); - - headers.RemoveHeader(RabbitMessageHeaders.SpringAutoDecompress); - return Message.Create(outStream.ToArray(), headers.ToMessageHeaders()); - } - catch (IOException e) - { - throw new RabbitIOException(e); - } - } - - return message; - } - - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return PostProcessMessage(message); - } - - protected abstract string GetEncoding(); - - protected abstract Stream GetDeCompressorStream(Stream stream); -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/AbstractDeflaterPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/AbstractDeflaterPostProcessor.cs deleted file mode 100644 index 8f95057ed7..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/AbstractDeflaterPostProcessor.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.IO.Compression; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public abstract class AbstractDeflaterPostProcessor : AbstractCompressingPostProcessor -{ - public virtual CompressionLevel Level { get; set; } = CompressionLevel.Fastest; - - protected AbstractDeflaterPostProcessor() - { - } - - protected AbstractDeflaterPostProcessor(bool autoDecompress) - : base(autoDecompress) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/DeflaterPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/DeflaterPostProcessor.cs deleted file mode 100644 index 4298f1fca4..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/DeflaterPostProcessor.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.IO.Compression; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public class DeflaterPostProcessor : AbstractDeflaterPostProcessor -{ - public DeflaterPostProcessor() - { - } - - public DeflaterPostProcessor(bool autoDecompress) - : base(autoDecompress) - { - } - - protected override Stream GetCompressorStream(Stream stream) - { - return new DeflateStream(stream, Level); - } - - protected override string GetEncoding() - { - return "deflate"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/DelegatingDecompressingPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/DelegatingDecompressingPostProcessor.cs deleted file mode 100644 index 4985647d6b..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/DelegatingDecompressingPostProcessor.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Order; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public class DelegatingDecompressingPostProcessor : IMessagePostProcessor, IOrdered -{ - private readonly Dictionary _decompressors = new(); - - public int Order { get; set; } - - public DelegatingDecompressingPostProcessor() - { - _decompressors.Add("gzip", new GUnzipPostProcessor()); - _decompressors.Add("zip", new UnzipPostProcessor()); - _decompressors.Add("deflate", new InflaterPostProcessor()); - } - - public void AddDecompressor(string contentEncoding, IMessagePostProcessor decompressor) - { - _decompressors[contentEncoding] = decompressor; - } - - public IMessagePostProcessor RemoveDecompressor(string contentEncoding) - { - _decompressors.Remove(contentEncoding, out IMessagePostProcessor result); - return result; - } - - public void SetDecompressors(Dictionary decompressors) - { - _decompressors.Clear(); - - foreach (KeyValuePair d in decompressors) - { - decompressors.Add(d.Key, d.Value); - } - } - - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return PostProcessMessage(message); - } - - public IMessage PostProcessMessage(IMessage message) - { - string encoding = message.Headers.ContentEncoding(); - - if (encoding == null) - { - return message; - } - - int colonAt = encoding.IndexOf(':'); - - if (colonAt > 0) - { - encoding = encoding.Substring(0, colonAt); - } - - _decompressors.TryGetValue(encoding, out IMessagePostProcessor decompressor); - - if (decompressor != null) - { - return decompressor.PostProcessMessage(message); - } - - return message; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/GUnzipPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/GUnzipPostProcessor.cs deleted file mode 100644 index 6f9de13646..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/GUnzipPostProcessor.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.IO.Compression; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public class GUnzipPostProcessor : AbstractDecompressingPostProcessor -{ - public GUnzipPostProcessor() - { - } - - public GUnzipPostProcessor(bool alwaysDecompress) - : base(alwaysDecompress) - { - } - - protected override Stream GetDeCompressorStream(Stream stream) - { - return new GZipStream(stream, CompressionMode.Decompress); - } - - protected override string GetEncoding() - { - return "gzip"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/GZipPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/GZipPostProcessor.cs deleted file mode 100644 index a2c9c1eb8a..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/GZipPostProcessor.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.IO.Compression; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public class GZipPostProcessor : AbstractDeflaterPostProcessor -{ - public GZipPostProcessor() - { - } - - public GZipPostProcessor(bool autoDecompress) - : base(autoDecompress) - { - } - - protected override Stream GetCompressorStream(Stream stream) - { - return new GZipStream(stream, Level); - } - - protected override string GetEncoding() - { - return "gzip"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/InflaterPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/InflaterPostProcessor.cs deleted file mode 100644 index 7f1e8237b2..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/InflaterPostProcessor.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.IO.Compression; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public class InflaterPostProcessor : AbstractDecompressingPostProcessor -{ - public InflaterPostProcessor() - { - } - - public InflaterPostProcessor(bool autoDecompress) - : base(autoDecompress) - { - } - - protected override Stream GetDeCompressorStream(Stream stream) - { - return new DeflateStream(stream, CompressionMode.Decompress); - } - - protected override string GetEncoding() - { - return "deflate"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/UnzipPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/UnzipPostProcessor.cs deleted file mode 100644 index 2c1080ffce..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/UnzipPostProcessor.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.IO.Compression; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public class UnzipPostProcessor : AbstractDecompressingPostProcessor -{ - public UnzipPostProcessor() - { - } - - public UnzipPostProcessor(bool alwaysDecompress) - : base(alwaysDecompress) - { - } - - protected override Stream GetDeCompressorStream(Stream stream) - { - var zipper = new ZipArchive(stream, ZipArchiveMode.Read); - ZipArchiveEntry entry = zipper.GetEntry("amqp"); - - if (entry == null) - { - throw new InvalidOperationException("Zip entryName 'amqp' does not exist"); - } - - return entry.Open(); - } - - protected override string GetEncoding() - { - return "zip"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/PostProcessor/ZipPostProcessor.cs b/src/Messaging/src/RabbitMQ/Support/PostProcessor/ZipPostProcessor.cs deleted file mode 100644 index 3e0330093b..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/PostProcessor/ZipPostProcessor.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.IO.Compression; -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging.RabbitMQ.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; - -public class ZipPostProcessor : AbstractDeflaterPostProcessor -{ - public ZipPostProcessor() - { - } - - public ZipPostProcessor(bool autoDecompress) - : base(autoDecompress) - { - } - - public override IMessage PostProcessMessage(IMessage message) - { - try - { - var zipped = new MemoryStream(); - var zipper = new ZipArchive(zipped, ZipArchiveMode.Create); - ZipArchiveEntry entry = zipper.CreateEntry("amqp", Level); - Stream compressor = entry.Open(); - var payStream = new MemoryStream((byte[])message.Payload); - payStream.CopyTo(compressor); - compressor.Close(); - zipper.Dispose(); - - byte[] compressed = zipped.ToArray(); - - Logger?.LogTrace("Compressed {beforeLength} to {afterLength} bytes.", ((byte[])message.Payload).Length, compressed.Length); - - return CreateMessage(message, compressed); - } - catch (IOException e) - { - throw new RabbitIOException(e); - } - } - - protected override Stream GetCompressorStream(Stream stream) - { - throw new NotImplementedException("GetCompressorStream should not be called"); - } - - protected override string GetEncoding() - { - return "zip"; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/RabbitExceptionTranslator.cs b/src/Messaging/src/RabbitMQ/Support/RabbitExceptionTranslator.cs deleted file mode 100644 index c06b4254fe..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/RabbitExceptionTranslator.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client.Exceptions; -using RabbitMQ.Client.Impl; -using Steeltoe.Common; -using Steeltoe.Messaging.RabbitMQ.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public static class RabbitExceptionTranslator -{ - public static Exception ConvertRabbitAccessException(Exception exception) - { - ArgumentGuard.NotNull(exception); - - return exception switch - { - RabbitException rabbitException => rabbitException, - ChannelAllocationException => new RabbitResourceNotAvailableException(exception), - ProtocolException or ShutdownSignalException => new RabbitConnectException(exception), - ConnectFailureException or BrokerUnreachableException => new RabbitConnectException(exception), - PossibleAuthenticationFailureException => new RabbitAuthenticationException(exception), - OperationInterruptedException => new RabbitIOException(exception), - IOException => new RabbitIOException(exception), - TimeoutException => new RabbitTimeoutException(exception), - ConsumerCancelledException => exception, - _ => new RabbitUncategorizedException(exception) - }; - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/RabbitHeaderAccessor.cs b/src/Messaging/src/RabbitMQ/Support/RabbitHeaderAccessor.cs deleted file mode 100644 index f3a767e1ee..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/RabbitHeaderAccessor.cs +++ /dev/null @@ -1,351 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public class RabbitHeaderAccessor : MessageHeaderAccessor -{ - public const int IntMask = 32; - public const int DefaultPriority = 0; - - public const string SpringBatchFormat = "springBatchFormat"; - public const string BatchFormatLengthHeader4 = "lengthHeader4"; - public const string SpringAutoDecompress = "springAutoDecompress"; - public const string XDelay = "x-delay"; - public const string DefaultContentType = Messaging.MessageHeaders.ContentTypeBytes; - - public const MessageDeliveryMode DefaultDeliveryMode = MessageDeliveryMode.Persistent; - - public string AppId - { - get => GetHeader(RabbitMessageHeaders.AppId) as string; - set => SetHeader(RabbitMessageHeaders.AppId, value); - } - - public string ClusterId - { - get => GetHeader(RabbitMessageHeaders.ClusterId) as string; - set => SetHeader(RabbitMessageHeaders.ClusterId, value); - } - - public string ConsumerQueue - { - get => GetHeader(RabbitMessageHeaders.ConsumerQueue) as string; - set => SetHeader(RabbitMessageHeaders.ConsumerQueue, value); - } - - public string ConsumerTag - { - get => GetHeader(RabbitMessageHeaders.ConsumerTag) as string; - set => SetHeader(RabbitMessageHeaders.ConsumerTag, value); - } - - public string ContentEncoding - { - get => GetHeader(RabbitMessageHeaders.ContentEncoding) as string; - set => SetHeader(RabbitMessageHeaders.ContentEncoding, value); - } - - public long? ContentLength - { - get => GetHeader(RabbitMessageHeaders.ContentLength) as long?; - set => SetHeader(RabbitMessageHeaders.ContentLength, value); - } - - public bool IsContentLengthSet => ContentLength.HasValue; - - public string CorrelationId - { - get => GetHeader(RabbitMessageHeaders.CorrelationId) as string; - set => SetHeader(RabbitMessageHeaders.CorrelationId, value); - } - - public int? Delay - { - get => GetHeader(XDelay) as int?; - set - { - if (value == null || value.Value < 0) - { - SetHeader(XDelay, null); - } - else - { - SetHeader(XDelay, value); - } - } - } - - public MessageDeliveryMode? DeliveryMode - { - get => GetHeader(RabbitMessageHeaders.DeliveryMode) as MessageDeliveryMode?; - set => SetHeader(RabbitMessageHeaders.DeliveryMode, value); - } - - public ulong? DeliveryTag - { - get => GetHeader(RabbitMessageHeaders.DeliveryTag) as ulong?; - set => SetHeader(RabbitMessageHeaders.DeliveryTag, value); - } - - public bool IsDeliveryTagSet => DeliveryTag.HasValue; - - public string Expiration - { - get => GetHeader(RabbitMessageHeaders.Expiration) as string; - set => SetHeader(RabbitMessageHeaders.Expiration, value); - } - - public Type InferredArgumentType - { - get => GetHeader(Messaging.MessageHeaders.InferredArgumentType) as Type; - set => SetHeader(Messaging.MessageHeaders.InferredArgumentType, value); - } - - public uint? MessageCount - { - get => GetHeader(RabbitMessageHeaders.MessageCount) as uint?; - set => SetHeader(RabbitMessageHeaders.MessageCount, value); - } - - public string MessageId - { - get => GetHeader(Messaging.MessageHeaders.IdName) as string; - set => SetHeader(Messaging.MessageHeaders.IdName, value); - } - - public int? Priority - { - get => GetHeader(RabbitMessageHeaders.Priority) as int?; - set => SetHeader(RabbitMessageHeaders.Priority, value); - } - - public ulong? PublishSequenceNumber - { - get => GetHeader(RabbitMessageHeaders.PublishSequenceNumber) as ulong?; - set => SetHeader(RabbitMessageHeaders.PublishSequenceNumber, value); - } - - public int? ReceivedDelay - { - get => GetHeader(RabbitMessageHeaders.ReceivedDelay) as int?; - set => SetHeader(RabbitMessageHeaders.ReceivedDelay, value); - } - - public MessageDeliveryMode? ReceivedDeliveryMode - { - get => GetHeader(RabbitMessageHeaders.ReceivedDeliveryMode) as MessageDeliveryMode?; - set => SetHeader(RabbitMessageHeaders.ReceivedDeliveryMode, value); - } - - public string ReceivedExchange - { - get => GetHeader(RabbitMessageHeaders.ReceivedExchange) as string; - set => SetHeader(RabbitMessageHeaders.ReceivedExchange, value); - } - - public string ReceivedRoutingKey - { - get => GetHeader(RabbitMessageHeaders.ReceivedRoutingKey) as string; - set => SetHeader(RabbitMessageHeaders.ReceivedRoutingKey, value); - } - - public string ReceivedUserId - { - get => GetHeader(RabbitMessageHeaders.ReceivedUserId) as string; - set => SetHeader(RabbitMessageHeaders.ReceivedUserId, value); - } - - public bool? Redelivered - { - get => GetHeader(RabbitMessageHeaders.Redelivered) as bool?; - set => SetHeader(RabbitMessageHeaders.Redelivered, value); - } - - public string ReplyTo - { - get => GetHeader(RabbitMessageHeaders.ReplyTo) as string; - set => SetHeader(RabbitMessageHeaders.ReplyTo, value); - } - - public Address ReplyToAddress - { - get - { - string result = ReplyTo; - - if (result != null) - { - return new Address(result); - } - - return null; - } - set => ReplyTo = value.ToString(); - } - - public object Target - { - get => GetHeader(RabbitMessageHeaders.Target); - set => SetHeader(RabbitMessageHeaders.Target, value); - } - - public MethodInfo TargetMethod - { - get => GetHeader(RabbitMessageHeaders.TargetMethod) as MethodInfo; - set => SetHeader(RabbitMessageHeaders.TargetMethod, value); - } - - public new long? Timestamp - { - get => base.Timestamp; - set => SetHeader(Messaging.MessageHeaders.TimestampName, value); - } - - public string Type - { - get => GetHeader(RabbitMessageHeaders.Type) as string; - set => SetHeader(RabbitMessageHeaders.Type, value); - } - - public string UserId - { - get => GetHeader(RabbitMessageHeaders.UserId) as string; - set => SetHeader(RabbitMessageHeaders.UserId, value); - } - - public bool? FinalRetryForMessageWithNoId - { - get => GetHeader(RabbitMessageHeaders.FinalRetryForMessageWithNoId) as bool?; - set => SetHeader(RabbitMessageHeaders.FinalRetryForMessageWithNoId, value); - } - - public bool IsFinalRetryForMessageWithNoId => FinalRetryForMessageWithNoId.HasValue; - - public bool? LastInBatch - { - get => GetHeader(RabbitMessageHeaders.LastInBatch) as bool?; - set => SetHeader(RabbitMessageHeaders.LastInBatch, value); - } - - public RabbitHeaderAccessor() - : this((IMessage)null) - { - } - - public RabbitHeaderAccessor(IMessage message) - : base(message) - { - headers = new RabbitAccessorMessageHeaders(this, message?.Headers); - } - - protected internal RabbitHeaderAccessor(MessageHeaders headers) - : base(headers) - { - this.headers = new RabbitAccessorMessageHeaders(this, headers); - } - - public static RabbitHeaderAccessor GetAccessor(IMessage message) - { - return GetAccessor(message.Headers); - } - - public static RabbitHeaderAccessor GetAccessor(IMessageHeaders messageHeaders) - { - if (messageHeaders is RabbitAccessorMessageHeaders accessorMessageHeaders) - { - return accessorMessageHeaders.Accessor; - } - - if (messageHeaders is MessageHeaders msgHeaders) - { - return new RabbitHeaderAccessor(msgHeaders); - } - - return null; - } - - public static RabbitHeaderAccessor GetMutableAccessor(IMessage message) - { - return GetMutableAccessor(message.Headers); - } - - public static RabbitHeaderAccessor GetMutableAccessor(IMessageHeaders headers) - { - RabbitHeaderAccessor messageHeaderAccessor = null; - - if (headers is RabbitAccessorMessageHeaders accessorMessageHeaders) - { - RabbitHeaderAccessor headerAccessor = accessorMessageHeaders.Accessor; - messageHeaderAccessor = headerAccessor.IsMutable ? headerAccessor : headerAccessor.CreateMutableAccessor(headers); - } - - if (messageHeaderAccessor == null && headers is MessageHeaders msgHeaders) - { - messageHeaderAccessor = new RabbitHeaderAccessor(msgHeaders); - } - - return messageHeaderAccessor; - } - - public List> GetXDeathHeader() - { - return GetHeader(RabbitMessageHeaders.XDeath) as List>; - } - - public override IMessageHeaders ToMessageHeaders() - { - return Messaging.MessageHeaders.From(headers); - } - - protected new RabbitHeaderAccessor CreateMutableAccessor(IMessage message) - { - return CreateMutableAccessor(message.Headers); - } - - protected new RabbitHeaderAccessor CreateMutableAccessor(IMessageHeaders messageHeaders) - { - if (messageHeaders is not MessageHeaders headers) - { - throw new InvalidOperationException( - $"Unable to create mutable accessor, message has no headers or headers are not of type {nameof(MessageHeaders)}."); - } - - return new RabbitHeaderAccessor(headers); - } - - protected override bool IsReadOnly(string headerName) - { - return !headers.IsMutable; - } - - protected override void VerifyType(string headerName, object headerValue) - { - base.VerifyType(headerName, headerValue); - - if (headerName == RabbitMessageHeaders.Priority && headerValue is not int) - { - throw new ArgumentException($"The '{headerName}' header value must be an {nameof(Int32)}.", nameof(headerName)); - } - } - - protected class RabbitAccessorMessageHeaders : AccessorMessageHeaders - { - public new RabbitHeaderAccessor Accessor => accessor as RabbitHeaderAccessor; - - public RabbitAccessorMessageHeaders(MessageHeaderAccessor accessor, MessageHeaders headers) - : base(accessor, headers) - { - } - - public RabbitAccessorMessageHeaders(MessageHeaderAccessor accessor, IDictionary headers) - : base(accessor, headers) - { - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/RabbitMessageBuilder.cs b/src/Messaging/src/RabbitMQ/Support/RabbitMessageBuilder.cs deleted file mode 100644 index 373134bad5..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/RabbitMessageBuilder.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public static class RabbitMessageBuilder -{ - public static AbstractMessageBuilder FromMessage(IMessage message) - { - return new RabbitMessageBuilder(message); - } - - public static AbstractMessageBuilder FromMessage(IMessage message, Type payloadType = null) - { - Type genParamType = MessageBuilder.GetGenericParamType(message, payloadType); - Type typeToCreate = typeof(RabbitMessageBuilder<>).MakeGenericType(genParamType); - - return (AbstractMessageBuilder)Activator.CreateInstance(typeToCreate, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, - new object[] - { - message - }, null, null); - } - - public static AbstractMessageBuilder WithPayload(TPayload payload) - { - return new RabbitMessageBuilder(payload, new RabbitHeaderAccessor()); - } - - public static AbstractMessageBuilder WithPayload(object payload, Type payloadType = null) - { - Type genParamType = MessageBuilder.GetGenericParamType(payload, payloadType); - Type typeToCreate = typeof(RabbitMessageBuilder<>).MakeGenericType(genParamType); - - return (AbstractMessageBuilder)Activator.CreateInstance(typeToCreate, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new[] - { - payload, - new RabbitHeaderAccessor() - }, null, null); - } - - public static IMessage CreateMessage(object payload, IMessageHeaders messageHeaders, Type payloadType = null) - { - ArgumentGuard.NotNull(payload); - ArgumentGuard.NotNull(messageHeaders); - - return Message.Create(payload, messageHeaders, payloadType); - } -} - -public class RabbitMessageBuilder : MessageBuilder -{ - protected internal RabbitMessageBuilder() - { - } - - protected internal RabbitMessageBuilder(IMessage message) - : base(message) - { - } - - protected internal RabbitMessageBuilder(IMessage message) - : base(message) - { - } - - protected internal RabbitMessageBuilder(RabbitHeaderAccessor accessor) - : base(accessor) - { - } - - protected internal RabbitMessageBuilder(TPayload payload, RabbitHeaderAccessor accessor) - : base(payload, accessor) - { - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/SendRetryContextAccessor.cs b/src/Messaging/src/RabbitMQ/Support/SendRetryContextAccessor.cs deleted file mode 100644 index f471c665d9..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/SendRetryContextAccessor.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Retry; -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public static class SendRetryContextAccessor -{ - public const string Message = "message"; - public const string Address = "address"; - - public static IMessage GetMessage(IRetryContext context) - { - return (IMessage)context.GetAttribute(Message); - } - - public static Address GetAddress(IRetryContext context) - { - return (Address)context.GetAttribute(Address); - } -} diff --git a/src/Messaging/src/RabbitMQ/Support/ShutdownSignalException.cs b/src/Messaging/src/RabbitMQ/Support/ShutdownSignalException.cs deleted file mode 100644 index 4ace51fdc6..0000000000 --- a/src/Messaging/src/RabbitMQ/Support/ShutdownSignalException.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Support; - -public class ShutdownSignalException : Exception -{ - public RC.ShutdownEventArgs Args { get; } - - public ShutdownSignalException(RC.ShutdownEventArgs args) - { - Args = args; - } - - public override string ToString() - { - return Args.ToString(); - } -} diff --git a/src/Messaging/src/RabbitMQ/Transaction/RabbitTransactionManager.cs b/src/Messaging/src/RabbitMQ/Transaction/RabbitTransactionManager.cs deleted file mode 100644 index 864eb07c72..0000000000 --- a/src/Messaging/src/RabbitMQ/Transaction/RabbitTransactionManager.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common; -using Steeltoe.Common.Transaction; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Exceptions; - -namespace Steeltoe.Messaging.RabbitMQ.Transaction; - -public class RabbitTransactionManager : AbstractPlatformTransactionManager, IResourceTransactionManager -{ - public IConnectionFactory ConnectionFactory { get; set; } - - public object ResourceFactory => ConnectionFactory; - - public RabbitTransactionManager() - { - TransactionSynchronization = SynchronizationNever; - } - - public RabbitTransactionManager(IConnectionFactory connectionFactory, ILogger logger = null) - : base(logger) - { - ArgumentGuard.NotNull(connectionFactory); - - ConnectionFactory = connectionFactory; - } - - protected override object DoGetTransaction() - { - var txObject = new RabbitTransactionObject((RabbitResourceHolder)TransactionSynchronizationManager.GetResource(ConnectionFactory)); - return txObject; - } - - protected override bool IsExistingTransaction(object transaction) - { - var txObject = (RabbitTransactionObject)transaction; - return txObject.ResourceHolder != null; - } - - protected override void DoBegin(object transaction, ITransactionDefinition definition) - { - if (definition.IsolationLevel != AbstractTransactionDefinition.IsolationDefault) - { - throw new InvalidIsolationLevelException("AMQP does not support an isolation level concept"); - } - - var txObject = (RabbitTransactionObject)transaction; - RabbitResourceHolder resourceHolder = null; - - try - { - resourceHolder = ConnectionFactoryUtils.GetTransactionalResourceHolder(ConnectionFactory, true); - Logger?.LogDebug("Created AMQP transaction on channel [{channel}]", resourceHolder.GetChannel()); - - txObject.ResourceHolder = resourceHolder; - txObject.ResourceHolder.SynchronizedWithTransaction = true; - int timeout = DetermineTimeout(definition); - - if (timeout != AbstractTransactionDefinition.TimeoutDefault) - { - txObject.ResourceHolder.SetTimeoutInSeconds(timeout); - } - - TransactionSynchronizationManager.BindResource(ConnectionFactory, txObject.ResourceHolder); - } - catch (RabbitException ex) - { - if (resourceHolder != null) - { - ConnectionFactoryUtils.ReleaseResources(resourceHolder); - } - - throw new CannotCreateTransactionException("Could not create AMQP transaction", ex); - } - } - - protected override object DoSuspend(object transaction) - { - var txObject = (RabbitTransactionObject)transaction; - txObject.ResourceHolder = null; - return TransactionSynchronizationManager.UnbindResource(ConnectionFactory); - } - - protected override void DoResume(object transaction, object suspendedResources) - { - var conHolder = (RabbitResourceHolder)suspendedResources; - TransactionSynchronizationManager.BindResource(ConnectionFactory, conHolder); - } - - protected override void DoCommit(DefaultTransactionStatus status) - { - var txObject = (RabbitTransactionObject)status.Transaction; - RabbitResourceHolder resourceHolder = txObject.ResourceHolder; - resourceHolder.CommitAll(); - } - - protected override void DoRollback(DefaultTransactionStatus status) - { - var txObject = (RabbitTransactionObject)status.Transaction; - RabbitResourceHolder resourceHolder = txObject.ResourceHolder; - resourceHolder.RollbackAll(); - } - - protected override void DoSetRollbackOnly(DefaultTransactionStatus status) - { - var txObject = (RabbitTransactionObject)status.Transaction; - txObject.ResourceHolder.RollbackOnly = true; - } - - protected override void DoCleanupAfterCompletion(object transaction) - { - var txObject = (RabbitTransactionObject)transaction; - TransactionSynchronizationManager.UnbindResource(ConnectionFactory); - txObject.ResourceHolder.CloseAll(); - txObject.ResourceHolder.Clear(); - } - - private sealed class RabbitTransactionObject : ISmartTransactionObject - { - public RabbitResourceHolder ResourceHolder { get; set; } - - public bool IsRollbackOnly => ResourceHolder.RollbackOnly; - - public RabbitTransactionObject(RabbitResourceHolder rabbitResourceHolder) - { - ResourceHolder = rabbitResourceHolder; - } - - public void Flush() - { - // Intentionally left empty. - } - } -} diff --git a/src/Messaging/src/RabbitMQ/Util/ActiveObjectCounter.cs b/src/Messaging/src/RabbitMQ/Util/ActiveObjectCounter.cs deleted file mode 100644 index 51b2e20c1b..0000000000 --- a/src/Messaging/src/RabbitMQ/Util/ActiveObjectCounter.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; - -namespace Steeltoe.Messaging.RabbitMQ.Util; - -public class ActiveObjectCounter -{ - private readonly ConcurrentDictionary _locks = new(); - - public int Count => _locks.Count; - - public bool IsActive { get; private set; } = true; - - public void Add(T activeObject) - { - var latch = new CountdownEvent(1); - _locks.TryAdd(activeObject, latch); - } - - public void Release(T activeObject) - { - if (_locks.TryRemove(activeObject, out CountdownEvent remove)) - { - remove.Signal(); - } - } - - public bool Wait(TimeSpan timeout) - { - long t0 = DateTimeOffset.Now.Ticks; - long t1 = t0 + timeout.Ticks; - - while (DateTimeOffset.Now.Ticks <= t1) - { - if (_locks.Count == 0) - { - return true; - } - - var objects = new HashSet(_locks.Keys); - - foreach (T activeObject in objects) - { - if (!_locks.TryGetValue(activeObject, out CountdownEvent latch)) - { - continue; - } - - t0 = DateTimeOffset.Now.Ticks; - - if (t0 >= t1) - { - break; - } - - if (latch.Wait(TimeSpan.FromTicks(t1 - t0))) - { - _locks.TryRemove(activeObject, out _); - } - } - } - - return false; - } - - public void Deactivate() - { - IsActive = false; - } - - public void Reset() - { - _locks.Clear(); - IsActive = false; - } -} diff --git a/src/Messaging/test/Benchmarks/Channel/BenchmarkDotNet.Artifacts/results/Program-report-github.md b/src/Messaging/test/Benchmarks/Channel/BenchmarkDotNet.Artifacts/results/Program-report-github.md deleted file mode 100644 index 9f1243e069..0000000000 --- a/src/Messaging/test/Benchmarks/Channel/BenchmarkDotNet.Artifacts/results/Program-report-github.md +++ /dev/null @@ -1,14 +0,0 @@ -``` ini - -BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041 -Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores -.NET Core SDK=3.1.101 - [Host] : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT - DefaultJob : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT - - -``` -| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | -|------------------------------------------------------- |---------:|--------:|--------:|------------:|------:|------:|----------:| -| TaskSchedulerSubscribableChannel_Send_10_000_000 | 489.2 ms | 7.76 ms | 7.26 ms | 229000.0000 | - | - | 686.65 MB | -| TaskSchedulerSubscribableChannel_WriteAsync_10_000_000 | 558.3 ms | 4.45 ms | 4.16 ms | 229000.0000 | - | - | 686.65 MB | diff --git a/src/Messaging/test/Benchmarks/Channel/BenchmarkDotNet.Artifacts/results/Program-report.csv b/src/Messaging/test/Benchmarks/Channel/BenchmarkDotNet.Artifacts/results/Program-report.csv deleted file mode 100644 index 4b01b23621..0000000000 --- a/src/Messaging/test/Benchmarks/Channel/BenchmarkDotNet.Artifacts/results/Program-report.csv +++ /dev/null @@ -1,3 +0,0 @@ -Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,Gen 0,Gen 1,Gen 2,Allocated -TaskSchedulerSubscribableChannel_Send_10_000_000,Default,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,489.2 ms,7.76 ms,7.26 ms,229000.0000,0.0000,0.0000,686.65 MB -TaskSchedulerSubscribableChannel_WriteAsync_10_000_000,Default,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET Core 3.1,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,1,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,558.3 ms,4.45 ms,4.16 ms,229000.0000,0.0000,0.0000,686.65 MB diff --git a/src/Messaging/test/Benchmarks/Channel/BenchmarkDotNet.Artifacts/results/Program-report.html b/src/Messaging/test/Benchmarks/Channel/BenchmarkDotNet.Artifacts/results/Program-report.html deleted file mode 100644 index a877e5b533..0000000000 --- a/src/Messaging/test/Benchmarks/Channel/BenchmarkDotNet.Artifacts/results/Program-report.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - Program-20200119-084403 - - - - -

-BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041
-Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
-.NET Core SDK=3.1.101
-  [Host]     : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT
-  DefaultJob : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT
-
-
- - - - - - - - - - - - - - - -
MethodMeanErrorStdDevGen 0Gen 1Gen 2Allocated
TaskSchedulerSubscribableChannel_Send_10_000_000489.2 ms7.76 ms7.26 ms229000.0000--686.65 MB
TaskSchedulerSubscribableChannel_WriteAsync_10_000_000558.3 ms4.45 ms4.16 ms229000.0000--686.65 MB
- - diff --git a/src/Messaging/test/Benchmarks/Channel/ChannelBenchmark.csproj b/src/Messaging/test/Benchmarks/Channel/ChannelBenchmark.csproj deleted file mode 100644 index 8cebc189f7..0000000000 --- a/src/Messaging/test/Benchmarks/Channel/ChannelBenchmark.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - Exe - net8.0;net6.0 - false - - - - - - - - - - - - diff --git a/src/Messaging/test/Benchmarks/Channel/Program.cs b/src/Messaging/test/Benchmarks/Channel/Program.cs deleted file mode 100644 index 1a3e2f4cb2..0000000000 --- a/src/Messaging/test/Benchmarks/Channel/Program.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace ChannelBenchmark; - -[MemoryDiagnoser] -public sealed class Program -{ - private static void Main() - { - BenchmarkRunner.Run(); - } - - [Benchmark] - public void TaskSchedulerSubscribableChannel_Send_10_000_000() - { - var channel = new TaskSchedulerSubscribableChannel(); - var handler = new CounterHandler(); - - channel.Subscribe(handler); - IMessage message = Message.Create("test"); - - for (int i = 0; i < 10_000_000; i++) - { - channel.Send(message); - } - } - - [Benchmark] - public async ValueTask TaskSchedulerSubscribableChannel_WriteAsync_10_000_000Async() - { - var channel = new TaskSchedulerSubscribableChannel(); - var handler = new CounterHandler(); - - channel.Subscribe(handler); - IMessage message = Message.Create("test"); - - for (int i = 0; i < 10_000_000; i++) - { - await channel.Writer.WriteAsync(message); - } - } - - private sealed class CounterHandler : IMessageHandler - { - public int Count { get; private set; } - - public string ServiceName { get; set; } = nameof(CounterHandler); - - public void HandleMessage(IMessage message) - { - Count++; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Converter/DefaultContentTypeResolverTest.cs b/src/Messaging/test/Messaging.Test/Converter/DefaultContentTypeResolverTest.cs deleted file mode 100644 index 557b1f934e..0000000000 --- a/src/Messaging/test/Messaging.Test/Converter/DefaultContentTypeResolverTest.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Xunit; - -namespace Steeltoe.Messaging.Test.Converter; - -public sealed class DefaultContentTypeResolverTest -{ - [Fact] - public void Resolve() - { - var map = new Dictionary - { - { MessageHeaders.ContentType, MimeTypeUtils.ApplicationJson } - }; - - var headers = new MessageHeaders(map); - var resolver = new DefaultContentTypeResolver(); - Assert.Equal(MimeTypeUtils.ApplicationJson, resolver.Resolve(headers)); - } - - [Fact] - public void ResolveStringContentType() - { - var map = new Dictionary - { - { MessageHeaders.ContentType, MimeTypeUtils.ApplicationJsonValue } - }; - - var headers = new MessageHeaders(map); - var resolver = new DefaultContentTypeResolver(); - Assert.Equal(MimeTypeUtils.ApplicationJson, resolver.Resolve(headers)); - } - - [Fact] - public void ResolveInvalidStringContentType() - { - var map = new Dictionary - { - { MessageHeaders.ContentType, "invalidContentType" } - }; - - var headers = new MessageHeaders(map); - var resolver = new DefaultContentTypeResolver(); - Assert.Throws(() => resolver.Resolve(headers)); - } - - [Fact] - public void ResolveUnknownHeaderType() - { - var map = new Dictionary - { - { MessageHeaders.ContentType, 1 } - }; - - var headers = new MessageHeaders(map); - var resolver = new DefaultContentTypeResolver(); - Assert.Throws(() => resolver.Resolve(headers)); - } - - [Fact] - public void ResolveNoContentTypeHeader() - { - var headers = new MessageHeaders(new Dictionary()); - var resolver = new DefaultContentTypeResolver(); - Assert.Null(resolver.Resolve(headers)); - } - - [Fact] - public void ResolveDefaultMimeType() - { - var resolver = new DefaultContentTypeResolver - { - DefaultMimeType = MimeTypeUtils.ApplicationJson - }; - - var headers = new MessageHeaders(new Dictionary()); - - Assert.Equal(MimeTypeUtils.ApplicationJson, resolver.Resolve(headers)); - } -} diff --git a/src/Messaging/test/Messaging.Test/Converter/DefaultTypeMapperTest.cs b/src/Messaging/test/Messaging.Test/Converter/DefaultTypeMapperTest.cs deleted file mode 100644 index 2e3b1337c6..0000000000 --- a/src/Messaging/test/Messaging.Test/Converter/DefaultTypeMapperTest.cs +++ /dev/null @@ -1,185 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Converter; - -public sealed class DefaultTypeMapperTest -{ - private readonly DefaultTypeMapper _typeMapper = new(); - private readonly MessageHeaders _headers = new(); - - [Fact] - public void GetAnObjectWhenClassIdNotPresent() - { - var type = _typeMapper.ToType(_headers); - Assert.Equal(typeof(object), type); - } - - [Fact] - public void ShouldLookInTheClassIdFieldNameToFindTheClassName() - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(_headers); - accessor.SetHeader("type", "System.String"); - _typeMapper.ClassIdFieldName = "type"; - - var type = _typeMapper.ToType(accessor.MessageHeaders); - Assert.Equal(typeof(string), type); - } - - [Fact] - public void ShouldUseTheClassProvidedByTheLookupMapIfPresent() - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(_headers); - accessor.SetHeader("__TypeId__", "trade"); - - _typeMapper.SetIdClassMapping(new Dictionary - { - { "trade", typeof(SimpleTrade) } - }); - - var type = _typeMapper.ToType(accessor.MessageHeaders); - Assert.Equal(typeof(SimpleTrade), type); - } - - [Fact] - public void FromTypeShouldPopulateWithTypeNameByDefault() - { - _typeMapper.FromType(typeof(SimpleTrade), _headers); - string className = _headers.Get(_typeMapper.ClassIdFieldName); - Assert.Equal(typeof(SimpleTrade).ToString(), className); - } - - [Fact] - public void ShouldUseSpecialNameForClassIfPresent() - { - _typeMapper.SetIdClassMapping(new Dictionary - { - { "daytrade", typeof(SimpleTrade) } - }); - - _typeMapper.FromType(typeof(SimpleTrade), _headers); - string className = _headers.Get(_typeMapper.ClassIdFieldName); - Assert.Equal("daytrade", className); - } - - [Fact] - public void ShouldThrowAnExceptionWhenContentClassIdIsNotPresentWhenClassIdIsContainerType() - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(_headers); - accessor.SetHeader(_typeMapper.ClassIdFieldName, typeof(List<>).FullName); - var exception = Assert.Throws(() => _typeMapper.ToType(accessor.MessageHeaders)); - Assert.Contains("Could not resolve ", exception.Message, StringComparison.Ordinal); - } - - [Fact] - public void ShouldLookInTheContentClassIdFieldNameToFindTheContainerClassIdWhenClassIdIsContainerType() - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(_headers); - accessor.SetHeader("contentType", typeof(string).ToString()); - accessor.SetHeader(_typeMapper.ClassIdFieldName, typeof(List<>).FullName); - _typeMapper.ContentClassIdFieldName = "contentType"; - var type = _typeMapper.ToType(accessor.MessageHeaders); - Assert.Equal(typeof(List), type); - } - - [Fact] - public void ShouldUseTheContentClassProvidedByTheLookupMapIfPresent() - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(_headers); - accessor.SetHeader(_typeMapper.ClassIdFieldName, typeof(List<>).FullName); - accessor.SetHeader("__ContentTypeId__", "trade"); - - var mapping = new Dictionary - { - { "trade", typeof(SimpleTrade) }, - { _typeMapper.ClassIdFieldName, typeof(List<>) } - }; - - _typeMapper.SetIdClassMapping(mapping); - - var type = _typeMapper.ToType(accessor.MessageHeaders); - Assert.Equal(typeof(List), type); - } - - [Fact] - public void FromTypeShouldPopulateWithContentTypeTypeNameByDefault() - { - _typeMapper.FromType(typeof(List), _headers); - - string className = _headers.Get(_typeMapper.ClassIdFieldName); - string contentClassName = _headers.Get(_typeMapper.ContentClassIdFieldName); - Assert.Equal(typeof(List<>).FullName, className); - Assert.Equal(typeof(SimpleTrade).ToString(), contentClassName); - } - - [Fact] - public void ShouldThrowAnExceptionWhenKeyClassIdIsNotPresentWhenClassIdIsAMap() - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(_headers); - accessor.SetHeader(_typeMapper.ClassIdFieldName, typeof(Dictionary<,>).FullName); - accessor.SetHeader(_typeMapper.KeyClassIdFieldName, typeof(string).ToString()); - - var exception = Assert.Throws(() => _typeMapper.ToType(accessor.MessageHeaders)); - Assert.Contains("Could not resolve ", exception.Message, StringComparison.Ordinal); - } - - [Fact] - public void ShouldLookInTheValueClassIdFieldNameToFindTheValueClassIdWhenClassIdIsAMap() - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(_headers); - accessor.SetHeader("keyType", typeof(int).ToString()); - accessor.SetHeader(_typeMapper.ContentClassIdFieldName, typeof(string).ToString()); - accessor.SetHeader(_typeMapper.ClassIdFieldName, typeof(Dictionary<,>).FullName); - _typeMapper.KeyClassIdFieldName = "keyType"; - - var type = _typeMapper.ToType(accessor.MessageHeaders); - Assert.Equal(typeof(Dictionary), type); - } - - [Fact] - public void ShouldUseTheKeyClassProvidedByTheLookupMapIfPresent() - { - MessageHeaderAccessor accessor = MessageHeaderAccessor.GetMutableAccessor(_headers); - accessor.SetHeader("__KeyTypeId__", "trade"); - accessor.SetHeader(_typeMapper.ContentClassIdFieldName, typeof(string).ToString()); - accessor.SetHeader(_typeMapper.ClassIdFieldName, typeof(Dictionary<,>).FullName); - - var mapping = new Dictionary - { - { "trade", typeof(SimpleTrade) }, - { _typeMapper.ClassIdFieldName, typeof(Dictionary<,>) }, - { _typeMapper.ContentClassIdFieldName, typeof(string) } - }; - - _typeMapper.SetIdClassMapping(mapping); - - var type = _typeMapper.ToType(accessor.MessageHeaders); - Assert.Equal(typeof(Dictionary), type); - } - - [Fact] - public void FromTypeShouldPopulateWithKeyTypeAndContentTypeNameByDefault() - { - _typeMapper.FromType(typeof(Dictionary), _headers); - - string className = _headers.Get(_typeMapper.ClassIdFieldName); - string contentClassName = _headers.Get(_typeMapper.ContentClassIdFieldName); - string keyClassName = _headers.Get(_typeMapper.KeyClassIdFieldName); - Assert.Equal(typeof(Dictionary<,>).FullName, className); - Assert.Equal(typeof(SimpleTrade).ToString(), keyClassName); - Assert.Equal(typeof(string).ToString(), contentClassName); - } - - [Fact] - public void RoundTrip() - { - _typeMapper.FromType(typeof(Dictionary), _headers); - var type = _typeMapper.ToType(_headers); - Assert.Equal(typeof(Dictionary), type); - } -} diff --git a/src/Messaging/test/Messaging.Test/Converter/GenericMessageConverterTest.cs b/src/Messaging/test/Messaging.Test/Converter/GenericMessageConverterTest.cs deleted file mode 100644 index a5de87c8e1..0000000000 --- a/src/Messaging/test/Messaging.Test/Converter/GenericMessageConverterTest.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using Steeltoe.Common.Converter; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Converter; - -public sealed class GenericMessageConverterTest -{ - private readonly IConversionService _conversionService = new DefaultConversionService(); - - [Fact] - public void FromMessageWithConversion() - { - var converter = new GenericMessageConverter(_conversionService); - IMessage content = MessageBuilder.WithPayload("33").Build(); - Assert.Equal(33, converter.FromMessage(content)); - } - - [Fact] - public void FromMessageNoConverter() - { - var converter = new GenericMessageConverter(_conversionService); - IMessage content = MessageBuilder.WithPayload(1234L).Build(); - Assert.Null(converter.FromMessage(content)); - } - - [Fact] - public void FromMessageWithFailedConversion() - { - var converter = new GenericMessageConverter(_conversionService); - IMessage content = MessageBuilder.WithPayload("test not a number").Build(); - Assert.Throws(() => converter.FromMessage(content)); - } -} diff --git a/src/Messaging/test/Messaging.Test/Converter/MessageConverterTest.cs b/src/Messaging/test/Messaging.Test/Converter/MessageConverterTest.cs deleted file mode 100644 index 3d512301c6..0000000000 --- a/src/Messaging/test/Messaging.Test/Converter/MessageConverterTest.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Converter; - -public sealed class MessageConverterTest -{ - [Fact] - public void SupportsTargetClass() - { - IMessage message = MessageBuilder.WithPayload("ABC").Build(); - var converter = new TestMessageConverter(); - Assert.Equal("success-from", converter.FromMessage(message, typeof(string))); - - Assert.Null(converter.FromMessage(message, typeof(int))); - } - - [Fact] - public void SupportsMimeType() - { - IMessage message = MessageBuilder.WithPayload("ABC").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build(); - var converter = new TestMessageConverter(); - Assert.Equal("success-from", converter.FromMessage(message, typeof(string))); - } - - [Fact] - public void SupportsMimeTypeNotSupported() - { - IMessage message = MessageBuilder.WithPayload("ABC").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.ApplicationJson).Build(); - var converter = new TestMessageConverter(); - Assert.Null(converter.FromMessage(message, typeof(string))); - } - - [Fact] - public void SupportsMimeTypeNotSpecified() - { - IMessage message = MessageBuilder.WithPayload("ABC").Build(); - var converter = new TestMessageConverter(); - Assert.Equal("success-from", converter.FromMessage(message, typeof(string))); - } - - [Fact] - public void SupportsMimeTypeNoneConfigured() - { - IMessage message = MessageBuilder.WithPayload("ABC").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.ApplicationJson).Build(); - var converter = new TestMessageConverter(new List()); - - Assert.Equal("success-from", converter.FromMessage(message, typeof(string))); - } - - [Fact] - public void CanConvertFromStrictContentTypeMatch() - { - var converter = new TestMessageConverter(new List - { - MimeTypeUtils.TextPlain - }) - { - StrictContentTypeMatch = true - }; - - IMessage message = MessageBuilder.WithPayload("ABC").Build(); - Assert.False(converter.CanConvertFrom(message, typeof(string))); - - message = MessageBuilder.WithPayload("ABC").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build(); - Assert.True(converter.CanConvertFrom(message, typeof(string))); - } - - [Fact] - public void SetStrictContentTypeMatchWithNoSupportedMimeTypes() - { - var converter = new TestMessageConverter(new List()); - Assert.Throws(() => converter.StrictContentTypeMatch = true); - } - - [Fact] - public void ToMessageWithHeaders() - { - var map = new Dictionary - { - { "foo", "bar" } - }; - - var headers = new MessageHeaders(map); - var converter = new TestMessageConverter(); - IMessage message = converter.ToMessage("ABC", headers); - - Assert.NotNull(message.Headers.Id); - Assert.NotNull(message.Headers.Timestamp); - Assert.Equal(MimeTypeUtils.TextPlain, message.Headers[MessageHeaders.ContentType]); - Assert.Equal("bar", message.Headers["foo"]); - } - - [Fact] - public void ToMessageWithMutableMessageHeaders() - { - var accessor = new MessageHeaderAccessor(); - accessor.SetHeader("foo", "bar"); - accessor.LeaveMutable = true; - - IMessageHeaders headers = accessor.MessageHeaders; - var converter = new TestMessageConverter(); - IMessage message = converter.ToMessage("ABC", headers); - - Assert.Same(headers, message.Headers); - Assert.Null(message.Headers.Id); - Assert.Null(message.Headers.Timestamp); - Assert.Equal(MimeTypeUtils.TextPlain, message.Headers[MessageHeaders.ContentType]); - } - - [Fact] - public void ToMessageContentTypeHeader() - { - var converter = new TestMessageConverter(); - IMessage message = converter.ToMessage("ABC", null); - Assert.Equal(MimeTypeUtils.TextPlain, message.Headers[MessageHeaders.ContentType]); - } - - private sealed class TestMessageConverter : AbstractMessageConverter - { - public override string ServiceName { get; set; } = nameof(TestMessageConverter); - - public TestMessageConverter() - : base(MimeTypeUtils.TextPlain) - { - } - - public TestMessageConverter(ICollection supportedMimeTypes) - : base(supportedMimeTypes) - { - } - - protected override bool Supports(Type type) - { - return type == typeof(string); - } - - protected override object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - return "success-from"; - } - - protected override object ConvertToInternal(object payload, IMessageHeaders headers, object conversionHint) - { - return "success-to"; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Converter/NewtonJsonMessageConverterTest.cs b/src/Messaging/test/Messaging.Test/Converter/NewtonJsonMessageConverterTest.cs deleted file mode 100644 index 6e3951e04d..0000000000 --- a/src/Messaging/test/Messaging.Test/Converter/NewtonJsonMessageConverterTest.cs +++ /dev/null @@ -1,326 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Converter; - -public sealed class NewtonJsonMessageConverterTest -{ - [Fact] - public void DefaultConstructor() - { - var converter = new NewtonJsonMessageConverter(); - Assert.Contains(new MimeType("application", "json", Encoding.UTF8), converter.SupportedMimeTypes); - Assert.Equal(MissingMemberHandling.Ignore, converter.Settings.MissingMemberHandling); - } - - [Fact] - public void MimetypeParametrizedConstructor() - { - var mimetype = new MimeType("application", "xml", Encoding.UTF8); - var converter = new NewtonJsonMessageConverter(mimetype); - Assert.Contains(mimetype, converter.SupportedMimeTypes); - Assert.Equal(MissingMemberHandling.Ignore, converter.Settings.MissingMemberHandling); - } - - [Fact] - public void MimetypesParametrizedConstructor() - { - var jsonMimetype = new MimeType("application", "json", Encoding.UTF8); - var xmlMimetype = new MimeType("application", "xml", Encoding.UTF8); - var converter = new NewtonJsonMessageConverter(jsonMimetype, xmlMimetype); - Assert.Contains(jsonMimetype, converter.SupportedMimeTypes); - Assert.Contains(xmlMimetype, converter.SupportedMimeTypes); - Assert.Equal(MissingMemberHandling.Ignore, converter.Settings.MissingMemberHandling); - } - - [Fact] - public void FromMessage() - { - var converter = new NewtonJsonMessageConverter(); - - const string payload = "{" + "\"bytes\":\"AQI=\"," + "\"array\":[\"Foo\",\"Bar\"]," + "\"number\":42," + "\"string\":\"Foo\"," + "\"bool\":true," + - "\"fraction\":42.0}"; - - byte[] bytes = Encoding.UTF8.GetBytes(payload); - IMessage message = MessageBuilder.WithPayload(bytes).Build(); - var actual = (MyBean)converter.FromMessage(message, typeof(MyBean)); - - Assert.Equal("Foo", actual.String); - Assert.Equal(42, actual.Number); - Assert.Equal(42F, actual.Fraction); - - Assert.Equal(new[] - { - "Foo", - "Bar" - }, actual.Array); - - Assert.True(actual.Bool); - - Assert.Equal(new byte[] - { - 0x1, - 0x2 - }, actual.Bytes); - } - - [Fact] - public void FromMessageUntyped() - { - var converter = new NewtonJsonMessageConverter(); - const string payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"]," + "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; - byte[] bytes = Encoding.UTF8.GetBytes(payload); - IMessage message = MessageBuilder.WithPayload(bytes).Build(); - - var actual = converter.FromMessage>(message); - - Assert.Equal("Foo", actual["string"]); - Assert.Equal(42L, actual["number"]); - Assert.Equal(42D, (double)actual["fraction"]); - - Assert.Equal(new[] - { - "Foo", - "Bar" - }, ((JArray)actual["array"]).ToObject()); - - Assert.Equal(true, actual["bool"]); - Assert.Equal("AQI=", actual["bytes"]); - } - - [Fact] - public void FromMessageMatchingInstance() - { - var myBean = new MyBean(); - var converter = new NewtonJsonMessageConverter(); - IMessage message = MessageBuilder.WithPayload(myBean).Build(); - Assert.Same(myBean, converter.FromMessage(message, typeof(MyBean))); - } - - [Fact] - public void FromMessageInvalidJson() - { - var converter = new NewtonJsonMessageConverter(); - const string payload = "FooBar"; - byte[] bytes = Encoding.UTF8.GetBytes(payload); - IMessage message = MessageBuilder.WithPayload(bytes).Build(); - Assert.Throws(() => converter.FromMessage(message)); - } - - [Fact] - public void FromMessageValidJsonWithUnknownProperty() - { - var converter = new NewtonJsonMessageConverter(); - const string payload = "{\"string\":\"string\",\"unknownProperty\":\"value\"}"; - byte[] bytes = Encoding.UTF8.GetBytes(payload); - IMessage message = MessageBuilder.WithPayload(bytes).Build(); - var myBean = converter.FromMessage(message); - Assert.Equal("string", myBean.String); - } - - [Fact] - public void FromMessageToList() - { - var converter = new NewtonJsonMessageConverter(); - const string payload = "[1, 2, 3, 4, 5, 6, 7, 8, 9]"; - byte[] bytes = Encoding.UTF8.GetBytes(payload); - IMessage message = MessageBuilder.WithPayload(bytes).Build(); - - ParameterInfo info = GetType().GetMethod(nameof(HandleList), BindingFlags.Instance | BindingFlags.NonPublic).GetParameters()[0]; - object actual = converter.FromMessage(message, typeof(List), info); - - Assert.NotNull(actual); - - Assert.Equal(new List - { - 1L, - 2L, - 3L, - 4L, - 5L, - 6L, - 7L, - 8L, - 9L - }, actual); - } - - [Fact] - public void FromMessageToMessageWithPojo() - { - var converter = new NewtonJsonMessageConverter(); - const string payload = "{\"string\":\"foo\"}"; - byte[] bytes = Encoding.UTF8.GetBytes(payload); - IMessage message = MessageBuilder.WithPayload(bytes).Build(); - - ParameterInfo info = GetType().GetMethod(nameof(HandleMessage), BindingFlags.Instance | BindingFlags.NonPublic).GetParameters()[0]; - object actual = converter.FromMessage(message, typeof(MyBean), info); - - Assert.IsType(actual); - Assert.Equal("foo", ((MyBean)actual).String); - } - - [Fact] - public void ToMessage() - { - var converter = new NewtonJsonMessageConverter(); - - var payload = new MyBean - { - String = "Foo", - Number = 42, - Fraction = 42F, - Array = new[] - { - "Foo", - "Bar" - }, - Bool = true, - Bytes = new byte[] - { - 0x1, - 0x2 - } - }; - - IMessage message = converter.ToMessage(payload, null); - - string actual = Encoding.UTF8.GetString((byte[])message.Payload); - - Assert.Contains("\"string\":\"Foo\"", actual, StringComparison.Ordinal); - Assert.Contains("\"number\":42", actual, StringComparison.Ordinal); - Assert.Contains("\"fraction\":42.0", actual, StringComparison.Ordinal); - Assert.Contains("\"array\":[\"Foo\",\"Bar\"]", actual, StringComparison.Ordinal); - Assert.Contains("\"bool\":true", actual, StringComparison.Ordinal); - Assert.Contains("\"bytes\":\"AQI=\"", actual, StringComparison.Ordinal); - Assert.Equal(new MimeType("application", "json", Encoding.UTF8), message.Headers[MessageHeaders.ContentType]); - } - - [Fact] - public void ToMessageUtf16() - { - var converter = new NewtonJsonMessageConverter(); - var encoding = new UnicodeEncoding(true, false); - var contentType = new MimeType("application", "json", encoding); - - var map = new Dictionary - { - { MessageHeaders.ContentType, contentType } - }; - - var headers = new MessageHeaders(map); - const string payload = "H\u00e9llo W\u00f6rld"; - IMessage message = converter.ToMessage(payload, headers); - string actual = encoding.GetString((byte[])message.Payload); - const string expected = $"\"{payload}\""; - Assert.Equal(expected, actual); - Assert.Equal(contentType, message.Headers[MessageHeaders.ContentType]); - } - - [Fact] - public void ToMessageUtf16String() - { - var converter = new NewtonJsonMessageConverter - { - SerializedPayloadClass = typeof(string) - }; - - var encoding = new UnicodeEncoding(true, false); - var contentType = new MimeType("application", "json", encoding); - - var map = new Dictionary - { - { MessageHeaders.ContentType, contentType } - }; - - var headers = new MessageHeaders(map); - const string payload = "H\u00e9llo W\u00f6rld"; - IMessage message = converter.ToMessage(payload, headers); - - Assert.Equal($"\"{payload}\"", message.Payload); - Assert.Equal(contentType, message.Headers[MessageHeaders.ContentType]); - } - - [Fact] - public void GetIMessageGenericType() - { - Assert.Null(NewtonJsonMessageConverter.GetIMessageGenericType(typeof(T1))); - Assert.Equal(typeof(MyBean), NewtonJsonMessageConverter.GetIMessageGenericType(typeof(T2))); - Assert.Equal(typeof(MyBean), NewtonJsonMessageConverter.GetIMessageGenericType(typeof(T3))); - Assert.Equal(typeof(MyBean), NewtonJsonMessageConverter.GetIMessageGenericType(typeof(T4))); - Assert.Equal(typeof(MyBean), NewtonJsonMessageConverter.GetIMessageGenericType(typeof(IMessage))); - Assert.Equal(typeof(MyBean), NewtonJsonMessageConverter.GetIMessageGenericType(typeof(IMyInterface))); - Assert.Null(NewtonJsonMessageConverter.GetIMessageGenericType(typeof(IMessage))); - } - - internal void HandleList(IList payload) - { - } - - internal void HandleMessage(IMessage message) - { - } - - public interface IMyInterface : IMessage - { - } - - public sealed class T1 : IMessage - { - public object Payload => throw new NotImplementedException(); - - public IMessageHeaders Headers => throw new NotImplementedException(); - } - - public sealed class T2 : IMessage - { - object IMessage.Payload => throw new NotImplementedException(); - - public MyBean Payload => throw new NotImplementedException(); - - public IMessageHeaders Headers => throw new NotImplementedException(); - } - - public sealed class T4 : IMyInterface - { - object IMessage.Payload => throw new NotImplementedException(); - - public T Payload => throw new NotImplementedException(); - - public IMessageHeaders Headers => throw new NotImplementedException(); - } - - public sealed class T3 : IMessage - { - object IMessage.Payload => throw new NotImplementedException(); - - public T Payload => throw new NotImplementedException(); - - public IMessageHeaders Headers => throw new NotImplementedException(); - } - - public sealed class MyBean - { - public byte[] Bytes { get; set; } - - public bool Bool { get; set; } - - public string String { get; set; } - - public string[] Array { get; set; } - - public int Number { get; set; } - - public float Fraction { get; set; } - } -} diff --git a/src/Messaging/test/Messaging.Test/Converter/SimpleMessageConverterTest.cs b/src/Messaging/test/Messaging.Test/Converter/SimpleMessageConverterTest.cs deleted file mode 100644 index 6dcfc7fda0..0000000000 --- a/src/Messaging/test/Messaging.Test/Converter/SimpleMessageConverterTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Converter; - -public sealed class SimpleMessageConverterTest -{ - [Fact] - public void ToMessageWithPayloadAndHeaders() - { - var headers = new MessageHeaders(new Dictionary - { - { "foo", "bar" } - }); - - var converter = new SimpleMessageConverter(); - IMessage message = converter.ToMessage("payload", headers); - - Assert.Equal("payload", message.Payload); - Assert.Equal("bar", message.Headers["foo"]); - } - - [Fact] - public void ToMessageWithPayloadAndMutableHeaders() - { - var accessor = new MessageHeaderAccessor(); - accessor.SetHeader("foo", "bar"); - accessor.LeaveMutable = true; - IMessageHeaders headers = accessor.MessageHeaders; - - var converter = new SimpleMessageConverter(); - IMessage message = converter.ToMessage("payload", headers); - - Assert.Equal("payload", message.Payload); - Assert.Same(headers, message.Headers); - Assert.Equal("bar", message.Headers["foo"]); - } -} diff --git a/src/Messaging/test/Messaging.Test/Converter/SimpleTrade.cs b/src/Messaging/test/Messaging.Test/Converter/SimpleTrade.cs deleted file mode 100644 index ac2ee3c755..0000000000 --- a/src/Messaging/test/Messaging.Test/Converter/SimpleTrade.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Test.Converter; - -public sealed class SimpleTrade -{ - public string Ticker { get; set; } - - public long Quantity { get; set; } - - public decimal Price { get; set; } - - public string OrderType { get; set; } - - public string AccountName { get; set; } - - public bool BuyRequest { get; set; } - - public string UserName { get; set; } - - public string RequestId { get; set; } - - public override int GetHashCode() - { - return HashCode.Combine(AccountName, BuyRequest, OrderType, Price, Quantity, RequestId, Ticker, UserName); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not SimpleTrade other || GetType() != obj.GetType()) - { - return false; - } - -#pragma warning disable S1067 // Expressions should not be too complex - return AccountName == other.AccountName && BuyRequest == other.BuyRequest && OrderType == other.OrderType && Price == other.Price && - Quantity == other.Quantity && RequestId == other.RequestId && Ticker == other.Ticker && UserName == other.UserName; -#pragma warning restore S1067 // Expressions should not be too complex - } -} diff --git a/src/Messaging/test/Messaging.Test/Converter/StringMessageConverterTest.cs b/src/Messaging/test/Messaging.Test/Converter/StringMessageConverterTest.cs deleted file mode 100644 index 960e782f49..0000000000 --- a/src/Messaging/test/Messaging.Test/Converter/StringMessageConverterTest.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Converter; - -public sealed class StringMessageConverterTest -{ - [Fact] - public void FromByteArrayMessage() - { - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("ABC")).SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build(); - var converter = new StringMessageConverter(); - Assert.Equal("ABC", converter.FromMessage(message)); - } - - [Fact] - public void FromStringMessage() - { - IMessage message = MessageBuilder.WithPayload("ABC").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build(); - var converter = new StringMessageConverter(); - Assert.Equal("ABC", converter.FromMessage(message)); - } - - [Fact] - public void FromMessageNoContentTypeHeader() - { - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("ABC")).Build(); - var converter = new StringMessageConverter(); - Assert.Equal("ABC", converter.FromMessage(message)); - } - - [Fact] - public void FromMessageCharset() - { - const string payload = "H\u00e9llo W\u00f6rld"; - byte[] bytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(payload); - - IMessage message = MessageBuilder.WithPayload(bytes) - .SetHeader(MessageHeaders.ContentType, new MimeType("text", "plain", Encoding.GetEncoding("ISO-8859-1"))).Build(); - - var converter = new StringMessageConverter(); - Assert.Equal(payload, converter.FromMessage(message)); - } - - [Fact] - public void FromMessageDefaultCharset() - { - const string payload = "H\u00e9llo W\u00f6rld"; - byte[] bytes = Encoding.UTF8.GetBytes(payload); - IMessage message = MessageBuilder.WithPayload(bytes).Build(); - var converter = new StringMessageConverter(); - Assert.Equal(payload, converter.FromMessage(message)); - } - - [Fact] - public void FromMessageTargetClassNotSupported() - { - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("ABC")).Build(); - var converter = new StringMessageConverter(); - Assert.Null(converter.FromMessage(message)); - } - - [Fact] - public void ToMessage() - { - var map = new Dictionary - { - { MessageHeaders.ContentType, MimeTypeUtils.TextPlain } - }; - - var headers = new MessageHeaders(map); - var converter = new StringMessageConverter(); - IMessage message = converter.ToMessage("ABC", headers); - string result = Encoding.UTF8.GetString((byte[])message.Payload); - Assert.Equal("ABC", result); - } -} diff --git a/src/Messaging/test/Messaging.Test/Core/CachingDestinationResolverTest.cs b/src/Messaging/test/Messaging.Test/Core/CachingDestinationResolverTest.cs deleted file mode 100644 index d1a9238613..0000000000 --- a/src/Messaging/test/Messaging.Test/Core/CachingDestinationResolverTest.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Messaging.Core; -using Xunit; - -namespace Steeltoe.Messaging.Test.Core; - -public sealed class CachingDestinationResolverTest -{ - [Fact] - public void CachedDestination() - { - var resolverMock = new Mock>(); - IDestinationResolver resolver = resolverMock.Object; - var resolverProxy = new CachingDestinationResolverProxy(resolver); - resolverMock.Setup(r => r.ResolveDestination("abcd")).Returns("dcba"); - resolverMock.Setup(r => r.ResolveDestination("1234")).Returns("4321"); - - Assert.Equal("dcba", resolverProxy.ResolveDestination("abcd")); - Assert.Equal("4321", resolverProxy.ResolveDestination("1234")); - Assert.Equal("4321", resolverProxy.ResolveDestination("1234")); - Assert.Equal("dcba", resolverProxy.ResolveDestination("abcd")); - - resolverMock.Verify(r => r.ResolveDestination("abcd"), Times.Once); - resolverMock.Verify(r => r.ResolveDestination("1234"), Times.Once); - } - - [Fact] - public void NullTargetThroughConstructor() - { - Assert.Throws(() => new CachingDestinationResolverProxy(null)); - } -} diff --git a/src/Messaging/test/Messaging.Test/Core/DestinationResolvingMessagingTemplateTest.cs b/src/Messaging/test/Messaging.Test/Core/DestinationResolvingMessagingTemplateTest.cs deleted file mode 100644 index 6ef679533b..0000000000 --- a/src/Messaging/test/Messaging.Test/Core/DestinationResolvingMessagingTemplateTest.cs +++ /dev/null @@ -1,416 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Core; - -public sealed class DestinationResolvingMessagingTemplateTest -{ - private readonly TestDestinationResolvingMessagingTemplate _template; - - private readonly TaskSchedulerSubscribableChannel _myChannel; - - private readonly Dictionary _headers; - - private readonly TestMessagePostProcessor _postProcessor; - - public DestinationResolvingMessagingTemplateTest() - { - var resolver = new TestMessageChannelDestinationResolver(); - - _myChannel = new TaskSchedulerSubscribableChannel(); - resolver.RegisterMessageChannel("myChannel", _myChannel); - - _template = new TestDestinationResolvingMessagingTemplate - { - DestinationResolver = resolver - }; - - _headers = new Dictionary - { - { "key", "value" } - }; - - _postProcessor = new TestMessagePostProcessor(); - } - - [Fact] - public async Task SendAsync() - { - IMessage message = Message.Create("payload"); - await _template.SendAsync("myChannel", message); - - Assert.Same(_myChannel, _template.MessageChannel); - Assert.Same(message, _template.Message); - } - - [Fact] - public void Send() - { - IMessage message = Message.Create("payload"); - _template.Send("myChannel", message); - - Assert.Same(_myChannel, _template.MessageChannel); - Assert.Same(message, _template.Message); - } - - [Fact] - public Task SendAsyncNoDestinationResolver() - { - var template = new TestDestinationResolvingMessagingTemplate(); - return Assert.ThrowsAsync(async () => await template.SendAsync("myChannel", Message.Create("payload"))); - } - - [Fact] - public void SendNoDestinationResolver() - { - var template = new TestDestinationResolvingMessagingTemplate(); - Assert.Throws(() => template.Send("myChannel", Message.Create("payload"))); - } - - [Fact] - public async Task ConvertAndSendAsyncPayload() - { - await _template.ConvertAndSendAsync("myChannel", "payload"); - - Assert.Same(_myChannel, _template.MessageChannel); - Assert.NotNull(_template.Message); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public void ConvertAndSendPayload() - { - _template.ConvertAndSend("myChannel", "payload"); - - Assert.Same(_myChannel, _template.MessageChannel); - Assert.NotNull(_template.Message); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public async Task ConvertAndSendAsyncPayloadAndHeaders() - { - await _template.ConvertAndSendAsync("myChannel", "payload", _headers); - Assert.Same(_myChannel, _template.MessageChannel); - Assert.NotNull(_template.Message); - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public void ConvertAndSendPayloadAndHeaders() - { - _template.ConvertAndSend("myChannel", "payload", _headers); - Assert.Same(_myChannel, _template.MessageChannel); - Assert.NotNull(_template.Message); - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public async Task ConvertAndSendAsyncPayloadWithPostProcessor() - { - await _template.ConvertAndSendAsync("myChannel", "payload", _postProcessor); - - Assert.Same(_myChannel, _template.MessageChannel); - Assert.NotNull(_template.Message); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_postProcessor.Message, _template.Message); - } - - [Fact] - public void ConvertAndSendPayloadWithPostProcessor() - { - _template.ConvertAndSend("myChannel", "payload", _postProcessor); - - Assert.Same(_myChannel, _template.MessageChannel); - Assert.NotNull(_template.Message); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_postProcessor.Message, _template.Message); - } - - [Fact] - public async Task ConvertAndSendAsyncPayloadAndHeadersWithPostProcessor() - { - await _template.ConvertAndSendAsync("myChannel", "payload", _headers, _postProcessor); - - Assert.Same(_myChannel, _template.MessageChannel); - Assert.NotNull(_template.Message); - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_postProcessor.Message, _template.Message); - } - - [Fact] - public void ConvertAndSendPayloadAndHeadersWithPostProcessor() - { - _template.ConvertAndSend("myChannel", "payload", _headers, _postProcessor); - - Assert.Same(_myChannel, _template.MessageChannel); - Assert.NotNull(_template.Message); - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_postProcessor.Message, _template.Message); - } - - [Fact] - public async Task ReceiveAsync() - { - IMessage expected = Message.Create("payload"); - _template.ReceiveMessage = expected; - IMessage actual = await _template.ReceiveAsync("myChannel"); - - Assert.Same(expected, actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public void Receive() - { - IMessage expected = Message.Create("payload"); - _template.ReceiveMessage = expected; - IMessage actual = _template.Receive("myChannel"); - - Assert.Same(expected, actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public async Task ReceiveAndConvertAsync() - { - IMessage expected = Message.Create("payload"); - _template.ReceiveMessage = expected; - string payload = await _template.ReceiveAndConvertAsync("myChannel"); - - Assert.Equal("payload", payload); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public void ReceiveAndConvert() - { - IMessage expected = Message.Create("payload"); - _template.ReceiveMessage = expected; - string payload = _template.ReceiveAndConvert("myChannel"); - - Assert.Equal("payload", payload); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public async Task SendAndReceiveAsync() - { - IMessage requestMessage = Message.Create("request"); - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - IMessage actual = await _template.SendAndReceiveAsync("myChannel", requestMessage); - - Assert.Equal(requestMessage, _template.Message); - Assert.Same(responseMessage, actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public void SendAndReceive() - { - IMessage requestMessage = Message.Create("request"); - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - IMessage actual = _template.SendAndReceive("myChannel", requestMessage); - - Assert.Equal(requestMessage, _template.Message); - Assert.Same(responseMessage, actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public async Task ConvertSendAndReceiveAsync() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string actual = await _template.ConvertSendAndReceiveAsync("myChannel", "request"); - - Assert.Equal("request", _template.Message.Payload); - Assert.Same("response", actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public void ConvertSendAndReceive() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string actual = _template.ConvertSendAndReceive("myChannel", "request"); - - Assert.Equal("request", _template.Message.Payload); - Assert.Same("response", actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public async Task ConvertSendAndReceiveAsyncWithHeaders() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string actual = await _template.ConvertSendAndReceiveAsync("myChannel", "request", _headers); - - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("request", _template.Message.Payload); - Assert.Same("response", actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public void ConvertSendAndReceiveWithHeaders() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string actual = _template.ConvertSendAndReceive("myChannel", "request", _headers); - - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("request", _template.Message.Payload); - Assert.Same("response", actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public async Task ConvertSendAndReceiveAsyncWithPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string actual = await _template.ConvertSendAndReceiveAsync("myChannel", "request", _postProcessor); - - Assert.Equal("request", _template.Message.Payload); - Assert.Equal("request", _postProcessor.Message.Payload); - Assert.Same("response", actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public void ConvertSendAndReceiveWithPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string actual = _template.ConvertSendAndReceive("myChannel", "request", _postProcessor); - - Assert.Equal("request", _template.Message.Payload); - Assert.Equal("request", _postProcessor.Message.Payload); - Assert.Same("response", actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public async Task ConvertSendAndReceiveAsyncWithHeadersAndPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string actual = await _template.ConvertSendAndReceiveAsync("myChannel", "request", _headers, _postProcessor); - - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("request", _template.Message.Payload); - Assert.Equal("request", _postProcessor.Message.Payload); - Assert.Same("response", actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - [Fact] - public void ConvertSendAndReceiveWithHeadersAndPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string actual = _template.ConvertSendAndReceive("myChannel", "request", _headers, _postProcessor); - - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("request", _template.Message.Payload); - Assert.Equal("request", _postProcessor.Message.Payload); - Assert.Same("response", actual); - Assert.Same(_myChannel, _template.MessageChannel); - } - - internal sealed class TestDestinationResolvingMessagingTemplate : AbstractDestinationResolvingMessagingTemplate - { - public IMessageChannel MessageChannel { get; set; } - - public IMessage Message { get; set; } - - public IMessage ReceiveMessage { get; set; } - - public TestDestinationResolvingMessagingTemplate() - : base(null) - { - } - - protected override Task DoSendAsync(IMessageChannel channel, IMessage message, CancellationToken cancellationToken) - { - MessageChannel = channel; - Message = message; - return Task.CompletedTask; - } - - protected override Task DoReceiveAsync(IMessageChannel channel, CancellationToken cancellationToken) - { - MessageChannel = channel; - return Task.FromResult(ReceiveMessage); - } - - protected override Task DoSendAndReceiveAsync(IMessageChannel channel, IMessage requestMessage, CancellationToken cancellationToken = default) - { - Message = requestMessage; - MessageChannel = channel; - return Task.FromResult(ReceiveMessage); - } - - protected override void DoSend(IMessageChannel channel, IMessage message) - { - MessageChannel = channel; - Message = message; - } - - protected override IMessage DoReceive(IMessageChannel channel) - { - MessageChannel = channel; - return ReceiveMessage; - } - - protected override IMessage DoSendAndReceive(IMessageChannel channel, IMessage requestMessage) - { - Message = requestMessage; - MessageChannel = channel; - return ReceiveMessage; - } - } - - internal sealed class TestMessageChannelDestinationResolver : IDestinationResolver - { - private readonly IDictionary _channels = new Dictionary(); - - public void RegisterMessageChannel(string name, IMessageChannel channel) - { - _channels.Add(name, channel); - } - - public IMessageChannel ResolveDestination(string name) - { - _channels.TryGetValue(name, out IMessageChannel chan); - return chan; - } - - object IDestinationResolver.ResolveDestination(string name) - { - return ResolveDestination(name); - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Core/GenericMessagingTemplateTest.cs b/src/Messaging/test/Messaging.Test/Core/GenericMessagingTemplateTest.cs deleted file mode 100644 index bcb2afb7a3..0000000000 --- a/src/Messaging/test/Messaging.Test/Core/GenericMessagingTemplateTest.cs +++ /dev/null @@ -1,380 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Core; - -public sealed class GenericMessagingTemplateTest -{ - internal MessageChannelTemplate Template { get; } - - internal StubMessageChannel MessageChannel { get; } - - public GenericMessagingTemplateTest() - { - MessageChannel = new StubMessageChannel(); - - Template = new MessageChannelTemplate - { - DefaultSendDestination = MessageChannel, - DestinationResolver = new TestDestinationResolver(this) - }; - } - - [Fact] - public void SendWithTimeout() - { - IMessage sent = null; - - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - chanMock.Setup(chan => chan.Send(It.IsAny(), It.Is(i => i == 30000))).Callback((m, _) => sent = m).Returns(true); - - IMessage message = MessageBuilder.WithPayload("request").SetHeader(MessageChannelTemplate.DefaultSendTimeoutHeader, 30000) - .SetHeader(MessageChannelTemplate.DefaultReceiveTimeoutHeader, 1).Build(); - - Template.Send(channel, message); - - chanMock.Verify(chan => chan.Send(It.IsAny(), It.Is(i => i == 30000))); - Assert.NotNull(sent); - Assert.False(sent.Headers.ContainsKey(MessageChannelTemplate.DefaultSendTimeoutHeader)); - Assert.False(sent.Headers.ContainsKey(MessageChannelTemplate.DefaultReceiveTimeoutHeader)); - } - - [Fact] - public async Task SendAsyncWithTimeout() - { - IMessage sent = null; - - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - - chanMock.Setup(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))) - .Callback((m, _) => sent = m).Returns(new ValueTask(true)); - - IMessage message = MessageBuilder.WithPayload("request").SetHeader(MessageChannelTemplate.DefaultSendTimeoutHeader, 30000) - .SetHeader(MessageChannelTemplate.DefaultReceiveTimeoutHeader, 1).Build(); - - await Template.SendAsync(channel, message); - - chanMock.Verify(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))); - Assert.NotNull(sent); - Assert.False(sent.Headers.ContainsKey(MessageChannelTemplate.DefaultSendTimeoutHeader)); - Assert.False(sent.Headers.ContainsKey(MessageChannelTemplate.DefaultReceiveTimeoutHeader)); - } - - [Fact] - public async Task SendAsyncWithTimeoutMutable() - { - IMessage sent = null; - - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - - chanMock.Setup(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))) - .Callback((m, _) => sent = m).Returns(new ValueTask(true)); - - var accessor = new MessageHeaderAccessor - { - LeaveMutable = true - }; - - IMessage message = Message.Create("request", accessor.MessageHeaders); - accessor.SetHeader(MessageChannelTemplate.DefaultSendTimeoutHeader, 30000); - await Template.SendAsync(channel, message); - chanMock.Verify(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))); - Assert.NotNull(sent); - Assert.False(sent.Headers.ContainsKey(MessageChannelTemplate.DefaultSendTimeoutHeader)); - Assert.False(sent.Headers.ContainsKey(MessageChannelTemplate.DefaultReceiveTimeoutHeader)); - } - - [Fact] - public void SendWithTimeoutMutable() - { - IMessage sent = null; - - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - chanMock.Setup(chan => chan.Send(It.IsAny(), It.Is(i => i == 30000))).Callback((m, _) => sent = m).Returns(true); - - var accessor = new MessageHeaderAccessor - { - LeaveMutable = true - }; - - IMessage message = Message.Create("request", accessor.MessageHeaders); - accessor.SetHeader(MessageChannelTemplate.DefaultSendTimeoutHeader, 30000); - Template.Send(channel, message); - chanMock.Verify(chan => chan.Send(It.IsAny(), It.Is(i => i == 30000))); - Assert.NotNull(sent); - Assert.False(sent.Headers.ContainsKey(MessageChannelTemplate.DefaultSendTimeoutHeader)); - Assert.False(sent.Headers.ContainsKey(MessageChannelTemplate.DefaultReceiveTimeoutHeader)); - } - - [Fact] - public void SendAndReceive() - { - var channel = new TaskSchedulerSubscribableChannel(TaskScheduler.Default); - channel.Subscribe(new SendAndReceiveTestHandler()); - - string actual = Template.ConvertSendAndReceive(channel, "request"); - Assert.Equal("response", actual); - } - - [Fact] - public async Task SendAndReceiveAsync() - { - var channel = new TaskSchedulerSubscribableChannel(TaskScheduler.Default); - channel.Subscribe(new SendAndReceiveTestHandler()); - - string actual = await Template.ConvertSendAndReceiveAsync(channel, "request"); - Assert.Equal("response", actual); - } - - [Fact] - public void SendAndReceiveTimeout() - { - var latch = new CountdownEvent(1); - - Template.ReceiveTimeout = 1; - Template.SendTimeout = 30000; - Template.ThrowExceptionOnLateReply = true; - - var handler = new LateReplierMessageHandler(latch); - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - - chanMock.Setup(chan => chan.Send(It.IsAny(), It.Is(i => i == 30000))).Callback((m, _) => - { - Task.Run(() => handler.HandleMessage(m)); - }).Returns(true); - - string result = Template.ConvertSendAndReceive(channel, "request"); - Assert.Null(result); - Assert.True(latch.Wait(10000)); - Assert.Null(handler.Failure); - - chanMock.Verify(chan => chan.Send(It.IsAny(), It.Is(i => i == 30000))); - } - - [Fact] - public async Task SendAndReceiveAsyncTimeout() - { - var latch = new CountdownEvent(1); - - Template.ReceiveTimeout = 1; - Template.SendTimeout = 30000; - Template.ThrowExceptionOnLateReply = true; - - var handler = new LateReplierMessageHandler(latch); - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - - chanMock.Setup(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))) - .Callback((m, t) => - { - Task.Run(() => handler.HandleMessage(m), t); - }).Returns(new ValueTask(true)); - - string result = await Template.ConvertSendAndReceiveAsync(channel, "request"); - Assert.Null(result); - Assert.True(latch.Wait(10000)); - Assert.Null(handler.Failure); - - chanMock.Verify(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))); - } - - [Fact] - public void SendAndReceiveVariableTimeout() - { - var latch = new CountdownEvent(1); - Template.ReceiveTimeout = 10000; - Template.SendTimeout = 20000; - Template.ThrowExceptionOnLateReply = true; - - var handler = new LateReplierMessageHandler(latch); - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - - chanMock.Setup(chan => chan.Send(It.IsAny(), It.IsAny())).Callback((m, _) => - { - Task.Run(() => handler.HandleMessage(m)); - }).Returns(true); - - IMessage message = MessageBuilder.WithPayload("request").SetHeader(MessageChannelTemplate.DefaultSendTimeoutHeader, 30000) - .SetHeader(MessageChannelTemplate.DefaultReceiveTimeoutHeader, 1).Build(); - - IMessage result = Template.SendAndReceive(channel, message); - Assert.Null(result); - Assert.True(latch.Wait(10000)); - Assert.Null(handler.Failure); - - chanMock.Verify(chan => chan.Send(It.IsAny(), It.Is(i => i == 30000))); - } - - [Fact] - public async Task SendAndReceiveAsyncVariableTimeout() - { - var latch = new CountdownEvent(1); - Template.ReceiveTimeout = 10000; - Template.SendTimeout = 20000; - Template.ThrowExceptionOnLateReply = true; - - var handler = new LateReplierMessageHandler(latch); - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - - chanMock.Setup(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))) - .Callback((m, t) => - { - Task.Run(() => handler.HandleMessage(m), t); - }).Returns(new ValueTask(true)); - - IMessage message = MessageBuilder.WithPayload("request").SetHeader(MessageChannelTemplate.DefaultSendTimeoutHeader, 30000) - .SetHeader(MessageChannelTemplate.DefaultReceiveTimeoutHeader, 1).Build(); - - IMessage result = await Template.SendAndReceiveAsync(channel, message); - Assert.Null(result); - Assert.True(latch.Wait(10000)); - Assert.Null(handler.Failure); - - chanMock.Verify(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))); - } - - [Fact] - public void SendAndReceiveVariableTimeoutCustomHeaders() - { - var latch = new CountdownEvent(1); - Template.ReceiveTimeout = 10000; - Template.SendTimeout = 20000; - Template.ThrowExceptionOnLateReply = true; - Template.SendTimeoutHeader = "sto"; - Template.ReceiveTimeoutHeader = "rto"; - - var handler = new LateReplierMessageHandler(latch); - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - - chanMock.Setup(chan => chan.Send(It.IsAny(), It.IsAny())).Callback((m, _) => - { - Task.Run(() => handler.HandleMessage(m)); - }).Returns(true); - - IMessage message = MessageBuilder.WithPayload("request").SetHeader("sto", 30000).SetHeader("rto", 1).Build(); - - IMessage result = Template.SendAndReceive(channel, message); - Assert.Null(result); - Assert.True(latch.Wait(10000)); - Assert.Null(handler.Failure); - - chanMock.Verify(chan => chan.Send(It.IsAny(), It.Is(i => i == 30000))); - } - - [Fact] - public async Task SendAndReceiveAsyncVariableTimeoutCustomHeaders() - { - var latch = new CountdownEvent(1); - Template.ReceiveTimeout = 10000; - Template.SendTimeout = 20000; - Template.ThrowExceptionOnLateReply = true; - Template.SendTimeoutHeader = "sto"; - Template.ReceiveTimeoutHeader = "rto"; - - var handler = new LateReplierMessageHandler(latch); - var chanMock = new Mock(); - ISubscribableChannel channel = chanMock.Object; - - chanMock.Setup(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))) - .Callback((m, t) => - { - Task.Run(() => handler.HandleMessage(m), t); - }).Returns(new ValueTask(true)); - - IMessage message = MessageBuilder.WithPayload("request").SetHeader("sto", 30000).SetHeader("rto", 1).Build(); - - IMessage result = await Template.SendAndReceiveAsync(channel, message); - Assert.Null(result); - Assert.True(latch.Wait(10000)); - Assert.Null(handler.Failure); - - chanMock.Verify(chan => chan.SendAsync(It.IsAny(), It.Is(t => !t.IsCancellationRequested))); - } - - internal sealed class LateReplierMessageHandler : IMessageHandler - { - public CountdownEvent Latch { get; } - public Exception Failure { get; private set; } - - public string ServiceName { get; set; } = nameof(LateReplierMessageHandler); - - public LateReplierMessageHandler(CountdownEvent latch) - { - Latch = latch; - } - - public void HandleMessage(IMessage message) - { - try - { - Thread.Sleep(1000); - var replyChannel = (IMessageChannel)message.Headers.ReplyChannel; - replyChannel.Send(Message.Create("response")); - Failure = new InvalidOperationException("Expected exception"); - } - catch (MessageDeliveryException ex) - { - const string expected = "Reply message received but the receiving thread has exited due to a timeout"; - string actual = ex.Message; - - if (actual != expected) - { - Failure = new InvalidOperationException($"Unexpected error: '{actual}'"); - } - } - catch (Exception e) - { - Failure = e; - } - finally - { - Latch.Signal(); - } - } - } - - internal sealed class SendAndReceiveTestHandler : IMessageHandler - { - public string ServiceName { get; set; } = nameof(SendAndReceiveTestHandler); - - public void HandleMessage(IMessage message) - { - var replyChannel = (IMessageChannel)message.Headers.ReplyChannel; - replyChannel.Send(Message.Create("response")); - } - } - - internal sealed class TestDestinationResolver : IDestinationResolver - { - private readonly GenericMessagingTemplateTest _test; - - public TestDestinationResolver(GenericMessagingTemplateTest test) - { - _test = test; - } - - public IMessageChannel ResolveDestination(string name) - { - return _test.MessageChannel; - } - - object IDestinationResolver.ResolveDestination(string name) - { - return ResolveDestination(name); - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Core/MessageReceivingTemplateTest.cs b/src/Messaging/test/Messaging.Test/Core/MessageReceivingTemplateTest.cs deleted file mode 100644 index c0318d4257..0000000000 --- a/src/Messaging/test/Messaging.Test/Core/MessageReceivingTemplateTest.cs +++ /dev/null @@ -1,218 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Converter; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Core; -using Xunit; - -namespace Steeltoe.Messaging.Test.Core; - -public sealed class MessageReceivingTemplateTest -{ - private readonly TestMessagingTemplate _template; - - public MessageReceivingTemplateTest() - { - _template = new TestMessagingTemplate(); - } - - [Fact] - public async Task ReceiveAsync() - { - IMessage expected = Message.Create("payload"); - _template.DefaultReceiveDestination = "home"; - _template.ReceiveMessage = expected; - IMessage actual = await _template.ReceiveAsync(); - - Assert.Equal("home", _template.Destination); - Assert.Same(expected, actual); - } - - [Fact] - public void Receive() - { - IMessage expected = Message.Create("payload"); - _template.DefaultReceiveDestination = "home"; - _template.ReceiveMessage = expected; - IMessage actual = _template.Receive(); - - Assert.Equal("home", _template.Destination); - Assert.Same(expected, actual); - } - - [Fact] - public async Task ReceiveAsyncMissingDefaultDestination() - { - await Assert.ThrowsAsync(async () => await _template.ReceiveAsync()); - } - - [Fact] - public void ReceiveMissingDefaultDestination() - { - Assert.Throws(() => _template.Receive()); - } - - [Fact] - public async Task ReceiveAsyncFromDestination() - { - IMessage expected = Message.Create("payload"); - _template.ReceiveMessage = expected; - IMessage actual = await _template.ReceiveAsync("somewhere"); - - Assert.Equal("somewhere", _template.Destination); - Assert.Same(expected, actual); - } - - [Fact] - public void ReceiveFromDestination() - { - IMessage expected = Message.Create("payload"); - _template.ReceiveMessage = expected; - IMessage actual = _template.Receive("somewhere"); - - Assert.Equal("somewhere", _template.Destination); - Assert.Same(expected, actual); - } - - [Fact] - public async Task ReceiveAsyncAndConvert() - { - IMessage expected = Message.Create("payload"); - _template.DefaultReceiveDestination = "home"; - _template.ReceiveMessage = expected; - string payload = await _template.ReceiveAndConvertAsync(); - Assert.Equal("home", _template.Destination); - Assert.Same("payload", payload); - } - - [Fact] - public void ReceiveAndConvert() - { - IMessage expected = Message.Create("payload"); - _template.DefaultReceiveDestination = "home"; - _template.ReceiveMessage = expected; - string payload = _template.ReceiveAndConvert(); - Assert.Equal("home", _template.Destination); - Assert.Same("payload", payload); - } - - [Fact] - public async Task ReceiveAndConvertAsyncFromDestination() - { - IMessage expected = Message.Create("payload"); - _template.ReceiveMessage = expected; - string payload = await _template.ReceiveAndConvertAsync("somewhere"); - Assert.Equal("somewhere", _template.Destination); - Assert.Same("payload", payload); - } - - [Fact] - public void ReceiveAndConvertFromDestination() - { - IMessage expected = Message.Create("payload"); - _template.ReceiveMessage = expected; - string payload = _template.ReceiveAndConvert("somewhere"); - Assert.Equal("somewhere", _template.Destination); - Assert.Same("payload", payload); - } - - [Fact] - public async Task ReceiveAndConvertAsyncFailed() - { - IMessage expected = Message.Create("not a number test"); - _template.ReceiveMessage = expected; - _template.MessageConverter = new GenericMessageConverter(); - var ext = await Assert.ThrowsAsync(async () => await _template.ReceiveAndConvertAsync("somewhere")); - Assert.IsType(ext.InnerException); - } - - [Fact] - public void ReceiveAndConvertFailed() - { - IMessage expected = Message.Create("not a number test"); - _template.ReceiveMessage = expected; - _template.MessageConverter = new GenericMessageConverter(); - var ext = Assert.Throws(() => _template.ReceiveAndConvert("somewhere")); - Assert.IsType(ext.InnerException); - } - - [Fact] - public void ReceiveAndConvertNoConverter() - { - IMessage expected = Message.Create("payload"); - _template.DefaultReceiveDestination = "home"; - _template.ReceiveMessage = expected; - _template.MessageConverter = new GenericMessageConverter(); - - try - { - _template.ReceiveAndConvert(); - } - catch (MessageConversionException ex) - { - Assert.Contains("payload", ex.Message, StringComparison.Ordinal); - Assert.Same(expected, ex.FailedMessage); - } - } - - [Fact] - public async Task ReceiveAndConvertAsyncNoConverter() - { - IMessage expected = Message.Create("payload"); - _template.DefaultReceiveDestination = "home"; - _template.ReceiveMessage = expected; - _template.MessageConverter = new GenericMessageConverter(); - - try - { - await _template.ReceiveAndConvertAsync(); - } - catch (MessageConversionException ex) - { - Assert.Contains("payload", ex.Message, StringComparison.Ordinal); - Assert.Same(expected, ex.FailedMessage); - } - } - - internal sealed class TestMessagingTemplate : AbstractMessagingTemplate - { - public string Destination { get; set; } - - public IMessage ReceiveMessage { get; set; } - - protected override Task DoSendAsync(string destination, IMessage message, CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - protected override Task DoReceiveAsync(string destination, CancellationToken cancellationToken) - { - Destination = destination; - return Task.FromResult(ReceiveMessage); - } - - protected override Task DoSendAndReceiveAsync(string destination, IMessage requestMessage, CancellationToken cancellationToken = default) - { - Destination = destination; - return Task.FromResult((IMessage)null); - } - - protected override void DoSend(string destination, IMessage message) - { - } - - protected override IMessage DoReceive(string destination) - { - Destination = destination; - return ReceiveMessage; - } - - protected override IMessage DoSendAndReceive(string destination, IMessage requestMessage) - { - Destination = destination; - return null; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Core/MessageRequestReplyTemplateTest.cs b/src/Messaging/test/Messaging.Test/Core/MessageRequestReplyTemplateTest.cs deleted file mode 100644 index 39edcdff3a..0000000000 --- a/src/Messaging/test/Messaging.Test/Core/MessageRequestReplyTemplateTest.cs +++ /dev/null @@ -1,286 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Core; -using Xunit; - -namespace Steeltoe.Messaging.Test.Core; - -public sealed class MessageRequestReplyTemplateTest -{ - private readonly TestMessagingTemplate _template; - - private readonly TestMessagePostProcessor _postProcessor; - - private readonly Dictionary _headers; - - public MessageRequestReplyTemplateTest() - { - _template = new TestMessagingTemplate(); - _postProcessor = new TestMessagePostProcessor(); - - _headers = new Dictionary - { - { "key", "value" } - }; - } - - [Fact] - public async Task SendAndReceiveAsync() - { - IMessage requestMessage = Message.Create("request"); - IMessage responseMessage = Message.Create("response"); - _template.DefaultSendDestination = "home"; - _template.ReceiveMessage = responseMessage; - IMessage actual = await _template.SendAndReceiveAsync(requestMessage); - - Assert.Equal("home", _template.Destination); - Assert.Same(requestMessage, _template.RequestMessage); - Assert.Same(responseMessage, actual); - } - - [Fact] - public void SendAndReceive() - { - IMessage requestMessage = Message.Create("request"); - IMessage responseMessage = Message.Create("response"); - _template.DefaultSendDestination = "home"; - _template.ReceiveMessage = responseMessage; - IMessage actual = _template.SendAndReceive(requestMessage); - - Assert.Equal("home", _template.Destination); - Assert.Same(requestMessage, _template.RequestMessage); - Assert.Same(responseMessage, actual); - } - - [Fact] - public async Task SendAndReceiveAsyncMissingDestination() - { - await Assert.ThrowsAsync(async () => await _template.SendAndReceiveAsync(Message.Create("request"))); - } - - [Fact] - public void SendAndReceiveMissingDestination() - { - Assert.Throws(() => _template.SendAndReceive(Message.Create("request"))); - } - - [Fact] - public async Task SendAndReceiveAsyncToDestination() - { - IMessage requestMessage = Message.Create("request"); - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - IMessage actual = await _template.SendAndReceiveAsync("somewhere", requestMessage); - Assert.Equal("somewhere", _template.Destination); - Assert.Same(requestMessage, _template.RequestMessage); - Assert.Same(responseMessage, actual); - } - - [Fact] - public void SendAndReceiveToDestination() - { - IMessage requestMessage = Message.Create("request"); - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - IMessage actual = _template.SendAndReceive("somewhere", requestMessage); - Assert.Equal("somewhere", _template.Destination); - Assert.Same(requestMessage, _template.RequestMessage); - Assert.Same(responseMessage, actual); - } - - [Fact] - public async Task ConvertAndSendAsync() - { - IMessage responseMessage = Message.Create("response"); - _template.DefaultSendDestination = "home"; - _template.ReceiveMessage = responseMessage; - - string response = await _template.ConvertSendAndReceiveAsync("request"); - Assert.Equal("home", _template.Destination); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - } - - [Fact] - public void ConvertAndSend() - { - IMessage responseMessage = Message.Create("response"); - _template.DefaultSendDestination = "home"; - _template.ReceiveMessage = responseMessage; - - string response = _template.ConvertSendAndReceive("request"); - Assert.Equal("home", _template.Destination); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - } - - [Fact] - public async Task ConvertAndSendAsyncToDestination() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string response = await _template.ConvertSendAndReceiveAsync("somewhere", "request"); - Assert.Equal("somewhere", _template.Destination); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - } - - [Fact] - public void ConvertAndSendToDestination() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string response = _template.ConvertSendAndReceive("somewhere", "request"); - Assert.Equal("somewhere", _template.Destination); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - } - - [Fact] - public async Task ConvertAndSendAsyncToDestinationWithHeaders() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string response = await _template.ConvertSendAndReceiveAsync("somewhere", "request", _headers); - Assert.Equal("somewhere", _template.Destination); - Assert.Equal("value", _template.RequestMessage.Headers["key"]); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - } - - [Fact] - public void ConvertAndSendToDestinationWithHeaders() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string response = _template.ConvertSendAndReceive("somewhere", "request", _headers); - Assert.Equal("somewhere", _template.Destination); - Assert.Equal("value", _template.RequestMessage.Headers["key"]); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - } - - [Fact] - public async Task ConvertAndSendAsyncWithPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.DefaultSendDestination = "home"; - _template.ReceiveMessage = responseMessage; - string response = await _template.ConvertSendAndReceiveAsync((object)"request", _postProcessor); - - Assert.Equal("home", _template.Destination); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - Assert.Same(_postProcessor.Message, _template.RequestMessage); - } - - [Fact] - public void ConvertAndSendWithPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.DefaultSendDestination = "home"; - _template.ReceiveMessage = responseMessage; - string response = _template.ConvertSendAndReceive((object)"request", _postProcessor); - - Assert.Equal("home", _template.Destination); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - Assert.Same(_postProcessor.Message, _template.RequestMessage); - } - - [Fact] - public async Task ConvertAndSendAsyncToDestinationWithPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string response = await _template.ConvertSendAndReceiveAsync("somewhere", "request", _postProcessor); - Assert.Equal("somewhere", _template.Destination); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - Assert.Same(_postProcessor.Message, _template.RequestMessage); - } - - [Fact] - public void ConvertAndSendToDestinationWithPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string response = _template.ConvertSendAndReceive("somewhere", "request", _postProcessor); - Assert.Equal("somewhere", _template.Destination); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - Assert.Same(_postProcessor.Message, _template.RequestMessage); - } - - [Fact] - public async Task ConvertAndSendAsyncToDestinationWithHeadersAndPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string response = await _template.ConvertSendAndReceiveAsync("somewhere", "request", _headers, _postProcessor); - Assert.Equal("somewhere", _template.Destination); - Assert.Equal("value", _template.RequestMessage.Headers["key"]); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - Assert.Same(_postProcessor.Message, _template.RequestMessage); - } - - [Fact] - public void ConvertAndSendToDestinationWithHeadersAndPostProcessor() - { - IMessage responseMessage = Message.Create("response"); - _template.ReceiveMessage = responseMessage; - string response = _template.ConvertSendAndReceive("somewhere", "request", _headers, _postProcessor); - Assert.Equal("somewhere", _template.Destination); - Assert.Equal("value", _template.RequestMessage.Headers["key"]); - Assert.Same("request", _template.RequestMessage.Payload); - Assert.Same("response", response); - Assert.Same(_postProcessor.Message, _template.RequestMessage); - } - - internal sealed class TestMessagingTemplate : AbstractMessagingTemplate - { - public string Destination { get; set; } - - public IMessage RequestMessage { get; set; } - - public IMessage ReceiveMessage { get; set; } - - protected override Task DoSendAsync(string destination, IMessage message, CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - protected override Task DoReceiveAsync(string destination, CancellationToken cancellationToken) - { - Destination = destination; - return Task.FromResult(ReceiveMessage); - } - - protected override Task DoSendAndReceiveAsync(string destination, IMessage requestMessage, CancellationToken cancellationToken = default) - { - Destination = destination; - RequestMessage = requestMessage; - return Task.FromResult(ReceiveMessage); - } - - protected override void DoSend(string destination, IMessage message) - { - } - - protected override IMessage DoReceive(string destination) - { - Destination = destination; - return ReceiveMessage; - } - - protected override IMessage DoSendAndReceive(string destination, IMessage requestMessage) - { - Destination = destination; - RequestMessage = requestMessage; - return ReceiveMessage; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Core/MessageSendingTemplateTest.cs b/src/Messaging/test/Messaging.Test/Core/MessageSendingTemplateTest.cs deleted file mode 100644 index 60bb8d3d37..0000000000 --- a/src/Messaging/test/Messaging.Test/Core/MessageSendingTemplateTest.cs +++ /dev/null @@ -1,325 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Support; -using Xunit; -using HeadersDictionary = System.Collections.Generic.IDictionary; - -namespace Steeltoe.Messaging.Test.Core; - -public sealed class MessageSendingTemplateTest -{ - private readonly TestMessageSendingTemplate _template; - - private readonly TestMessagePostProcessor _postProcessor; - - private readonly Dictionary _headers; - - public MessageSendingTemplateTest() - { - _template = new TestMessageSendingTemplate(); - _postProcessor = new TestMessagePostProcessor(); - - _headers = new Dictionary - { - { "key", "value" } - }; - } - - [Fact] - public async Task SendAsync() - { - IMessage message = Message.Create("payload"); - _template.DefaultSendDestination = "home"; - await _template.SendAsync(message); - - Assert.Equal("home", _template.Destination); - Assert.Same(message, _template.Message); - } - - [Fact] - public async Task SendAsyncToDestination() - { - IMessage message = Message.Create("payload"); - await _template.SendAsync("somewhere", message); - - Assert.Equal("somewhere", _template.Destination); - Assert.Same(message, _template.Message); - } - - [Fact] - public async Task SendAsyncMissingDestination() - { - IMessage message = Message.Create("payload"); - await Assert.ThrowsAsync(async () => await _template.SendAsync(message)); - } - - [Fact] - public async Task ConvertAndSendAsync() - { - await _template.ConvertAndSendAsync("somewhere", "payload", _headers, _postProcessor); - - Assert.Equal("somewhere", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_template.Message, _postProcessor.Message); - } - - [Fact] - public async Task ConvertAndSendAsyncPayload() - { - _template.DefaultSendDestination = "home"; - await _template.ConvertAndSendAsync("payload"); - - Assert.Equal("home", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal(2, ((HeadersDictionary)_template.Message.Headers).Count); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public async Task ConvertAndSendAsyncPayloadToDestination() - { - await _template.ConvertAndSendAsync("somewhere", "payload"); - - Assert.Equal("somewhere", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal(2, ((HeadersDictionary)_template.Message.Headers).Count); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public async Task ConvertAndSendAsyncPayloadAndHeadersToDestination() - { - await _template.ConvertAndSendAsync("somewhere", "payload", _headers); - - Assert.Equal("somewhere", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public async Task ConvertAndSendAsyncPayloadAndMutableHeadersToDestination() - { - var accessor = new MessageHeaderAccessor(); - accessor.SetHeader("foo", "bar"); - accessor.LeaveMutable = true; - IMessageHeaders messageHeaders = accessor.MessageHeaders; - - _template.MessageConverter = new StringMessageConverter(); - await _template.ConvertAndSendAsync("somewhere", "payload", messageHeaders); - - IMessageHeaders actual = _template.Message.Headers; - Assert.Same(messageHeaders, actual); - Assert.Equal(new MimeType("text", "plain", Encoding.UTF8), actual[MessageHeaders.ContentType]); - Assert.Equal("bar", actual["foo"]); - } - - [Fact] - public async Task ConvertAndSendAsyncPayloadWithPostProcessor() - { - _template.DefaultSendDestination = "home"; - await _template.ConvertAndSendAsync((object)"payload", _postProcessor); - - Assert.Equal("home", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal(2, ((HeadersDictionary)_template.Message.Headers).Count); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_template.Message, _postProcessor.Message); - } - - [Fact] - public async Task ConvertAndSendAsyncPayloadWithPostProcessorToDestination() - { - await _template.ConvertAndSendAsync("somewhere", "payload", _postProcessor); - - Assert.Equal("somewhere", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal(2, ((HeadersDictionary)_template.Message.Headers).Count); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_template.Message, _postProcessor.Message); - } - - [Fact] - public async Task ConvertAndSendAsyncNoMatchingConverter() - { - var converter = new CompositeMessageConverter(new List - { - new NewtonJsonMessageConverter() - }); - - _template.MessageConverter = converter; - - _headers.Add(MessageHeaders.ContentType, MimeTypeUtils.ApplicationXml); - await Assert.ThrowsAsync(async () => await _template.ConvertAndSendAsync("home", "payload", new MessageHeaders(_headers))); - } - - [Fact] - public void Send() - { - IMessage message = Message.Create("payload"); - _template.DefaultSendDestination = "home"; - _template.Send(message); - - Assert.Equal("home", _template.Destination); - Assert.Same(message, _template.Message); - } - - [Fact] - public void SendToDestination() - { - IMessage message = Message.Create("payload"); - _template.Send("somewhere", message); - - Assert.Equal("somewhere", _template.Destination); - Assert.Same(message, _template.Message); - } - - [Fact] - public void SendMissingDestination() - { - IMessage message = Message.Create("payload"); - Assert.Throws(() => _template.Send(message)); - } - - [Fact] - public void ConvertAndSend() - { - _template.ConvertAndSend("somewhere", "payload", _headers, _postProcessor); - - Assert.Equal("somewhere", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_template.Message, _postProcessor.Message); - } - - [Fact] - public void ConvertAndSendPayload() - { - _template.DefaultSendDestination = "home"; - _template.ConvertAndSend("payload"); - - Assert.Equal("home", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal(2, ((HeadersDictionary)_template.Message.Headers).Count); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public void ConvertAndSendPayloadToDestination() - { - _template.ConvertAndSend("somewhere", "payload"); - - Assert.Equal("somewhere", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal(2, ((HeadersDictionary)_template.Message.Headers).Count); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public void ConvertAndSendPayloadAndHeadersToDestination() - { - _template.ConvertAndSend("somewhere", "payload", _headers); - - Assert.Equal("somewhere", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal("value", _template.Message.Headers["key"]); - Assert.Equal("payload", _template.Message.Payload); - } - - [Fact] - public void ConvertAndSendPayloadAndMutableHeadersToDestination() - { - var accessor = new MessageHeaderAccessor(); - accessor.SetHeader("foo", "bar"); - accessor.LeaveMutable = true; - IMessageHeaders messageHeaders = accessor.MessageHeaders; - - _template.MessageConverter = new StringMessageConverter(); - _template.ConvertAndSend("somewhere", "payload", messageHeaders); - - IMessageHeaders actual = _template.Message.Headers; - Assert.Same(messageHeaders, actual); - Assert.Equal(new MimeType("text", "plain", Encoding.UTF8), actual[MessageHeaders.ContentType]); - Assert.Equal("bar", actual["foo"]); - } - - [Fact] - public void ConvertAndSendPayloadWithPostProcessor() - { - _template.DefaultSendDestination = "home"; - _template.ConvertAndSend((object)"payload", _postProcessor); - - Assert.Equal("home", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal(2, ((HeadersDictionary)_template.Message.Headers).Count); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_template.Message, _postProcessor.Message); - } - - [Fact] - public void ConvertAndSendPayloadWithPostProcessorToDestination() - { - _template.ConvertAndSend("somewhere", "payload", _postProcessor); - - Assert.Equal("somewhere", _template.Destination); - Assert.NotNull(_template.Message); - Assert.Equal(2, ((HeadersDictionary)_template.Message.Headers).Count); - Assert.Equal("payload", _template.Message.Payload); - - Assert.NotNull(_postProcessor.Message); - Assert.Same(_template.Message, _postProcessor.Message); - } - - [Fact] - public void ConvertAndSendNoMatchingConverter() - { - var converter = new CompositeMessageConverter(new List - { - new NewtonJsonMessageConverter() - }); - - _template.MessageConverter = converter; - - _headers.Add(MessageHeaders.ContentType, MimeTypeUtils.ApplicationXml); - Assert.Throws(() => _template.ConvertAndSend("home", "payload", new MessageHeaders(_headers))); - } - - internal sealed class TestMessageSendingTemplate : AbstractMessageSendingTemplate - { - public string Destination { get; set; } - - public IMessage Message { get; set; } - - protected override Task DoSendAsync(string destination, IMessage message, CancellationToken cancellationToken) - { - Destination = destination; - Message = message; - return Task.CompletedTask; - } - - protected override void DoSend(string destination, IMessage message) - { - Destination = destination; - Message = message; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Core/TestMessagePostProcessor.cs b/src/Messaging/test/Messaging.Test/Core/TestMessagePostProcessor.cs deleted file mode 100644 index 3033550da8..0000000000 --- a/src/Messaging/test/Messaging.Test/Core/TestMessagePostProcessor.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Core; - -namespace Steeltoe.Messaging.Test.Core; - -internal sealed class TestMessagePostProcessor : IMessagePostProcessor -{ - public IMessage Message { get; private set; } - - public IMessage PostProcessMessage(IMessage message) - { - Message = message; - return message; - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Attributes/MessagingPredicates.cs b/src/Messaging/test/Messaging.Test/Handler/Attributes/MessagingPredicates.cs deleted file mode 100644 index 86d0f89090..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Attributes/MessagingPredicates.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.Handler.Attributes; - -namespace Steeltoe.Messaging.Test.Handler.Attributes; - -internal sealed class MessagingPredicates -{ - public static DestinationVariablePredicate DestinationVar() - { - return new DestinationVariablePredicate(); - } - - public static DestinationVariablePredicate DestinationVar(string value) - { - return new DestinationVariablePredicate().Name(value); - } - - public static HeaderPredicate Header() - { - return new HeaderPredicate(); - } - - public static HeaderPredicate Header(string name) - { - return new HeaderPredicate().Name(name); - } - - public static HeaderPredicate Header(string name, string defaultValue) - { - return new HeaderPredicate().Name(name).DefaultValue(defaultValue); - } - - public static HeaderPredicate HeaderPlain() - { - return new HeaderPredicate().NoAttributes(); - } - - internal interface IPredicate - { - bool Test(T t); - } - - internal sealed class DestinationVariablePredicate : IPredicate - { - private string _value; - - public DestinationVariablePredicate Name(string value) - { - _value = value; - return this; - } - - public DestinationVariablePredicate NoName() - { - _value = string.Empty; - return this; - } - - public bool Test(ParameterInfo parameter) - { - var annotation = parameter.GetCustomAttribute(); - return annotation != null && (_value == null || annotation.Name == _value); - } - } - - internal sealed class HeaderPredicate : IPredicate - { - private string _name = string.Empty; - private bool _required = true; - private string _defaultValue; - - public HeaderPredicate Name(string value) - { - _name = value; - return this; - } - - public HeaderPredicate NoName() - { - _name = string.Empty; - return this; - } - - public HeaderPredicate Required(bool value) - { - _required = value; - return this; - } - - public HeaderPredicate DefaultValue(string value) - { - _defaultValue = value; - return this; - } - - public HeaderPredicate NoAttributes() - { - _name = string.Empty; - _required = true; - _defaultValue = null; - return this; - } - - public bool Test(ParameterInfo parameter) - { - var annotation = parameter.GetCustomAttribute(); - return annotation != null && _name == annotation.Name && annotation.Required == _required && _defaultValue == annotation.DefaultValue; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/AttributeExceptionHandlerMethodResolverTest.cs b/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/AttributeExceptionHandlerMethodResolverTest.cs deleted file mode 100644 index c6c5b9518c..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/AttributeExceptionHandlerMethodResolverTest.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Net.Sockets; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Handler.Attributes.Support; - -public sealed class AttributeExceptionHandlerMethodResolverTest -{ - private AttributeExceptionHandlerMethodResolver _resolver = new(typeof(ExceptionController)); - - [Fact] - public void ResolveMethodFromAttribute() - { - var exception = new IOException(); - Assert.Equal("HandleIOException", _resolver.ResolveMethod(exception).Name); - } - - [Fact] - public void ResolveMethodFromArgument() - { - var exception = new ArgumentException("Example message."); - Assert.Equal("HandleArgumentException", _resolver.ResolveMethod(exception).Name); - } - - [Fact] - public void ResolveMethodExceptionSubType() - { - IOException ioException = new FileNotFoundException(); - Assert.Equal("HandleIOException", _resolver.ResolveMethod(ioException).Name); - SocketException bindException = new BindException(); - Assert.Equal("HandleSocketException", _resolver.ResolveMethod(bindException).Name); - } - - [Fact] - public void ResolveMethodBestMatch() - { - var exception = new SocketException(); - Assert.Equal("HandleSocketException", _resolver.ResolveMethod(exception).Name); - } - - [Fact] - public void ResolveMethodNoMatch() - { - var exception = new Exception(); - Assert.Null(_resolver.ResolveMethod(exception)); // 1st lookup - Assert.Null(_resolver.ResolveMethod(exception)); // 2nd lookup from cache - } - - [Fact] - public void ResolveMethodInherited() - { - _resolver = new AttributeExceptionHandlerMethodResolver(typeof(InheritedController)); - var exception = new IOException(); - Assert.Equal("HandleIOException", _resolver.ResolveMethod(exception).Name); - } - - [Fact] - public void ResolveMethodAgainstCause() - { - var exception = new AggregateException(new IOException()); - Assert.Equal("HandleIOException", _resolver.ResolveMethod(exception).Name); - } - - [Fact] - public void AmbiguousExceptionMapping() - { - Assert.Throws(() => new AttributeExceptionHandlerMethodResolver(typeof(AmbiguousController))); - } - - [Fact] - public void NoExceptionMapping() - { - Assert.Throws(() => new AttributeExceptionHandlerMethodResolver(typeof(NoExceptionController))); - } - - internal sealed class BindException : SocketException - { - } - - internal abstract class ExceptionController - { - public virtual void Handle() - { - } - - [MessageExceptionHandler(typeof(IOException))] - public virtual void HandleIOException() - { - } - - [MessageExceptionHandler(typeof(SocketException))] - public virtual void HandleSocketException() - { - } - - [MessageExceptionHandler] - public virtual void HandleArgumentException(ArgumentException exception) - { - } - - [MessageExceptionHandler] - public virtual void HandleInvalidOperationException(InvalidOperationException exception) - { - } - } - - internal sealed class InheritedController : ExceptionController - { - public override void HandleIOException() - { - } - } - - internal sealed class AmbiguousController - { - public void Handle() - { - } - - [MessageExceptionHandler(typeof(BindException), typeof(ArgumentException))] - public string Handle1(Exception ex) - { - return ex.GetType().Name; - } - - [MessageExceptionHandler] - public string Handle2(ArgumentException ex) - { - return ex.GetType().Name; - } - } - - internal sealed class NoExceptionController - { - [MessageExceptionHandler] - public void Handle() - { - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/DefaultMessageHandlerMethodFactoryTest.cs b/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/DefaultMessageHandlerMethodFactoryTest.cs deleted file mode 100644 index fea7028f24..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/DefaultMessageHandlerMethodFactoryTest.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection; -using Steeltoe.Common.Converter; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Handler.Attributes.Support; - -public sealed class DefaultMessageHandlerMethodFactoryTest -{ - private readonly SampleBean _sample = new(); - - [Fact] - public void CustomConversion() - { - var conversionService = new GenericConversionService(); - conversionService.AddConverter(new SampleBeanConverter()); - DefaultMessageHandlerMethodFactory instance = CreateInstance(conversionService); - - IInvocableHandlerMethod invocableHandlerMethod = CreateInvocableHandlerMethod(instance, "SimpleString", typeof(string)); - - invocableHandlerMethod.Invoke(MessageBuilder.WithPayload(_sample).Build()); - AssertMethodInvocation(_sample, "SimpleString"); - } - - [Fact] - public void CustomConversionServiceFailure() - { - var conversionService = new GenericConversionService(); - DefaultMessageHandlerMethodFactory instance = CreateInstance(conversionService); - Assert.False(conversionService.CanConvert(typeof(int), typeof(string))); - IInvocableHandlerMethod invocableHandlerMethod = CreateInvocableHandlerMethod(instance, "SimpleString", typeof(string)); - Assert.Throws(() => invocableHandlerMethod.Invoke(MessageBuilder.WithPayload(123).Build())); - } - - [Fact] - public void CustomMessageConverterFailure() - { - IMessageConverter messageConverter = new ByteArrayMessageConverter(); - DefaultMessageHandlerMethodFactory instance = CreateInstance(messageConverter); - - IInvocableHandlerMethod invocableHandlerMethod = CreateInvocableHandlerMethod(instance, "SimpleString", typeof(string)); - Assert.Throws(() => invocableHandlerMethod.Invoke(MessageBuilder.WithPayload(123).Build())); - } - - [Fact] - public void CustomArgumentResolver() - { - var customResolvers = new List - { - new CustomHandlerMethodArgumentResolver() - }; - - DefaultMessageHandlerMethodFactory instance = CreateInstance(customResolvers); - - IInvocableHandlerMethod invocableHandlerMethod = CreateInvocableHandlerMethod(instance, "CustomArgumentResolver", typeof(CultureInfo)); - - invocableHandlerMethod.Invoke(MessageBuilder.WithPayload(123).Build()); - AssertMethodInvocation(_sample, "CustomArgumentResolver"); - } - - [Fact] - public void OverrideArgumentResolvers() - { - var instance = new DefaultMessageHandlerMethodFactory(); - - var customResolvers = new List - { - new CustomHandlerMethodArgumentResolver() - }; - - instance.SetArgumentResolvers(customResolvers); // Override defaults - - IMessage message = MessageBuilder.WithPayload("sample").Build(); - - // This will work as the local resolver is set - IInvocableHandlerMethod invocableHandlerMethod = CreateInvocableHandlerMethod(instance, "CustomArgumentResolver", typeof(CultureInfo)); - invocableHandlerMethod.Invoke(message); - AssertMethodInvocation(_sample, "CustomArgumentResolver"); - - // This won't work as no resolver is known for the payload - IInvocableHandlerMethod invocableHandlerMethod2 = CreateInvocableHandlerMethod(instance, "SimpleString", typeof(string)); - Assert.Throws(() => invocableHandlerMethod2.Invoke(message)); - } - - [Fact] - public void NoValidationByDefault() - { - var instance = new DefaultMessageHandlerMethodFactory(); - IInvocableHandlerMethod invocableHandlerMethod = CreateInvocableHandlerMethod(instance, "PayloadValidation", typeof(string)); - invocableHandlerMethod.Invoke(MessageBuilder.WithPayload("failure").Build()); - AssertMethodInvocation(_sample, "PayloadValidation"); - } - - private DefaultMessageHandlerMethodFactory CreateInstance(List customResolvers) - { - var factory = new DefaultMessageHandlerMethodFactory(null, null, customResolvers); - return factory; - } - - private DefaultMessageHandlerMethodFactory CreateInstance(IMessageConverter converter) - { - var factory = new DefaultMessageHandlerMethodFactory(null, converter); - return factory; - } - - private DefaultMessageHandlerMethodFactory CreateInstance(GenericConversionService conversionService) - { - var factory = new DefaultMessageHandlerMethodFactory(conversionService); - return factory; - } - - private IInvocableHandlerMethod CreateInvocableHandlerMethod(DefaultMessageHandlerMethodFactory factory, string methodName, params Type[] parameterTypes) - { - return factory.CreateInvocableHandlerMethod(_sample, GetListenerMethod(methodName, parameterTypes)); - } - - private MethodInfo GetListenerMethod(string methodName, params Type[] parameterTypes) - { - MethodInfo method = typeof(SampleBean).GetMethod(methodName, parameterTypes); - Assert.NotNull(method); - return method; - } - - private void AssertMethodInvocation(SampleBean bean, string methodName) - { - Assert.True(bean.Invocations[methodName]); - } - - internal sealed class SampleBeanConverter : AbstractConverter - { - public override string Convert(SampleBean source) - { - return "foo bar"; - } - } - - internal sealed class SampleBean - { - public Dictionary Invocations { get; } = new(); - - public void SimpleString(string value) - { - Invocations.Add("SimpleString", true); - } - - public void PayloadValidation([Payload] string value) - { - Invocations.Add("PayloadValidation", true); - } - - public void CustomArgumentResolver(CultureInfo locale) - { - Invocations.Add("CustomArgumentResolver", true); - Assert.Equal(CultureInfo.CurrentCulture, locale); - } - } - - internal sealed class CustomHandlerMethodArgumentResolver : IHandlerMethodArgumentResolver - { - public bool SupportsParameter(ParameterInfo parameter) - { - return parameter.ParameterType.IsAssignableFrom(typeof(CultureInfo)); - } - - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - return CultureInfo.CurrentCulture; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/DestinationVariableMethodArgumentResolverTest.cs b/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/DestinationVariableMethodArgumentResolverTest.cs deleted file mode 100644 index d2ec996b52..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/DestinationVariableMethodArgumentResolverTest.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Support; -using Steeltoe.Messaging.Test.Handler.Invocation; -using Xunit; - -namespace Steeltoe.Messaging.Test.Handler.Attributes.Support; - -public sealed class DestinationVariableMethodArgumentResolverTest -{ - private readonly DestinationVariableMethodArgumentResolver _resolver = new(new DefaultConversionService()); - private readonly ResolvableMethod _resolvable = ResolvableMethod.On().Named(nameof(HandleMessage)).Build(); - - [Fact] - public void SupportsParameter() - { - Assert.True(_resolver.SupportsParameter(_resolvable.Annotation(MessagingPredicates.DestinationVar().NoName()).Arg())); - Assert.False(_resolver.SupportsParameter(_resolvable.AnnotationNotPresent(typeof(DestinationVariableAttribute)).Arg())); - } - - [Fact] - public void ResolveArgument() - { - var vars = new Dictionary - { - { "foo", "bar" }, - { "name", "value" } - }; - - IMessage message = MessageBuilder.WithPayload(Array.Empty()) - .SetHeader(DestinationVariableMethodArgumentResolver.DestinationTemplateVariablesHeader, vars).Build(); - - ParameterInfo param = _resolvable.Annotation(MessagingPredicates.DestinationVar().NoName()).Arg(); - object result = _resolver.ResolveArgument(param, message); - Assert.Equal("bar", result); - - param = _resolvable.Annotation(MessagingPredicates.DestinationVar("name")).Arg(); - result = _resolver.ResolveArgument(param, message); - Assert.Equal("value", result); - } - - [Fact] - public void ResolveArgumentNotFound() - { - IMessage message = MessageBuilder.WithPayload(Array.Empty()).Build(); - - Assert.Throws(() => - _resolver.ResolveArgument(_resolvable.Annotation(MessagingPredicates.DestinationVar().NoName()).Arg(), message)); - } - - private void HandleMessage([DestinationVariable] string foo, [DestinationVariable("name")] string param1, string param3) - { - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/HeaderMethodArgumentResolverTest.cs b/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/HeaderMethodArgumentResolverTest.cs deleted file mode 100644 index e8b2c929e5..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/HeaderMethodArgumentResolverTest.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Support; -using Steeltoe.Messaging.Test.Handler.Invocation; -using Xunit; - -namespace Steeltoe.Messaging.Test.Handler.Attributes.Support; - -public sealed class HeaderMethodArgumentResolverTest -{ - private readonly HeaderMethodArgumentResolver _resolver = new(new DefaultConversionService()); - - private readonly ResolvableMethod _resolvable = ResolvableMethod.On().Named(nameof(HandleMessage)).Build(); - - [Fact] - public void SupportsParameter() - { - Assert.True(_resolver.SupportsParameter(_resolvable.Annotation(MessagingPredicates.HeaderPlain()).Arg())); - Assert.False(_resolver.SupportsParameter(_resolvable.AnnotationNotPresent(typeof(HeaderAttribute)).Arg())); - } - - [Fact] - public void ResolveArgument() - { - IMessage message = MessageBuilder.WithPayload(Array.Empty()).SetHeader("param1", "foo").Build(); - object result = _resolver.ResolveArgument(_resolvable.Annotation(MessagingPredicates.HeaderPlain()).Arg(), message); - Assert.Equal("foo", result); - } - - [Fact] - public void ResolveArgumentNativeHeader() - { - var headers = new TestMessageHeaderAccessor(); - headers.SetNativeHeader("param1", "foo"); - IMessage message = MessageBuilder.WithPayload(Array.Empty()).SetHeaders(headers).Build(); - Assert.Equal("foo", _resolver.ResolveArgument(_resolvable.Annotation(MessagingPredicates.HeaderPlain()).Arg(), message)); - } - - [Fact] - public void ResolveArgumentNativeHeaderAmbiguity() - { - var headers = new TestMessageHeaderAccessor(); - headers.SetHeader("param1", "foo"); - headers.SetNativeHeader("param1", "native-foo"); - IMessage message = MessageBuilder.WithPayload(Array.Empty()).SetHeaders(headers).Build(); - - Assert.Equal("foo", _resolver.ResolveArgument(_resolvable.Annotation(MessagingPredicates.HeaderPlain()).Arg(), message)); - Assert.Equal("native-foo", _resolver.ResolveArgument(_resolvable.Annotation(MessagingPredicates.Header("nativeHeaders.param1")).Arg(), message)); - } - - [Fact] - public void ResolveArgumentNotFound() - { - IMessage message = MessageBuilder.WithPayload(Array.Empty()).Build(); - Assert.Throws(() => _resolver.ResolveArgument(_resolvable.Annotation(MessagingPredicates.HeaderPlain()).Arg(), message)); - } - - [Fact] - public void ResolveArgumentDefaultValue() - { - IMessage message = MessageBuilder.WithPayload(Array.Empty()).Build(); - object result = _resolver.ResolveArgument(_resolvable.Annotation(MessagingPredicates.Header("name", "bar")).Arg(), message); - Assert.Equal("bar", result); - } - - [Fact] - public void ResolveOptionalHeaderWithValue() - { - IMessage message = MessageBuilder.WithPayload("foo").SetHeader("foo", "bar").Build(); - ParameterInfo param = _resolvable.Annotation(MessagingPredicates.Header("foo")).Arg(); - object result = _resolver.ResolveArgument(param, message); - Assert.Equal("bar", result); - } - - [Fact] - public void ResolveOptionalHeaderAsEmpty() - { - IMessage message = MessageBuilder.WithPayload("foo").Build(); - ParameterInfo param = _resolvable.Annotation(MessagingPredicates.Header("foo")).Arg(); - object result = _resolver.ResolveArgument(param, message); - Assert.Null(result); - } - - private void HandleMessage([Header] string param1, [Header(Name = "name", DefaultValue = "bar")] string param2, - [Header(Name = "name", DefaultValue = "#{systemProperties.systemProperty}")] string param3, - [Header(Name = "#{systemProperties.systemProperty}")] string param4, string param5, [Header("nativeHeaders.param1")] string nativeHeaderParam1, - [Header("foo")] string param6 = null) - { - } - - internal sealed class TestMessageHeaderAccessor : NativeMessageHeaderAccessor - { - public TestMessageHeaderAccessor() - : base((IDictionary>)null) - { - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/HeadersMethodArgumentResolverTest.cs b/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/HeadersMethodArgumentResolverTest.cs deleted file mode 100644 index e924b48045..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/HeadersMethodArgumentResolverTest.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Support; -using Steeltoe.Messaging.Test.Handler.Invocation; -using Xunit; - -namespace Steeltoe.Messaging.Test.Handler.Attributes.Support; - -public sealed class HeadersMethodArgumentResolverTest -{ - private readonly HeadersMethodArgumentResolver _resolver = new(); - - private readonly IMessage _message = MessageBuilder.WithPayload(Array.Empty()).CopyHeaders(new Dictionary - { - { "foo", "bar" } - }).Build(); - - private readonly ResolvableMethod _resolvable = ResolvableMethod.On().Named(nameof(HandleMessage)).Build(); - - [Fact] - public void SupportsParameter() - { - Assert.True(_resolver.SupportsParameter(_resolvable.AnnotationPresent(typeof(HeadersAttribute)).Arg(typeof(IDictionary)))); - - Assert.True(_resolver.SupportsParameter(_resolvable.Arg(typeof(MessageHeaders)))); - Assert.True(_resolver.SupportsParameter(_resolvable.Arg(typeof(MessageHeaderAccessor)))); - Assert.True(_resolver.SupportsParameter(_resolvable.Arg(typeof(TestMessageHeaderAccessor)))); - - Assert.False(_resolver.SupportsParameter(_resolvable.AnnotationPresent(typeof(HeadersAttribute)).Arg(typeof(string)))); - } - - [Fact] - public void ResolveArgumentAnnotated() - { - ParameterInfo param = _resolvable.AnnotationPresent(typeof(HeadersAttribute)).Arg(typeof(IDictionary)); - object resolved = _resolver.ResolveArgument(param, _message); - - bool condition = resolved is IDictionary; - Assert.True(condition); - - var headers = resolved as IDictionary; - Assert.Equal("bar", headers["foo"]); - } - - [Fact] - public void ResolveArgumentAnnotatedNotMap() - { - Assert.Throws(() => - _resolver.ResolveArgument(_resolvable.AnnotationPresent(typeof(HeadersAttribute)).Arg(typeof(string)), _message)); - } - - [Fact] - public void ResolveArgumentMessageHeaders() - { - object resolved = _resolver.ResolveArgument(_resolvable.Arg(typeof(MessageHeaders)), _message); - - bool condition = resolved is MessageHeaders; - Assert.True(condition); - var headers = (MessageHeaders)resolved; - Assert.Equal("bar", headers["foo"]); - } - - [Fact] - public void ResolveArgumentMessageHeaderAccessor() - { - ParameterInfo param = _resolvable.Arg(typeof(MessageHeaderAccessor)); - object resolved = _resolver.ResolveArgument(param, _message); - - bool condition = resolved is MessageHeaderAccessor; - Assert.True(condition); - var headers = (MessageHeaderAccessor)resolved; - Assert.Equal("bar", headers.GetHeader("foo")); - } - - [Fact] - public void ResolveArgumentMessageHeaderAccessorSubclass() - { - ParameterInfo param = _resolvable.Arg(typeof(TestMessageHeaderAccessor)); - object resolved = _resolver.ResolveArgument(param, _message); - - bool condition = resolved is TestMessageHeaderAccessor; - Assert.True(condition); - var headers = (TestMessageHeaderAccessor)resolved; - Assert.Equal("bar", headers.GetHeader("foo")); - } - - private void HandleMessage([Headers] IDictionary param1, [Headers] string param2, MessageHeaders param3, MessageHeaderAccessor param4, - TestMessageHeaderAccessor param5) - { - } - - internal sealed class TestMessageHeaderAccessor : NativeMessageHeaderAccessor - { - public TestMessageHeaderAccessor(IMessage message) - : base(message) - { - } - - public static TestMessageHeaderAccessor Wrap(IMessage message) - { - return new TestMessageHeaderAccessor(message); - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/MessageMethodArgumentResolverTest.cs b/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/MessageMethodArgumentResolverTest.cs deleted file mode 100644 index 1081fa1a00..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/MessageMethodArgumentResolverTest.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Moq; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Handler.Attributes.Support; - -public sealed class MessageMethodArgumentResolverTest -{ - private readonly IMessageConverter _converter; - - private readonly MethodInfo _method; - - private readonly Mock _mock; - - private MessageMethodArgumentResolver _resolver; - - public MessageMethodArgumentResolverTest() - { - _method = typeof(MessageMethodArgumentResolverTest).GetMethod(nameof(Handle), BindingFlags.NonPublic | BindingFlags.Instance); - - _mock = new Mock(); - _converter = _mock.Object; - _resolver = new MessageMethodArgumentResolver(_converter); - } - - [Fact] - public void ResolveWithPayloadTypeAsObject() - { - IMessage message = MessageBuilder.WithPayload("test").Build(); - ParameterInfo parameter = _method.GetParameters()[0]; - - Assert.True(_resolver.SupportsParameter(parameter)); - Assert.Same(message, _resolver.ResolveArgument(parameter, message)); - } - - [Fact] - public void ResolveWithMatchingPayloadType() - { - IMessage message = MessageBuilder.WithPayload(123).Build(); - ParameterInfo parameter = _method.GetParameters()[1]; - - Assert.True(_resolver.SupportsParameter(parameter)); - Assert.Same(message, _resolver.ResolveArgument(parameter, message)); - } - - [Fact] - public void ResolveWithConversion() - { - IMessage message = MessageBuilder.WithPayload("test").Build(); - ParameterInfo parameter = _method.GetParameters()[1]; - _mock.Setup(c => c.FromMessage(message, typeof(int))).Returns(4); - - var actual = (IMessage)_resolver.ResolveArgument(parameter, message); - - Assert.NotNull(actual); - Assert.Equal(message.Headers, actual.Headers); - Assert.Equal(4, actual.Payload); - } - - [Fact] - public void ResolveWithConversionNoMatchingConverter() - { - IMessage message = MessageBuilder.WithPayload("test").Build(); - ParameterInfo parameter = _method.GetParameters()[1]; - - Assert.True(_resolver.SupportsParameter(parameter)); - var ex = Assert.Throws(() => _resolver.ResolveArgument(parameter, message)); - Assert.Contains("Int32", ex.Message, StringComparison.Ordinal); - Assert.Contains("String", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void ResolveWithConversionEmptyPayload() - { - IMessage message = MessageBuilder.WithPayload(string.Empty).Build(); - ParameterInfo parameter = _method.GetParameters()[1]; - Assert.True(_resolver.SupportsParameter(parameter)); - var ex = Assert.Throws(() => _resolver.ResolveArgument(parameter, message)); - Assert.Contains("payload is empty", ex.Message, StringComparison.Ordinal); - Assert.Contains("Int32", ex.Message, StringComparison.Ordinal); - Assert.Contains("String", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void ResolveMessageSubclassMatch() - { - var message = new ErrorMessage(new InvalidOperationException()); - ParameterInfo parameter = _method.GetParameters()[4]; - - Assert.True(_resolver.SupportsParameter(parameter)); - Assert.Same(message, _resolver.ResolveArgument(parameter, message)); - } - - [Fact] - public void ResolveWithMessageSubclassAndPayloadWildcard() - { - var message = new ErrorMessage(new InvalidOperationException()); - ParameterInfo parameter = _method.GetParameters()[0]; - Assert.True(_resolver.SupportsParameter(parameter)); - Assert.Same(message, _resolver.ResolveArgument(parameter, message)); - } - - [Fact] - public void ResolveWithPayloadTypeAsWildcardAndNoConverter() - { - _resolver = new MessageMethodArgumentResolver(); - - IMessage message = MessageBuilder.WithPayload("test").Build(); - ParameterInfo parameter = _method.GetParameters()[0]; - Assert.True(_resolver.SupportsParameter(parameter)); - Assert.Same(message, _resolver.ResolveArgument(parameter, message)); - } - - [Fact] - public void ResolveWithConversionNeededButNoConverter() - { - _resolver = new MessageMethodArgumentResolver(); - - IMessage message = MessageBuilder.WithPayload("test").Build(); - ParameterInfo parameter = _method.GetParameters()[1]; - Assert.True(_resolver.SupportsParameter(parameter)); - var ex = Assert.Throws(() => _resolver.ResolveArgument(parameter, message)); - Assert.Contains("Int32", ex.Message, StringComparison.Ordinal); - Assert.Contains("String", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void ResolveWithConversionEmptyPayloadButNoConverter() - { - _resolver = new MessageMethodArgumentResolver(); - - IMessage message = MessageBuilder.WithPayload(string.Empty).Build(); - ParameterInfo parameter = _method.GetParameters()[1]; - Assert.True(_resolver.SupportsParameter(parameter)); - var ex = Assert.Throws(() => _resolver.ResolveArgument(parameter, message)); - Assert.Contains("payload is empty", ex.Message, StringComparison.Ordinal); - Assert.Contains("Int32", ex.Message, StringComparison.Ordinal); - Assert.Contains("String", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void ResolveWithNewtonJSonConverter() - { - IMessage inMessage = MessageBuilder.WithPayload("{\"prop\":\"bar\"}").Build(); - ParameterInfo parameter = _method.GetParameters()[5]; - - _resolver = new MessageMethodArgumentResolver(new NewtonJsonMessageConverter()); - object actual = _resolver.ResolveArgument(parameter, inMessage); - - bool condition1 = actual is IMessage; - Assert.True(condition1); - var outMessage = (IMessage)actual; - bool condition = outMessage.Payload is Foo; - Assert.True(condition); - Assert.Equal("bar", ((Foo)outMessage.Payload).Prop); - } - - private void Handle(IMessage wildcardPayload, IMessage integerPayload, IMessage numberPayload, IMessage anyNumberPayload, - ErrorMessage subClass, IMessage fooPayload) - { - } - - internal sealed class Foo - { - public string Prop { get; set; } - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/PayloadMethodArgumentResolverTest.cs b/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/PayloadMethodArgumentResolverTest.cs deleted file mode 100644 index 988a752a26..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Attributes/Support/PayloadMethodArgumentResolverTest.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection; -using System.Text; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Handler.Attributes.Support; - -public sealed class PayloadMethodArgumentResolverTest -{ - private readonly PayloadMethodArgumentResolver _resolver; - - private readonly ParameterInfo _paramAnnotated; - - private readonly ParameterInfo _paramAnnotatedNotRequired; - - private readonly ParameterInfo _paramAnnotatedRequired; - - private readonly ParameterInfo _paramWithSpelExpression; - - private readonly ParameterInfo _paramNotAnnotated; - - public PayloadMethodArgumentResolverTest() - { - _resolver = new PayloadMethodArgumentResolver(new StringMessageConverter()); - - MethodInfo payloadMethod = typeof(PayloadMethodArgumentResolverTest).GetMethod(nameof(HandleMessage), BindingFlags.NonPublic | BindingFlags.Instance); - - _paramAnnotated = payloadMethod.GetParameters()[0]; - _paramAnnotatedNotRequired = payloadMethod.GetParameters()[1]; - _paramAnnotatedRequired = payloadMethod.GetParameters()[2]; - _paramWithSpelExpression = payloadMethod.GetParameters()[3]; - _paramNotAnnotated = payloadMethod.GetParameters()[4]; - } - - [Fact] - public void SupportsParameter() - { - Assert.True(_resolver.SupportsParameter(_paramAnnotated)); - Assert.True(_resolver.SupportsParameter(_paramNotAnnotated)); - - var strictResolver = new PayloadMethodArgumentResolver(new StringMessageConverter(), false); - - Assert.True(strictResolver.SupportsParameter(_paramAnnotated)); - Assert.False(strictResolver.SupportsParameter(_paramNotAnnotated)); - } - - [Fact] - public void ResolveRequired() - { - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("ABC")).Build(); - object actual = _resolver.ResolveArgument(_paramAnnotated, message); - Assert.Equal("ABC", actual); - } - - [Fact] - public void ResolveRequiredEmpty() - { - IMessage message = MessageBuilder.WithPayload(string.Empty).Build(); - Assert.Throws(() => _resolver.ResolveArgument(_paramAnnotated, message)); - } - - [Fact] - public void ResolveRequiredEmptyNonAnnotatedParameter() - { - IMessage message = MessageBuilder.WithPayload(string.Empty).Build(); - Assert.Throws(() => _resolver.ResolveArgument(_paramNotAnnotated, message)); - } - - [Fact] - public void ResolveNotRequired() - { - IMessage emptyByteArrayMessage = MessageBuilder.WithPayload(Array.Empty()).Build(); - Assert.Null(_resolver.ResolveArgument(_paramAnnotatedNotRequired, emptyByteArrayMessage)); - - IMessage emptyStringMessage = MessageBuilder.WithPayload(string.Empty).Build(); - Assert.Null(_resolver.ResolveArgument(_paramAnnotatedNotRequired, emptyStringMessage)); - - IMessage notEmptyMessage = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("ABC")).Build(); - Assert.Equal("ABC", _resolver.ResolveArgument(_paramAnnotatedNotRequired, notEmptyMessage)); - } - - [Fact] - public void ResolveNonConvertibleParam() - { - IMessage notEmptyMessage = MessageBuilder.WithPayload(123).Build(); - var ex = Assert.Throws(() => _resolver.ResolveArgument(_paramAnnotatedRequired, notEmptyMessage)); - Assert.Contains("Cannot convert", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void ResolveSpelExpressionNotSupported() - { - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("ABC")).Build(); - Assert.Throws(() => _resolver.ResolveArgument(_paramWithSpelExpression, message)); - } - - [Fact] - public void ResolveNonAnnotatedParameter() - { - IMessage notEmptyMessage = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("ABC")).Build(); - Assert.Equal("ABC", _resolver.ResolveArgument(_paramNotAnnotated, notEmptyMessage)); - } - - internal void HandleMessage([Payload] string param, [Payload(Required = false)] string paramNotRequired, - [Payload(Required = true)] CultureInfo nonConvertibleRequiredParam, [Payload("foo.bar")] string paramWithExpression, string paramNotAnnotated) - { - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/DestinationPatternsMessageConditionTest.cs b/src/Messaging/test/Messaging.Test/Handler/DestinationPatternsMessageConditionTest.cs deleted file mode 100644 index 2685cc0d8c..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/DestinationPatternsMessageConditionTest.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Handler; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Handler; - -public sealed class DestinationPatternsMessageConditionTest -{ - [Fact] - public void PrependSlash() - { - DestinationPatternsMessageCondition c = Condition("foo"); - Assert.Equal("/foo", c.Patterns.Single()); - } - - [Fact] - public void PrependSlashWithCustomPathSeparator() - { - var c = new DestinationPatternsMessageCondition(new[] - { - "foo" - }, new AntPathMatcher(".")); - - Assert.Equal("foo", c.Patterns.Single()); - } - - [Fact] - public void PrependNonEmptyPatternsOnly() - { - DestinationPatternsMessageCondition c = Condition(string.Empty); - Assert.Equal(string.Empty, c.Patterns.Single()); - } - - [Fact] - public void CombineEmptySets() - { - DestinationPatternsMessageCondition c1 = Condition(); - DestinationPatternsMessageCondition c2 = Condition(); - Assert.Equal(Condition(string.Empty), c1.Combine(c2)); - } - - [Fact] - public void CombineOnePatternWithEmptySet() - { - DestinationPatternsMessageCondition c1 = Condition("/type1", "/type2"); - DestinationPatternsMessageCondition c2 = Condition(); - - Assert.Equal(Condition("/type1", "/type2"), c1.Combine(c2)); - - c1 = Condition(); - c2 = Condition("/method1", "/method2"); - - Assert.Equal(Condition("/method1", "/method2"), c1.Combine(c2)); - } - - [Fact] - public void CombineMultiplePatterns() - { - DestinationPatternsMessageCondition c1 = Condition("/t1", "/t2"); - DestinationPatternsMessageCondition c2 = Condition("/m1", "/m2"); - - Assert.Equal(new DestinationPatternsMessageCondition("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2"), c1.Combine(c2)); - } - - [Fact] - public void MatchDirectPath() - { - DestinationPatternsMessageCondition condition = Condition("/foo"); - DestinationPatternsMessageCondition match = condition.GetMatchingCondition(MessageTo("/foo")); - - Assert.NotNull(match); - } - - [Fact] - public void MatchPattern() - { - DestinationPatternsMessageCondition condition = Condition("/foo/*"); - DestinationPatternsMessageCondition match = condition.GetMatchingCondition(MessageTo("/foo/bar")); - - Assert.NotNull(match); - } - - [Fact] - public void MatchSortPatterns() - { - DestinationPatternsMessageCondition condition = Condition("/**", "/foo/bar", "/foo/*"); - DestinationPatternsMessageCondition match = condition.GetMatchingCondition(MessageTo("/foo/bar")); - DestinationPatternsMessageCondition expected = Condition("/foo/bar", "/foo/*", "/**"); - - Assert.Equal(expected, match); - } - - [Fact] - public void CompareEqualPatterns() - { - DestinationPatternsMessageCondition c1 = Condition("/foo*"); - DestinationPatternsMessageCondition c2 = Condition("/foo*"); - - Assert.Equal(0, c1.CompareTo(c2, MessageTo("/foo"))); - } - - [Fact] - public void ComparePatternSpecificity() - { - DestinationPatternsMessageCondition c1 = Condition("/fo*"); - DestinationPatternsMessageCondition c2 = Condition("/foo"); - - Assert.Equal(1, c1.CompareTo(c2, MessageTo("/foo"))); - } - - [Fact] - public void CompareNumberOfMatchingPatterns() - { - IMessage message = MessageTo("/foo"); - - DestinationPatternsMessageCondition c1 = Condition("/foo", "bar"); - DestinationPatternsMessageCondition c2 = Condition("/foo", "f*"); - - DestinationPatternsMessageCondition match1 = c1.GetMatchingCondition(message); - DestinationPatternsMessageCondition match2 = c2.GetMatchingCondition(message); - - Assert.Equal(1, match1.CompareTo(match2, message)); - } - - private DestinationPatternsMessageCondition Condition(params string[] patterns) - { - return new DestinationPatternsMessageCondition(patterns); - } - - private IMessage MessageTo(string destination) - { - return MessageBuilder.WithPayload(Array.Empty()).SetHeader(DestinationPatternsMessageCondition.LookupDestinationHeader, destination).Build(); - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Invocation/InvocableHandlerMethodTest.cs b/src/Messaging/test/Messaging.Test/Handler/Invocation/InvocableHandlerMethodTest.cs deleted file mode 100644 index d1fd71ab06..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Invocation/InvocableHandlerMethodTest.cs +++ /dev/null @@ -1,417 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Reflection; -using Moq; -using Steeltoe.Messaging.Handler; -using Steeltoe.Messaging.Handler.Invocation; -using Xunit; -using Xunit.Abstractions; - -namespace Steeltoe.Messaging.Test.Handler.Invocation; - -public sealed class InvocableHandlerMethodTest -{ - private readonly ITestOutputHelper _outputHelper; - private HandlerMethodArgumentResolverComposite _resolvers; - private IMessage _message; - - public InvocableHandlerMethodTest(ITestOutputHelper output) - { - _outputHelper = output; - } - - [Fact] - public void ResolveArg() - { - var messageMock = new Mock(); - _message = messageMock.Object; - - _resolvers = new HandlerMethodArgumentResolverComposite(); - _resolvers.AddResolver(new StubArgumentResolver(99)); - _resolvers.AddResolver(new StubArgumentResolver("value")); - - MethodInfo method = typeof(Handler).GetMethod(nameof(Handler.Handle), new[] - { - typeof(int?), - typeof(string) - }); - - object value = Invoke(new Handler(), method); - - Assert.Single(GetStubResolver(0).ResolvedParameters); - Assert.Single(GetStubResolver(1).ResolvedParameters); - Assert.Equal("99-value", value); - Assert.Equal("intArg", GetStubResolver(0).ResolvedParameters[0].Name); - Assert.Equal("stringArg", GetStubResolver(1).ResolvedParameters[0].Name); - } - - [Fact] - public void ResolveNoArgValue() - { - var messageMock = new Mock(); - _message = messageMock.Object; - - _resolvers = new HandlerMethodArgumentResolverComposite(); - _resolvers.AddResolver(new StubArgumentResolver(typeof(int?))); - _resolvers.AddResolver(new StubArgumentResolver(typeof(string))); - - MethodInfo method = typeof(Handler).GetMethod(nameof(Handler.Handle), new[] - { - typeof(int?), - typeof(string) - }); - - object value = Invoke(new Handler(), method); - - Assert.Single(GetStubResolver(0).ResolvedParameters); - Assert.Single(GetStubResolver(1).ResolvedParameters); - Assert.Equal("null-null", value); - } - - [Fact] - public void CannotResolveArg() - { - MethodInfo method = typeof(Handler).GetMethod(nameof(Handler.Handle), new[] - { - typeof(int?), - typeof(string) - }); - - var messageMock = new Mock(); - _message = messageMock.Object; - - _resolvers = new HandlerMethodArgumentResolverComposite(); - var ex = Assert.Throws(() => Invoke(new Handler(), method)); - Assert.Contains("Could not resolve parameter [0]", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void ResolveProvidedArg() - { - MethodInfo method = typeof(Handler).GetMethod(nameof(Handler.Handle), new[] - { - typeof(int?), - typeof(string) - }); - - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - - object value = Invoke(new Handler(), method, 99, "value"); - - Assert.NotNull(value); - Assert.IsType(value); - Assert.Equal("99-value", value); - } - - [Fact] - public void ResolveProvidedArgFirst() - { - MethodInfo method = typeof(Handler).GetMethod(nameof(Handler.Handle), new[] - { - typeof(int?), - typeof(string) - }); - - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - - _resolvers.AddResolver(new StubArgumentResolver(1)); - _resolvers.AddResolver(new StubArgumentResolver("value1")); - object value = Invoke(new Handler(), method, 2, "value2"); - - Assert.Equal("2-value2", value); - } - - [Fact] - public void ExceptionInResolvingArg() - { - MethodInfo method = typeof(Handler).GetMethod(nameof(Handler.Handle), new[] - { - typeof(int?), - typeof(string) - }); - - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - - _resolvers.AddResolver(new ExceptionRaisingArgumentResolver()); - Assert.Throws(() => Invoke(new Handler(), method)); - - // expected - allow HandlerMethodArgumentResolver exceptions to propagate - } - - [Fact] - public void IllegalArgumentException() - { - MethodInfo method = typeof(Handler).GetMethod(nameof(Handler.Handle), new[] - { - typeof(int?), - typeof(string) - }); - - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - - _resolvers.AddResolver(new StubArgumentResolver(typeof(int?), "__not_an_int__")); - _resolvers.AddResolver(new StubArgumentResolver("value")); - var ex = Assert.Throws(() => Invoke(new Handler(), method)); - Assert.Contains("Endpoint [", ex.Message, StringComparison.Ordinal); - Assert.Contains("Method [", ex.Message, StringComparison.Ordinal); - Assert.Contains("with argument values:", ex.Message, StringComparison.Ordinal); - Assert.Contains("[0] [type=System.String] [value=__not_an_int__]", ex.Message, StringComparison.Ordinal); - Assert.Contains("[1] [type=System.String] [value=value", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void InvocationTargetException() - { - var handler = new Handler(); - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - MethodInfo method = typeof(Handler).GetMethod(nameof(Handler.HandleWithException)); - - var runtimeException = new Exception("error"); - var ex = Assert.Throws(() => Invoke(handler, method, runtimeException)); - Assert.Same(runtimeException, ex); - - var error = new IndexOutOfRangeException("error"); - var ex2 = Assert.Throws(() => Invoke(handler, method, error)); - Assert.Same(error, ex2); - } - - [Fact] - public void HandleSinglePrimitiveReturnVoid() - { - var handler = new Handler2(); - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - MethodInfo method = typeof(Handler2).GetMethod(nameof(Handler2.HandleSinglePrimitiveReturnVoid)); - Invoke(handler, method, 1.0d); - Assert.Equal(1.0d, handler.DoubleValue); - } - - [Fact] - public void HandleMultiPrimitiveReturnVoid() - { - var handler = new Handler2(); - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - MethodInfo method = typeof(Handler2).GetMethod(nameof(Handler2.HandleMultiPrimitiveReturnVoid)); - Invoke(handler, method, 1.0d, 2); - Assert.Equal(1.0d, handler.DoubleValue); - Assert.Equal(2, handler.IntValue); - } - - [Fact] - public void HandleMultiReturnVoid() - { - var handler = new Handler2(); - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - MethodInfo method = typeof(Handler2).GetMethod(nameof(Handler2.HandleMultiReturnVoid)); - Invoke(handler, method, 1.0d, 2, handler); - Assert.Equal(1.0d, handler.DoubleValue); - Assert.Equal(2, handler.IntValue); - Assert.Same(handler, handler.ObjectValue); - } - - [Fact] - public void HandleNullablePrimitive() - { - var handler = new Handler2(); - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - MethodInfo method = typeof(Handler2).GetMethod(nameof(Handler2.HandleNullablePrimitive)); - object result = Invoke(handler, method, 10, "stringArg"); - Assert.Equal(1, handler.InvocationCount); - Assert.Equal("10-stringArg", result); - } - - [Fact] - public void ComparePerfHandleSinglePrimitiveReturnVoid() - { - var handler = new Handler2(); - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - MethodInfo method = typeof(Handler2).GetMethod(nameof(Handler2.HandleSinglePrimitiveReturnVoid)); - long ticks1 = TimedInvoke(handler, method, 100_000, 1.0d); - Assert.Equal(100_000, handler.InvocationCount); - long ticks2 = TimedReflectionInvoke(handler, method, 100_000, 1.0d); - Assert.Equal(200_000, handler.InvocationCount); - Assert.True(ticks2 > ticks1); - } - - [Fact] - public async Task HandleAsyncVoidMethod() - { - var handler = new Handler2(); - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - MethodInfo method = typeof(Handler2).GetMethod(nameof(Handler2.HandleAsyncVoidMethodAsync)); - var result = Invoke(handler, method, 1.0d) as Task; - await result; - Assert.Equal(1, handler.InvocationCount); - Assert.Equal(1.0d, handler.DoubleValue); - } - - [Fact] - public async Task HandleAsyncStringMethod() - { - var handler = new Handler2(); - var messageMock = new Mock(); - _message = messageMock.Object; - _resolvers = new HandlerMethodArgumentResolverComposite(); - MethodInfo method = typeof(Handler2).GetMethod(nameof(Handler2.HandleAsyncStringMethodAsync)); - var result = Invoke(handler, method, 10, "stringArg") as Task; - string str = await result; - Assert.Equal(1, handler.InvocationCount); - Assert.Equal("10-stringArg", str); - } - - private object Invoke(object handler, MethodInfo method, params object[] providedArgs) - { - var handlerMethod = new InvocableHandlerMethod(handler, method) - { - MessageMethodArgumentResolvers = _resolvers - }; - - return handlerMethod.Invoke(_message, providedArgs); - } - - private long TimedInvoke(object handler, MethodInfo method, int count, params object[] providedArgs) - { - var handlerMethod = new InvocableHandlerMethod(handler, method); - HandlerMethod.Invoker invoker = handlerMethod.HandlerInvoker; - - long start = DateTime.UtcNow.Ticks; - - for (int i = 0; i < count; i++) - { - invoker(handler, providedArgs); - } - - long end = DateTime.UtcNow.Ticks; - _outputHelper.WriteLine("Ticks: " + (end - start)); - return end - start; - } - - private long TimedReflectionInvoke(object handler, MethodInfo method, int count, params object[] providedArgs) - { - long start = DateTime.UtcNow.Ticks; - - for (int i = 0; i < count; i++) - { - method.Invoke(handler, providedArgs); - } - - long end = DateTime.UtcNow.Ticks; - _outputHelper.WriteLine("Ticks: " + (end - start)); - return end - start; - } - - private StubArgumentResolver GetStubResolver(int index) - { - return (StubArgumentResolver)_resolvers.Resolvers[index]; - } - - internal sealed class Handler - { - public string Handle(int? intArg, string stringArg) - { - return $"{(intArg.HasValue ? intArg.Value.ToString(CultureInfo.InvariantCulture) : "null")}-{stringArg ?? "null"}"; - } - - public void Handle(double amount) - { - // Intentionally left empty. - } - - public void HandleWithException(Exception ex) - { - throw ex; - } - } - - internal sealed class Handler2 - { - public long InvocationCount { get; private set; } - public double DoubleValue { get; private set; } - public int IntValue { get; private set; } - public object ObjectValue { get; private set; } - - public string HandleNullablePrimitive(int? intArg, string stringArg) - { - InvocationCount++; - return $"{(intArg == null ? "null" : intArg.Value.ToString(CultureInfo.InvariantCulture))}-{stringArg ?? "null"}"; - } - - public void HandleSinglePrimitiveReturnVoid(double value) - { - InvocationCount++; - DoubleValue = value; - } - - public void HandleMultiPrimitiveReturnVoid(double value1, int value2) - { - InvocationCount++; - DoubleValue = value1; - IntValue = value2; - } - - public void HandleMultiReturnVoid(double value1, int value2, Handler2 value3) - { - InvocationCount++; - DoubleValue = value1; - IntValue = value2; - ObjectValue = value3; - } - - public void HandleWithException(Exception ex) - { - InvocationCount++; - throw ex; - } - - public Task HandleAsyncVoidMethodAsync(double value) - { - InvocationCount++; - DoubleValue = value; - return Task.CompletedTask; - } - - public Task HandleAsyncStringMethodAsync(int? intArg, string stringArg) - { - InvocationCount++; - string result = $"{(intArg == null ? "null" : intArg.Value.ToString(CultureInfo.InvariantCulture))}-{stringArg ?? "null"}"; - return Task.FromResult(result); - } - } - - internal sealed class ExceptionRaisingArgumentResolver : IHandlerMethodArgumentResolver - { - public bool SupportsParameter(ParameterInfo parameter) - { - return true; - } - - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - throw new ArgumentException("oops, can't read"); - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Invocation/MethodMessageHandlerTest.cs b/src/Messaging/test/Messaging.Test/Handler/Invocation/MethodMessageHandlerTest.cs deleted file mode 100644 index 63ba9de84a..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Invocation/MethodMessageHandlerTest.cs +++ /dev/null @@ -1,248 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Handler.Invocation; - -public sealed class MethodMessageHandlerTest -{ - private const string DestinationHeader = "destination"; - - private readonly TestMethodMessageHandler _messageHandler; - - private readonly TestController _testController; - - public MethodMessageHandlerTest() - { - var destinationPrefixes = new List - { - "/test" - }; - - _messageHandler = new TestMethodMessageHandler - { - DestinationPrefixes = destinationPrefixes - }; - - _testController = new TestController(); - _messageHandler.RegisterHandler(_testController); - } - - [Fact] - public void DuplicateMapping() - { - Assert.Throws(() => _messageHandler.RegisterHandler(new DuplicateMappingsController())); - } - - [Fact] - public void RegisteredMappings() - { - IDictionary handlerMethods = _messageHandler.HandlerMethods; - - Assert.NotNull(handlerMethods); - Assert.Equal(3, handlerMethods.Count); - } - - [Fact] - public void PatternMatch() - { - MethodInfo method = typeof(TestController).GetMethod(nameof(TestController.HandlerPathMatchWildcard)); - _messageHandler.RegisterHandlerMethodPublic(_testController, method, "/handlerPathMatch*"); - - _messageHandler.HandleMessage(ToDestination("/test/handlerPathMatchFoo")); - - Assert.Equal("PathMatchWildcard", _testController.Method); - } - - [Fact] - public void BestMatch() - { - MethodInfo method = typeof(TestController).GetMethod(nameof(TestController.BestMatch)); - _messageHandler.RegisterHandlerMethodPublic(_testController, method, "/bestmatch/{foo}/path"); - - method = typeof(TestController).GetMethod(nameof(TestController.SecondBestMatch)); - _messageHandler.RegisterHandlerMethodPublic(_testController, method, "/bestmatch/*/*"); - - _messageHandler.HandleMessage(ToDestination("/test/bestmatch/bar/path")); - - Assert.Equal("BestMatch", _testController.Method); - } - - [Fact] - public void ArgumentResolution() - { - _messageHandler.HandleMessage(ToDestination("/test/HandlerArgumentResolver")); - - Assert.Equal("HandlerArgumentResolver", _testController.Method); - Assert.NotNull(_testController.Arguments["message"]); - } - - [Fact] - public void HandleException() - { - _messageHandler.HandleMessage(ToDestination("/test/HandlerThrowsExc")); - - Assert.Equal("InvalidOperationException", _testController.Method); - Assert.NotNull(_testController.Arguments["exception"]); - } - - private IMessage ToDestination(string destination) - { - return MessageBuilder.WithPayload(Array.Empty()).SetHeader(DestinationHeader, destination).Build(); - } - - internal sealed class TestController - { - public Dictionary Arguments { get; } = new(); - - public string Method { get; set; } - - public void HandlerPathMatchWildcard() - { - Method = "PathMatchWildcard"; - } - - public void HandlerArgumentResolver(IMessage message) - { - Method = "HandlerArgumentResolver"; - Arguments.Add("message", message); - } - - public void HandlerThrowsExc() - { - throw new InvalidOperationException(); - } - - public void BestMatch() - { - Method = "BestMatch"; - } - - public void SecondBestMatch() - { - Method = "SecondBestMatch"; - } - - public void HandleInvalidOperationException(InvalidOperationException exception) - { - Method = "InvalidOperationException"; - Arguments.Add("exception", exception); - } - } - - internal sealed class DuplicateMappingsController - { - public void HandlerFoo() - { - } - - public void HandlerFoo(string arg) - { - } - } - - internal sealed class TestMethodMessageHandler : AbstractMethodMessageHandler - { - private readonly IPathMatcher _pathMatcher = new AntPathMatcher(); - - public void RegisterHandler(object handler) - { - DetectHandlerMethods(handler); - } - - public void RegisterHandlerMethodPublic(object handler, MethodInfo method, string mapping) - { - RegisterHandlerMethod(handler, method, mapping); - } - - protected override IList InitArgumentResolvers() - { - var resolvers = new List - { - new MessageMethodArgumentResolver(new SimpleMessageConverter()) - }; - - resolvers.AddRange(CustomArgumentResolvers); - return resolvers; - } - - protected override IList InitReturnValueHandlers() - { - var handlers = new List(); - handlers.AddRange(CustomReturnValueHandlers); - return handlers; - } - - protected override string GetMappingForMethod(MethodInfo method, Type handlerType) - { - string methodName = method.Name; - - if (methodName.StartsWith("Handler", StringComparison.Ordinal)) - { - return $"/{methodName}"; - } - - return null; - } - - protected override ISet GetDirectLookupDestinations(string mapping) - { - ISet result = new HashSet(); - - if (!_pathMatcher.IsPattern(mapping)) - { - result.Add(mapping); - } - - return result; - } - - protected override string GetDestination(IMessage message) - { - return (string)message.Headers[DestinationHeader]; - } - - protected override string GetMatchingMapping(string mapping, IMessage message) - { - string destination = GetLookupDestination(GetDestination(message)); - Assert.NotNull(destination); - return mapping == destination || _pathMatcher.Match(mapping, destination) ? mapping : null; - } - - protected override IComparer GetMappingComparer(IMessage message) - { - return new MappingComparer(message); - } - - protected override AbstractExceptionHandlerMethodResolver CreateExceptionHandlerMethodResolverFor(Type beanType) - { - return new TestExceptionResolver(beanType); - } - - internal sealed class MappingComparer : IComparer - { - private readonly IMessage _message; - - public MappingComparer(IMessage message) - { - _message = message; - } - - public int Compare(string info1, string info2) - { - var cond1 = new DestinationPatternsMessageCondition(info1); - var cond2 = new DestinationPatternsMessageCondition(info2); - return cond1.CompareTo(cond2, _message); - } - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Invocation/ResolvableMethod.cs b/src/Messaging/test/Messaging.Test/Handler/Invocation/ResolvableMethod.cs deleted file mode 100644 index c56fa347ce..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Invocation/ResolvableMethod.cs +++ /dev/null @@ -1,357 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common; -using static Steeltoe.Messaging.Test.Handler.Attributes.MessagingPredicates; - -namespace Steeltoe.Messaging.Test.Handler.Invocation; - -internal sealed class ResolvableMethod -{ - public MethodInfo Method { get; } - - public ParameterInfo ReturnType => Method.ReturnParameter; - - public ResolvableMethod(MethodInfo method) - { - ArgumentGuard.NotNull(method); - - Method = method; - } - - public static Builder On() - { - return new Builder(); - } - - public ParameterInfo Arg(Type type) - { - return new ArgResolver(this).Arg(type); - } - - public ArgResolver Annotation(params IPredicate[] filters) - { - return new ArgResolver(this, filters); - } - - public ArgResolver AnnotationPresent(params Type[] annotationTypes) - { - return new ArgResolver(this).AnnotationPresent(annotationTypes); - } - - public ArgResolver AnnotationNotPresent(params Type[] annotationTypes) - { - return new ArgResolver(this).AnnotationNotPresent(annotationTypes); - } - - public override string ToString() - { - return $"ResolvableMethod={Method}"; - } - - internal sealed class Builder - { - private readonly Type _objectClass; - - private readonly List> _filters = new(); - - public Builder() - { - _objectClass = typeof(T); - } - - public Builder Named(string methodName) - { - AddFilter($"methodName={methodName}", method => method.Name == methodName); - return this; - } - - public Builder ArgTypes(params Type[] types) - { - AddFilter($"argTypes={string.Join(",", types)}", method => - { - Type[] paramTypes = method.GetParameters().Select(p => p.ParameterType).ToArray(); - - if (paramTypes.Length != types.Length) - { - return false; - } - - for (int i = 0; i < types.Length; i++) - { - if (types[i] != paramTypes[i]) - { - return false; - } - } - - return true; - }); - - return this; - } - - public Builder Annotation(params IPredicate[] filters) - { - _filters.AddRange(filters); - return this; - } - - public Builder AnnotationPresent(params Type[] annotationTypes) - { - string message = $"annotationPresent={string.Join(",", annotationTypes)}"; - - AddFilter(message, method => - { - foreach (Type type in annotationTypes) - { - if (method.GetCustomAttribute(type) == null) - { - return false; - } - } - - return true; - }); - - return this; - } - - public Builder AnnotationNotPresent(params Type[] annotationTypes) - { - string message = $"annotationNotPresent={string.Join(",", annotationTypes)}"; - - AddFilter(message, method => - { - if (annotationTypes.Length != 0) - { - foreach (Type type in annotationTypes) - { - if (method.GetCustomAttribute(type) != null) - { - return false; - } - } - - return true; - } - - return method.GetCustomAttributes().ToList().Count == 0; - }); - - return this; - } - - public Builder Returning(Type returnType) - { - string message = $"returnType={returnType}"; - AddFilter(message, method => method.ReturnType == returnType); - return this; - } - - public ResolvableMethod Build() - { - List methods = _objectClass.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static) - .Where(IsMatch).ToList(); - - if (methods.Count == 0) - { - throw new InvalidOperationException($"No matching method: {this}"); - } - - if (methods.Count > 1) - { - throw new InvalidOperationException($"Multiple matching methods: {this}"); - } - - return new ResolvableMethod(methods[0]); - } - - // Build & resolve shortcuts... - public MethodInfo ResolveMethod() - { - return Build().Method; - } - - public MethodInfo ResolveMethod(string methodName) - { - return Named(methodName).Build().Method; - } - - public ParameterInfo ResolveReturnType() - { - return Build().ReturnType; - } - - public ParameterInfo ResolveReturnType(Type returnType) - { - return Returning(returnType).Build().ReturnType; - } - - private void AddFilter(string message, Func func) - { - _filters.Add(new LabeledPredicate(message, func)); - } - - private bool IsMatch(MethodInfo method) - { - foreach (IPredicate predicate in _filters) - { - if (!predicate.Test(method)) - { - return false; - } - } - - return true; - } - } - - internal sealed class ArgResolver - { - private readonly List> _filters = new(); - - private readonly ResolvableMethod _resolvable; - - public ArgResolver(ResolvableMethod resolvable, params IPredicate[] filters) - { - ArgumentGuard.NotNull(resolvable); - - _resolvable = resolvable; - _filters.AddRange(filters); - } - - public ArgResolver Annotation(params IPredicate[] filters) - { - _filters.AddRange(filters); - return this; - } - - public ArgResolver AnnotationPresent(params Type[] annotationTypes) - { - _filters.Add(new FuncPredicate(param => - { - foreach (Type attr in annotationTypes) - { - if (param.GetCustomAttribute(attr) == null) - { - return false; - } - } - - return true; - })); - - return this; - } - - public ArgResolver AnnotationNotPresent(params Type[] annotationTypes) - { - _filters.Add(new FuncPredicate(param => - { - if (annotationTypes.Length > 0) - { - foreach (Type attr in annotationTypes) - { - if (param.GetCustomAttribute(attr) != null) - { - return false; - } - } - - return true; - } - - return param.GetCustomAttributes().ToList().Count == 0; - })); - - return this; - } - - public ParameterInfo Arg(Type type) - { - _filters.Add(new FuncPredicate(param => param.ParameterType == type)); - - return Arg(); - } - - public ParameterInfo Arg() - { - List matches = ApplyFilters(); - - if (matches.Count == 0) - { - throw new InvalidOperationException($"No matching arg in method: {_resolvable.Method}"); - } - - if (matches.Count > 1) - { - throw new InvalidOperationException($"Multiple matching args in method: {_resolvable.Method} {string.Join(",", matches)}"); - } - - return matches[0]; - } - - private List ApplyFilters() - { - var matches = new List(); - - for (int i = 0; i < _resolvable.Method.GetParameters().Length; i++) - { - ParameterInfo param = _resolvable.Method.GetParameters()[i]; - - bool allFiltersMatch = true; - - foreach (IPredicate p in _filters) - { - if (!p.Test(param)) - { - allFiltersMatch = false; - } - } - - if (allFiltersMatch) - { - matches.Add(param); - } - } - - return matches; - } - - internal sealed class FuncPredicate : IPredicate - { - private readonly Func _func; - - public FuncPredicate(Func func) - { - ArgumentGuard.NotNull(func); - - _func = func; - } - - public bool Test(ParameterInfo t) - { - return _func(t); - } - } - } - - internal sealed class LabeledPredicate : IPredicate - { - private readonly Func _del; - - public LabeledPredicate(string label, Func del) - { - _ = label; - _del = del; - } - - public bool Test(T t) - { - return _del(t); - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Invocation/StubArgumentResolver.cs b/src/Messaging/test/Messaging.Test/Handler/Invocation/StubArgumentResolver.cs deleted file mode 100644 index bb8db5be10..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Invocation/StubArgumentResolver.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Test.Handler.Invocation; - -internal sealed class StubArgumentResolver : IHandlerMethodArgumentResolver -{ - private readonly Type _valueType; - - private readonly object _value; - - public List ResolvedParameters { get; } = new(); - - public StubArgumentResolver(object value) - : this(value.GetType(), value) - { - } - - public StubArgumentResolver(Type valueType) - : this(valueType, null) - { - } - - public StubArgumentResolver(Type valueType, object value) - { - _valueType = valueType; - _value = value; - } - - public bool SupportsParameter(ParameterInfo parameter) - { - return parameter.ParameterType.IsAssignableFrom(_valueType); - } - - public object ResolveArgument(ParameterInfo parameter, IMessage message) - { - ResolvedParameters.Add(parameter); - return _value; - } -} diff --git a/src/Messaging/test/Messaging.Test/Handler/Invocation/TestExceptionResolver.cs b/src/Messaging/test/Messaging.Test/Handler/Invocation/TestExceptionResolver.cs deleted file mode 100644 index 31d8ae61e2..0000000000 --- a/src/Messaging/test/Messaging.Test/Handler/Invocation/TestExceptionResolver.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Messaging.Test.Handler.Invocation; - -internal sealed class TestExceptionResolver : AbstractExceptionHandlerMethodResolver -{ - public TestExceptionResolver(Type handlerType) - : base(InitExceptionMappings(handlerType)) - { - } - - private static IDictionary InitExceptionMappings(Type handlerType) - { - IDictionary result = new Dictionary(); - - foreach (MethodInfo method in GetExceptionHandlerMethods(handlerType)) - { - foreach (Type exception in GetExceptionsFromMethodSignature(method)) - { - result.Add(exception, method); - } - } - - return result; - } - - private static IEnumerable GetExceptionHandlerMethods(Type handlerType) - { - var results = new List(); - MethodInfo[] methods = handlerType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); - - foreach (MethodInfo method in methods) - { - if (method.Name.StartsWith("Handle", StringComparison.Ordinal) && method.Name.EndsWith("Exception", StringComparison.Ordinal)) - { - results.Add(method); - } - } - - return results; - } -} diff --git a/src/Messaging/test/Messaging.Test/MessageHeadersTest.cs b/src/Messaging/test/Messaging.Test/MessageHeadersTest.cs deleted file mode 100644 index 2bc5bf5e68..0000000000 --- a/src/Messaging/test/Messaging.Test/MessageHeadersTest.cs +++ /dev/null @@ -1,181 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Xunit; - -namespace Steeltoe.Messaging.Test; - -public sealed class MessageHeadersTest -{ - [Fact] - public void TestTimestamp() - { - var headers = new MessageHeaders(); - Assert.NotNull(headers.Timestamp); - } - - [Fact] - public async Task TestTimestampOverwritten() - { - var headers1 = new MessageHeaders(); - await Task.Delay(50); - var headers2 = new MessageHeaders(headers1); - Assert.NotEqual(headers1.Timestamp, headers2.Timestamp); - } - - [Fact] - public void TestTimestampProvided() - { - var headers = new MessageHeaders(null, null, 10L); - Assert.Equal(10L, (long)headers.Timestamp); - } - - [Fact] - public void TestTimestampProvidedNullValue() - { - var input = new Dictionary - { - { MessageHeaders.TimestampName, 1L } - }; - - var headers = new MessageHeaders(input, null, null); - Assert.NotNull(headers.Timestamp); - } - - [Fact] - public void TestTimestampNone() - { - var headers = new MessageHeaders(null, null, -1L); - Assert.Null(headers.Timestamp); - } - - [Fact] - public void TestIdOverwritten() - { - var headers1 = new MessageHeaders(); - var headers2 = new MessageHeaders(headers1); - Assert.NotEqual(headers1.Id, headers2.Id); - } - - [Fact] - public void TestId() - { - var headers = new MessageHeaders(); - Assert.NotNull(headers.Id); - } - - [Fact] - public void TestIdProvided() - { - var id = Guid.NewGuid(); - var headers = new MessageHeaders(null, id.ToString(), null); - Assert.Equal(id.ToString(), headers.Id); - } - - [Fact] - public void TestIdProvidedNullValue() - { - var id = Guid.NewGuid(); - - var input = new Dictionary - { - { MessageHeaders.IdName, id } - }; - - var headers = new MessageHeaders(input, null, null); - Assert.NotNull(headers.Id); - } - - [Fact] - public void TestIdNone() - { - var headers = new MessageHeaders(null, MessageHeaders.IdValueNone, null); - Assert.Null(headers.Id); - } - - [Fact] - public void TestNonTypedAccessOfHeaderValue() - { - var map = new Dictionary - { - { "test", 123 } - }; - - var headers = new MessageHeaders(map); - Assert.Equal(123, headers["test"]); - } - - [Fact] - public void TestTypedAccessOfHeaderValue() - { - var map = new Dictionary - { - { "test", 123 } - }; - - var headers = new MessageHeaders(map); - Assert.Equal(123, headers.Get("test")); - } - - [Fact] - public void TestHeaderValueAccessWithIncorrectType() - { - var map = new Dictionary - { - { "test", 123 } - }; - - var headers = new MessageHeaders(map); - Assert.Throws(() => headers.Get("test")); - } - - [Fact] - public void TestNullHeaderValue() - { - var map = new Dictionary(); - var headers = new MessageHeaders(map); - headers.TryGetValue("nosuchattribute", out object val); - Assert.Null(val); - } - - [Fact] - public void TestNullHeaderValueWithTypedAccess() - { - var map = new Dictionary(); - var headers = new MessageHeaders(map); - Assert.Null(headers.Get("nosuchattribute")); - } - - [Fact] - public void TestHeaderKeys() - { - var map = new Dictionary - { - { "key1", "val1" }, - { "key2", 123 } - }; - - var headers = new MessageHeaders(map); - ICollection keys = headers.Keys; - Assert.True(keys.Contains("key1")); - Assert.True(keys.Contains("key2")); - } - - [Fact] - public void SubclassWithCustomIdAndNoTimestamp() - { - var id = Guid.NewGuid(); - MessageHeaders headers = new MyMessageHeaders(id); - Assert.Equal(id.ToString(), headers.Id); - Assert.Single(headers); - } - - private sealed class MyMessageHeaders : MessageHeaders - { - public MyMessageHeaders(Guid id) - : base(null, id.ToString(), -1L) - { - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Steeltoe.Messaging.Test.csproj b/src/Messaging/test/Messaging.Test/Steeltoe.Messaging.Test.csproj deleted file mode 100644 index 59d3971bcb..0000000000 --- a/src/Messaging/test/Messaging.Test/Steeltoe.Messaging.Test.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net8.0;net6.0 - - - - - - - - - - - - diff --git a/src/Messaging/test/Messaging.Test/StubMessageChannel.cs b/src/Messaging/test/Messaging.Test/StubMessageChannel.cs deleted file mode 100644 index 0c94946d03..0000000000 --- a/src/Messaging/test/Messaging.Test/StubMessageChannel.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.Test; - -internal sealed class StubMessageChannel : ISubscribableChannel -{ - private readonly List> _messages = new(); - - private readonly List _handlers = new(); - - public string ServiceName { get; set; } = "StubMessageChannel"; - - public ValueTask SendAsync(IMessage message, CancellationToken cancellationToken = default) - { - _messages.Add((IMessage)message); - return new ValueTask(true); - } - - public bool Send(IMessage message) - { - return Send(message, -1); - } - - public bool Send(IMessage message, int timeout) - { - _messages.Add((IMessage)message); - return true; - } - - public bool Subscribe(IMessageHandler handler) - { - _handlers.Add(handler); - return true; - } - - public bool Unsubscribe(IMessageHandler handler) - { - _handlers.Remove(handler); - return true; - } -} diff --git a/src/Messaging/test/Messaging.Test/Support/ChannelInterceptorTest.cs b/src/Messaging/test/Messaging.Test/Support/ChannelInterceptorTest.cs deleted file mode 100644 index 193a507d74..0000000000 --- a/src/Messaging/test/Messaging.Test/Support/ChannelInterceptorTest.cs +++ /dev/null @@ -1,304 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Moq; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Support; - -public sealed class ChannelInterceptorTest -{ - private readonly TaskSchedulerSubscribableChannel _channel; - - private readonly TestMessageHandler _messageHandler; - - public ChannelInterceptorTest() - { - _channel = new TaskSchedulerSubscribableChannel(); - _messageHandler = new TestMessageHandler(); - _channel.Subscribe(_messageHandler); - } - - [Fact] - public void PreSendInterceptorReturningModifiedMessage() - { - IMessage expected = new Mock().Object; - - var interceptor = new PreSendInterceptor - { - MessageToReturn = expected - }; - - _channel.AddInterceptor(interceptor); - _channel.Send(MessageBuilder.WithPayload("test").Build()); - - Assert.Single(_messageHandler.Messages); - IMessage result = _messageHandler.Messages[0]; - - Assert.NotNull(result); - Assert.Same(expected, result); - Assert.True(interceptor.WasAfterCompletionInvoked); - } - - [Fact] - public void PreSendInterceptorReturningNull() - { - var interceptor1 = new PreSendInterceptor(); - var interceptor2 = new NullReturningPreSendInterceptor(); - _channel.AddInterceptor(interceptor1); - _channel.AddInterceptor(interceptor2); - IMessage message = MessageBuilder.WithPayload("test").Build(); - _channel.Send(message); - - Assert.Equal(1, interceptor1.Counter); - Assert.Equal(1, interceptor2.Counter); - Assert.Empty(_messageHandler.Messages); - Assert.True(interceptor1.WasAfterCompletionInvoked); - Assert.False(interceptor2.WasAfterCompletionInvoked); - } - - [Fact] - public void PostSendInterceptorMessageWasSent() - { - var interceptor = new PostSendInterceptorMessageWasSentChannelInterceptor(_channel); - _channel.AddInterceptor(interceptor); - - _channel.Send(MessageBuilder.WithPayload("test").Build()); - Assert.True(interceptor.PreSendInvoked); - Assert.True(interceptor.CompletionInvoked); - } - - [Fact] - public void PostSendInterceptorMessageWasNotSent() - { - AbstractMessageChannel testChannel = new PostSendInterceptorMessageWasNotSentChannel(); - var interceptor = new PostSendInterceptorMessageWasNotSentInterceptor(testChannel); - - testChannel.AddInterceptor(interceptor); - - testChannel.Send(MessageBuilder.WithPayload("test").Build()); - Assert.True(interceptor.PreSendInvoked); - Assert.True(interceptor.CompletionInvoked); - } - - [Fact] - public void AfterCompletionWithSendException() - { - AbstractMessageChannel testChannel = new AfterCompletionWithSendExceptionChannel(); - - var interceptor1 = new PreSendInterceptor(); - var interceptor2 = new PreSendInterceptor(); - testChannel.AddInterceptor(interceptor1); - testChannel.AddInterceptor(interceptor2); - - try - { - testChannel.Send(MessageBuilder.WithPayload("test").Build()); - } - catch (Exception ex) - { - Assert.Equal("Simulated exception", ex.InnerException.Message); - } - - Assert.True(interceptor1.WasAfterCompletionInvoked); - Assert.True(interceptor2.WasAfterCompletionInvoked); - } - - [Fact] - public void AfterCompletionWithPreSendException() - { - var interceptor1 = new PreSendInterceptor(); - - var interceptor2 = new PreSendInterceptor - { - ExceptionToRaise = new Exception("Simulated exception") - }; - - _channel.AddInterceptor(interceptor1); - _channel.AddInterceptor(interceptor2); - - try - { - _channel.Send(MessageBuilder.WithPayload("test").Build()); - } - catch (Exception ex) - { - Assert.Equal("Simulated exception", ex.InnerException.Message); - } - - Assert.True(interceptor1.WasAfterCompletionInvoked); - Assert.False(interceptor2.WasAfterCompletionInvoked); - } - - internal sealed class AfterCompletionWithSendExceptionChannel : AbstractMessageChannel - { - public AfterCompletionWithSendExceptionChannel() - { - Writer = new AfterCompletionWithSendExceptionChannelWriter(this); - } - - protected override bool DoSendInternal(IMessage message, CancellationToken cancellationToken) - { - throw new Exception("Simulated exception"); - } - } - - internal sealed class AfterCompletionWithSendExceptionChannelWriter : AbstractMessageChannelWriter - { - public AfterCompletionWithSendExceptionChannelWriter(AbstractMessageChannel channel, ILogger logger = null) - : base(channel, logger) - { - } - } - - internal sealed class PostSendInterceptorMessageWasNotSentChannel : AbstractMessageChannel - { - public PostSendInterceptorMessageWasNotSentChannel() - { - Writer = new PostSendInterceptorMessageWasNotSentChannelWriter(this); - } - - protected override bool DoSendInternal(IMessage message, CancellationToken cancellationToken) - { - return false; - } - } - - internal sealed class PostSendInterceptorMessageWasNotSentChannelWriter : AbstractMessageChannelWriter - { - public PostSendInterceptorMessageWasNotSentChannelWriter(AbstractMessageChannel channel, ILogger logger = null) - : base(channel, logger) - { - } - } - - internal sealed class PostSendInterceptorMessageWasNotSentInterceptor : AbstractChannelInterceptor - { - public bool PreSendInvoked { get; private set; } - public bool CompletionInvoked { get; private set; } - public IMessageChannel ExpectedChannel { get; } - - public PostSendInterceptorMessageWasNotSentInterceptor(IMessageChannel expectedChannel) - { - ExpectedChannel = expectedChannel; - } - - public override void PostSend(IMessage message, IMessageChannel channel, bool sent) - { - AssertInput(message, channel, sent); - PreSendInvoked = true; - } - - public override void AfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception exception) - { - AssertInput(message, channel, sent); - CompletionInvoked = true; - } - - private void AssertInput(IMessage message, IMessageChannel channel, bool sent) - { - Assert.NotNull(message); - Assert.NotNull(channel); - Assert.Same(ExpectedChannel, channel); - Assert.False(sent); - } - } - - internal sealed class PostSendInterceptorMessageWasSentChannelInterceptor : AbstractChannelInterceptor - { - public bool PreSendInvoked { get; set; } - public bool CompletionInvoked { get; set; } - public IMessageChannel ExpectedChannel { get; } - - public PostSendInterceptorMessageWasSentChannelInterceptor(IMessageChannel expectedChannel) - { - ExpectedChannel = expectedChannel; - } - - public override void PostSend(IMessage message, IMessageChannel channel, bool sent) - { - AssertInput(message, channel, sent); - PreSendInvoked = true; - } - - public override void AfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception exception) - { - AssertInput(message, channel, sent); - CompletionInvoked = true; - } - - private void AssertInput(IMessage message, IMessageChannel channel, bool sent) - { - Assert.NotNull(message); - Assert.NotNull(channel); - Assert.Same(ExpectedChannel, channel); - Assert.True(sent); - } - } - - internal sealed class TestMessageHandler : IMessageHandler - { - public List Messages { get; } = new(); - - public string ServiceName { get; set; } = nameof(TestMessageHandler); - - public void HandleMessage(IMessage message) - { - Messages.Add(message); - } - } - - internal abstract class AbstractTestInterceptor : AbstractTaskSchedulerChannelInterceptor - { - private volatile int _counter; - - private volatile bool _afterCompletionInvoked; - - public int Counter => _counter; - - public bool WasAfterCompletionInvoked => _afterCompletionInvoked; - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - Assert.NotNull(message); - Interlocked.Increment(ref _counter); - return message; - } - - public override void AfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception exception) - { - _afterCompletionInvoked = true; - } - } - - internal sealed class PreSendInterceptor : AbstractTestInterceptor - { - public IMessage MessageToReturn { get; set; } - - public Exception ExceptionToRaise { get; set; } - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - base.PreSend(message, channel); - - if (ExceptionToRaise != null) - { - throw ExceptionToRaise; - } - - return MessageToReturn ?? message; - } - } - - internal sealed class NullReturningPreSendInterceptor : AbstractTestInterceptor - { - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - base.PreSend(message, channel); - return null; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Support/ErrorMessageTest.cs b/src/Messaging/test/Messaging.Test/Support/ErrorMessageTest.cs deleted file mode 100644 index fa61d6a0d9..0000000000 --- a/src/Messaging/test/Messaging.Test/Support/ErrorMessageTest.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Support; - -public sealed class ErrorMessageTest -{ - [Fact] - public void TestToString() - { - var em = new ErrorMessage(new Exception("foo")); - string emString = em.ToString(); - Assert.DoesNotContain("original", emString, StringComparison.Ordinal); - - em = new ErrorMessage(new Exception("foo"), Message.Create("bar")); - emString = em.ToString(); - Assert.Contains("original", emString, StringComparison.Ordinal); - Assert.Contains(em.OriginalMessage.ToString(), emString, StringComparison.Ordinal); - } - - [Fact] - public void TestAnyExceptionType() - { - var em = new ErrorMessage(new InvalidOperationException("foo")); - string emString = em.ToString(); - Assert.Contains("InvalidOperationException", emString, StringComparison.Ordinal); - } -} diff --git a/src/Messaging/test/Messaging.Test/Support/MessageBuilderTest.cs b/src/Messaging/test/Messaging.Test/Support/MessageBuilderTest.cs deleted file mode 100644 index 4e5564844d..0000000000 --- a/src/Messaging/test/Messaging.Test/Support/MessageBuilderTest.cs +++ /dev/null @@ -1,214 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Support; - -public sealed class MessageBuilderTest -{ - [Fact] - public void TestSimpleIMessageCreation() - { - IMessage message = MessageBuilder.WithPayload("foo").Build(); - Assert.Equal("foo", message.Payload); - } - - [Fact] - public void TestHeaderValues() - { - IMessage message = MessageBuilder.WithPayload("test").SetHeader("foo", "bar").SetHeader("count", 123).Build(); - Assert.Equal("bar", message.Headers.Get("foo")); - Assert.Equal(123, message.Headers.Get("count")); - } - - [Fact] - public void TestCopiedHeaderValues() - { - IMessage message1 = MessageBuilder.WithPayload("test1").SetHeader("foo", "1").SetHeader("bar", "2").Build(); - IMessage message2 = MessageBuilder.WithPayload("test2").CopyHeaders(message1.Headers).SetHeader("foo", "42").SetHeaderIfAbsent("bar", "99").Build(); - Assert.Equal("test1", message1.Payload); - Assert.Equal("test2", message2.Payload); - Assert.Equal("1", message1.Headers.Get("foo")); - Assert.Equal("42", message2.Headers.Get("foo")); - Assert.Equal("2", message1.Headers.Get("bar")); - Assert.Equal("2", message2.Headers.Get("bar")); - } - - [Fact] - public void TestIdHeaderValueReadOnly() - { - var id = Guid.NewGuid(); - Assert.Throws(() => MessageBuilder.WithPayload("test").SetHeader(MessageHeaders.IdName, id)); - } - - [Fact] - public void TestTimestampValueReadOnly() - { - const long timestamp = 12345L; - Assert.Throws(() => MessageBuilder.WithPayload("test").SetHeader(MessageHeaders.TimestampName, timestamp).Build()); - } - - [Fact] - public void CopyHeadersIfAbsent() - { - IMessage message1 = MessageBuilder.WithPayload("test1").SetHeader("foo", "bar").Build(); - IMessage message2 = MessageBuilder.WithPayload("test2").SetHeader("foo", 123).CopyHeadersIfAbsent(message1.Headers).Build(); - Assert.Equal("test2", message2.Payload); - Assert.Equal(123, message2.Headers.Get("foo")); - } - - [Fact] - public void CreateFromIMessage() - { - IMessage message1 = MessageBuilder.WithPayload("test").SetHeader("foo", "bar").Build(); - IMessage message2 = MessageBuilder.FromMessage(message1).Build(); - Assert.Equal("test", message2.Payload); - Assert.Equal("bar", message2.Headers.Get("foo")); - } - - [Fact] - public void CreateIdRegenerated() - { - IMessage message1 = MessageBuilder.WithPayload("test").SetHeader("foo", "bar").Build(); - IMessage message2 = MessageBuilder.FromMessage(message1).SetHeader("another", 1).Build(); - Assert.Equal("bar", message2.Headers.Get("foo")); - Assert.NotEqual(message1.Headers.Id, message2.Headers.Id); - } - - [Fact] - public void TestRemove() - { - IMessage message1 = MessageBuilder.WithPayload(1).SetHeader("foo", "bar").Build(); - IMessage message2 = MessageBuilder.FromMessage(message1).RemoveHeader("foo").Build(); - Assert.False(message2.Headers.ContainsKey("foo")); - } - - [Fact] - public void TestSettingToNullRemoves() - { - IMessage message1 = MessageBuilder.WithPayload(1).SetHeader("foo", "bar").Build(); - IMessage message2 = MessageBuilder.FromMessage(message1).SetHeader("foo", null).Build(); - Assert.False(message2.Headers.ContainsKey("foo")); - } - - [Fact] - public void TestNotModifiedSameIMessage() - { - IMessage original = MessageBuilder.WithPayload("foo").Build(); - IMessage result = MessageBuilder.FromMessage(original).Build(); - Assert.Equal(original, result); - } - - [Fact] - public void TestContainsHeaderNotModifiedSameIMessage() - { - IMessage original = MessageBuilder.WithPayload("foo").SetHeader("bar", 42).Build(); - IMessage result = MessageBuilder.FromMessage(original).Build(); - Assert.Equal(original, result); - } - - [Fact] - public void TestSameHeaderValueAddedNotModifiedSameIMessage() - { - IMessage original = MessageBuilder.WithPayload("foo").SetHeader("bar", 42).Build(); - IMessage result = MessageBuilder.FromMessage(original).SetHeader("bar", 42).Build(); - Assert.Equal(original, result); - } - - [Fact] - public void TestCopySameHeaderValuesNotModifiedSameIMessage() - { - DateTime current = DateTime.UtcNow; - - IDictionary originalHeaders = new Dictionary - { - { "b", "xyz" }, - { "c", current } - }; - - IMessage original = MessageBuilder.WithPayload("foo").SetHeader("a", 123).CopyHeaders(originalHeaders).Build(); - - IDictionary newHeaders = new Dictionary - { - { "a", 123 }, - { "b", "xyz" }, - { "c", current } - }; - - IMessage result = MessageBuilder.FromMessage(original).CopyHeaders(newHeaders).Build(); - Assert.Equal(original, result); - } - - [Fact] - public void TestBuildIMessageWithMutableHeaders() - { - var accessor = new MessageHeaderAccessor - { - LeaveMutable = true - }; - - IMessageHeaders headers = accessor.MessageHeaders; - IMessage message = MessageBuilder.CreateMessage("payload", headers); - accessor.SetHeader("foo", "bar"); - - Assert.Equal("bar", headers.Get("foo")); - Assert.Equal(accessor, MessageHeaderAccessor.GetAccessor(message, typeof(MessageHeaderAccessor))); - } - - [Fact] - public void TestBuildIMessageWithDefaultMutability() - { - var accessor = new MessageHeaderAccessor(); - IMessageHeaders headers = accessor.MessageHeaders; - IMessage message = MessageBuilder.CreateMessage("foo", headers); - - Assert.Throws(() => accessor.SetHeader("foo", "bar")); - - Assert.Equal(accessor, MessageHeaderAccessor.GetAccessor(message, typeof(MessageHeaderAccessor))); - } - - [Fact] - public void TestBuildIMessageWithoutIdAndTimestamp() - { - var headerAccessor = new MessageHeaderAccessor - { - IdGenerator = new TestIdGenerator() - }; - - IMessage message = MessageBuilder.CreateMessage("foo", headerAccessor.MessageHeaders); - Assert.Null(message.Headers.Id); - Assert.Null(message.Headers.Timestamp); - } - - [Fact] - public void TestBuildMultipleIMessages() - { - var headerAccessor = new MessageHeaderAccessor(); - AbstractMessageBuilder messageBuilder = MessageBuilder.WithPayload("payload").SetHeaders(headerAccessor); - - headerAccessor.SetHeader("foo", "bar1"); - IMessage message1 = messageBuilder.Build(); - - headerAccessor.SetHeader("foo", "bar2"); - IMessage message2 = messageBuilder.Build(); - - headerAccessor.SetHeader("foo", "bar3"); - IMessage message3 = messageBuilder.Build(); - - Assert.Equal("bar1", message1.Headers.Get("foo")); - Assert.Equal("bar2", message2.Headers.Get("foo")); - Assert.Equal("bar3", message3.Headers.Get("foo")); - } - - private sealed class TestIdGenerator : IIdGenerator - { - public string GenerateId() - { - return MessageHeaders.IdValueNone; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Support/MessageHeaderAccessorTest.cs b/src/Messaging/test/Messaging.Test/Support/MessageHeaderAccessorTest.cs deleted file mode 100644 index 91499ecf44..0000000000 --- a/src/Messaging/test/Messaging.Test/Support/MessageHeaderAccessorTest.cs +++ /dev/null @@ -1,386 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Support; -using Xunit; -using HeadersDictionary = System.Collections.Generic.IDictionary; - -namespace Steeltoe.Messaging.Test.Support; - -public sealed class MessageHeaderAccessorTest -{ - [Fact] - public void NewEmptyHeaders() - { - var accessor = new MessageHeaderAccessor(); - Assert.Empty(accessor.ToDictionary()); - } - - [Fact] - public void ExistingHeaders() - { - HeadersDictionary map = new Dictionary - { - { "foo", "bar" }, - { "bar", "baz" } - }; - - IMessage message = Message.Create("payload", map); - - var accessor = new MessageHeaderAccessor(message); - IMessageHeaders actual = accessor.MessageHeaders; - - Assert.Equal(3, ((HeadersDictionary)actual).Count); - Assert.Equal("bar", actual.Get("foo")); - Assert.Equal("baz", actual.Get("bar")); - } - - [Fact] - public async Task ExistingHeadersModification() - { - HeadersDictionary map = new Dictionary - { - { "foo", "bar" }, - { "bar", "baz" } - }; - - IMessage message = Message.Create("payload", map); - - await Task.Delay(50); - - var accessor = new MessageHeaderAccessor(message); - accessor.SetHeader("foo", "BAR"); - IMessageHeaders actual = accessor.MessageHeaders; - - Assert.Equal(3, ((HeadersDictionary)actual).Count); - Assert.NotEqual(message.Headers.Id, actual.Id); - Assert.Equal("BAR", actual.Get("foo")); - Assert.Equal("baz", actual.Get("bar")); - } - - [Fact] - public void TestRemoveHeader() - { - IMessage message = Message.Create("payload", SingletonMap("foo", "bar")); - var accessor = new MessageHeaderAccessor(message); - accessor.RemoveHeader("foo"); - HeadersDictionary headers = accessor.ToDictionary(); - Assert.False(headers.ContainsKey("foo")); - } - - [Fact] - public void TestRemoveHeaderEvenIfNull() - { - IMessage message = Message.Create("payload", SingletonMap("foo", null)); - var accessor = new MessageHeaderAccessor(message); - accessor.RemoveHeader("foo"); - HeadersDictionary headers = accessor.ToDictionary(); - Assert.False(headers.ContainsKey("foo")); - } - - [Fact] - public void RemoveHeaders() - { - HeadersDictionary map = new Dictionary - { - { "foo", "bar" }, - { "bar", "baz" } - }; - - IMessage message = Message.Create("payload", map); - var accessor = new MessageHeaderAccessor(message); - - accessor.RemoveHeaders("fo*"); - - IMessageHeaders actual = accessor.MessageHeaders; - Assert.Equal(2, ((HeadersDictionary)actual).Count); - Assert.Null(actual.Get("foo")); - Assert.Equal("baz", actual.Get("bar")); - } - - [Fact] - public void CopyHeaders() - { - HeadersDictionary map1 = new Dictionary - { - { "foo", "bar" } - }; - - IMessage message = Message.Create("payload", map1); - var accessor = new MessageHeaderAccessor(message); - - HeadersDictionary map2 = new Dictionary - { - { "foo", "BAR" }, - { "bar", "baz" } - }; - - accessor.CopyHeaders(map2); - - IMessageHeaders actual = accessor.MessageHeaders; - Assert.Equal(3, ((HeadersDictionary)actual).Count); - Assert.Equal("BAR", actual.Get("foo")); - Assert.Equal("baz", actual.Get("bar")); - } - - [Fact] - public void CopyHeadersIfAbsent() - { - HeadersDictionary map1 = new Dictionary - { - { "foo", "bar" } - }; - - IMessage message = Message.Create("payload", map1); - var accessor = new MessageHeaderAccessor(message); - - HeadersDictionary map2 = new Dictionary - { - { "foo", "BAR" }, - { "bar", "baz" } - }; - - accessor.CopyHeadersIfAbsent(map2); - - IMessageHeaders actual = accessor.MessageHeaders; - Assert.Equal(3, ((HeadersDictionary)actual).Count); - Assert.Equal("bar", actual.Get("foo")); - Assert.Equal("baz", actual.Get("bar")); - } - - [Fact] - public void CopyHeadersFromNullMap() - { - var headers = new MessageHeaderAccessor(); - headers.CopyHeaders(null); - headers.CopyHeadersIfAbsent(null); - - Assert.Single(headers.MessageHeaders); - Assert.Contains("id", ((HeadersDictionary)headers.MessageHeaders).Keys); - } - - [Fact] - public void ToDictionary() - { - var accessor = new MessageHeaderAccessor(); - - accessor.SetHeader("foo", "bar1"); - HeadersDictionary map1 = accessor.ToDictionary(); - - accessor.SetHeader("foo", "bar2"); - HeadersDictionary map2 = accessor.ToDictionary(); - - accessor.SetHeader("foo", "bar3"); - HeadersDictionary map3 = accessor.ToDictionary(); - - Assert.Single(map1); - Assert.Single(map2); - Assert.Single(map3); - - Assert.Equal("bar1", map1["foo"]); - Assert.Equal("bar2", map2["foo"]); - Assert.Equal("bar3", map3["foo"]); - } - - [Fact] - public void LeaveMutable() - { - var accessor = new MessageHeaderAccessor(); - accessor.SetHeader("foo", "bar"); - accessor.LeaveMutable = true; - IMessageHeaders headers = accessor.MessageHeaders; - IMessage message = MessageBuilder.CreateMessage("payload", headers); - - accessor.SetHeader("foo", "baz"); - - Assert.Equal("baz", headers.Get("foo")); - Assert.Same(accessor, MessageHeaderAccessor.GetAccessor(message, typeof(MessageHeaderAccessor))); - } - - [Fact] - public void LeaveMutableDefaultBehavior() - { - var accessor = new MessageHeaderAccessor(); - accessor.SetHeader("foo", "bar"); - IMessageHeaders headers = accessor.MessageHeaders; - IMessage message = MessageBuilder.CreateMessage("payload", headers); - - Assert.Throws(() => accessor.LeaveMutable = true); - - Assert.Throws(() => accessor.SetHeader("foo", "baz")); - - Assert.Equal("bar", headers.Get("foo")); - Assert.Same(accessor, MessageHeaderAccessor.GetAccessor(message, typeof(MessageHeaderAccessor))); - } - - [Fact] - public void GetAccessor() - { - var expected = new MessageHeaderAccessor(); - IMessage message = MessageBuilder.CreateMessage("payload", expected.MessageHeaders); - Assert.Same(expected, MessageHeaderAccessor.GetAccessor(message, typeof(MessageHeaderAccessor))); - } - - [Fact] - public void GetMutableAccessorSameInstance() - { - var expected = new TestMessageHeaderAccessor - { - LeaveMutable = true - }; - - IMessage message = MessageBuilder.CreateMessage("payload", expected.MessageHeaders); - - MessageHeaderAccessor actual = MessageHeaderAccessor.GetMutableAccessor(message); - Assert.NotNull(actual); - Assert.True(actual.IsMutable); - Assert.Same(expected, actual); - - actual.SetHeader("foo", "bar"); - Assert.Equal("bar", message.Headers.Get("foo")); - } - - [Fact] - public void GetMutableAccessorNewInstance() - { - IMessage message = MessageBuilder.WithPayload("payload").Build(); - - MessageHeaderAccessor actual = MessageHeaderAccessor.GetMutableAccessor(message); - Assert.NotNull(actual); - Assert.True(actual.IsMutable); - - actual.SetHeader("foo", "bar"); - } - - [Fact] - public void GetMutableAccessorNewInstanceMatchingType() - { - var expected = new TestMessageHeaderAccessor(); - IMessage message = MessageBuilder.CreateMessage("payload", expected.MessageHeaders); - - MessageHeaderAccessor actual = MessageHeaderAccessor.GetMutableAccessor(message); - Assert.NotNull(actual); - Assert.True(actual.IsMutable); - Assert.Equal(typeof(TestMessageHeaderAccessor), actual.GetType()); - } - - [Fact] - public void TimestampEnabled() - { - var accessor = new MessageHeaderAccessor - { - EnableTimestamp = true - }; - - Assert.NotNull(accessor.MessageHeaders.Timestamp); - } - - [Fact] - public void TimestampDefaultBehavior() - { - var accessor = new MessageHeaderAccessor(); - Assert.Null(accessor.MessageHeaders.Timestamp); - } - - [Fact] - public void IdGeneratorCustom() - { - var id = Guid.NewGuid(); - - var accessor = new MessageHeaderAccessor - { - IdGenerator = new TestIdGenerator - { - Id = id.ToString() - } - }; - - Assert.Equal(id.ToString(), accessor.MessageHeaders.Id); - } - - [Fact] - public void IdGeneratorDefaultBehavior() - { - var accessor = new MessageHeaderAccessor(); - Assert.NotNull(accessor.MessageHeaders.Id); - } - - [Fact] - public void IdTimestampWithMutableHeaders() - { - var accessor = new MessageHeaderAccessor - { - IdGenerator = new TestIdGenerator - { - Id = MessageHeaders.IdValueNone - }, - EnableTimestamp = false, - LeaveMutable = true - }; - - IMessageHeaders headers = accessor.MessageHeaders; - - Assert.Null(headers.Id); - Assert.Null(headers.Timestamp); - - var id = Guid.NewGuid(); - - accessor.IdGenerator = new TestIdGenerator - { - Id = id.ToString() - }; - - accessor.EnableTimestamp = true; - accessor.SetImmutable(); - - Assert.Equal(id.ToString(), accessor.MessageHeaders.Id); - Assert.NotNull(headers.Timestamp); - } - - private static HeadersDictionary SingletonMap(string key, object value) - { - return new Dictionary - { - { key, value } - }; - } - - private sealed class TestIdGenerator : IIdGenerator - { - public string Id { get; set; } - - public string GenerateId() - { - return Id; - } - } - - private sealed class TestMessageHeaderAccessor : MessageHeaderAccessor - { - public TestMessageHeaderAccessor() - { - } - - private TestMessageHeaderAccessor(IMessage message) - : base(message) - { - } - - private TestMessageHeaderAccessor(MessageHeaders headers) - : base(headers) - { - } - - protected override MessageHeaderAccessor CreateMutableAccessor(IMessage message) - { - return new TestMessageHeaderAccessor(message); - } - - protected override MessageHeaderAccessor CreateMutableAccessor(IMessageHeaders messageHeaders) - { - return new TestMessageHeaderAccessor((MessageHeaders)messageHeaders); - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Support/NativeMessageHeaderAccessorTest.cs b/src/Messaging/test/Messaging.Test/Support/NativeMessageHeaderAccessorTest.cs deleted file mode 100644 index 19f11c30c2..0000000000 --- a/src/Messaging/test/Messaging.Test/Support/NativeMessageHeaderAccessorTest.cs +++ /dev/null @@ -1,288 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Support; - -public sealed class NativeMessageHeaderAccessorTest -{ - [Fact] - public void CreateFromNativeHeaderMap() - { - var inputNativeHeaders = new Dictionary> - { - { - "foo", new List - { - "bar" - } - }, - { - "bar", new List - { - "baz" - } - } - }; - - var headerAccessor = new NativeMessageHeaderAccessor(inputNativeHeaders); - IDictionary actual = headerAccessor.ToDictionary(); - - Assert.Single(actual); - Assert.NotNull(actual[NativeMessageHeaderAccessor.NativeHeaders]); - Assert.Equal(inputNativeHeaders, actual[NativeMessageHeaderAccessor.NativeHeaders]); - Assert.NotSame(inputNativeHeaders, actual[NativeMessageHeaderAccessor.NativeHeaders]); - } - - [Fact] - public void CreateFromMessage() - { - var inputNativeHeaders = new Dictionary> - { - { - "foo", new List - { - "bar" - } - }, - { - "bar", new List - { - "baz" - } - } - }; - - var inputHeaders = new Dictionary - { - { "a", "b" }, - { NativeMessageHeaderAccessor.NativeHeaders, inputNativeHeaders } - }; - - IMessage message = Message.Create("p", inputHeaders); - var headerAccessor = new NativeMessageHeaderAccessor(message); - IDictionary actual = headerAccessor.ToDictionary(); - - Assert.Equal(2, actual.Count); - Assert.Equal("b", actual["a"]); - Assert.NotNull(actual[NativeMessageHeaderAccessor.NativeHeaders]); - Assert.Equal(inputNativeHeaders, actual[NativeMessageHeaderAccessor.NativeHeaders]); - Assert.NotSame(inputNativeHeaders, actual[NativeMessageHeaderAccessor.NativeHeaders]); - } - - [Fact] - public void CreateFromMessageNull() - { - var headerAccessor = new NativeMessageHeaderAccessor((IMessage)null); - - IDictionary actual = headerAccessor.ToDictionary(); - Assert.Empty(actual); - - IDictionary> actualNativeHeaders = headerAccessor.ToNativeHeaderDictionary(); - Assert.Empty(actualNativeHeaders); - } - - [Fact] - public void CreateFromMessageAndModify() - { - var inputNativeHeaders = new Dictionary> - { - { - "foo", new List - { - "bar" - } - }, - { - "bar", new List - { - "baz" - } - } - }; - - var nativeHeaders = new Dictionary - { - { "a", "b" }, - { NativeMessageHeaderAccessor.NativeHeaders, inputNativeHeaders } - }; - - IMessage message = Message.Create("p", nativeHeaders); - - var headerAccessor = new NativeMessageHeaderAccessor(message); - headerAccessor.SetHeader("a", "B"); - headerAccessor.SetNativeHeader("foo", "BAR"); - - IDictionary actual = headerAccessor.ToDictionary(); - - Assert.Equal(2, actual.Count); - Assert.Equal("B", actual["a"]); - - var actualNativeHeaders = (IDictionary>)actual[NativeMessageHeaderAccessor.NativeHeaders]; - - Assert.NotNull(actualNativeHeaders); - - Assert.Equal(new List - { - "BAR" - }, actualNativeHeaders["foo"]); - - Assert.Equal(new List - { - "baz" - }, actualNativeHeaders["bar"]); - } - - [Fact] - public void SetNativeHeader() - { - var nativeHeaders = new Dictionary> - { - { - "foo", new List - { - "bar" - } - } - }; - - var headers = new NativeMessageHeaderAccessor(nativeHeaders); - headers.SetNativeHeader("foo", "baz"); - - Assert.Equal(new List - { - "baz" - }, headers.GetNativeHeader("foo")); - } - - [Fact] - public void SetNativeHeaderNullValue() - { - var nativeHeaders = new Dictionary> - { - { - "foo", new List - { - "bar" - } - } - }; - - var headers = new NativeMessageHeaderAccessor(nativeHeaders); - headers.SetNativeHeader("foo", null); - - Assert.Null(headers.GetNativeHeader("foo")); - } - - [Fact] - public void SetNativeHeaderLazyInit() - { - var headerAccessor = new NativeMessageHeaderAccessor(); - headerAccessor.SetNativeHeader("foo", "baz"); - - Assert.Equal(new List - { - "baz" - }, headerAccessor.GetNativeHeader("foo")); - } - - [Fact] - public void SetNativeHeaderLazyInitNullValue() - { - var headerAccessor = new NativeMessageHeaderAccessor(); - headerAccessor.SetNativeHeader("foo", null); - - Assert.Null(headerAccessor.GetNativeHeader("foo")); - Assert.Null(headerAccessor.MessageHeaders[NativeMessageHeaderAccessor.NativeHeaders]); - } - - [Fact] - public void SetNativeHeaderImmutable() - { - var headerAccessor = new NativeMessageHeaderAccessor(); - headerAccessor.SetNativeHeader("foo", "bar"); - headerAccessor.SetImmutable(); - var ex = Assert.Throws(() => headerAccessor.SetNativeHeader("foo", "baz")); - Assert.Contains("Already immutable", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void AddNativeHeader() - { - var nativeHeaders = new Dictionary> - { - { - "foo", new List - { - "bar" - } - } - }; - - var headers = new NativeMessageHeaderAccessor(nativeHeaders); - headers.AddNativeHeader("foo", "baz"); - - Assert.Equal(new List - { - "bar", - "baz" - }, headers.GetNativeHeader("foo")); - } - - [Fact] - public void AddNativeHeaderNullValue() - { - var nativeHeaders = new Dictionary> - { - { - "foo", new List - { - "bar" - } - } - }; - - var headers = new NativeMessageHeaderAccessor(nativeHeaders); - headers.AddNativeHeader("foo", null); - - Assert.Equal(new List - { - "bar" - }, headers.GetNativeHeader("foo")); - } - - [Fact] - public void AddNativeHeaderLazyInit() - { - var headerAccessor = new NativeMessageHeaderAccessor(); - headerAccessor.AddNativeHeader("foo", "bar"); - - Assert.Equal(new List - { - "bar" - }, headerAccessor.GetNativeHeader("foo")); - } - - [Fact] - public void AddNativeHeaderLazyInitNullValue() - { - var headerAccessor = new NativeMessageHeaderAccessor(); - headerAccessor.AddNativeHeader("foo", null); - - Assert.Null(headerAccessor.GetNativeHeader("foo")); - Assert.Null(headerAccessor.MessageHeaders[NativeMessageHeaderAccessor.NativeHeaders]); - } - - [Fact] - public void AddNativeHeaderImmutable() - { - var headerAccessor = new NativeMessageHeaderAccessor(); - headerAccessor.AddNativeHeader("foo", "bar"); - headerAccessor.SetImmutable(); - var ex = Assert.Throws(() => headerAccessor.AddNativeHeader("foo", "baz")); - Assert.Contains("Already immutable", ex.Message, StringComparison.Ordinal); - } -} diff --git a/src/Messaging/test/Messaging.Test/Support/TaskSchedulerSubscribableChannelTest.cs b/src/Messaging/test/Messaging.Test/Support/TaskSchedulerSubscribableChannelTest.cs deleted file mode 100644 index eafcbf431c..0000000000 --- a/src/Messaging/test/Messaging.Test/Support/TaskSchedulerSubscribableChannelTest.cs +++ /dev/null @@ -1,303 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Messaging.Support; -using Xunit; - -namespace Steeltoe.Messaging.Test.Support; - -public sealed class TaskSchedulerSubscribableChannelTest -{ - internal TaskSchedulerSubscribableChannel Channel { get; } - internal object Payload { get; } - internal IMessage Message { get; } - internal IMessageHandler Handler { get; set; } - - public TaskSchedulerSubscribableChannelTest() - { - Channel = new TaskSchedulerSubscribableChannel(); - Payload = new object(); - Message = MessageBuilder.WithPayload(Payload).Build(); - } - - [Fact] - public void MessageMustNotBeNull() - { - var ex = Assert.Throws(() => Channel.Send(null)); - Assert.Contains("message", ex.Message, StringComparison.Ordinal); - } - - [Fact] - public void SendNoInterceptors() - { - var mock = new Mock(); - Handler = mock.Object; - Channel.Subscribe(Handler); - Channel.Send(Message); - mock.Verify(h => h.HandleMessage(Message)); - } - - [Fact] - public void SendWithoutScheduler() - { - var mock = new Mock(); - Handler = mock.Object; - var interceptor = new BeforeHandleInterceptor(); - Channel.AddInterceptor(interceptor); - Channel.Subscribe(Handler); - Channel.Send(Message); - mock.Verify(h => h.HandleMessage(Message)); - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.WasAfterHandledInvoked); - } - - [Fact] - public void SendWithScheduler() - { - var mock = new Mock(); - Handler = mock.Object; - var interceptor = new BeforeHandleInterceptor(); - var scheduler = new TestScheduler(); - var testChannel = new TaskSchedulerSubscribableChannel(scheduler); - testChannel.AddInterceptor(interceptor); - testChannel.Subscribe(Handler); - testChannel.Send(Message); - Assert.True(scheduler.WasTaskScheduled); - mock.Verify(h => h.HandleMessage(Message)); - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.WasAfterHandledInvoked); - } - - [Fact] - public void SubscribeTwice() - { - var mock = new Mock(); - Handler = mock.Object; - Assert.True(Channel.Subscribe(Handler)); - Assert.False(Channel.Subscribe(Handler)); - Channel.Send(Message); - mock.Verify(h => h.HandleMessage(Message), Times.Once); - } - - [Fact] - public void UnsubscribeTwice() - { - var mock = new Mock(); - Handler = mock.Object; - Channel.Subscribe(Handler); - Assert.True(Channel.Unsubscribe(Handler)); - Assert.False(Channel.Unsubscribe(Handler)); - Channel.Send(Message); - mock.Verify(h => h.HandleMessage(Message), Times.Never); - } - - [Fact] - public void FailurePropagates() - { - var ex = new Exception("My exception"); - var mock = new Mock(); - mock.Setup(h => h.HandleMessage(Message)).Throws(ex); - Handler = mock.Object; - - var mock2 = new Mock(); - IMessageHandler secondHandler = mock2.Object; - - Channel.Subscribe(Handler); - Channel.Subscribe(secondHandler); - bool exceptionThrown = false; - - try - { - Channel.Send(Message); - } - catch (MessageDeliveryException actualException) - { - exceptionThrown = true; - Assert.Equal(ex, actualException.InnerException); - Assert.Contains("My exception", actualException.InnerException.Message, StringComparison.Ordinal); - } - - Assert.True(exceptionThrown); - mock2.Verify(h => h.HandleMessage(Message), Times.Never); - } - - [Fact] - public void ConcurrentModification() - { - var mock = new Mock(); - Handler = mock.Object; - var unsubscribeHandler = new UnsubscribeHandler(this); - Channel.Subscribe(unsubscribeHandler); - Channel.Subscribe(Handler); - Channel.Send(Message); - mock.Verify(h => h.HandleMessage(Message), Times.Once); - } - - [Fact] - public void InterceptorWithModifiedMessage() - { - var mock = new Mock(); - Handler = mock.Object; - var mock2 = new Mock(); - IMessage expected = mock2.Object; - - var interceptor = new BeforeHandleInterceptor - { - MessageToReturn = expected - }; - - Channel.AddInterceptor(interceptor); - Channel.Subscribe(Handler); - Channel.Send(Message); - mock.Verify(h => h.HandleMessage(expected), Times.Once); - - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.WasAfterHandledInvoked); - } - - [Fact] - public void InterceptorWithNull() - { - var mock = new Mock(); - Handler = mock.Object; - var interceptor1 = new BeforeHandleInterceptor(); - var interceptor2 = new NullReturningBeforeHandleInterceptor(); - Channel.AddInterceptor(interceptor1); - Channel.AddInterceptor(interceptor2); - Channel.Subscribe(Handler); - Channel.Send(Message); - mock.Verify(h => h.HandleMessage(Message), Times.Never); - Assert.Equal(1, interceptor1.Counter); - Assert.Equal(1, interceptor2.Counter); - Assert.True(interceptor1.WasAfterHandledInvoked); - } - - [Fact] - public void InterceptorWithException() - { - var expected = new Exception("Fake exception"); - var mock = new Mock(); - mock.Setup(h => h.HandleMessage(Message)).Throws(expected); - Handler = mock.Object; - - var interceptor = new BeforeHandleInterceptor(); - Channel.AddInterceptor(interceptor); - Channel.Subscribe(Handler); - bool exceptionThrown = false; - - try - { - Channel.Send(Message); - } - catch (MessageDeliveryException actual) - { - exceptionThrown = true; - Assert.Same(expected, actual.InnerException); - } - - Assert.True(exceptionThrown); - mock.Verify(h => h.HandleMessage(Message), Times.Once); - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.WasAfterHandledInvoked); - } - - internal sealed class UnsubscribeHandler : IMessageHandler - { - private readonly TaskSchedulerSubscribableChannelTest _test; - private readonly TaskSchedulerSubscribableChannelWriterTest _test2; - - public string ServiceName { get; set; } = nameof(UnsubscribeHandler); - - public UnsubscribeHandler(TaskSchedulerSubscribableChannelTest test) - { - _test = test; - } - - public UnsubscribeHandler(TaskSchedulerSubscribableChannelWriterTest test) - { - _test2 = test; - } - - public void HandleMessage(IMessage message) - { - _test?.Channel.Unsubscribe(_test.Handler); - _test2?.Channel.Unsubscribe(_test2.Handler); - } - } - - internal sealed class TestScheduler : TaskScheduler - { - public bool WasTaskScheduled { get; private set; } - - protected override IEnumerable GetScheduledTasks() - { - throw new NotImplementedException(); - } - - protected override void QueueTask(Task task) - { - WasTaskScheduled = true; - TryExecuteTask(task); - } - - protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) - { - WasTaskScheduled = true; - TryExecuteTask(task); - return true; - } - } - - internal abstract class AbstractTestInterceptor : AbstractTaskSchedulerChannelInterceptor - { - private volatile int _counter; - - private volatile bool _afterHandledInvoked; - - public int Counter => _counter; - - public bool WasAfterHandledInvoked => _afterHandledInvoked; - - public override IMessage BeforeHandled(IMessage message, IMessageChannel channel, IMessageHandler handler) - { - Assert.NotNull(message); - Interlocked.Increment(ref _counter); - return message; - } - - public override void AfterMessageHandled(IMessage message, IMessageChannel channel, IMessageHandler handler, Exception exception) - { - _afterHandledInvoked = true; - } - } - - internal sealed class BeforeHandleInterceptor : AbstractTestInterceptor - { - public IMessage MessageToReturn { get; set; } - - public Exception ExceptionToRaise { get; set; } - - public override IMessage BeforeHandled(IMessage message, IMessageChannel channel, IMessageHandler handler) - { - base.BeforeHandled(message, channel, handler); - - if (ExceptionToRaise != null) - { - throw ExceptionToRaise; - } - - return MessageToReturn ?? message; - } - } - - internal sealed class NullReturningBeforeHandleInterceptor : AbstractTestInterceptor - { - public override IMessage BeforeHandled(IMessage message, IMessageChannel channel, IMessageHandler handler) - { - base.BeforeHandled(message, channel, handler); - return null; - } - } -} diff --git a/src/Messaging/test/Messaging.Test/Support/TaskSchedulerSubscribableChannelWriterTest.cs b/src/Messaging/test/Messaging.Test/Support/TaskSchedulerSubscribableChannelWriterTest.cs deleted file mode 100644 index 42a842dc74..0000000000 --- a/src/Messaging/test/Messaging.Test/Support/TaskSchedulerSubscribableChannelWriterTest.cs +++ /dev/null @@ -1,261 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Messaging.Support; -using Xunit; -using static Steeltoe.Messaging.Test.Support.TaskSchedulerSubscribableChannelTest; - -namespace Steeltoe.Messaging.Test.Support; - -public sealed class TaskSchedulerSubscribableChannelWriterTest -{ - internal TaskSchedulerSubscribableChannel Channel { get; } - internal object Payload { get; } - internal IMessage Message { get; } - internal IMessageHandler Handler { get; private set; } - - public TaskSchedulerSubscribableChannelWriterTest() - { - Channel = new TaskSchedulerSubscribableChannel(); - Payload = new object(); - Message = MessageBuilder.WithPayload(Payload).Build(); - } - - [Fact] - public async Task MessageMustNotBeNull() - { - Exception exception = null; - - try - { - await Channel.Writer.WriteAsync(null); - } - catch (Exception ex) - { - exception = ex; - } - - Assert.NotNull(exception); - Assert.Contains("message", exception.Message, StringComparison.Ordinal); - } - - [Fact] - public async Task WriteAsyncNoInterceptors() - { - var mock = new Mock(); - Handler = mock.Object; - Channel.Subscribe(Handler); - await Channel.Writer.WriteAsync(Message); - mock.Verify(h => h.HandleMessage(Message)); - } - - [Fact] - public async Task WriteAsyncWithoutScheduler() - { - var mock = new Mock(); - Handler = mock.Object; - var interceptor = new BeforeHandleInterceptor(); - Channel.AddInterceptor(interceptor); - Channel.Subscribe(Handler); - await Channel.Writer.WriteAsync(Message); - mock.Verify(h => h.HandleMessage(Message)); - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.WasAfterHandledInvoked); - } - - [Fact] - public async Task WriteAsyncWithScheduler() - { - var mock = new Mock(); - Handler = mock.Object; - var interceptor = new BeforeHandleInterceptor(); - var scheduler = new TestScheduler(); - var testChannel = new TaskSchedulerSubscribableChannel(scheduler); - testChannel.AddInterceptor(interceptor); - testChannel.Subscribe(Handler); - await testChannel.Writer.WriteAsync(Message); - Assert.True(scheduler.WasTaskScheduled); - mock.Verify(h => h.HandleMessage(Message)); - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.WasAfterHandledInvoked); - } - - [Fact] - public async Task SubscribeTwice() - { - var mock = new Mock(); - Handler = mock.Object; - Assert.True(Channel.Subscribe(Handler)); - Assert.False(Channel.Subscribe(Handler)); - await Channel.Writer.WriteAsync(Message); - mock.Verify(h => h.HandleMessage(Message), Times.Once); - } - - [Fact] - public async Task UnsubscribeTwice() - { - var mock = new Mock(); - Handler = mock.Object; - Channel.Subscribe(Handler); - Assert.True(Channel.Unsubscribe(Handler)); - Assert.False(Channel.Unsubscribe(Handler)); - await Channel.Writer.WriteAsync(Message); - mock.Verify(h => h.HandleMessage(Message), Times.Never); - } - - [Fact] - public async Task FailurePropagates() - { - var ex = new Exception("My exception"); - var mock = new Mock(); - mock.Setup(h => h.HandleMessage(Message)).Throws(ex); - Handler = mock.Object; - - var mock2 = new Mock(); - IMessageHandler secondHandler = mock2.Object; - - Channel.Subscribe(Handler); - Channel.Subscribe(secondHandler); - bool exceptionThrown = false; - - try - { - await Channel.Writer.WriteAsync(Message); - } - catch (MessageDeliveryException actualException) - { - exceptionThrown = true; - Assert.Equal(ex, actualException.InnerException); - Assert.Contains("My exception", actualException.InnerException.Message, StringComparison.Ordinal); - } - - Assert.True(exceptionThrown); - mock2.Verify(h => h.HandleMessage(Message), Times.Never); - } - - [Fact] - public async Task ConcurrentModification() - { - var mock = new Mock(); - Handler = mock.Object; - var unsubscribeHandler = new UnsubscribeHandler(this); - Channel.Subscribe(unsubscribeHandler); - Channel.Subscribe(Handler); - await Channel.Writer.WriteAsync(Message); - mock.Verify(h => h.HandleMessage(Message), Times.Once); - } - - [Fact] - public async Task InterceptorWithModifiedMessage() - { - var mock = new Mock(); - Handler = mock.Object; - var mock2 = new Mock(); - IMessage expected = mock2.Object; - - var interceptor = new BeforeHandleInterceptor - { - MessageToReturn = expected - }; - - Channel.AddInterceptor(interceptor); - Channel.Subscribe(Handler); - await Channel.Writer.WriteAsync(Message); - mock.Verify(h => h.HandleMessage(expected), Times.Once); - - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.WasAfterHandledInvoked); - } - - [Fact] - public async Task InterceptorWithNull() - { - var mock = new Mock(); - Handler = mock.Object; - var interceptor1 = new BeforeHandleInterceptor(); - var interceptor2 = new NullReturningBeforeHandleInterceptor(); - Channel.AddInterceptor(interceptor1); - Channel.AddInterceptor(interceptor2); - Channel.Subscribe(Handler); - await Channel.Writer.WriteAsync(Message); - mock.Verify(h => h.HandleMessage(Message), Times.Never); - Assert.Equal(1, interceptor1.Counter); - Assert.Equal(1, interceptor2.Counter); - Assert.True(interceptor1.WasAfterHandledInvoked); - } - - [Fact] - public async Task InterceptorWithException() - { - var expected = new Exception("Fake exception"); - var mock = new Mock(); - mock.Setup(h => h.HandleMessage(Message)).Throws(expected); - Handler = mock.Object; - - var interceptor = new BeforeHandleInterceptor(); - Channel.AddInterceptor(interceptor); - Channel.Subscribe(Handler); - bool exceptionThrown = false; - - try - { - await Channel.Writer.WriteAsync(Message); - } - catch (MessageDeliveryException actual) - { - exceptionThrown = true; - Assert.Same(expected, actual.InnerException); - } - - Assert.True(exceptionThrown); - mock.Verify(h => h.HandleMessage(Message), Times.Once); - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.WasAfterHandledInvoked); - } - - [Fact] - public async Task TestWaitToWriteAsync() - { - var mock = new Mock(); - Handler = mock.Object; - Channel.Subscribe(Handler); - Assert.True(await Channel.Writer.WaitToWriteAsync()); - Channel.Unsubscribe(Handler); - Assert.False(await Channel.Writer.WaitToWriteAsync()); - } - - [Fact] - public void TestTryComplete() - { - var mock = new Mock(); - Handler = mock.Object; - Channel.Subscribe(Handler); - Assert.False(Channel.Writer.TryComplete()); - } - - [Fact] - public void TryWriteNoInterceptors() - { - var mock = new Mock(); - Handler = mock.Object; - Channel.Subscribe(Handler); - Assert.True(Channel.Writer.TryWrite(Message)); - mock.Verify(h => h.HandleMessage(Message)); - } - - [Fact] - public void TryWriteWithInterceptors() - { - var mock = new Mock(); - Handler = mock.Object; - var interceptor = new BeforeHandleInterceptor(); - Channel.AddInterceptor(interceptor); - Channel.Subscribe(Handler); - Assert.True(Channel.Writer.TryWrite(Message)); - mock.Verify(h => h.HandleMessage(Message)); - Assert.Equal(1, interceptor.Counter); - Assert.True(interceptor.WasAfterHandledInvoked); - } -} diff --git a/src/Messaging/test/Messaging.Test/xunit.runner.json b/src/Messaging/test/Messaging.Test/xunit.runner.json deleted file mode 100644 index fdeefaa456..0000000000 --- a/src/Messaging/test/Messaging.Test/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "maxParallelThreads": 1, - "parallelizeTestCollections": false -} diff --git a/src/Messaging/test/RabbitMQ.Test/AbstractTest.cs b/src/Messaging/test/RabbitMQ.Test/AbstractTest.cs deleted file mode 100644 index 34b521bb82..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/AbstractTest.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Test; - -public abstract class AbstractTest -{ - protected virtual ServiceCollection CreateContainer() - { - var services = new ServiceCollection(); - var configurationBuilder = new ConfigurationBuilder(); - - IConfigurationRoot configuration = configurationBuilder.Build(); - services.AddSingleton(configuration); - services.AddRabbitHostingServices(); - return services; - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Attributes/AsyncListenerTest.cs b/src/Messaging/test/RabbitMQ.Test/Attributes/AsyncListenerTest.cs deleted file mode 100644 index 32c22e17c3..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Attributes/AsyncListenerTest.cs +++ /dev/null @@ -1,325 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Attributes; - -[Trait("Category", "Integration")] -public sealed class AsyncListenerTest : IClassFixture -{ - private readonly ServiceProvider _provider; - private readonly StartupFixture _fixture; - - public AsyncListenerTest(StartupFixture fix) - { - _fixture = fix; - _provider = _fixture.Provider; - } - - [Fact] - public async Task TestAsyncListener() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - IApplicationContext context = _provider.GetApplicationContext(); - var queue1 = context.GetService("queue1"); - string reply = template.ConvertSendAndReceive(queue1.QueueName, "foo"); - Assert.Equal("FOO", reply); - - string reply2 = await template.ConvertSendAndReceiveAsync(queue1.QueueName, "foo"); - Assert.Equal("FOO", reply2); - var pp = template.AfterReceivePostProcessors[0] as TemplateAfterReceivePostProcessor; - Assert.Equal("System.String", pp.TypeId); - - var queue2 = context.GetService("queue2"); - string reply3 = template.ConvertSendAndReceive(queue2.QueueName, "foo"); - Assert.Equal("FOO", reply3); - Assert.Equal("System.String", pp.TypeId); - - var queue3 = context.GetService("queue3"); - var reply4 = template.ConvertSendAndReceive>(queue3.QueueName, "foo"); - Assert.Equal("FOO", reply4[0]); - Assert.Equal("System.Collections.Generic.List`1", pp.TypeId); - Assert.Equal("System.String", pp.ContentTypeId); - - var queue4 = context.GetService("queue4"); - template.ConvertAndSend(queue4.QueueName, "foo"); - var listener = _provider.GetService(); - Assert.True(listener.Latch4.Wait(TimeSpan.FromSeconds(10))); - } - - [Fact] - public void TestRouteToDlq() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - IApplicationContext context = _provider.GetApplicationContext(); - var queue5 = context.GetService("queue5"); - var listener = _provider.GetService(); - - template.ConvertAndSend(queue5.QueueName, "foo"); - Assert.True(listener.Latch5.Wait(TimeSpan.FromSeconds(10))); - - var queue6 = context.GetService("queue6"); - template.ConvertAndSend(queue6.QueueName, "foo"); - Assert.True(listener.Latch6.Wait(TimeSpan.FromSeconds(10))); - } - - [Fact] - public void TestOverrideDoNotRequeue() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - IApplicationContext context = _provider.GetApplicationContext(); - var queue7 = context.GetService("queue7"); - Assert.Equal("listen7", template.ConvertSendAndReceive(queue7.QueueName, "foo")); - } - - [Fact] - public void TestAuthByProps() - { - var registry = _provider.GetRequiredService() as RabbitListenerEndpointRegistry; - var container = registry.GetListenerContainer("foo") as DirectMessageListenerContainer; - Assert.False(container.PossibleAuthenticationFailureFatal); - } - - [Fact] - public async Task TestAsyncListenerErrorHandler() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - IApplicationContext context = _provider.GetApplicationContext(); - var queueAsyncErrorHandler = context.GetService("queueAsyncErrorHandler"); - string reply = await template.ConvertSendAndReceiveAsync(queueAsyncErrorHandler.QueueName, "foo"); - Assert.Equal($"{nameof(CustomListenerErrorHandler)} handled/processed", reply); - } - - public sealed class StartupFixture : IDisposable - { - private readonly IServiceCollection _services; - - public ServiceProvider Provider { get; set; } - - public StartupFixture() - { - _services = CreateContainer(); - Provider = _services.BuildServiceProvider(true); - Provider.GetRequiredService().StartAsync(default).GetAwaiter().GetResult(); - } - - private ServiceCollection CreateContainer(IConfiguration configuration = null) - { - var services = new ServiceCollection(); - - configuration ??= new ConfigurationBuilder().AddInMemoryCollection(new Dictionary - { - { "spring:rabbitmq:listener:direct:PossibleAuthenticationFailureFatal", "False" } - }).Build(); - - services.AddLogging(b => - { - b.SetMinimumLevel(LogLevel.Debug); - b.AddDebug(); - b.AddConsole(); - }); - - services.ConfigureRabbitOptions(configuration); - services.AddSingleton(configuration); - services.AddRabbitHostingServices(); - services.AddRabbitJsonMessageConverter(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitConnectionFactory(); - services.AddRabbitAdmin(); - - services.AddRabbitTemplate((_, t) => - { - t.SetAfterReceivePostProcessors(new TemplateAfterReceivePostProcessor()); - }); - - var queue5Dlq = new AnonymousQueue("queue5DLQ"); - var queue6Dlq = new AnonymousQueue("queue6DLQ"); - var queue1 = new AnonymousQueue("queue1"); - var queue2 = new AnonymousQueue("queue2"); - var queue3 = new AnonymousQueue("queue3"); - var queue4 = new AnonymousQueue("queue4"); - var queue5 = new AnonymousQueue("queue5"); - var queueAsyncErrorHandler = new AnonymousQueue("queueAsyncErrorHandler"); - queue5.Arguments.Add("x-dead-letter-exchange", string.Empty); - queue5.Arguments.Add("x-dead-letter-routing-key", queue5Dlq.QueueName); - var queue6 = new AnonymousQueue("queue6"); - queue6.Arguments.Add("x-dead-letter-exchange", string.Empty); - queue6.Arguments.Add("x-dead-letter-routing-key", queue6Dlq.QueueName); - var queue7 = new AnonymousQueue("queue7"); - services.AddRabbitQueues(queue1, queue2, queue3, queue4, queue5, queue6, queue5Dlq, queue6Dlq, queue7, queueAsyncErrorHandler); - - // Add default container factory - services.AddRabbitListenerContainerFactory((_, f) => - { - f.MismatchedQueuesFatal = true; - f.AcknowledgeMode = AcknowledgeMode.Manual; - }); - - // Add doNotRequeueFactory container factory - services.AddRabbitListenerContainerFactory((_, f) => - { - f.ServiceName = "doNotRequeueFactory"; - f.MismatchedQueuesFatal = true; - f.AcknowledgeMode = AcknowledgeMode.Manual; - f.DefaultRequeueRejected = false; - }); - - services.AddSingleton(); - services.AddRabbitListeners(configuration); - services.AddRabbitListenerErrorHandler(nameof(CustomListenerErrorHandler)); - - return services; - } - - public void Dispose() - { - Provider.Dispose(); - } - } - - public sealed class Listener - { - public AtomicBoolean FooFirst { get; set; } = new(true); - - public AtomicBoolean BarFirst { get; set; } = new(true); - - public CountdownEvent Latch4 { get; set; } = new(1); - - public CountdownEvent Latch5 { get; set; } = new(1); - - public CountdownEvent Latch6 { get; set; } = new(1); - - public AtomicBoolean First7 { get; set; } = new(true); - - [RabbitListener("queue1", Id = "foo")] - public Task Listen1Async(string foo) - { - if (FooFirst.GetAndSet(false)) - { - return Task.FromException(new Exception("Future.exception")); - } - - return Task.FromResult(foo.ToUpperInvariant()); - } - - [RabbitListener("queue2", Id = "bar")] - public Task Listen2Async(string foo) - { - if (BarFirst.GetAndSet(false)) - { - return Task.FromException(new Exception("Mono.error()")); - } - - return Task.FromResult(foo.ToUpperInvariant()); - } - - [RabbitListener("queue3", Id = "baz")] - public Task> Listen3Async(string foo) - { - return Task.FromResult(new List - { - foo.ToUpperInvariant() - }); - } - - [RabbitListener("queue4", Id = "qux")] - public Task Listen4Async(string foo) - { - Latch4.Signal(); - return Task.CompletedTask; - } - - [RabbitListener("queue5", Id = "fiz")] - public Task Listen5Async(string foo) - { - return Task.FromException(new RabbitRejectAndDoNotRequeueException("asyncToDLQ")); - } - - [RabbitListener("queue5DLQ", Id = "buz")] - public void Listen5Dlq(string foo) - { - Latch5.Signal(); - } - - [RabbitListener("queue6", Id = "fix", ContainerFactory = "doNotRequeueFactory")] - public Task Listen6Async(string foo) - { - return Task.FromException(new InvalidOperationException("asyncDefaultToDLQ")); - } - - [RabbitListener("queue6DLQ", Id = "fox")] - public void Listen6Dlq(string foo) - { - Latch6.Signal(); - } - - [RabbitListener("queue7", Id = "overrideFactoryRequeue", ContainerFactory = "doNotRequeueFactory")] - public Task Listen7Async(string foo) - { - if (First7.CompareAndSet(true, false)) - { - return Task.FromException(new ImmediateRequeueException("asyncOverrideDefaultToDLQ")); - } - - return Task.FromResult("listen7"); - } - - [RabbitListener(Queue = "queueAsyncErrorHandler", Id = "asyncErrorHandler", ErrorHandler = nameof(CustomListenerErrorHandler))] - public async Task HandleMessageAsync(string msg) - { - await Task.Run(() => Console.WriteLine("Running Listener")); - - throw new Exception($"Test {nameof(Listener)} exception"); - } - } - - public sealed class TemplateAfterReceivePostProcessor : IMessagePostProcessor - { - public object TypeId { get; set; } - - public object ContentTypeId { get; set; } - - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - TypeId = message.Headers.Get("__TypeId__"); - ContentTypeId = message.Headers.Get("__ContentTypeId__"); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - TypeId = message.Headers.Get("__TypeId__"); - ContentTypeId = message.Headers.Get("__ContentTypeId__"); - return message; - } - } - - public sealed class CustomListenerErrorHandler : IRabbitListenerErrorHandler - { - public string ServiceName { get; set; } = nameof(CustomListenerErrorHandler); - - public object HandleError(IMessage originalMessage, IMessage message, ListenerExecutionFailedException exception) - { - return $"{ServiceName} handled/processed"; - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Attributes/ComplexTypeJsonIntegrationTest.cs b/src/Messaging/test/RabbitMQ.Test/Attributes/ComplexTypeJsonIntegrationTest.cs deleted file mode 100644 index 306ed16266..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Attributes/ComplexTypeJsonIntegrationTest.cs +++ /dev/null @@ -1,251 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Attributes; - -[Trait("Category", "Integration")] -public sealed class ComplexTypeJsonIntegrationTest : IClassFixture -{ - public const string TestQueue = "test.complex.send.and.receive"; - public const string TestQueue2 = "test.complex.receive"; - - private readonly ServiceProvider _provider; - - public ComplexTypeJsonIntegrationTest(StartupFixture fixture) - { - _provider = fixture.Provider; - } - - [Fact] - public void TestSendAndReceive() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - IMessagePostProcessor pp = new EmptyPostProcessor(); - object message = "foo"; - Assert.NotNull(template.ConvertSendAndReceiveAsType(message, typeof(Foo>))); - Assert.NotNull(template.ConvertSendAndReceiveAsType(message, pp, typeof(Foo>))); - Assert.NotNull(template.ConvertSendAndReceiveAsType(TestQueue, message, typeof(Foo>))); - Assert.NotNull(template.ConvertSendAndReceiveAsType(TestQueue, message, pp, null, typeof(Foo>))); - Assert.NotNull(template.ConvertSendAndReceiveAsType(string.Empty, TestQueue, message, typeof(Foo>))); - Assert.NotNull(template.ConvertSendAndReceiveAsType(string.Empty, TestQueue, message, pp, typeof(Foo>))); - } - - [Fact] - public void TestReceive() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - Foo> foo = MakeAFoo(); - var pp = new TestPostProcessor(); - template.ConvertAndSend(TestQueue2, foo, pp); - var result = template.ReceiveAndConvert>>(10000); - Assert.NotNull(result); - } - - [Fact] - public async Task TestReceiveNoWait() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - Foo> foo = MakeAFoo(); - var pp = new TestPostProcessor(); - template.ConvertAndSend(TestQueue2, foo, pp); - var result = template.ReceiveAndConvert>>(); - - int n = 0; - - while (n++ < 100 && foo == null) - { - await Task.Delay(100); - result = template.ReceiveAndConvert>>(); - } - - Assert.NotNull(result); - } - - [Fact] - public async Task TestAsyncSendAndReceive() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - IMessagePostProcessor pp = new EmptyPostProcessor(); - object message = "foo"; - Assert.NotNull(await template.ConvertSendAndReceiveAsTypeAsync(message, typeof(Foo>))); - Assert.NotNull(await template.ConvertSendAndReceiveAsTypeAsync(message, pp, typeof(Foo>))); - Assert.NotNull(await template.ConvertSendAndReceiveAsTypeAsync(TestQueue, message, typeof(Foo>))); - Assert.NotNull(await template.ConvertSendAndReceiveAsTypeAsync(TestQueue, message, pp, null, typeof(Foo>))); - Assert.NotNull(await template.ConvertSendAndReceiveAsTypeAsync(string.Empty, TestQueue, message, typeof(Foo>))); - Assert.NotNull(await template.ConvertSendAndReceiveAsTypeAsync(string.Empty, TestQueue, message, pp, typeof(Foo>))); - } - - public static Foo> MakeAFoo() - { - var foo = new Foo>(); - - var bar = new Bar - { - AField = new Baz("foo"), - BField = new Qux(42) - }; - - foo.Field = bar; - return foo; - } - - public sealed class EmptyPostProcessor : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - return message; - } - } - - public sealed class TestPostProcessor : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.RemoveHeaders("__TypeId__"); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.RemoveHeaders("__TypeId__"); - return message; - } - } - - public sealed class StartupFixture : IDisposable - { - public ServiceProvider Provider { get; set; } - - public StartupFixture() - { - IServiceCollection services = CreateContainer(); - Provider = services.BuildServiceProvider(true); - Provider.GetRequiredService().StartAsync(default).GetAwaiter().GetResult(); - } - - private ServiceCollection CreateContainer(IConfiguration configuration = null) - { - var services = new ServiceCollection(); - configuration ??= new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.SetMinimumLevel(LogLevel.Debug); - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configuration); - services.AddRabbitHostingServices(); - services.AddRabbitJsonMessageConverter(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerContainerFactory(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitConnectionFactory(); - services.AddRabbitAdmin(); - - services.AddRabbitTemplate((_, t) => - { - t.DefaultReceiveDestination = TestQueue2; - t.DefaultSendDestination = TestQueue; - }); - - services.AddRabbitQueue(TestQueue); - services.AddRabbitQueue(TestQueue2); - - services.AddSingleton(); - services.AddRabbitListeners(configuration); - return services; - } - - public void Dispose() - { - RabbitAdmin admin = Provider.GetRabbitAdmin(); - admin.DeleteQueue(TestQueue); - admin.DeleteQueue(TestQueue2); - Provider.Dispose(); - } - } - - public sealed class Listener - { - [RabbitListener(TestQueue)] - public Foo> Listen(string input) - { - return MakeAFoo(); - } - } - - public sealed class Foo - { - public T Field { get; set; } - - public override string ToString() - { - return $"Foo [field={Field}]"; - } - } - - public sealed class Bar - { - public TFieldA AField { get; set; } - - public TFieldB BField { get; set; } - - public override string ToString() - { - return $"Bar [aField={AField}, bField={BField}]"; - } - } - - public sealed class Baz - { - public string BazField { get; set; } - - public Baz(string s) - { - BazField = s; - } - - public override string ToString() - { - return $"Baz [baz={BazField}]"; - } - } - - public sealed class Qux - { - public int QuxField { get; set; } - - public Qux(int i) - { - QuxField = i; - } - - public override string ToString() - { - return $"Qux [qux={QuxField}]"; - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitIntegrationCustomConfigTest.cs b/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitIntegrationCustomConfigTest.cs deleted file mode 100644 index 818153ee8f..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitIntegrationCustomConfigTest.cs +++ /dev/null @@ -1,438 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Xunit; -using RC = RabbitMQ.Client; -using SimpleMessageConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter.SimpleMessageConverter; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Attributes; - -[Trait("Category", "Integration")] -public sealed class EnableRabbitIntegrationCustomConfigTest : IClassFixture -{ - private readonly ServiceProvider _provider; - private readonly CustomStartupFixture _fixture; - - public EnableRabbitIntegrationCustomConfigTest(CustomStartupFixture fix) - { - _fixture = fix; - _provider = _fixture.Provider; - } - - [Fact] - public void TestConverted() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - - var foo1 = new Foo1 - { - Bar = "bar" - }; - - var ctx = _provider.GetService(); - var converter = ctx.GetService(JsonMessageConverter.DefaultServiceName) as JsonMessageConverter; - converter.TypeMapper.DefaultType = typeof(Dictionary); - converter.Precedence = TypePrecedence.TypeId; - var returned = template.ConvertSendAndReceive("test.converted", foo1); - Assert.IsType(returned); - Assert.Equal("bar", returned.Bar); - converter.Precedence = TypePrecedence.Inferred; - - template.MessageConverter = new SimpleMessageConverter(); - var messagePostProcessor = new MessagePostProcessor(); - byte[] returned2 = template.ConvertSendAndReceive(string.Empty, "test.converted", "{ \"bar\" : \"baz\" }", messagePostProcessor); - Assert.Equal("{\"bar\":\"baz\"}", Encoding.UTF8.GetString(returned2)); - - byte[] returned3 = template.ConvertSendAndReceive(string.Empty, "test.converted.list", "[{ \"bar\" : \"baz\" }]", messagePostProcessor); - Assert.Equal("{\"bar\":\"BAZZZZ\"}", Encoding.UTF8.GetString(returned3)); - - byte[] returned4 = template.ConvertSendAndReceive(string.Empty, "test.converted.array", "[{ \"bar\" : \"baz\" }]", messagePostProcessor); - Assert.Equal("{\"bar\":\"BAZZxx\"}", Encoding.UTF8.GetString(returned4)); - - byte[] returned5 = template.ConvertSendAndReceive(string.Empty, "test.converted.args1", "{ \"bar\" : \"baz\" }", messagePostProcessor); - Assert.Equal("\"bar=baztest.converted.args1\"", Encoding.UTF8.GetString(returned5)); - - byte[] returned6 = template.ConvertSendAndReceive(string.Empty, "test.converted.args2", "{ \"bar\" : \"baz\" }", messagePostProcessor); - Assert.Equal("\"bar=baztest.converted.args2\"", Encoding.UTF8.GetString(returned6)); - - var beanMethodHeaders = new List(); - var mpp = new AfterReceivePostProcessors(beanMethodHeaders); - template.SetAfterReceivePostProcessors(mpp); - byte[] returned7 = template.ConvertSendAndReceive(string.Empty, "test.converted.message", "{ \"bar\" : \"baz\" }", messagePostProcessor); - Assert.Equal("\"bar=bazFoo2MessageFoo2Service\"", Encoding.UTF8.GetString(returned7)); - Assert.Equal(2, beanMethodHeaders.Count); - Assert.Equal("Foo2Service", beanMethodHeaders[0]); - Assert.Equal("Foo2Message", beanMethodHeaders[1]); - - template.RemoveAfterReceivePostProcessor(mpp); - var foo2Service = ctx.GetService(); - Assert.IsType(foo2Service.Bean); - Assert.NotNull(foo2Service.Method); - Assert.Equal("Foo2Message", foo2Service.Method.Name); - - byte[] returned8 = template.ConvertSendAndReceive(string.Empty, "test.notconverted.message", "{ \"bar\" : \"baz\" }", messagePostProcessor); - Assert.Equal("\"fooMessage`1\"", Encoding.UTF8.GetString(returned8)); - Assert.Equal("string", foo2Service.StringHeader); - Assert.Equal(42, foo2Service.IntHeader); - - byte[] returned9 = template.ConvertSendAndReceive(string.Empty, "test.notconverted.channel", "{ \"bar\" : \"baz\" }", messagePostProcessor); - Assert.Equal("\"barAndChannel\"", Encoding.UTF8.GetString(returned9)); - - byte[] returned10 = - template.ConvertSendAndReceive(string.Empty, "test.notconverted.messagechannel", "{ \"bar\" : \"baz\" }", messagePostProcessor); - - Assert.Equal("\"bar=bazMessage`1AndChannel\"", Encoding.UTF8.GetString(returned10)); - - byte[] returned11 = - template.ConvertSendAndReceive(string.Empty, "test.notconverted.messagingmessage", "{ \"bar\" : \"baz\" }", messagePostProcessor); - - Assert.Equal("\"Message`1Dictionary`2\"", Encoding.UTF8.GetString(returned11)); - - byte[] returned12 = template.ConvertSendAndReceive(string.Empty, "test.converted.foomessage", "{ \"bar\" : \"baz\" }", messagePostProcessor); - Assert.Equal("\"Message`1Foo2guest\"", Encoding.UTF8.GetString(returned12)); - - byte[] returned13 = template.ConvertSendAndReceive(string.Empty, "test.notconverted.messagingmessagenotgeneric", "{ \"bar\" : \"baz\" }", - messagePostProcessor); - - Assert.Equal("\"Message`1Dictionary`2\"", Encoding.UTF8.GetString(returned13)); - } - - public sealed class AfterReceivePostProcessors : IMessagePostProcessor - { - public List Headers { get; } - - public AfterReceivePostProcessors(List headers) - { - Headers = headers; - } - - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - Headers.Add(message.Headers.Get("bean")); - Headers.Add(message.Headers.Get("method")); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - Headers.Add(message.Headers.Get("bean")); - Headers.Add(message.Headers.Get("method")); - return message; - } - } - - public sealed class MessagePostProcessor : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = "application/json"; - accessor.UserId = "guest"; - accessor.SetHeader("stringHeader", "string"); - accessor.SetHeader("intHeader", 42); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = "application/json"; - accessor.UserId = "guest"; - accessor.SetHeader("stringHeader", "string"); - accessor.SetHeader("intHeader", 42); - return message; - } - } - - public sealed class CustomStartupFixture : IDisposable - { - private readonly CachingConnectionFactory _adminCf; - private readonly RabbitAdmin _admin; - private readonly IServiceCollection _services; - - public static string[] Queues { get; } = - { - "test.converted", - "test.converted.list", - "test.converted.array", - "test.converted.args1", - "test.converted.args2", - "test.converted.message", - "test.notconverted.message", - "test.notconverted.channel", - "test.notconverted.messagechannel", - "test.notconverted.messagingmessage", - "test.converted.foomessage", - "test.notconverted.messagingmessagenotgeneric", - "test.simple.direct" - }; - - public ServiceProvider Provider { get; set; } - - public CustomStartupFixture() - { - _adminCf = new CachingConnectionFactory("localhost"); - _admin = new RabbitAdmin(_adminCf); - - foreach (string q in Queues) - { - var queue = new Queue(q); - _admin.DeclareQueue(queue); - } - - _services = CreateContainer(); - Provider = _services.BuildServiceProvider(true); - Provider.GetRequiredService().StartAsync(default).GetAwaiter().GetResult(); - } - - public void Dispose() - { - foreach (string q in Queues) - { - _admin.DeleteQueue(q); - } - - _adminCf.Dispose(); - - Provider.Dispose(); - } - - private ServiceCollection CreateContainer(IConfiguration configuration = null) - { - var services = new ServiceCollection(); - configuration ??= new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configuration); - services.AddRabbitHostingServices(); - services.AddRabbitJsonMessageConverter(); - - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitConnectionFactory(); - - services.AddSingleton(); - - services.AddRabbitMessageHandlerMethodFactory((_, f) => - { - f.ServiceName = "myHandlerMethodFactory"; - var service = DefaultConversionService.Singleton as DefaultConversionService; - service.AddConverter(new Foo1ToFoo2Converter()); - f.ConversionService = service; - f.MessageConverter = new GenericMessageConverter(service); - }); - - // Add default container factory - services.AddRabbitListenerContainerFactory((_, f) => - { - f.SetBeforeSendReplyPostProcessors(new AddSomeHeadersPostProcessor()); - }); - - services.AddRabbitAdmin(); - - services.AddRabbitTemplate((_, t) => - { - t.ReplyTimeout = 60000; - }); - - services.AddSingleton(); - services.AddRabbitListeners(configuration); - - return services; - } - - public sealed class MyRabbitListenerConfigurer : IRabbitListenerConfigurer - { - private readonly IApplicationContext _context; - - public MyRabbitListenerConfigurer(IApplicationContext context) - { - _context = context; - } - - public void ConfigureRabbitListeners(IRabbitListenerEndpointRegistrar registrar) - { - var handler = _context.GetService("myHandlerMethodFactory"); - registrar.MessageHandlerMethodFactory = handler; - } - } - } - - public sealed class Foo2Service - { - public object Bean { get; set; } - - public MethodInfo Method { get; set; } - - public string StringHeader { get; set; } - - public int IntHeader { get; set; } - - [RabbitListener("test.converted")] - public Foo2 Foo2(Foo2 value) - { - return value; - } - - [RabbitListener("test.converted.list")] - public Foo2 Foo2(List foo2S) - { - Foo2 foo2 = foo2S[0]; - foo2.Bar = "BAZZZZ"; - return foo2; - } - - [RabbitListener("test.converted.array")] - public Foo2 Foo2(Foo2[] foo2S) - { - Foo2 foo2 = foo2S[0]; - foo2.Bar = "BAZZxx"; - return foo2; - } - - [RabbitListener("test.converted.args1")] - public string Foo2(Foo2 value, [Header(RabbitMessageHeaders.ConsumerQueue)] string queue) - { - return value + queue; - } - - [RabbitListener("test.converted.args2")] - public string Foo2A([Payload] Foo2 foo2, [Header(RabbitMessageHeaders.ConsumerQueue)] string queue) - { - return foo2 + queue; - } - - [RabbitListener("test.converted.message")] - public string Foo2Message([Payload] Foo2 foo2, IMessage message) - { - Bean = message.Headers.Target(); - Method = message.Headers.TargetMethod(); - return foo2 + Method.Name + Bean.GetType().Name; - } - - [RabbitListener("test.notconverted.message")] - public string JustMessage(IMessage message) - { - StringHeader = message.Headers.Get("stringHeader"); - IntHeader = message.Headers.Get("intHeader"); - return $"foo{message.GetType().Name}"; - } - - [RabbitListener("test.notconverted.channel")] - public string JustChannel(RC.IModel channel) - { - return "barAndChannel"; - } - - [RabbitListener("test.notconverted.messagechannel")] - public string MessageChannel(Foo2 foo2, IMessage message, RC.IModel channel) - { - return $"{foo2}{message.GetType().Name}AndChannel"; - } - - [RabbitListener("test.notconverted.messagingmessage")] - public string MessagingMessage(IMessage message) - { - return message.GetType().Name + message.Payload.GetType().Name; - } - - [RabbitListener("test.converted.foomessage")] - public string MessagingMessage(IMessage message, [Header("", Required = false)] string h, - [Header(RabbitMessageHeaders.ReceivedUserId)] string userId) - { - return message.GetType().Name + message.Payload.GetType().Name + userId; - } - - [RabbitListener("test.notconverted.messagingmessagenotgeneric")] - public string MessagingMessage(IMessage message, [Header("", Required = false)] int? h) - { - return message.GetType().Name + message.Payload.GetType().Name; - } - } - - public sealed class Foo1 - { - public string Bar { get; set; } - } - - public sealed class Foo2 - { - public string Bar { get; set; } - - public override string ToString() - { - return $"bar={Bar}"; - } - } - - public sealed class Foo1ToFoo2Converter : AbstractGenericConverter - { - public Foo1ToFoo2Converter() - : base(new HashSet<(Type, Type)> - { - (typeof(Foo1), typeof(Foo2)) - }) - { - } - - public Foo2 Convert(Foo1 source) - { - var foo2 = new Foo2 - { - Bar = source.Bar - }; - - return foo2; - } - - public override object Convert(object source, Type sourceType, Type targetType) - { - return Convert((Foo1)source); - } - } - - public sealed class AddSomeHeadersPostProcessor : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.SetHeader("bean", accessor.Target.GetType().Name); - accessor.SetHeader("method", accessor.TargetMethod.Name); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.SetHeader("bean", accessor.Target.GetType().Name); - accessor.SetHeader("method", accessor.TargetMethod.Name); - return message; - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitIntegrationTest.cs b/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitIntegrationTest.cs deleted file mode 100644 index 523a45f630..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitIntegrationTest.cs +++ /dev/null @@ -1,1664 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Net; -using System.Net.Http.Headers; -using System.Reflection; -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Retry; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Steeltoe.Messaging.RabbitMQ.Test.Test; -using Steeltoe.Messaging.Support; -using Xunit; -using RC = RabbitMQ.Client; - -#pragma warning disable S3872 // Parameter names should not duplicate the names of their methods - -namespace Steeltoe.Messaging.RabbitMQ.Test.Attributes; - -[Trait("Category", "Integration")] -public sealed class EnableRabbitIntegrationTest : IClassFixture -{ - private readonly IApplicationContext _context; - private readonly IServiceProvider _provider; - private readonly StartupFixture _fixture; - - public EnableRabbitIntegrationTest(StartupFixture fix) - { - _fixture = fix; - _provider = _fixture.Provider; - _context = _provider.GetRequiredService(); - } - - [Fact] - public void AutoDeclare() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("auto.exch", "auto.rk", "foo"); - Assert.StartsWith("FOO", reply, StringComparison.Ordinal); - var myService = _context.GetService(); - Assert.NotNull(myService); - Assert.True(myService.ChannelBoundOk); - } - - [Fact] - public void AutoSimpleDeclare() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("test.simple.declare", "foo"); - Assert.StartsWith("FOO", reply, StringComparison.Ordinal); - } - - [Fact] - public void AutoSimpleDeclareAnonymousQueue() - { - var registry = _context.GetService() as RabbitListenerEndpointRegistry; - var container = registry.GetListenerContainer("anonymousQueue575") as DirectMessageListenerContainer; - Assert.Single(container.GetQueueNames()); - - RabbitTemplate template = _context.GetRabbitTemplate(); - Assert.Equal("viaAnonymous:foo", template.ConvertSendAndReceive(container.GetQueueNames()[0], "foo")); - var messageListener = container.MessageListener as MessagingMessageListenerAdapter; - Assert.NotNull(messageListener.RetryTemplate); - Assert.NotNull(messageListener.RecoveryCallback); - } - - [Fact] - public async Task AutoStart() - { - var registry = _context.GetService() as RabbitListenerEndpointRegistry; - var container = registry.GetListenerContainer("notStarted") as DirectMessageListenerContainer; - Assert.NotNull(container); - Assert.False(container.IsAutoStartup); - Assert.False(container.IsRunning); - container.IsAutoStartup = true; - await registry.StartAsync(); - Assert.True(container.IsRunning); - await container.StopAsync(); - } - - [Fact] - public void AutoDeclareFanOut() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("auto.exch.fanout", string.Empty, "foo"); - Assert.Equal("FOOFOO", reply); - } - - [Fact] - public void AutoDeclareAnonWitAttributes() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string received = template.ConvertSendAndReceive("auto.exch", "auto.anon.atts.rk", "foo"); - Assert.StartsWith("foo:", received, StringComparison.Ordinal); - var queue = new Queue(received.Substring(4), true, true, true); - IRabbitAdmin admin = _context.GetRabbitAdmin(); - admin.DeclareQueue(queue); - } - - [Fact] - public void AutoDeclareAnon() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("auto.exch", "auto.anon.rk", "foo"); - Assert.Equal("FOO", reply); - } - - [Fact] - public void SimpleEndpoint() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("test.simple", "foo"); - Assert.Equal("FOO", reply); - - var containers = _context.GetService("testGroup"); - Assert.Equal(2, containers.Containers.Count); - } - - [Fact] - public async Task SimpleDirectEndpoint() - { - var registry = _context.GetService() as RabbitListenerEndpointRegistry; - var container = registry.GetListenerContainer("direct") as DirectMessageListenerContainer; - Assert.False(container.IsRunning); - await container.StartAsync(); - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("test.simple.direct", "foo"); - Assert.StartsWith("FOOfoo", reply, StringComparison.Ordinal); - Assert.Equal(2, container.ConsumersPerQueue); - } - - [Fact] - public void SimpleDirectEndpointWithConcurrency() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("test.simple.direct2", "foo"); - Assert.StartsWith("FOOfoo", reply, StringComparison.Ordinal); - var registry = _context.GetService() as RabbitListenerEndpointRegistry; - var container = registry.GetListenerContainer("directWithConcurrency") as DirectMessageListenerContainer; - Assert.Equal(3, container.ConsumersPerQueue); - } - - [Fact] - public void SimpleInheritanceMethod() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("test.inheritance", "foo"); - Assert.Equal("FOO", reply); - } - - [Fact] - public void SimpleInheritanceClass() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("test.inheritance.class", "foo"); - Assert.Equal("FOOBAR", reply); - } - - [Fact] - public void Commas() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("test,with,commas", "foo"); - Assert.Equal("FOOfoo", reply); - var commaContainers = _context.GetService("commas"); - Assert.Single(commaContainers.Containers); - var container = commaContainers.Containers[0] as DirectMessageListenerContainer; - string[] queueNames = container.GetQueueNames(); - Assert.Contains("test.comma.1", queueNames); - Assert.Contains("test.comma.2", queueNames); - Assert.Contains("test.comma.3", queueNames); - Assert.Contains("test.comma.4", queueNames); - Assert.Contains("test,with,commas", queueNames); - Assert.Equal(5, queueNames.Length); - } - - [Fact] - public void MultiListener() - { - var foo = new Foo - { - Field = "foo" - }; - - RabbitTemplate template = _context.GetRabbitTemplate("jsonRabbitTemplate"); - string reply = template.ConvertSendAndReceive("multi.exch", "multi.rk", foo); - Assert.Equal("FOO: foo handled by default handler", reply); - - var bar = new Bar - { - Field = "bar" - }; - - template.ConvertAndSend("multi.exch", "multi.rk", bar); - template.ReceiveTimeout = 10000; - string reply2 = template.ReceiveAndConvert("sendTo.replies"); - Assert.Equal("BAR: bar", reply2); - - bar.Field = "crash"; - template.ConvertAndSend("multi.exch", "multi.rk", bar); - string reply3 = template.ReceiveAndConvert("sendTo.replies"); - Assert.Equal("CRASHCRASH Test reply from error handler", reply3); - bar.Field = "bar"; - - var baz = new Baz - { - Field = "baz" - }; - - string reply4 = template.ConvertSendAndReceive("multi.exch", "multi.rk", baz); - Assert.Equal("BAZ: baz", reply4); - - var qux = new Qux - { - Field = "qux" - }; - - var beanMethodHeaders = new List(); - var mpp = new MultiListenerMessagePostProcessor(beanMethodHeaders); - template.SetAfterReceivePostProcessors(mpp); - - string reply5 = template.ConvertSendAndReceive("multi.exch", "multi.rk", qux); - Assert.Equal("QUX: qux: multi.rk", reply5); - - Assert.Equal(2, beanMethodHeaders.Count); - Assert.Equal("MultiListenerService", beanMethodHeaders[0]); - Assert.Equal("Qux", beanMethodHeaders[1]); - template.RemoveAfterReceivePostProcessor(mpp); - - string reply6 = template.ConvertSendAndReceive("multi.exch.tx", "multi.rk.tx", bar); - Assert.Equal("BAR: barbar", reply6); - string reply7 = template.ConvertSendAndReceive("multi.exch.tx", "multi.rk.tx", baz); - Assert.Equal("BAZ: bazbaz: multi.rk.tx", reply7); - var multiBean = _context.GetService(); - Assert.NotNull(multiBean); - Assert.IsType(multiBean.Bean); - Assert.NotNull(multiBean.Method); - Assert.Equal("Baz", multiBean.Method.Name); - } - - [Fact] - public void MultiListenerJson() - { - RabbitTemplate template = _context.GetRabbitTemplate("jsonRabbitTemplate"); - - var bar = new Bar - { - Field = "bar" - }; - - const string exchange = "multi.json.exch"; - const string routingKey = "multi.json.rk"; - string reply = template.ConvertSendAndReceive(exchange, routingKey, bar); - Assert.Equal("BAR: barMultiListenerJsonService", reply); - - var baz = new Baz - { - Field = "baz" - }; - - reply = template.ConvertSendAndReceive(exchange, routingKey, baz); - Assert.Equal("BAZ: baz", reply); - - var qux = new Qux - { - Field = "qux" - }; - - reply = template.ConvertSendAndReceive(exchange, routingKey, qux); - Assert.Equal("QUX: qux: multi.json.rk", reply); - - template.ConvertAndSend(exchange, routingKey, bar); - template.ReceiveTimeout = 10000; - reply = template.ReceiveAndConvert("sendTo.replies.spel"); - Assert.Equal("BAR: barMultiListenerJsonService", reply); - var registry = _context.GetService() as RabbitListenerEndpointRegistry; - var container = registry.GetListenerContainer("multi") as DirectMessageListenerContainer; - Assert.NotNull(container); - var listener = container.MessageListener as MessagingMessageListenerAdapter; - Assert.NotNull(listener); - Assert.NotNull(listener.ErrorHandler); - Assert.True(listener.ReturnExceptions); - } - - [Fact] - public void EndpointWithHeader() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - - var properties = new MessageHeaders(new Dictionary - { - { "prefix", "prefix-" } - }); - - IMessage request = MessageTestUtils.CreateTextMessage("foo", properties); - IMessage reply = template.SendAndReceive("test.header", request); - Assert.Equal("prefix-FOO", MessageTestUtils.ExtractText(reply)); - Assert.True(reply.Headers.Get("replyMPPApplied")); - Assert.Equal("MyService", reply.Headers.Get("bean")); - Assert.Equal("CapitalizeWithHeader", reply.Headers.Get("method")); - } - - [Fact] - public void EndpointWithMessage() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - - var properties = new MessageHeaders(new Dictionary - { - { "prefix", "prefix-" } - }); - - IMessage request = MessageTestUtils.CreateTextMessage("foo", properties); - IMessage reply = template.SendAndReceive("test.message", request); - Assert.Equal("prefix-FOO", MessageTestUtils.ExtractText(reply)); - } - - [Fact] - public void EndpointWithComplexReply() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - var context = _context.GetService(); - var strategy = context.GetService("consumerTagStrategy") as ConsumerTagStrategy; - - var properties = new MessageHeaders(new Dictionary - { - { "foo", "fooValue" } - }); - - IMessage request = MessageTestUtils.CreateTextMessage("content", properties); - IMessage reply = template.SendAndReceive("test.reply", request); - Assert.Equal("content", MessageTestUtils.ExtractText(reply)); - Assert.Equal("fooValue", reply.Headers.Get("foo")); - Assert.StartsWith(strategy.TagPrefix, reply.Headers.Get("bar"), StringComparison.Ordinal); - } - - [Fact] - public void SimpleEndpointWithSendTo() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ConvertAndSend("test.sendTo", "bar"); - template.ReceiveTimeout = 10000; - string result = template.ReceiveAndConvert("test.sendTo.reply"); - Assert.NotNull(result); - Assert.Equal("BAR", result); - } - - [Fact] - public void SimpleEndpointWithSendToSpel() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ConvertAndSend("test.sendTo.spel", "bar"); - template.ReceiveTimeout = 10000; - string result = template.ReceiveAndConvert("test.sendTo.reply.spel"); - Assert.NotNull(result); - Assert.Equal("BARbar", result); - } - - [Fact] - public void SimpleEndpointWithSendToSpelRuntime() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ConvertAndSend("test.sendTo.runtimespel", "spel"); - template.ReceiveTimeout = 10000; - string result = template.ReceiveAndConvert("test.sendTo.reply.runtimespel"); - Assert.NotNull(result); - Assert.Equal("runtimespel", result); - } - - [Fact] - public void SimpleEndpointWithSendToSpelRuntimeMessagingMessage() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ConvertAndSend("test.sendTo.runtimespelsource", "spel"); - template.ReceiveTimeout = 10000; - string result = template.ReceiveAndConvert("test.sendTo.runtimespelsource.reply"); - Assert.NotNull(result); - Assert.Equal("sourceEval", result); - } - - [Fact] - public void TestInvalidPojoConversion() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - _fixture.ErrorHandlerLatch.Reset(); - _fixture.ErrorHandlerError.Value = null; - template.ConvertAndSend("test.invalidPojo", "bar"); - Assert.True(_fixture.ErrorHandlerLatch.Wait(TimeSpan.FromSeconds(10))); - Exception exception = _fixture.ErrorHandlerError.Value; - Assert.NotNull(exception); - Assert.IsType(exception); - Exception cause = exception.InnerException; - Assert.IsType(cause); - Exception cause2 = cause.InnerException; - Assert.IsType(cause2); - Assert.Contains("Cannot convert from [String] to [DateTime]", cause2.Message, StringComparison.Ordinal); - } - - [Fact] - public void TestDifferentTypes() - { - var foo = new Foo1 - { - Bar = "bar" - }; - - RabbitTemplate template = _context.GetRabbitTemplate("jsonRabbitTemplate"); - var service = _context.GetService(); - service.Latch.Reset(); - service.Foos.Clear(); - template.ConvertAndSend("differentTypes", foo); - Assert.True(service.Latch.Wait(TimeSpan.FromSeconds(10))); - Assert.NotEmpty(service.Foos); - Assert.IsType(service.Foos[0]); - var foo2 = (Foo2)service.Foos[0]; - Assert.Equal("bar", foo2.Bar); - var registry = _context.GetService() as RabbitListenerEndpointRegistry; - var container = registry.GetListenerContainer("different") as DirectMessageListenerContainer; - Assert.NotNull(container); - Assert.Equal(1, container.ConsumersPerQueue); - } - - [Fact] - public void TestDifferentTypesWithConcurrency() - { - var foo = new Foo1 - { - Bar = "bar" - }; - - RabbitTemplate template = _context.GetRabbitTemplate("jsonRabbitTemplate"); - var service = _context.GetService(); - service.Latch.Reset(); - service.Foos.Clear(); - template.ConvertAndSend("differentTypes2", foo); - Assert.True(service.Latch.Wait(TimeSpan.FromSeconds(10))); - Assert.NotEmpty(service.Foos); - Assert.IsType(service.Foos[0]); - var foo2 = (Foo2)service.Foos[0]; - Assert.Equal("bar", foo2.Bar); - var registry = _context.GetService() as RabbitListenerEndpointRegistry; - var container = registry.GetListenerContainer("differentWithConcurrency") as DirectMessageListenerContainer; - Assert.NotNull(container); - Assert.Equal(3, container.ConsumersPerQueue); - } - - [Fact] - public void TestFanOut() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ConvertAndSend("test.metaFanout", string.Empty, "foo"); - var service = _context.GetService(); - Assert.True(service.Latch.Wait(TimeSpan.FromSeconds(10))); - } - - [Fact] - public void TestHeadersExchange() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("auto.headers", string.Empty, "foo", new TestHeadersExchangeMpp1()); - Assert.Equal("FOO", reply); - string reply1 = template.ConvertSendAndReceive("auto.headers", string.Empty, "bar", new TestHeadersExchangeMpp2()); - Assert.Equal("BAR", reply1); - } - - [Fact] - public async Task DeadLetterOnDefaultExchange() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ConvertAndSend("amqp656", "foo"); - string reply = template.ReceiveAndConvert("amqp656dlq", 10000); - Assert.Equal("foo", reply); - _ = _context.GetRabbitAdmin(); - - var client = new HttpClient(); - byte[] authToken = Encoding.ASCII.GetBytes("guest:guest"); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)); - - HttpResponseMessage result = await client.GetAsync(new Uri("http://localhost:15672/api/queues/%2F/" + "amqp656")); - Assert.Equal(HttpStatusCode.OK, result.StatusCode); - string content = await result.Content.ReadAsStringAsync(); - Assert.Contains("test-empty", content, StringComparison.Ordinal); - Assert.Contains("test-null", content, StringComparison.Ordinal); - } - - [Fact] - public void ReturnExceptionWithRethrowAdapter() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ThrowReceivedExceptions = true; - var e = Assert.Throws(() => template.ConvertSendAndReceive("test.return.exceptions", "foo")); - Assert.Contains("return this", e.Message, StringComparison.Ordinal); - template.ThrowReceivedExceptions = false; - Assert.IsType(template.ConvertSendAndReceive("test.return.exceptions", "foo")); - } - - [Fact] - public void ListenerErrorHandler() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - string reply = template.ConvertSendAndReceive("test.pojo.errors", "foo"); - Assert.Equal("BAR", reply); - } - - [Fact] - public void ListenerErrorHandlerException() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ThrowReceivedExceptions = true; - var e = Assert.Throws(() => template.ConvertSendAndReceive("test.pojo.errors2", "foo")); - Assert.Contains("from error handler", e.Message, StringComparison.Ordinal); - Assert.Contains("return this", e.InnerException.Message, StringComparison.Ordinal); - template.ThrowReceivedExceptions = false; - Assert.NotNull(_fixture.ErrorHandlerChannel.Value); - } - - [Fact] - public void TestGenericReturnTypes() - { - RabbitTemplate template = _context.GetRabbitTemplate("jsonRabbitTemplate"); - var returned = template.ConvertSendAndReceive>("test.generic.list", new JsonObject("baz")); - Assert.NotNull(returned[0]); - - var returned1 = template.ConvertSendAndReceive>("test.generic.map", new JsonObject("baz")); - Assert.NotNull(returned1["key"]); - Assert.IsType(returned1["key"]); - } - - [Fact] - public void TestManualContainer() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ConvertAndSend("test.manual.container", "foo"); - Assert.True(_fixture.ManualContainerLatch.Wait(TimeSpan.FromSeconds(10))); - Assert.NotNull(_fixture.Message.Value); - string msg = Encoding.UTF8.GetString((byte[])_fixture.Message.Value.Payload); - Assert.Equal("foo", msg); - } - - [Fact] - public void TestNoListenerYet() - { - RabbitTemplate template = _context.GetRabbitTemplate(); - template.ConvertAndSend("test.no.listener.yet", "bar"); - Assert.True(_fixture.NoListenerLatch.Wait(TimeSpan.FromSeconds(10))); - Assert.NotNull(_fixture.Message.Value); - string msg = Encoding.UTF8.GetString((byte[])_fixture.Message.Value.Payload); - Assert.Equal("bar", msg); - } - - [Fact] - public void MessagingMessageReturned() - { - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("\"messaging\"")).SetHeader(MessageHeaders.ContentType, "application/json") - .Build(); - - RabbitTemplate template = _context.GetRabbitTemplate(); - message = template.SendAndReceive("test.messaging.message", message); - Assert.NotNull(message); - string str = Encoding.UTF8.GetString((byte[])message.Payload); - Assert.Equal("{\"field\":\"MESSAGING\"}", str); - Assert.Equal("bar", message.Headers.Get("foo")); - } - - [Fact] - public void ByteArrayMessageReturned() - { - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("amqp")).SetHeader(MessageHeaders.ContentType, "text/plain").Build(); - RabbitTemplate template = _context.GetRabbitTemplate(); - message = template.SendAndReceive("test.amqp.message", message); - Assert.NotNull(message); - string str = Encoding.UTF8.GetString((byte[])message.Payload); - Assert.Equal("AMQP", str); - Assert.Equal("bar", message.Headers.Get("foo")); - } - - [Fact] - public void BytesToString() - { - IMessage message = Message.Create(Encoding.UTF8.GetBytes("bytes")); - RabbitTemplate template = _context.GetRabbitTemplate(); - IMessage returned = template.SendAndReceive("test.bytes.to.string", message); - Assert.NotNull(returned); - Assert.IsType(returned.Payload); - string str = Encoding.UTF8.GetString((byte[])returned.Payload); - Assert.Equal("BYTES", str); - } - - [Fact] - public void TestManualOverride() - { - var registry = _context.GetService() as RabbitListenerEndpointRegistry; - var container = registry.GetListenerContainer("manual.acks.1") as DirectMessageListenerContainer; - Assert.Equal(AcknowledgeMode.Manual, container.AcknowledgeMode); - var container2 = registry.GetListenerContainer("manual.acks.2") as DirectMessageListenerContainer; - Assert.Equal(AcknowledgeMode.Manual, container2.AcknowledgeMode); - } - - public sealed class TestHeadersExchangeMpp1 : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.SetHeader("foo", "bar"); - accessor.SetHeader("baz", "qux"); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.SetHeader("foo", "bar"); - accessor.SetHeader("baz", "qux"); - return message; - } - } - - public sealed class TestHeadersExchangeMpp2 : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.SetHeader("baz", "fiz"); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.SetHeader("baz", "fiz"); - return message; - } - } - - public sealed class StartupFixture : IDisposable - { - private readonly CachingConnectionFactory _adminCf; - private readonly RabbitAdmin _admin; - private readonly IServiceCollection _services; - - public static string[] Queues { get; } = - { - "test.manual.container", - "test.no.listener.yet", - "test.simple", - "test.header", - "test.message", - "test.reply", - "test.sendTo", - "test.sendTo.reply", - "test.sendTo.spel", - "test.sendTo.reply.spel", - "test.sendTo.runtimespel", - "test.sendTo.reply.runtimespel", - "test.sendTo.runtimespelsource", - "test.sendTo.runtimespelsource.reply", - "test.intercepted", - "test.intercepted.withReply", - "test.invalidPojo", - "differentTypes", - "differentTypes2", - "differentTypes3", - "test.inheritance", - "test.inheritance.class", - "test.comma.1", - "test.comma.2", - "test.comma.3", - "test.comma.4", - "test,with,commas", - "test.converted", - "test.converted.list", - "test.converted.array", - "test.converted.args1", - "test.converted.args2", - "test.converted.message", - "test.notconverted.message", - "test.notconverted.channel", - "test.notconverted.messagechannel", - "test.notconverted.messagingmessage", - "test.converted.foomessage", - "test.notconverted.messagingmessagenotgeneric", - "test.simple.direct", - "test.simple.direct2", - "test.generic.list", - "test.generic.map", - "amqp656dlq", - "test.simple.declare", - "test.return.exceptions", - "test.pojo.errors", - "test.pojo.errors2", - "test.messaging.message", - "test.amqp.message", - "test.bytes.to.string", - "test.projection", - "manual.acks.1", - "manual.acks.2", - "erit.batch.1", - "erit.batch.2", - "erit.batch.3" - }; - - public ServiceProvider Provider { get; set; } - - public CountdownEvent ManualContainerLatch { get; set; } = new(1); - - public CountdownEvent NoListenerLatch { get; set; } = new(1); - - public CountdownEvent ErrorHandlerLatch { get; set; } = new(1); - - public AtomicReference ErrorHandlerError { get; set; } = new(); - - public AtomicReference Message { get; set; } = new(); - - public AtomicReference ErrorHandlerChannel { get; set; } = new(); - - public StartupFixture() - { - _adminCf = new CachingConnectionFactory("localhost"); - _admin = new RabbitAdmin(_adminCf); - - foreach (string q in Queues) - { - var queue = new Queue(q); - _admin.DeclareQueue(queue); - } - - _services = CreateContainer(); - Provider = _services.BuildServiceProvider(true); - Provider.GetRequiredService().StartAsync(default).GetAwaiter().GetResult(); - } - - public void Dispose() - { - foreach (string q in Queues) - { - _admin.DeleteQueue(q); - } - - _admin.DeleteQueue("sendTo.replies"); - _admin.DeleteQueue("sendTo.replies.spel"); - _adminCf.Dispose(); - - Provider.Dispose(); - } - - private ServiceCollection CreateContainer(IConfiguration configuration = null) - { - var services = new ServiceCollection(); - configuration ??= new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.SetMinimumLevel(LogLevel.Debug); - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configuration); - services.AddRabbitHostingServices(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitConnectionFactory(); - - // Manual container, register as ISmartLifecycle so auto started - services.AddSingleton(p => - { - var context = p.GetRequiredService(); - var loggerFactory = p.GetService(); - - var defFactory = - context.GetService(DirectRabbitListenerContainerFactory.DefaultServiceName) as - DirectRabbitListenerContainerFactory; - - var listener = new TestManualContainerListener(ManualContainerLatch, Message); - var endpoint = new SimpleRabbitListenerEndpoint(context, listener, loggerFactory); - endpoint.SetQueueNames("test.manual.container"); - DirectMessageListenerContainer container = defFactory.CreateListenerContainer(endpoint); - container.ServiceName = "factoryCreatedContainerSimpleListener"; - container.Initialize(); - return container; - }); - - // Manual container, register as ISmartLifecycle so auto started - services.AddSingleton(p => - { - var context = p.GetRequiredService(); - - var defFactory = context.GetService(DirectRabbitListenerContainerFactory.DefaultServiceName); - - var container = defFactory.CreateListenerContainer() as DirectMessageListenerContainer; - container.ServiceName = "factoryCreatedContainerNoListener"; - container.SetQueueNames("test.no.listener.yet"); - container.MessageListener = new TestManualContainerListener(NoListenerLatch, Message); - container.Initialize(); - return container; - }); - - // Add named container factory rabbitAutoStartFalseListenerContainerFactory - services.AddRabbitListenerContainerFactory((_, f) => - { - f.ServiceName = "rabbitAutoStartFalseListenerContainerFactory"; - f.AutoStartup = false; - }); - - // Add default Rabbit listener container factory. In Spring, RetryTemplate and RecoveryCallback are setup by default. - // In Steeltoe, no retry policy is added by default (additional configuration is required). - // To resolve this difference, we do not setup the retryTemplate here. - // Tests that need to work with retries now use txListenerContainerFactory - - services.AddRabbitListenerContainerFactory((p, f) => - { - IApplicationContext context = p.GetApplicationContext(); - f.ErrorHandler = context.GetService("errorHandler"); - f.ConsumerTagStrategy = context.GetService("consumerTagStrategy"); - f.SetBeforeSendReplyPostProcessors(new AddSomeHeadersPostProcessor()); - }); - - // Add named container factory txListenerContainerFactory - services.AddRabbitListenerContainerFactory((p, f) => - { - IApplicationContext context = p.GetApplicationContext(); - f.ServiceName = "txListenerContainerFactory"; - f.ErrorHandler = context.GetService("errorHandler"); - f.ConsumerTagStrategy = context.GetService("consumerTagStrategy"); - f.RetryTemplate = new PollyRetryTemplate(3, 1, 1, 1); - f.ReplyRecoveryCallback = new DefaultReplyRecoveryCallback(); - f.IsChannelTransacted = true; - }); - - // Add named container factory directListenerContainerFactory - services.AddRabbitListenerContainerFactory((p, f) => - { - IApplicationContext context = p.GetApplicationContext(); - f.ServiceName = "directListenerContainerFactory"; - f.ConsumersPerQueue = 2; - f.ErrorHandler = context.GetService("errorHandler"); - f.ConsumerTagStrategy = context.GetService("consumerTagStrategy"); - }); - - // Add named container factory jsonListenerContainerFactory - services.AddRabbitListenerContainerFactory((p, f) => - { - IApplicationContext context = p.GetApplicationContext(); - f.ServiceName = "jsonListenerContainerFactory"; - f.ErrorHandler = context.GetService("errorHandler"); - f.ConsumerTagStrategy = context.GetService("consumerTagStrategy"); - f.MessageConverter = new JsonMessageConverter(); - f.RetryTemplate = new PollyRetryTemplate(3, 1, 1, 1); - f.ReplyRecoveryCallback = new DefaultReplyRecoveryCallback(); - f.SetBeforeSendReplyPostProcessors(new AddSomeHeadersPostProcessor()); - }); - - // Add named container factory consumerBatchContainerFactory - services.AddRabbitListenerContainerFactory((p, f) => - { - IApplicationContext context = p.GetApplicationContext(); - f.ServiceName = "consumerBatchContainerFactory"; - f.ConsumerTagStrategy = context.GetService("consumerTagStrategy"); - f.BatchListener = true; - }); - - services.AddRabbitAdmin(); - services.AddRabbitTemplate(); - - // Add named RabbitTemplate txRabbitTemplate - services.AddRabbitTemplate((_, t) => - { - t.ServiceName = "txRabbitTemplate"; - t.IsChannelTransacted = true; - }); - - // Add named RabbitTemplate jsonRabbitTemplate - services.AddRabbitTemplate((_, t) => - { - t.ServiceName = "jsonRabbitTemplate"; - t.MessageConverter = new JsonMessageConverter(); - }); - - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - - services.AddRabbitListeners(configuration); - services.AddRabbitListeners(configuration); - services.AddRabbitListeners(configuration); - services.AddRabbitListeners(configuration); - services.AddRabbitListeners(configuration); - services.AddRabbitListeners(configuration); - services.AddRabbitListeners(configuration); - - services.AddRabbitQueue(new Queue("sendTo.replies", false, false, false)); - services.AddRabbitQueue(new Queue("sendTo.replies.spel", false, false, false)); - - services.AddRabbitQueues(new Queue("auto.headers1", true, false, true, new Dictionary - { - { "x-message-ttl", 10000 } - }), new Queue("auto.headers2", true, false, true, new Dictionary - { - { "x-message-ttl", 10000 } - })); - - services.AddRabbitExchange(new HeadersExchange("auto.headers", true, true)); - - services.AddRabbitBindings(new QueueBinding("auto.headers1.binding", "auto.headers1", "auto.headers", string.Empty, new Dictionary - { - { "x-match", "all" }, - { "foo", "bar" }, - { "baz", null } - }), new QueueBinding("auto.headers2.binding", "auto.headers2", "auto.headers", string.Empty, new Dictionary - { - { "x-match", "any" }, - { "foo", "bax" }, - { "baz", "fiz" } - })); - - services.AddRabbitQueues(new Queue("amqp656", true, false, true, new Dictionary - { - { "x-dead-letter-exchange", string.Empty }, - { "x-dead-letter-routing-key", "amqp656dlq" }, - { "test-empty", string.Empty }, - { "test-null", string.Empty } - })); - - services.AddRabbitExchange(new TopicExchange("amqp656.topic", true, true)); - services.AddRabbitBindings(new QueueBinding("amqp656.binding", "amqp656", "amqp656.topic", "foo", null)); - - services.AddSingleton(_ => - { - var result = new ConditionalRejectingErrorHandler1(ErrorHandlerLatch, ErrorHandlerError) - { - ServiceName = "errorHandler" - }; - - return result; - }); - - services.AddSingleton(_ => - { - var result = new ConsumerTagStrategy - { - ServiceName = "consumerTagStrategy" - }; - - return result; - }); - - services.AddRabbitListenerErrorHandler("upcaseAndRepeatErrorHandler"); - services.AddRabbitListenerErrorHandler("alwaysBARHandler"); - services.AddRabbitListenerErrorHandler("throwANewException", _ => new ThrowANewExceptionErrorHandler(ErrorHandlerChannel)); - return services; - } - } - - [DeclareExchange(Name = "test.metaFanout", AutoDelete = "True", Type = ExchangeType.FanOut)] - public sealed class FanOutListener - { - public CountdownEvent Latch { get; } = new(2); - - [DeclareAnonymousQueue("fanout1")] - [DeclareQueueBinding(Name = "fanout1.binding", ExchangeName = "test.metaFanout", QueueName = "#{@fanout1}")] - [RabbitListener(Binding = "fanout1.binding")] - public void Handle1(string foo) - { - Latch.Signal(); - } - - [DeclareAnonymousQueue("fanout2")] - [DeclareQueueBinding(Name = "fanout2.binding", ExchangeName = "test.metaFanout", QueueName = "#{@fanout2}")] - [RabbitListener(Binding = "fanout2.binding")] - public void Handle2(string foo) - { - Latch.Signal(); - } - } - - public sealed class MyService - { - private readonly IApplicationContext _context; - - private RabbitTemplate TxRabbitTemplate => _context.GetRabbitTemplate("txRabbitTemplate"); - - public bool? ChannelBoundOk { get; set; } - - public CountdownEvent Latch { get; set; } = new(1); - - public List Foos { get; } = new(); - - public CountdownEvent Batch1Latch { get; set; } = new(1); - - public CountdownEvent Batch2Latch { get; set; } = new(1); - - public CountdownEvent Batch3Latch { get; set; } = new(1); - - public List> AmqpMessagesReceived { get; } = new(); - - public List MessagingMessagesReceived { get; } = new(); - - public List Batch3Strings { get; } = new(); - - public MyService(IApplicationContext context) - { - _context = context; - } - - [DeclareQueue(Name = "auto.declare", AutoDelete = "True", Admin = "rabbitAdmin")] - [DeclareExchange(Name = "auto.exch", AutoDelete = "True")] - [DeclareQueueBinding(Name = "auto.binding", QueueName = "auto.declare", ExchangeName = "auto.exch", RoutingKey = "auto.rk")] - [RabbitListener(Id = "threadNamer", Binding = "auto.binding", ContainerFactory = "txListenerContainerFactory")] - public string HandleWithDeclare(string foo, RC.IModel channel) - { - ChannelBoundOk = TxRabbitTemplate.Execute(c => c.Equals(channel)); - return foo.ToUpperInvariant() + Thread.CurrentThread.Name; - } - - [DeclareQueue(Name = "${jjjj?test.simple.declare}", Durable = "True")] - [RabbitListener("${jjjj?test.simple.declare}")] - public string HandleWithSimpleDeclare(string foo) - { - return foo.ToUpperInvariant() + Thread.CurrentThread.Name; - } - - [DeclareAnonymousQueue("myAnonymous")] - [RabbitListener(Queue = "#{@myAnonymous}", Id = "anonymousQueue575", ContainerFactory = "txListenerContainerFactory")] - public string HandleWithAnonymousQueueToDeclare(string data) - { - return $"viaAnonymous:{data}"; - } - - [DeclareAnonymousQueue("anon1", AutoDelete = "True", Exclusive = "True", Durable = "True")] - [DeclareExchange(Name = "auto.start", AutoDelete = "True", Delayed = "${no:prop?false}")] - [DeclareQueueBinding(Name = "auto.start.binding", QueueName = "#{@anon1}", ExchangeName = "auto.start", RoutingKey = "auto.start")] - [RabbitListener(Id = "notStarted", Binding = "auto.start.binding", ContainerFactory = "rabbitAutoStartFalseListenerContainerFactory")] - public void HandleWithAutoStartFalse(string foo) - { - } - - [DeclareQueue(Name = "auto.declare.fanout", AutoDelete = "True")] - [DeclareExchange(Name = "auto.exch.fanout", AutoDelete = "True", Type = ExchangeType.FanOut)] - [DeclareQueueBinding(Name = "auto.fanout.binding", QueueName = "auto.declare.fanout", ExchangeName = "auto.exch.fanout")] - [RabbitListener(Binding = "auto.fanout.binding")] - public string HandleWithFanOut(string foo) - { - return foo.ToUpperInvariant() + foo.ToUpperInvariant(); - } - - [DeclareAnonymousQueue("anon2")] - [DeclareExchange(Name = "auto.exch", AutoDelete = "True")] - [DeclareQueueBinding(Name = "auto.exch.anon.rk", QueueName = "#{@anon2}", ExchangeName = "auto.exch", RoutingKey = "auto.anon.rk")] - [RabbitListener(Binding = "auto.exch.anon.rk")] - public string HandleWithDeclareAnon(string foo) - { - return foo.ToUpperInvariant(); - } - - [DeclareAnonymousQueue("anon3", AutoDelete = "True", Exclusive = "True", Durable = "True")] - [DeclareExchange(Name = "auto.exch", AutoDelete = "True")] - [DeclareQueueBinding(Name = "auto.exch.anon.atts.rk", QueueName = "#{@anon3}", ExchangeName = "auto.exch", RoutingKey = "auto.anon.atts.rk")] - [RabbitListener(Binding = "auto.exch.anon.atts.rk")] - public string HandleWithDeclareAnonQueueWithAttributes(string foo, [Header(RabbitMessageHeaders.ConsumerQueue)] string queue) - { - return $"{foo}:{queue}"; - } - - [RabbitListener("test.simple", Group = "testGroup")] - public string Capitalize(string foo) - { - return foo.ToUpperInvariant(); - } - - [RabbitListener("test.header", Group = "testGroup")] - public string CapitalizeWithHeader([Payload] string content, [Header] string prefix) - { - return prefix + content.ToUpperInvariant(); - } - - [RabbitListener("test.simple.direct", Id = "direct", AutoStartup = "${no:property:here?False}", ContainerFactory = "directListenerContainerFactory")] - public string CapitalizeDirect1(string foo) - { - return foo.ToUpperInvariant() + foo + Thread.CurrentThread.Name; - } - - [RabbitListener("test.simple.direct2", Id = "directWithConcurrency", Concurrency = "${ffffx?3}", ContainerFactory = "directListenerContainerFactory")] - public string CapitalizeDirect2(string foo) - { - return foo.ToUpperInvariant() + foo + Thread.CurrentThread.Name; - } - - [RabbitListener("test.comma.1", "test.comma.2", "test,with,commas", "test.comma.3", "test.comma.4", Group = "commas")] - public string MultiQueuesConfig(string foo) - { - return foo.ToUpperInvariant() + foo; - } - - [RabbitListener("test.message")] - public string CapitalizeWithMessage(IMessage message) - { - return message.Headers.Get("prefix") + message.Payload.ToUpperInvariant(); - } - - [RabbitListener("test.reply")] - public IMessage Reply(string payload, [Header] string foo, [Header(RabbitMessageHeaders.ConsumerTag)] string tag) - { - return RabbitMessageBuilder.WithPayload(payload).SetHeader("foo", foo).SetHeader("bar", tag).Build(); - } - - [RabbitListener("test.sendTo")] - [SendTo("${foo:bar?test.sendTo.reply}")] - public string CapitalizeAndSendTo(string foo) - { - return foo.ToUpperInvariant(); - } - - [RabbitListener("test.sendTo.spel")] - [SendTo("test.sendTo.reply.spel")] - public string CapitalizeAndSendToSpel(string foo) - { - return foo.ToUpperInvariant() + foo; - } - - [RabbitListener("test.sendTo.runtimespel")] - [SendTo("!{'test.sendTo.reply.' + Result}")] - public string CapitalizeAndSendToSpelRuntime(string foo) - { - return $"runtime{foo}"; - } - - [RabbitListener("test.sendTo.runtimespelsource")] - [SendTo("!{Source.Headers['internal_consumerQueue'] + '.reply'}")] - public string CapitalizeAndSendToSpelRuntimeSource(string foo) - { - return "sourceEval"; - } - - [RabbitListener("test.invalidPojo")] - public void HandleIt(DateTime body) - { - } - - [RabbitListener("differentTypes", Id = "different", ContainerFactory = "jsonListenerContainerFactory")] - public void HandleDifferent(Foo2 foo) - { - InnerHandleDifferent(foo); - } - - [RabbitListener("differentTypes2", Id = "differentWithConcurrency", ContainerFactory = "jsonListenerContainerFactory", Concurrency = "3")] - public void HandleDifferentWithConcurrency(Foo2 foo) - { - InnerHandleDifferent(foo); - } - - private void InnerHandleDifferent(Foo2 foo) - { - Foos.Add(foo); - Latch.Signal(); - } - - [RabbitListener(Bindings = new[] - { - "auto.headers1.binding", - "auto.headers2.binding" - })] - public string HandleWithHeadersExchange(string foo) - { - return foo.ToUpperInvariant(); - } - - [RabbitListener(Id = "defaultDLX", Binding = "amqp656.binding")] - public string HandleWithDeadLetterDefaultExchange(string foo) - { - throw new RabbitRejectAndDoNotRequeueException("dlq"); - } - - [RabbitListener("test.return.exceptions", ReturnExceptions = "${some:prop?True}")] - public string AlwaysFails(string data) - { - throw new InvalidOperationException("return this"); - } - - [RabbitListener("test.pojo.errors", ErrorHandler = "#{@alwaysBARHandler}")] - public string AlwaysFailsWithErrorHandler(string data) - { - throw new Exception("return this"); - } - - [RabbitListener("test.pojo.errors2", ErrorHandler = "#{throwANewException}", ReturnExceptions = "True")] - public string AlwaysFailsWithErrorHandlerThrowAnother(string data) - { - throw new Exception("return this"); - } - - [RabbitListener("test.generic.list", ContainerFactory = "jsonListenerContainerFactory")] - public List GenericList(JsonObject input) - { - return new List - { - input - }; - } - - [RabbitListener("test.generic.map", ContainerFactory = "jsonListenerContainerFactory")] - public Dictionary GenericMap(JsonObject input) - { - return new Dictionary - { - { "key", input } - }; - } - - [RabbitListener("test.messaging.message", ContainerFactory = "jsonListenerContainerFactory")] - public IMessage MessagingMessage(string input) - { - var bar = new Bar - { - Field = input.ToUpperInvariant() - }; - - var headers = new MessageHeaders(new Dictionary - { - { "foo", "bar" } - }); - - return Message.Create(bar, headers); - } - - [RabbitListener("test.amqp.message")] - public IMessage AmqpMessage(string input) - { - return (IMessage)MessageBuilder.WithPayload(Encoding.UTF8.GetBytes(input.ToUpperInvariant())) - .SetHeader(MessageHeaders.ContentType, "text/plain").SetHeader("foo", "bar").Build(); - } - - [RabbitListener("test.bytes.to.string")] - public string BytesToString(string input) - { - return input.ToUpperInvariant(); - } - - [RabbitListener("manual.acks.1", Id = "manual.acks.1", AckMode = "Manual")] - public string Manual1(string input, RC.IModel channel, [Header(RabbitMessageHeaders.DeliveryTag)] ulong tag) - { - return InnerManual(input, channel, tag); - } - - [RabbitListener("manual.acks.2", Id = "manual.acks.2", AckMode = "#{T(Steeltoe.Messaging.RabbitMQ.Core.AcknowledgeMode).Manual}")] - public string Manual2(string input, RC.IModel channel, [Header(RabbitMessageHeaders.DeliveryTag)] ulong tag) - { - return InnerManual(input, channel, tag); - } - - private static string InnerManual(string input, RC.IModel channel, ulong tag) - { - channel.BasicAck(tag, false); - return input.ToUpperInvariant(); - } - - [RabbitListener("erit.batch.1", ContainerFactory = "consumerBatchContainerFactory")] - public void ConsumerBatch1(List> amqpMessages) - { - AmqpMessagesReceived.Clear(); - AmqpMessagesReceived.AddRange(amqpMessages); - - if (!Batch1Latch.IsSet) - { - Batch1Latch.Signal(); - } - } - - [RabbitListener("erit.batch.2", ContainerFactory = "consumerBatchContainerFactory")] - public void ConsumerBatch2(List messages) - { - MessagingMessagesReceived.Clear(); - MessagingMessagesReceived.AddRange(messages); - - if (!Batch2Latch.IsSet) - { - Batch2Latch.Signal(); - } - } - - [RabbitListener("erit.batch.3", ContainerFactory = "consumerBatchContainerFactory")] - public void ConsumerBatch3(List strings) - { - Batch3Strings.Clear(); - Batch3Strings.AddRange(strings); - - if (!Batch3Latch.IsSet) - { - Batch3Latch.Signal(); - } - } - } - - public interface IMyServiceInterface - { - [RabbitListener("test.inheritance")] - string TestAnnotationInheritance(string foo); - } - - public sealed class MyServiceInterfaceImpl : IMyServiceInterface - { - public string TestAnnotationInheritance(string foo) - { - return foo.ToUpperInvariant(); - } - } - - [DeclareAnonymousQueue("TxClassLevel")] - [DeclareExchange(Name = "multi.exch.tx", AutoDelete = "True")] - [DeclareQueueBinding(Name = "multi.exch.binding.tx", ExchangeName = "multi.exch.tx", RoutingKey = "multi.rk.tx", QueueName = "#{@TxClassLevel}")] - [RabbitListener(Binding = "multi.exch.binding.tx", ContainerFactory = "jsonListenerContainerFactory")] - public interface ITxClassLevel - { - // @Transactional - [RabbitHandler] - string Foo(Bar bar); - - // @Transactional - [RabbitHandler] - string Baz([Payload] Baz baz, [Header(RabbitMessageHeaders.ReceivedRoutingKey)] string rk); - } - - public sealed class TxClassLevel : ITxClassLevel - { - public string Foo(Bar bar) - { - return $"BAR: {bar.Field}{bar.Field}"; - } - - public string Baz(Baz baz, string rk) - { - return $"BAZ: {baz.Field}{baz.Field}: {rk}"; - } - } - - public sealed class JsonObject - { - public string Bar { get; set; } - - public JsonObject() - { - } - - public JsonObject(string bar) - { - Bar = bar; - } - - public override string ToString() - { - return $"JsonObject [bar={Bar}]"; - } - } - - public class Foo - { - public string Field { get; set; } - } - - public sealed class Bar : Foo - { - } - - public sealed class Baz : Foo - { - } - - public sealed class Qux : Foo - { - } - - public sealed class Foo1 - { - public string Bar { get; set; } - } - - public sealed class Foo2 - { - public string Bar { get; set; } - - public override string ToString() - { - return $"bar={Bar}"; - } - } - - [DeclareAnonymousQueue("multiListenerAnon")] - [DeclareExchange(Name = "multi.exch", AutoDelete = "True")] - [DeclareQueueBinding(Name = "multi.exch.multi.listener", ExchangeName = "multi.exch", QueueName = "#{@multiListenerAnon}", RoutingKey = "multi.rk")] - [RabbitListener(Binding = "multi.exch.multi.listener", ErrorHandler = "upcaseAndRepeatErrorHandler", ContainerFactory = "jsonListenerContainerFactory")] - public sealed class MultiListenerService - { - public object Bean { get; private set; } - - public MethodInfo Method { get; private set; } - - [RabbitHandler] - [SendTo("${foo:bar?sendTo.replies}")] - public string Bar(Bar bar) - { - if (bar.Field == "crash") - { - throw new Exception("Test reply from error handler"); - } - - return $"BAR: {bar.Field}"; - } - - [RabbitHandler] - public string Baz(Baz baz, IMessage message) - { - Bean = message.Headers.Target(); - Method = message.Headers.TargetMethod(); - return $"BAZ: {baz.Field}"; - } - - [RabbitHandler] - public string Qux([Header(RabbitMessageHeaders.ReceivedRoutingKey)] string rk, [Payload] Qux qux) - { - return $"QUX: {qux.Field}: {rk}"; - } - - [RabbitHandler(true)] - public string DefaultHandler([Payload] object payload) - { - return payload is Foo foo ? $"FOO: {foo.Field} handled by default handler" : $"{payload} handled by default handler"; - } - } - - [RabbitListener("test.inheritance.class")] - public interface IMyServiceInterface2 - { - [RabbitHandler] - string TestAnnotationInheritance(string foo); - } - - public sealed class MyServiceInterfaceImpl2 : IMyServiceInterface2 - { - public string TestAnnotationInheritance(string foo) - { - return $"{foo.ToUpperInvariant()}BAR"; - } - } - - [DeclareAnonymousQueue("multiListenerJson")] - [DeclareExchange(Name = "multi.json.exch", AutoDelete = "True")] - [DeclareQueueBinding(Name = "multi.exch.multi.json.listener", ExchangeName = "multi.json.exch", QueueName = "#{@multiListenerJson}", - RoutingKey = "multi.json.rk")] - [RabbitListener(Id = "multi", Binding = "multi.exch.multi.json.listener", ErrorHandler = "alwaysBARHandler", - ContainerFactory = "jsonListenerContainerFactory", ReturnExceptions = "True")] - public sealed class MultiListenerJsonService - { - [RabbitHandler] - [SendTo("sendTo.replies.spel")] - public string Bar(Bar bar, IMessage message) - { - return $"BAR: {bar.Field}{message.Headers.Target().GetType().Name}"; - } - - [RabbitHandler] - public string Baz(Baz baz) - { - return $"BAZ: {baz.Field}"; - } - - [RabbitHandler] - public string Qux([Header(RabbitMessageHeaders.ReceivedRoutingKey)] string rk, [Payload] Qux qux) - { - return $"QUX: {qux.Field}: {rk}"; - } - } - - public sealed class DefaultReplyRecoveryCallback : IRecoveryCallback - { - public object Recover(IRetryContext context) - { - return null; - } - } - - public sealed class ConditionalRejectingErrorHandler1 : ConditionalRejectingErrorHandler - { - public CountdownEvent ErrorHandlerLatch { get; } - - public AtomicReference ErrorHandlerError { get; } - - public ConditionalRejectingErrorHandler1(CountdownEvent errorHandlerLatch, AtomicReference errorHandlerError) - { - ErrorHandlerLatch = errorHandlerLatch; - ErrorHandlerError = errorHandlerError; - } - - public override bool HandleError(Exception exception) - { - try - { - return base.HandleError(exception); - } - catch (Exception e) - { - ErrorHandlerError.Value = e; - - if (!ErrorHandlerLatch.IsSet) - { - ErrorHandlerLatch.Signal(); - } - - throw; - } - } - } - - public sealed class AlwaysBarListenerErrorHandler : IRabbitListenerErrorHandler - { - public string ServiceName { get; set; } = nameof(AlwaysBarListenerErrorHandler); - - public object HandleError(IMessage originalMessage, IMessage message, ListenerExecutionFailedException exception) - { - return "BAR"; - } - } - - public sealed class UpperCaseAndRepeatListenerErrorHandler : IRabbitListenerErrorHandler - { - public string ServiceName { get; set; } = nameof(UpperCaseAndRepeatListenerErrorHandler); - - public object HandleError(IMessage originalMessage, IMessage message, ListenerExecutionFailedException exception) - { - var barPayload = message.Payload as Bar; - string upperPayload = barPayload.Field.ToUpperInvariant(); - return $"{upperPayload}{upperPayload} {exception.InnerException.Message}"; - } - } - - public sealed class ThrowANewExceptionErrorHandler : IRabbitListenerErrorHandler - { - public string ServiceName { get; set; } = "throwANewException"; - - public AtomicReference ErrorHandlerChannel { get; } - - public ThrowANewExceptionErrorHandler(AtomicReference errorHandlerChannel) - { - ErrorHandlerChannel = errorHandlerChannel; - } - - public object HandleError(IMessage originalMessage, IMessage message, ListenerExecutionFailedException exception) - { - ErrorHandlerChannel.Value = message.Headers.Get(RabbitMessageHeaders.Channel); - throw new InvalidOperationException("from error handler", exception.InnerException); - } - } - - public sealed class ConsumerTagStrategy : IConsumerTagStrategy - { - private int _increment; - - public string TagPrefix { get; } = Guid.NewGuid().ToString(); - - public string ServiceName { get; set; } - - public string CreateConsumerTag(string queue) - { - return TagPrefix + Interlocked.Increment(ref _increment); - } - } - - public sealed class MultiListenerMessagePostProcessor : IMessagePostProcessor - { - public List ServiceMethodHeaders { get; } - - public MultiListenerMessagePostProcessor(List serviceMethodHeaders) - { - ServiceMethodHeaders = serviceMethodHeaders; - } - - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - ServiceMethodHeaders.Add(message.Headers.Get("bean")); - ServiceMethodHeaders.Add(message.Headers.Get("method")); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - ServiceMethodHeaders.Add(message.Headers.Get("bean")); - ServiceMethodHeaders.Add(message.Headers.Get("method")); - return message; - } - } - - public sealed class AddSomeHeadersPostProcessor : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.SetHeader("replyMPPApplied", true); - accessor.SetHeader("bean", accessor.Target.GetType().Name); - accessor.SetHeader("method", accessor.TargetMethod.Name); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.SetHeader("replyMPPApplied", true); - accessor.SetHeader("bean", accessor.Target.GetType().Name); - accessor.SetHeader("method", accessor.TargetMethod.Name); - return message; - } - } - - public sealed class TestManualContainerListener : IMessageListener - { - public CountdownEvent ManualContainerLatch { get; set; } - - public AtomicReference Message { get; set; } - - public AcknowledgeMode ContainerAckMode { get; set; } - - public TestManualContainerListener(CountdownEvent latch, AtomicReference message) - { - ManualContainerLatch = latch; - Message = message; - } - - public void OnMessage(IMessage message) - { - Message.Value = message; - ManualContainerLatch.Signal(); - } - - public void OnMessageBatch(List messages) - { - Message.Value = messages[0]; - ManualContainerLatch.Signal(); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitReturnTypesTest.cs b/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitReturnTypesTest.cs deleted file mode 100644 index e092f3a55a..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitReturnTypesTest.cs +++ /dev/null @@ -1,269 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Xunit; -using Xunit.Abstractions; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Attributes; - -[Trait("Category", "Integration")] -public sealed class EnableRabbitReturnTypesTest -{ - public const string Q1 = "EnableRabbitReturnTypesTests.1"; - public const string Q2 = "EnableRabbitReturnTypesTests.2"; - public const string Q3 = "EnableRabbitReturnTypesTests.3"; - public const string Q4 = "EnableRabbitReturnTypesTests.4"; - - private static ITestOutputHelper _output; - - public EnableRabbitReturnTypesTest(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - public async Task TestInterfaceReturn() - { - var queues = new List - { - new Queue(Q1), - new Queue(Q2), - new Queue(Q3), - new Queue(Q4) - }; - - ServiceProvider provider = null; - - try - { - provider = await CreateAndStartServicesAsync(null, queues, typeof(Listener)); - RabbitTemplate template = provider.GetRabbitTemplate(); - var reply = template.ConvertSendAndReceive("EnableRabbitReturnTypesTests.1", "3"); - Assert.NotNull(reply); - var reply2 = template.ConvertSendAndReceive("EnableRabbitReturnTypesTests.1", "4"); - Assert.NotNull(reply2); - } - finally - { - RabbitAdmin admin = provider.GetRabbitAdmin(); - admin.DeleteQueue(Q1); - admin.DeleteQueue(Q2); - admin.DeleteQueue(Q3); - admin.DeleteQueue(Q4); - provider?.Dispose(); - } - } - - [Fact] - public async Task TestAbstractReturn() - { - var queues = new List - { - new Queue(Q1), - new Queue(Q2), - new Queue(Q3), - new Queue(Q4) - }; - - ServiceProvider provider = null; - - try - { - provider = await CreateAndStartServicesAsync(null, queues, typeof(Listener)); - RabbitTemplate template = provider.GetRabbitTemplate(); - var reply = template.ConvertSendAndReceive("EnableRabbitReturnTypesTests.2", "3"); - Assert.NotNull(reply); - var reply2 = template.ConvertSendAndReceive("EnableRabbitReturnTypesTests.2", "4"); - Assert.NotNull(reply2); - } - finally - { - RabbitAdmin admin = provider.GetRabbitAdmin(); - admin.DeleteQueue(Q1); - admin.DeleteQueue(Q2); - admin.DeleteQueue(Q3); - admin.DeleteQueue(Q4); - provider?.Dispose(); - } - } - - [Fact] - public async Task TestListOfThree() - { - var queues = new List - { - new Queue(Q1), - new Queue(Q2), - new Queue(Q3), - new Queue(Q4) - }; - - ServiceProvider provider = null; - - try - { - provider = await CreateAndStartServicesAsync(null, queues, typeof(Listener)); - RabbitTemplate template = provider.GetRabbitTemplate(); - var reply = template.ConvertSendAndReceive>("EnableRabbitReturnTypesTests.3", "3"); - Assert.NotNull(reply); - } - finally - { - RabbitAdmin admin = provider.GetRabbitAdmin(); - admin.DeleteQueue(Q1); - admin.DeleteQueue(Q2); - admin.DeleteQueue(Q3); - admin.DeleteQueue(Q4); - provider?.Dispose(); - } - } - - [Fact] - public async Task TestGenericInterfaceReturn() - { - var queues = new List - { - new Queue(Q1), - new Queue(Q2), - new Queue(Q3), - new Queue(Q4) - }; - - ServiceProvider provider = null; - - try - { - provider = await CreateAndStartServicesAsync(null, queues, typeof(Listener)); - RabbitTemplate template = provider.GetRabbitTemplate(); - var reply = template.ConvertSendAndReceive("EnableRabbitReturnTypesTests.4", "3"); - Assert.NotNull(reply); - var reply2 = template.ConvertSendAndReceive("EnableRabbitReturnTypesTests.4", "4"); - Assert.NotNull(reply2); - } - finally - { - RabbitAdmin admin = provider.GetRabbitAdmin(); - admin.DeleteQueue(Q1); - admin.DeleteQueue(Q2); - admin.DeleteQueue(Q3); - admin.DeleteQueue(Q4); - provider?.Dispose(); - } - } - - public static async Task CreateAndStartServicesAsync(IConfiguration configuration, List queues, params Type[] listeners) - { - var services = new ServiceCollection(); - IConfiguration effectiveConfiguration = configuration ?? new ConfigurationBuilder().Build(); - - services.AddSingleton(effectiveConfiguration); - services.AddRabbitHostingServices(); - services.AddRabbitMessageConverter(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - services.AddRabbitListenerAttributeProcessor(); - services.AddSingleton(_ => new CachingConnectionFactory("localhost")); - - services.AddRabbitListenerContainerFactory((_, f) => - { - f.DefaultRequeueRejected = false; - }); - - services.AddRabbitAdmin(); - services.AddRabbitTemplate(); - services.AddRabbitQueues(queues.ToArray()); - - foreach (Type listener in listeners) - { - services.AddSingleton(listener); - } - - services.AddRabbitListeners(effectiveConfiguration, listeners); - ServiceProvider provider = services.BuildServiceProvider(true); - await provider.GetRequiredService().StartAsync(default); - return provider; - } - - public sealed class Listener - { - [RabbitListener("EnableRabbitReturnTypesTests.1")] - public IOne Listen1(string input) - { - _output.WriteLine("Listen1Async " + input); - - if (input == "3") - { - return new Three(); - } - - return new Four(); - } - - [RabbitListener("EnableRabbitReturnTypesTests.2")] - public Two Listen2(string input) - { - _output.WriteLine("Listen2Async " + input); - - if (input == "3") - { - return new Three(); - } - - return new Four(); - } - - [RabbitListener("EnableRabbitReturnTypesTests.3")] - public List Listen3(string input) - { - _output.WriteLine("Listen3Async " + input); - - var list = new List - { - new() - }; - - return list; - } - - [RabbitListener("EnableRabbitReturnTypesTests.4")] - public IOne Listen4(string input) - { - _output.WriteLine("Listen4Async " + input); - - if (input == "3") - { - return new Three(); - } - - return new Four(); - } - } - - public interface IOne - { - } - - public abstract class Two : IOne - { - public string Field { get; set; } - } - - public sealed class Three : Two - { - } - - public sealed class Four : Two - { - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitTest.cs b/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitTest.cs deleted file mode 100644 index 025fd5cc2f..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Attributes/EnableRabbitTest.cs +++ /dev/null @@ -1,740 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Test.Configuration; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Attributes; - -[Trait("Category", "Integration")] -public sealed class EnableRabbitTest -{ - [Fact] - public async Task SampleConfiguration() - { - ServiceProvider services = await RabbitSampleConfig.CreateAndStartServicesAsync(); - var context = services.GetRequiredService(); - TestSampleConfiguration(context, 2); - } - - [Fact] - public async Task FullConfiguration() - { - ServiceProvider services = await RabbitFullConfig.CreateAndStartServicesAsync(); - var context = services.GetRequiredService(); - TestFullConfiguration(context); - } - - [Fact] - public async Task FullConfigurableConfiguration() - { - ServiceProvider services = await RabbitFullConfigurableConfig.CreateAndStartServicesAsync(); - var context = services.GetRequiredService(); - TestFullConfiguration(context); - } - - [Fact] - public async Task NoRabbitAdminConfiguration() - { - var exception = await Assert.ThrowsAsync(async () => await RabbitSampleConfig.CreateAndStartServicesAsync(typeof(FullBean))); - Assert.Contains("rabbitAdmin", exception.Message, StringComparison.Ordinal); - } - - [Fact] - public async Task CustomConfiguration() - { - ServiceProvider services = await RabbitCustomConfig.CreateAndStartServicesAsync(); - var context = services.GetRequiredService(); - TestCustomConfiguration(context); - } - - [Fact] - public async Task ExplicitContainerFactory() - { - ServiceProvider services = await RabbitCustomContainerFactoryConfig.CreateAndStartServicesAsync(); - var context = services.GetRequiredService(); - TestExplicitContainerFactoryConfiguration(context); - } - - [Fact] - public async Task DefaultContainerFactory() - { - ServiceProvider services = await RabbitDefaultContainerFactoryConfig.CreateAndStartServicesAsync(typeof(DefaultBean)); - var context = services.GetRequiredService(); - TestDefaultContainerFactoryConfiguration(context); - } - - [Fact] - public async Task RabbitListeners() - { - ServiceProvider services = - await RabbitDefaultContainerFactoryConfig.CreateAndStartServicesAsync(typeof(RabbitListenersBean), typeof(ClassLevelListenersBean)); - - var context = services.GetRequiredService(); - TestRabbitListenerRepeatable(context); - } - - [Fact] - public async Task UnknownFactory() - { - var exception = await Assert.ThrowsAsync(async () => - await RabbitSampleConfig.CreateAndStartServicesAsync(typeof(CustomBean))); - - Assert.Contains("customFactory", exception.Message, StringComparison.Ordinal); - } - - [Fact] - public async Task InvalidPriorityConfiguration() - { - var exception = await Assert.ThrowsAsync(async () => - await RabbitSampleConfig.CreateAndStartServicesAsync(typeof(InvalidPriorityBean))); - - Assert.Contains("NotANumber", exception.Message, StringComparison.Ordinal); - } - - [Fact] - public async Task TestProperShutdownOnException() - { - ServiceProvider services = await TestProperShutdownOnExceptionConfig.CreateAndStartServicesAsync(); - var context = services.GetRequiredService(); - var listenerEndpointRegistry = context.GetService(); - await services.DisposeAsync(); - - foreach (IMessageListenerContainer messageListenerContainer in listenerEndpointRegistry.GetListenerContainers()) - { - Assert.False(messageListenerContainer.IsRunning); - } - } - - private void TestRabbitListenerRepeatable(IApplicationContext context) - { - var defaultFactory = (RabbitListenerContainerTestFactory)context.GetService("rabbitListenerContainerFactory"); - Assert.Equal(4, defaultFactory.GetListenerContainers().Count); - - var first = (AbstractRabbitListenerEndpoint)defaultFactory.GetListenerContainer("first").Endpoint; - Assert.Equal("first", first.Id); - Assert.Single(first.QueueNames); - Assert.Equal("myQueue", first.QueueNames[0]); - - var second = (AbstractRabbitListenerEndpoint)defaultFactory.GetListenerContainer("second").Endpoint; - Assert.Equal("second", second.Id); - Assert.Single(second.QueueNames); - Assert.Equal("anotherQueue", second.QueueNames[0]); - - var third = (AbstractRabbitListenerEndpoint)defaultFactory.GetListenerContainer("third").Endpoint; - Assert.Equal("third", third.Id); - Assert.Single(third.QueueNames); - Assert.Equal("class1", third.QueueNames[0]); - - var fourth = (AbstractRabbitListenerEndpoint)defaultFactory.GetListenerContainer("fourth").Endpoint; - Assert.Equal("fourth", fourth.Id); - Assert.Single(fourth.QueueNames); - Assert.Equal("class2", fourth.QueueNames[0]); - } - - private void TestSampleConfiguration(IApplicationContext context, int expectedDefaultContainers) - { - var defaultFactory = (RabbitListenerContainerTestFactory)context.GetService("rabbitListenerContainerFactory"); - var simpleFactory = (RabbitListenerContainerTestFactory)context.GetService("simpleFactory"); - - Assert.Equal(expectedDefaultContainers, defaultFactory.ListenerContainers.Count); - Assert.Single(simpleFactory.ListenerContainers); - - IEnumerable queues = context.GetRabbitQueues(); - - foreach (IQueue queue in queues) - { - Assert.True(queue.IgnoreDeclarationExceptions); - Assert.False(queue.ShouldDeclare); - CheckAdmins(queue.DeclaringAdmins); - } - - IEnumerable exchanges = context.GetRabbitExchanges(); - - foreach (IExchange exchange in exchanges) - { - Assert.True(exchange.IgnoreDeclarationExceptions); - Assert.False(exchange.ShouldDeclare); - CheckAdmins(exchange.DeclaringAdmins); - } - - IEnumerable bindings = context.GetRabbitBindings(); - - foreach (IBinding binding in bindings) - { - Assert.True(binding.IgnoreDeclarationExceptions); - Assert.False(binding.ShouldDeclare); - CheckAdmins(binding.DeclaringAdmins); - } - } - - private void TestFullConfiguration(IApplicationContext context) - { - var simpleFactory = (RabbitListenerContainerTestFactory)context.GetService("simpleFactory"); - Assert.Single(simpleFactory.ListenerContainers); - - MessageListenerTestContainer testContainer = simpleFactory.GetListenerContainers()[0]; - var endpoint = (AbstractRabbitListenerEndpoint)testContainer.Endpoint; - Assert.Equal("listener1", endpoint.Id); - AssertQueues(endpoint, "queue1", "queue2"); - Assert.Empty(endpoint.Queues); - Assert.True(endpoint.Exclusive); - Assert.Equal(34, endpoint.Priority); - IRabbitAdmin admin = context.GetRabbitAdmin(); - Assert.Same(endpoint.Admin, admin); - - var container = new DirectMessageListenerContainer(context); - endpoint.SetupListenerContainer(container); - var listener = (MessagingMessageListenerAdapter)container.MessageListener; - - var accessor = new RabbitHeaderAccessor - { - ContentType = MessageHeaders.ContentTypeTextPlain - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("Hello"), accessor.MessageHeaders); - var mockChannel = new Mock(); - - listener.OnMessage(message, mockChannel.Object); - } - - private void TestCustomConfiguration(IApplicationContext context) - { - var customFactory = (RabbitListenerContainerTestFactory)context.GetService("customFactory"); - var defaultFactory = (RabbitListenerContainerTestFactory)context.GetService("rabbitListenerContainerFactory"); - Assert.Single(defaultFactory.ListenerContainers); - Assert.Single(customFactory.ListenerContainers); - MessageListenerTestContainer testContainer = defaultFactory.GetListenerContainers()[0]; - IRabbitListenerEndpoint endpoint = testContainer.Endpoint; - Assert.IsType(endpoint); - var simpleEndpoint = (SimpleRabbitListenerEndpoint)endpoint; - Assert.IsType(simpleEndpoint.MessageListener); - var customRegistry = context.GetService(); - Assert.IsType(customRegistry); - Assert.Equal(2, customRegistry.GetListenerContainerIds().Count); - Assert.Equal(2, customRegistry.GetListenerContainers().Count); - Assert.NotNull(customRegistry.GetListenerContainer("listenerId")); - Assert.NotNull(customRegistry.GetListenerContainer("myCustomEndpointId")); - } - - private void TestDefaultContainerFactoryConfiguration(IApplicationContext context) - { - var defaultFactory = (RabbitListenerContainerTestFactory)context.GetService("rabbitListenerContainerFactory"); - Assert.Single(defaultFactory.GetListenerContainers()); - } - - private void TestExplicitContainerFactoryConfiguration(IApplicationContext context) - { - var defaultFactory = (RabbitListenerContainerTestFactory)context.GetService("simpleFactory"); - Assert.Single(defaultFactory.GetListenerContainers()); - } - - private void AssertQueues(AbstractRabbitListenerEndpoint endpoint, params string[] queues) - { - List actualQueues = endpoint.QueueNames; - - foreach (string expectedQueue in queues) - { - Assert.Contains(expectedQueue, actualQueues); - } - - Assert.Equal(queues.Length, actualQueues.Count); - } - - private void CheckAdmins(List admins) - { - Assert.Single(admins); - - if (admins[0] is RabbitAdmin admin) - { - Assert.Equal("myAdmin", admin.ServiceName); - } - else - { - Assert.Equal("myAdmin", (string)admins[0]); - } - } - - public static class TestProperShutdownOnExceptionConfig - { - public static async Task CreateAndStartServicesAsync() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateChannel(It.IsAny())).Returns(mockChannel.Object); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - var queueName = new AtomicReference(); - - mockChannel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(() => new RC.QueueDeclareOk(queueName.Value, 0, 0)) - .Callback(name => queueName.Value = name); - - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - - services.AddSingleton(mockConnectionFactory.Object); - services.AddRabbitListenerContainerFactory("rabbitListenerContainerFactory"); - services.AddRabbitAdmin(); - - IQueue myQueue = QueueBuilder.Durable("myQueue").Build(); - IQueue anotherQueue = QueueBuilder.Durable("anotherQueue").Build(); - IQueue class1 = QueueBuilder.Durable("class1").Build(); - IQueue class2 = QueueBuilder.Durable("class2").Build(); - - services.AddRabbitQueues(myQueue, anotherQueue, class1, class2); - services.AddSingleton(); - services.AddSingleton(); - services.AddRabbitListeners(configurationRoot, typeof(RabbitListenersBean), typeof(ClassLevelListenersBean)); - - ServiceProvider container = services.BuildServiceProvider(true); - await container.GetRequiredService().StartAsync(default); - return container; - } - } - - public static class RabbitSampleConfig - { - internal static async Task CreateAndStartServicesAsync(Type listenerBeanType = null) - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateChannel(It.IsAny())).Returns(mockChannel.Object); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - var queueName = new AtomicReference(); - - mockChannel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(() => new RC.QueueDeclareOk(queueName.Value, 0, 0)) - .Callback(name => queueName.Value = name); - - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - - services.AddSingleton(mockConnectionFactory.Object); - services.AddRabbitListenerContainerFactory("rabbitListenerContainerFactory"); - services.AddRabbitListenerContainerFactory("simpleFactory"); - services.AddRabbitAdmin("myAdmin"); - - IQueue foo = QueueBuilder.Durable("foo").Build(); - foo.ShouldDeclare = false; - foo.IgnoreDeclarationExceptions = true; - foo.DeclaringAdmins.Add("myAdmin"); - - var bar = (DirectExchange)ExchangeBuilder.DirectExchange("bar").IgnoreDeclarationExceptions().SuppressDeclaration().Admins("myAdmin").Build(); - - IBinding binding = BindingBuilder.Bind(foo).To(bar).With("baz"); - binding.DeclaringAdmins.Add("myAdmin"); - binding.IgnoreDeclarationExceptions = true; - binding.ShouldDeclare = false; - - services.AddRabbitQueue(foo); - services.AddRabbitExchange(bar); - services.AddRabbitBinding(binding); - - listenerBeanType ??= typeof(SampleBean); - - services.AddSingleton(listenerBeanType); - services.AddSingleton(); - - services.AddRabbitListeners(configurationRoot, listenerBeanType, typeof(Listener)); - ServiceProvider container = services.BuildServiceProvider(true); - await container.GetRequiredService().StartAsync(default); - return container; - } - - public sealed class Listener - { - [RabbitListener("foo")] - public void Handle(string foo) - { - } - } - } - - public static class RabbitFullConfig - { - public static async Task CreateAndStartServicesAsync() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateChannel(It.IsAny())).Returns(mockChannel.Object); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - var queueName = new AtomicReference(); - - mockChannel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(() => new RC.QueueDeclareOk(queueName.Value, 0, 0)) - .Callback(name => queueName.Value = name); - - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - - services.AddSingleton(mockConnectionFactory.Object); - services.AddRabbitListenerContainerFactory("simpleFactory"); - services.AddRabbitAdmin(); - - IQueue queue1 = QueueBuilder.Durable("queue1").Build(); - IQueue queue2 = QueueBuilder.Durable("queue2").Build(); - services.AddRabbitQueues(queue1, queue2); - - services.AddSingleton(); - services.AddRabbitListeners(); - ServiceProvider container = services.BuildServiceProvider(true); - await container.GetRequiredService().StartAsync(default); - return container; - } - } - - public static class RabbitFullConfigurableConfig - { - public static async Task CreateAndStartServicesAsync() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateChannel(It.IsAny())).Returns(mockChannel.Object); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - var queueName = new AtomicReference(); - - mockChannel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(() => new RC.QueueDeclareOk(queueName.Value, 0, 0)) - .Callback(name => queueName.Value = name); - - var services = new ServiceCollection(); - var configBuilder = new ConfigurationBuilder(); - - configBuilder.AddInMemoryCollection(new Dictionary - { - { "rabbit:listener:id", "listener1" }, - { "rabbit:listener:containerFactory", "simpleFactory" }, - { "rabbit.listener.queue", "queue1" }, - { "rabbit.listener.priority", "34" }, - { "rabbit.listener.responseRoutingKey", "routing-123" }, - { "rabbit.listener.admin", "rabbitAdmin" } - }); - - IConfigurationRoot configurationRoot = configBuilder.Build(); - - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - - services.AddSingleton(mockConnectionFactory.Object); - services.AddRabbitListenerContainerFactory("simpleFactory"); - services.AddRabbitAdmin(); - - IQueue queue1 = QueueBuilder.Durable("queue1").Build(); - IQueue queue2 = QueueBuilder.Durable("queue2").Build(); - services.AddRabbitQueues(queue1, queue2); - - services.AddSingleton(); - services.AddRabbitListeners(); - ServiceProvider container = services.BuildServiceProvider(true); - await container.GetRequiredService().StartAsync(default); - return container; - } - } - - public static class RabbitDefaultContainerFactoryConfig - { - public static async Task CreateAndStartServicesAsync(params Type[] listeners) - { - var mockConnection = new Mock(); - var services = new ServiceCollection(); - var configBuilder = new ConfigurationBuilder(); - IConfigurationRoot configurationRoot = configBuilder.Build(); - - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitListenerEndpointRegistrar(); - services.AddRabbitListenerEndpointRegistry(); - services.AddSingleton(mockConnection.Object); - - services.AddRabbitListenerContainerFactory("rabbitListenerContainerFactory"); - services.AddRabbitAdmin(); - - IQueue queue1 = QueueBuilder.Durable("myQueue").Build(); - services.AddRabbitQueues(queue1); - - foreach (Type listener in listeners) - { - services.AddSingleton(listener); - } - - services.AddRabbitListeners(configurationRoot, listeners); - ServiceProvider container = services.BuildServiceProvider(true); - await container.GetRequiredService().StartAsync(default); - return container; - } - } - - public sealed class RabbitCustomConfig : IRabbitListenerConfigurer - { - private readonly IRabbitListenerEndpointRegistry _registry; - private readonly IApplicationContext _context; - - public RabbitCustomConfig(IApplicationContext context, IRabbitListenerEndpointRegistry registry) - { - _registry = registry; - _context = context; - } - - public static async Task CreateAndStartServicesAsync() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateChannel(It.IsAny())).Returns(mockChannel.Object); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - var queueName = new AtomicReference(); - - mockChannel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(() => new RC.QueueDeclareOk(queueName.Value, 0, 0)) - .Callback(name => queueName.Value = name); - - var services = new ServiceCollection(); - var configBuilder = new ConfigurationBuilder(); - IConfigurationRoot configurationRoot = configBuilder.Build(); - - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitListenerEndpointRegistrar(); - - services.AddSingleton(mockConnectionFactory.Object); - services.AddSingleton(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerContainerFactory("rabbitListenerContainerFactory"); - services.AddRabbitListenerContainerFactory("customFactory"); - services.AddRabbitAdmin(); - - IQueue queue1 = QueueBuilder.Durable("myQueue").Build(); - services.AddRabbitQueues(queue1); - - services.AddRabbitListeners(); - ServiceProvider container = services.BuildServiceProvider(true); - await container.GetRequiredService().StartAsync(default); - return container; - } - - public void ConfigureRabbitListeners(IRabbitListenerEndpointRegistrar registrar) - { - registrar.EndpointRegistry = _registry; - - var endpoint = new SimpleRabbitListenerEndpoint(_context) - { - Id = "myCustomEndpointId" - }; - - endpoint.SetQueueNames("myQueue"); - endpoint.MessageListener = new MessageListenerAdapter(_context); - registrar.RegisterEndpoint(endpoint); - } - } - - public sealed class RabbitCustomContainerFactoryConfig : IRabbitListenerConfigurer - { - private readonly IApplicationContext _context; - - public RabbitCustomContainerFactoryConfig(IApplicationContext context) - { - _context = context; - } - - public static async Task CreateAndStartServicesAsync() - { - var mockConnection = new Mock(); - var services = new ServiceCollection(); - var configBuilder = new ConfigurationBuilder(); - IConfigurationRoot configurationRoot = configBuilder.Build(); - - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitListenerEndpointRegistrar(); - - services.AddSingleton(mockConnection.Object); - services.AddSingleton(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerContainerFactory("simpleFactory"); - services.AddRabbitAdmin(); - - IQueue queue1 = QueueBuilder.Durable("myQueue").Build(); - services.AddRabbitQueues(queue1); - - services.AddRabbitListeners(); - ServiceProvider container = services.BuildServiceProvider(true); - await container.GetRequiredService().StartAsync(default); - return container; - } - - public void ConfigureRabbitListeners(IRabbitListenerEndpointRegistrar registrar) - { - registrar.ContainerFactory = _context.GetService("simpleFactory"); - } - } - - private sealed class TestDirectRabbitListenerContainerFactory : DirectRabbitListenerContainerFactory - { - public TestDirectRabbitListenerContainerFactory(IApplicationContext applicationContext, IConnectionFactory connectionFactory, - ILoggerFactory loggerFactory = null) - : base(applicationContext, connectionFactory, loggerFactory) - { - } - - protected override DirectMessageListenerContainer CreateContainerInstance() - { - return new TestDirectMessageListenerContainer(); - } - } - - public sealed class TestDirectMessageListenerContainer : DirectMessageListenerContainer - { - public override void Shutdown() - { - throw new Exception("Exception in Shutdown()"); - } - } - - public sealed class SampleBean - { - [RabbitListener("nyQueue")] - public void DefaultHandle(string foo) - { - } - - [RabbitListener("muQueue", ContainerFactory = "simpleFactory")] - public void SimpleHandle(string msg) - { - } - } - - public sealed class FullBean - { - [RabbitListener("queue1", "queue2", Id = "listener1", ContainerFactory = "simpleFactory", Exclusive = true, Priority = "34", Admin = "rabbitAdmin")] - public void FullHandle(string msg) - { - } - } - - public sealed class FullConfigurableBean - { - [RabbitListener("${rabbit:listener:queue}", "queue2", Id = "${rabbit:listener:id}", ContainerFactory = "${rabbit:listener:containerFactory}", - Exclusive = true, Priority = "${rabbit: listener:priority}", Admin = "${rabbit:listener:admin}")] - public void FullHandle(string msg) - { - } - } - - public sealed class CustomBean - { - [RabbitListener("myQueue", Id = "listenerId", ContainerFactory = "customFactory")] - public void CustomHandle(string msg) - { - } - } - - public sealed class DefaultBean - { - [RabbitListener("myQueue")] - public void HandleIt(string msg) - { - } - } - - public sealed class InvalidPriorityBean - { - [RabbitListener("myQueue", Priority = "NotANumber")] - public void CustomHandle(string msg) - { - } - } - - public sealed class RabbitListenersBean - { - [RabbitListener("myQueue", Id = "first")] - [RabbitListener("anotherQueue", Id = "second")] - public void RepeatableHandle(string msg) - { - } - } - - [RabbitListener("class1", Id = "third")] - [RabbitListener("class2", Id = "fourth")] - public sealed class ClassLevelListenersBean - { - [RabbitHandler] - public void RepeatableHandle(string msg) - { - } - } - - private class CustomRabbitListenerEndpointRegistry : RabbitListenerEndpointRegistry - { - public CustomRabbitListenerEndpointRegistry(IApplicationContext applicationContext, ILogger logger = null) - : base(applicationContext, logger) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Attributes/MessageHandlerTest.cs b/src/Messaging/test/RabbitMQ.Test/Attributes/MessageHandlerTest.cs deleted file mode 100644 index 70038a417e..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Attributes/MessageHandlerTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Attributes; - -public sealed class MessageHandlerTest -{ - [Fact] - public void TestMessages() - { - var services = new ServiceCollection(); - services.AddRabbitServices(); - services.AddSingleton(new ConfigurationBuilder().Build()); - ServiceProvider provider = services.BuildServiceProvider(true); - var bpp = provider.GetService() as RabbitListenerAttributeProcessor; - bpp.Initialize(); - IMessageHandlerMethodFactory factory = bpp.MessageHandlerMethodFactory; - var foo = new Foo(); - IInvocableHandlerMethod invMethod = factory.CreateInvocableHandlerMethod(foo, typeof(Foo).GetMethod(nameof(Foo.Listen1))); - IMessage message = Message.Create("foo"); - - var list = new List - { - message - }; - - var mockChannel = new Mock(); - var adapter = new HandlerAdapter(invMethod); - adapter.Invoke(Message.Create(list), mockChannel.Object); - Assert.Same(list, foo.MessagingMessages); - } - - public sealed class Foo - { - public List MessagingMessages { get; private set; } - - public void Listen1(List mMessages) - { - MessagingMessages = mMessages; - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Attributes/RabbitListenerAttributeProcessorTest.cs b/src/Messaging/test/RabbitMQ.Test/Attributes/RabbitListenerAttributeProcessorTest.cs deleted file mode 100644 index e8fa18f2b9..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Attributes/RabbitListenerAttributeProcessorTest.cs +++ /dev/null @@ -1,364 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Attributes; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Test.Configuration; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Attributes; - -[Trait("Category", "Integration")] -public sealed class RabbitListenerAttributeProcessorTest -{ - [Fact] - public async Task SimpleMessageListener() - { - var queues = new List - { - QueueBuilder.Durable("testQueue").Build(), - QueueBuilder.Durable("secondQueue").Build() - }; - - ServiceProvider provider = await Configuration.CreateAndStartServicesAsync(null, queues, typeof(SimpleMessageListenerTestBean)); - var context = provider.GetService(); - var factory = (RabbitListenerContainerTestFactory)context.GetService(); - Assert.Single(factory.GetListenerContainers()); - MessageListenerTestContainer container = factory.GetListenerContainers()[0]; - - IRabbitListenerEndpoint endpoint = container.Endpoint; - Assert.IsType(endpoint); - var methodEndpoint = (MethodRabbitListenerEndpoint)endpoint; - Assert.NotNull(methodEndpoint.Instance); - Assert.NotNull(methodEndpoint.Method); - - var listenerContainer = new DirectMessageListenerContainer(context); - methodEndpoint.SetupListenerContainer(listenerContainer); - Assert.NotNull(listenerContainer.MessageListener); - Assert.True(container.IsStarted); - await provider.DisposeAsync(); - Assert.True(container.IsStopped); - } - - [Fact] - public async Task SimpleMessageListenerWithMixedAnnotations() - { - var configBuilder = new ConfigurationBuilder(); - - configBuilder.AddInMemoryCollection(new Dictionary - { - { "rabbit:myQueue", "secondQueue" } - }); - - IConfigurationRoot configurationRoot = configBuilder.Build(); - - var queues = new List - { - QueueBuilder.Durable("testQueue").Build(), - QueueBuilder.Durable("secondQueue").Build() - }; - - ServiceProvider provider = - await Configuration.CreateAndStartServicesAsync(configurationRoot, queues, typeof(SimpleMessageListenerWithMixedAnnotationsTestBean)); - - var context = provider.GetService(); - var factory = (RabbitListenerContainerTestFactory)context.GetService(); - Assert.Single(factory.GetListenerContainers()); - MessageListenerTestContainer container = factory.GetListenerContainers()[0]; - - IRabbitListenerEndpoint endpoint = container.Endpoint; - Assert.IsType(endpoint); - var methodEndpoint = (MethodRabbitListenerEndpoint)endpoint; - Assert.NotNull(methodEndpoint.Instance); - Assert.NotNull(methodEndpoint.Method); - - Assert.Equal(2, methodEndpoint.QueueNames.Count); - Assert.Contains("testQueue", methodEndpoint.QueueNames); - Assert.Contains("secondQueue", methodEndpoint.QueueNames); - - var listenerContainer = new DirectMessageListenerContainer(context); - methodEndpoint.SetupListenerContainer(listenerContainer); - Assert.NotNull(listenerContainer.MessageListener); - Assert.True(container.IsStarted); - await provider.DisposeAsync(); - Assert.True(container.IsStopped); - } - - [Fact] - public async Task MultipleQueueNamesTestBeanTest() - { - var queues = new List - { - QueueBuilder.Durable("metaTestQueue").Build(), - QueueBuilder.Durable("metaTestQueue2").Build() - }; - - ServiceProvider provider = await Configuration.CreateAndStartServicesAsync(null, queues, typeof(MultipleQueueNamesTestBean)); - var context = provider.GetService(); - var factory = (RabbitListenerContainerTestFactory)context.GetService(); - Assert.Single(factory.GetListenerContainers()); - MessageListenerTestContainer container = factory.GetListenerContainers()[0]; - - var endpoint = container.Endpoint as AbstractRabbitListenerEndpoint; - Assert.NotNull(endpoint); - - Assert.Equal(2, endpoint.QueueNames.Count); - Assert.Contains("metaTestQueue", endpoint.QueueNames); - Assert.Contains("metaTestQueue2", endpoint.QueueNames); - } - - [Fact] - public async Task MultipleQueuesTestBeanTest() - { - IQueue queue1 = QueueBuilder.Durable("metaTestQueue").Build(); - queue1.ServiceName = "queue1"; - IQueue queue2 = QueueBuilder.Durable("metaTestQueue2").Build(); - queue2.ServiceName = "queue2"; - - var queues = new List - { - queue1, - queue2 - }; - - ServiceProvider provider = await Configuration.CreateAndStartServicesAsync(null, queues, typeof(MultipleQueuesTestBean)); - var context = provider.GetService(); - var factory = (RabbitListenerContainerTestFactory)context.GetService(); - Assert.Single(factory.GetListenerContainers()); - MessageListenerTestContainer container = factory.GetListenerContainers()[0]; - - var endpoint = container.Endpoint as AbstractRabbitListenerEndpoint; - Assert.NotNull(endpoint); - - Assert.Equal(2, endpoint.QueueNames.Count); - Assert.Contains("metaTestQueue", endpoint.QueueNames); - Assert.Contains("metaTestQueue2", endpoint.QueueNames); - } - - [Fact] - public async Task MixedQueuesAndQueueNamesTestBeanTest() - { - IQueue queue1 = QueueBuilder.Durable("metaTestQueue").Build(); - queue1.ServiceName = "queue1"; - IQueue queue2 = QueueBuilder.Durable("metaTestQueue2").Build(); - - var queues = new List - { - queue1, - queue2 - }; - - ServiceProvider provider = await Configuration.CreateAndStartServicesAsync(null, queues, typeof(MixedQueuesAndQueueNamesTestBean)); - var context = provider.GetService(); - var factory = (RabbitListenerContainerTestFactory)context.GetService(); - Assert.Single(factory.GetListenerContainers()); - MessageListenerTestContainer container = factory.GetListenerContainers()[0]; - - var endpoint = container.Endpoint as AbstractRabbitListenerEndpoint; - Assert.NotNull(endpoint); - - Assert.Equal(2, endpoint.QueueNames.Count); - Assert.Contains("metaTestQueue", endpoint.QueueNames); - Assert.Contains("metaTestQueue2", endpoint.QueueNames); - } - - [Fact] - public async Task PropertyPlaceholderResolvingToQueueTestBeanTest() - { - var configBuilder = new ConfigurationBuilder(); - - configBuilder.AddInMemoryCollection(new Dictionary - { - { "rabbit:myQueue", "#{@queue1}" } - }); - - IConfigurationRoot configurationRoot = configBuilder.Build(); - - IQueue queue1 = QueueBuilder.Durable("metaTestQueue").Build(); - queue1.ServiceName = "queue1"; - IQueue queue2 = QueueBuilder.Durable("metaTestQueue2").Build(); - queue2.ServiceName = "queue2"; - - var queues = new List - { - queue1, - queue2 - }; - - ServiceProvider provider = - await Configuration.CreateAndStartServicesAsync(configurationRoot, queues, typeof(PropertyPlaceholderResolvingToQueueTestBean)); - - var context = provider.GetService(); - var factory = (RabbitListenerContainerTestFactory)context.GetService(); - Assert.Single(factory.GetListenerContainers()); - MessageListenerTestContainer container = factory.GetListenerContainers()[0]; - - var endpoint = container.Endpoint as AbstractRabbitListenerEndpoint; - Assert.NotNull(endpoint); - - Assert.Equal(2, endpoint.QueueNames.Count); - Assert.Contains("metaTestQueue", endpoint.QueueNames); - Assert.Contains("metaTestQueue2", endpoint.QueueNames); - } - - [Fact] - public async Task InvalidValueInAnnotationTestBeanTest() - { - IQueue queue1 = QueueBuilder.Durable("metaTestQueue").Build(); - queue1.ServiceName = "queue1"; - IQueue queue2 = QueueBuilder.Durable("metaTestQueue2").Build(); - queue2.ServiceName = "queue2"; - - var queues = new List - { - queue1, - queue2 - }; - - await Assert.ThrowsAsync(async () => - await Configuration.CreateAndStartServicesAsync(null, queues, typeof(InvalidValueInAnnotationTestBean))); - } - - [Fact] - public void CreateExchangeReturnsCorrectType() - { - var services = new ServiceCollection(); - - RabbitListenerDeclareAttributeProcessor.ProcessDeclareAttributes(services, null, typeof(TestTarget)); - IExchange[] exchanges = services.BuildServiceProvider(true).GetServices().ToArray(); - Assert.Contains(exchanges, ex => ex.Type == ExchangeType.Direct); - Assert.Contains(exchanges, ex => ex.Type == ExchangeType.Topic); - Assert.Contains(exchanges, ex => ex.Type == ExchangeType.FanOut); - Assert.Contains(exchanges, ex => ex.Type == ExchangeType.Headers); - Assert.Contains(exchanges, ex => ex.Type == ExchangeType.System); - } - - public static class Configuration - { - public static async Task CreateAndStartServicesAsync(IConfiguration configuration, List queues, params Type[] listeners) - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateChannel(It.IsAny())).Returns(mockChannel.Object); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - var queueName = new AtomicReference(); - - mockChannel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(() => new RC.QueueDeclareOk(queueName.Value, 0, 0)) - .Callback(name => queueName.Value = name); - - var services = new ServiceCollection(); - IConfiguration effectiveConfiguration = configuration ?? new ConfigurationBuilder().Build(); - - services.AddSingleton(effectiveConfiguration); - services.AddSingleton(mockConnectionFactory.Object); - services.AddRabbitHostingServices(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - services.AddRabbitListenerContainerFactory("testFactory"); - - services.AddRabbitListenerAttributeProcessor((_, r) => - { - r.ContainerFactoryServiceName = "testFactory"; - }); - - services.AddRabbitQueues(queues.ToArray()); - - foreach (Type listener in listeners) - { - services.AddSingleton(listener); - } - - services.AddRabbitListeners(effectiveConfiguration, listeners); - - ServiceProvider provider = services.BuildServiceProvider(true); - await provider.GetRequiredService().StartAsync(default); - return provider; - } - } - - public sealed class TestTarget - { - [DeclareExchange(Name = "test", Type = ExchangeType.Direct)] - [DeclareExchange(Name = "test", Type = ExchangeType.Topic)] - [DeclareExchange(Name = "test", Type = ExchangeType.FanOut)] - [DeclareExchange(Name = "test", Type = ExchangeType.Headers)] - [DeclareExchange(Name = "test", Type = ExchangeType.System)] - public void Method() - { - } - } - - public sealed class SimpleMessageListenerTestBean - { - [RabbitListener("testQueue")] - public void HandleIt(string body) - { - } - } - - public sealed class SimpleMessageListenerWithMixedAnnotationsTestBean - { - [RabbitListener("testQueue", "${rabbit.myQueue}")] - public void HandleIt(string body) - { - } - } - - public sealed class MultipleQueueNamesTestBean - { - [RabbitListener("metaTestQueue", "metaTestQueue2")] - public void HandleIt(string body) - { - } - } - - public sealed class MultipleQueuesTestBean - { - [RabbitListener("#{@queue1}", "#{@queue2}")] - public void HandleIt(string body) - { - } - } - - public sealed class MixedQueuesAndQueueNamesTestBean - { - [RabbitListener("metaTestQueue2", "#{@queue1}")] - public void HandleIt(string body) - { - } - } - - public sealed class PropertyPlaceholderResolvingToQueueTestBean - { - [RabbitListener("${rabbit:myQueue}", "#{@queue2}")] - public void HandleIt(string body) - { - } - } - - public sealed class InvalidValueInAnnotationTestBean - { - [RabbitListener("#{@testFactory}")] - public void HandleIt(string body) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Configuration/BindingBuilderTest.cs b/src/Messaging/test/RabbitMQ.Test/Configuration/BindingBuilderTest.cs deleted file mode 100644 index 77be68f956..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Configuration/BindingBuilderTest.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Configuration; - -public sealed class BindingBuilderTest -{ - private readonly IQueue _queue; - - public BindingBuilderTest() - { - _queue = new Queue("q"); - } - - [Fact] - public void FanOutBinding() - { - var fanOutExchange = new FanOutExchange("f"); - IBinding binding = BindingBuilder.Bind(_queue).To(fanOutExchange); - Assert.NotNull(binding); - Assert.Equal(fanOutExchange.ExchangeName, binding.Exchange); - Assert.Equal(string.Empty, binding.RoutingKey); - Assert.Equal(Binding.DestinationType.Queue, binding.Type); - Assert.Equal(_queue.QueueName, binding.Destination); - } - - [Fact] - public void DirectBinding() - { - var directExchange = new DirectExchange("d"); - const string routingKey = "r"; - IBinding binding = BindingBuilder.Bind(_queue).To(directExchange).With(routingKey); - Assert.NotNull(binding); - Assert.Equal(directExchange.ExchangeName, binding.Exchange); - Assert.Equal(Binding.DestinationType.Queue, binding.Type); - Assert.Equal(_queue.QueueName, binding.Destination); - Assert.Equal(routingKey, binding.RoutingKey); - } - - [Fact] - public void DirectBindingWithQueueName() - { - var directExchange = new DirectExchange("d"); - IBinding binding = BindingBuilder.Bind(_queue).To(directExchange).WithQueueName(); - Assert.NotNull(binding); - Assert.Equal(directExchange.ExchangeName, binding.Exchange); - Assert.Equal(Binding.DestinationType.Queue, binding.Type); - Assert.Equal(_queue.QueueName, binding.Destination); - Assert.Equal(_queue.QueueName, binding.RoutingKey); - } - - [Fact] - public void TopicBinding() - { - var topicExchange = new TopicExchange("t"); - const string routingKey = "r"; - IBinding binding = BindingBuilder.Bind(_queue).To(topicExchange).With(routingKey); - Assert.NotNull(binding); - Assert.Equal(topicExchange.ExchangeName, binding.Exchange); - Assert.Equal(Binding.DestinationType.Queue, binding.Type); - Assert.Equal(_queue.QueueName, binding.Destination); - Assert.Equal(routingKey, binding.RoutingKey); - } - - [Fact] - public void HeaderBinding() - { - var headersExchange = new HeadersExchange("h"); - const string headerKey = "headerKey"; - IBinding binding = BindingBuilder.Bind(_queue).To(headersExchange).Where(headerKey).Exists(); - Assert.NotNull(binding); - Assert.Equal(headersExchange.ExchangeName, binding.Exchange); - Assert.Equal(Binding.DestinationType.Queue, binding.Type); - Assert.Equal(_queue.QueueName, binding.Destination); - Assert.Equal(string.Empty, binding.RoutingKey); - } - - [Fact] - public void CustomBinding() - { - object argumentObject = new(); - var customExchange = new CustomExchange("c"); - const string routingKey = "r"; - - IBinding binding = BindingBuilder.Bind(_queue).To(customExchange).With(routingKey).And(new Dictionary - { - { "k", argumentObject } - }); - - Assert.NotNull(binding); - Assert.Equal(argumentObject, binding.Arguments["k"]); - Assert.Equal(customExchange.ExchangeName, binding.Exchange); - Assert.Equal(Binding.DestinationType.Queue, binding.Type); - Assert.Equal(_queue.QueueName, binding.Destination); - Assert.Equal(routingKey, binding.RoutingKey); - } - - [Fact] - public void ExchangeBinding() - { - var directExchange = new DirectExchange("d"); - var fanOutExchange = new FanOutExchange("f"); - IBinding binding = BindingBuilder.Bind(directExchange).To(fanOutExchange); - Assert.NotNull(binding); - Assert.Equal(fanOutExchange.ExchangeName, binding.Exchange); - Assert.Equal(Binding.DestinationType.Exchange, binding.Type); - Assert.Equal(directExchange.ExchangeName, binding.Destination); - Assert.Equal(string.Empty, binding.RoutingKey); - } - - private sealed class CustomExchange : AbstractExchange - { - public override string Type { get; } = "x-custom"; - - public CustomExchange(string name) - : base(name) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Configuration/MessageListenerTestContainer.cs b/src/Messaging/test/RabbitMQ.Test/Configuration/MessageListenerTestContainer.cs deleted file mode 100644 index 93ca923634..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Configuration/MessageListenerTestContainer.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Messaging.RabbitMQ.Listener; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Configuration; - -public sealed class MessageListenerTestContainer : IMessageListenerContainer -{ - internal bool StopInvoked { get; set; } - internal bool StartInvoked { get; set; } - internal bool DestroyInvoked { get; set; } - internal bool InitializationInvoked { get; set; } - internal IRabbitListenerEndpoint Endpoint { get; } - - public bool IsStarted => StartInvoked && InitializationInvoked; - - public bool IsStopped => StopInvoked && DestroyInvoked; - - public bool IsAutoStartup => true; - - public bool IsRunning => StartInvoked && !StopInvoked; - - public int Phase => 0; - - public string ServiceName { get; set; } = nameof(MessageListenerTestContainer); - - public MessageListenerTestContainer(IRabbitListenerEndpoint endpoint) - { - Endpoint = endpoint; - } - - public void Dispose() - { - if (!StopInvoked) - { - StopAsync().GetAwaiter().GetResult(); - } - - DestroyInvoked = true; - } - - public void Initialize() - { - InitializationInvoked = true; - } - - public void LazyLoad() - { - } - - public void SetupMessageListener(IMessageListener messageListener) - { - } - - public Task StartAsync() - { - if (!InitializationInvoked) - { - throw new InvalidOperationException($"afterPropertiesSet should have been invoked before start on {this}"); - } - - if (StartInvoked) - { - throw new InvalidOperationException($"Start already invoked on {this}"); - } - - StartInvoked = true; - return Task.CompletedTask; - } - - public Task StopAsync(Action callback) - { - StopInvoked = true; - callback(); - return Task.CompletedTask; - } - - public Task StopAsync() - { - if (StopInvoked) - { - throw new InvalidOperationException($"Stop already invoked on {this}"); - } - - StopInvoked = true; - return Task.CompletedTask; - } - - public override string ToString() - { - var sb = new StringBuilder("TestContainer{"); - sb.Append("endpoint=").Append(Endpoint); - sb.Append(", startInvoked=").Append(StartInvoked); - sb.Append(", initializationInvoked=").Append(InitializationInvoked); - sb.Append(", stopInvoked=").Append(StopInvoked); - sb.Append(", destroyInvoked=").Append(DestroyInvoked); - sb.Append('}'); - return sb.ToString(); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Configuration/QueueBuilderTest.cs b/src/Messaging/test/RabbitMQ.Test/Configuration/QueueBuilderTest.cs deleted file mode 100644 index 421f08193b..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Configuration/QueueBuilderTest.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Configuration; - -public sealed class QueueBuilderTest -{ - [Fact] - public void BuildsDurableQueue() - { - IQueue queue = QueueBuilder.Durable("name").Build(); - - Assert.True(queue.IsDurable); - Assert.Equal("name", queue.QueueName); - } - - [Fact] - public void BuildsNonDurableQueue() - { - IQueue queue = QueueBuilder.NonDurable("name").Build(); - - Assert.False(queue.IsDurable); - Assert.Equal("name", queue.QueueName); - } - - [Fact] - public void BuildsAutoDeleteQueue() - { - IQueue queue = QueueBuilder.Durable("name").AutoDelete().Build(); - - Assert.True(queue.IsAutoDelete); - } - - [Fact] - public void BuildsExclusiveQueue() - { - IQueue queue = QueueBuilder.Durable("name").Exclusive().Build(); - - Assert.True(queue.IsExclusive); - } - - [Fact] - public void AddsArguments() - { - IQueue queue = QueueBuilder.Durable("name").WithArgument("key1", "value1").WithArgument("key2", "value2").Build(); - - Dictionary args = queue.Arguments; - - Assert.Equal("value1", args["key1"]); - Assert.Equal("value2", args["key2"]); - } - - [Fact] - public void AddsMultipleArgumentsAtOnce() - { - var arguments = new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }; - - IQueue queue = QueueBuilder.Durable("name").WithArguments(arguments).Build(); - Dictionary args = queue.Arguments; - - Assert.Equal("value1", args["key1"]); - Assert.Equal("value2", args["key2"]); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Configuration/QueueNameTest.cs b/src/Messaging/test/RabbitMQ.Test/Configuration/QueueNameTest.cs deleted file mode 100644 index d19931e0e6..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Configuration/QueueNameTest.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Core; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Configuration; - -public sealed class QueueNameTest -{ - [Fact] - public void TestAnonymous() - { - var q = new AnonymousQueue(); - Assert.StartsWith("spring.gen-", q.QueueName, StringComparison.Ordinal); - q = new AnonymousQueue(new Base64UrlNamingStrategy("foo-")); - Assert.StartsWith("foo-", q.QueueName, StringComparison.Ordinal); - q = new AnonymousQueue(GuidNamingStrategy.Default); - Assert.Matches("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}", q.QueueName); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Configuration/RabbitListenerContainerTestFactory.cs b/src/Messaging/test/RabbitMQ.Test/Configuration/RabbitListenerContainerTestFactory.cs deleted file mode 100644 index 52765db5f6..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Configuration/RabbitListenerContainerTestFactory.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Listener; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Configuration; - -internal sealed class RabbitListenerContainerTestFactory : IRabbitListenerContainerFactory -{ - private static int _counter = 1; - - public Dictionary ListenerContainers { get; } = new(); - - public string ServiceName { get; set; } - - public RabbitListenerContainerTestFactory(string name = null) - { - ServiceName = name ?? $"RabbitListenerContainerTestFactory@{GetHashCode()}"; - } - - public List GetListenerContainers() - { - return new List(ListenerContainers.Values); - } - - public MessageListenerTestContainer CreateListenerContainer(IRabbitListenerEndpoint endpoint) - { - var container = new MessageListenerTestContainer(endpoint); - - if (endpoint.Id == null && endpoint is AbstractRabbitListenerEndpoint) - { - int id = Interlocked.Increment(ref _counter); - endpoint.Id = $"endpoint#{id}"; - } - - Assert.NotNull(endpoint.Id); - ListenerContainers.Add(endpoint.Id, container); - return container; - } - - public MessageListenerTestContainer GetListenerContainer(string id) - { - ListenerContainers.TryGetValue(id, out MessageListenerTestContainer result); - return result; - } - - IMessageListenerContainer IRabbitListenerContainerFactory.CreateListenerContainer(IRabbitListenerEndpoint endpoint) - { - return CreateListenerContainer(endpoint); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/AbstractConnectionFactoryTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/AbstractConnectionFactoryTest.cs deleted file mode 100644 index f95c4b2f39..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/AbstractConnectionFactoryTest.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -public abstract class AbstractConnectionFactoryTest -{ - [Fact] - public void TestWithListener() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - - AbstractConnectionFactory connectionFactory = CreateConnectionFactory(mockConnectionFactory.Object); - var listener = new IncrementConnectionListener(); - - connectionFactory.SetConnectionListeners(new List - { - listener - }); - - IConnection con = connectionFactory.CreateConnection(); - Assert.Equal(1, listener.Called); - - con.Close(); - Assert.Equal(1, listener.Called); - mockConnection.Verify(c => c.Close(It.IsAny()), Times.Never); - connectionFactory.CreateConnection(); - Assert.Equal(1, listener.Called); - connectionFactory.Destroy(); - Assert.Equal(0, listener.Called); - mockConnection.Verify(c => c.Close(It.IsAny()), Times.AtLeastOnce); - mockConnectionFactory.Verify(f => f.CreateConnection(It.IsAny()), Times.Once); - - connectionFactory.SetAddresses("foo:5672,bar:5672"); - con = connectionFactory.CreateConnection(); - Assert.Equal(1, listener.Called); - - con.Close(); - connectionFactory.Destroy(); - Assert.Equal(0, listener.Called); - } - - [Fact] - public void TestWithListenerRegisteredAfterOpen() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var listener = new IncrementConnectionListener(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - AbstractConnectionFactory connectionFactory = CreateConnectionFactory(mockConnectionFactory.Object); - IConnection con = connectionFactory.CreateConnection(); - Assert.Equal(0, listener.Called); - - connectionFactory.SetConnectionListeners(new List - { - listener - }); - - Assert.Equal(1, listener.Called); - con.Close(); - Assert.Equal(1, listener.Called); - mockConnection.Verify(c => c.Close(It.IsAny()), Times.Never); - _ = connectionFactory.CreateConnection(); - Assert.Equal(1, listener.Called); - connectionFactory.Destroy(); - Assert.Equal(0, listener.Called); - mockConnection.Verify(c => c.Close(It.IsAny()), Times.AtLeastOnce); - mockConnectionFactory.Verify(f => f.CreateConnection(It.IsAny()), Times.Once); - } - - [Fact] - public void TestCloseInvalidConnection() - { - var mockConnectionFactory = new Mock(); - var mockConnection1 = new Mock(); - var mockConnection2 = new Mock(); - var mockChanel2 = new Mock(); - mockConnectionFactory.SetupSequence(f => f.CreateConnection(It.IsAny())).Returns(mockConnection1.Object).Returns(mockConnection2.Object); - mockConnection1.Setup(c => c.IsOpen).Returns(false); - mockConnection2.Setup(c => c.CreateModel()).Returns(mockChanel2.Object); - AbstractConnectionFactory connectionFactory = CreateConnectionFactory(mockConnectionFactory.Object); - IConnection connection = connectionFactory.CreateConnection(); - - // the dead connection should be discarded - _ = connection.CreateChannel(); - mockConnectionFactory.Verify(f => f.CreateConnection(It.IsAny()), Times.Exactly(2)); - mockConnection2.Verify(c => c.CreateModel(), Times.Once); - connectionFactory.Destroy(); - mockConnection2.Verify(c => c.Close(It.IsAny()), Times.Once); - } - - [Fact] - public void TestDestroyBeforeUsed() - { - var mockConnectionFactory = new Mock(); - AbstractConnectionFactory connectionFactory = CreateConnectionFactory(mockConnectionFactory.Object); - connectionFactory.Destroy(); - mockConnectionFactory.Verify(f => f.CreateConnection(It.IsAny()), Times.Never); - } - - protected abstract AbstractConnectionFactory CreateConnectionFactory(RC.IConnectionFactory connectionFactory); - - protected sealed class IncrementConnectionListener : IConnectionListener - { - private int _called; - - public int Called => _called; - - public void OnClose(IConnection connection) - { - Interlocked.Decrement(ref _called); - } - - public void OnCreate(IConnection connection) - { - Interlocked.Increment(ref _called); - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/CachePropertiesTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/CachePropertiesTest.cs deleted file mode 100644 index e7c042593a..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/CachePropertiesTest.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Xunit; -using static Steeltoe.Messaging.RabbitMQ.Connection.CachingConnectionFactory; -using IConnection = Steeltoe.Messaging.RabbitMQ.Connection.IConnection; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -[Trait("Category", "Integration")] -public sealed class CachePropertiesTest -{ - [Fact] - public void TestChannelCache() - { - using var channelCf = new CachingConnectionFactory("localhost"); - channelCf.ServiceName = "testChannelCache"; - channelCf.ChannelCacheSize = 4; - - using IConnection c1 = channelCf.CreateConnection(); - using IConnection c2 = channelCf.CreateConnection(); - Assert.Same(c1, c2); - IModel ch1 = c1.CreateChannel(); - IModel ch2 = c1.CreateChannel(); - IModel ch3 = c1.CreateChannel(true); - IModel ch4 = c1.CreateChannel(true); - IModel ch5 = c1.CreateChannel(true); - ch1.Close(); - ch2.Close(); - ch3.Close(); - ch4.Close(); - ch5.Close(); - IDictionary props = channelCf.GetCacheProperties(); - Assert.StartsWith("testChannelCache", (string)props["connectionName"], StringComparison.Ordinal); - Assert.Equal(4, props["channelCacheSize"]); - Assert.Equal(2, props["idleChannelsNotTx"]); - Assert.Equal(3, props["idleChannelsTx"]); - Assert.Equal(2, props["idleChannelsNotTxHighWater"]); - Assert.Equal(3, props["idleChannelsTxHighWater"]); - c1.CreateChannel(); - c1.CreateChannel(true); - props = channelCf.GetCacheProperties(); - Assert.Equal(1, props["idleChannelsNotTx"]); - Assert.Equal(2, props["idleChannelsTx"]); - Assert.Equal(2, props["idleChannelsNotTxHighWater"]); - Assert.Equal(3, props["idleChannelsTxHighWater"]); - ch1 = c1.CreateChannel(); - ch2 = c1.CreateChannel(); - ch3 = c1.CreateChannel(true); - ch4 = c1.CreateChannel(true); - ch5 = c1.CreateChannel(true); - IModel ch6 = c1.CreateChannel(true); - IModel ch7 = c1.CreateChannel(true); // #5 - ch1.Close(); - ch2.Close(); - ch3.Close(); - ch4.Close(); - ch5.Close(); - ch6.Close(); - ch7.Close(); - props = channelCf.GetCacheProperties(); - Assert.Equal(2, props["idleChannelsNotTx"]); - Assert.Equal(4, props["idleChannelsTx"]); - Assert.Equal(2, props["idleChannelsNotTxHighWater"]); - Assert.Equal(4, props["idleChannelsTxHighWater"]); - } - - [Fact] - public void TestConnectionCache() - { - using var connectionCf = new CachingConnectionFactory("localhost"); - connectionCf.ChannelCacheSize = 10; - connectionCf.ConnectionCacheSize = 5; - connectionCf.CacheMode = CachingMode.Connection; - connectionCf.ServiceName = "testConnectionCache"; - - using IConnection c1 = connectionCf.CreateConnection(); - using IConnection c2 = connectionCf.CreateConnection(); - IModel ch1 = c1.CreateChannel(); - IModel ch2 = c1.CreateChannel(); - IModel ch3 = c2.CreateChannel(true); - IModel ch4 = c2.CreateChannel(true); - IModel ch5 = c2.CreateChannel(); - ch1.Close(); - ch2.Close(); - ch3.Close(); - ch4.Close(); - ch5.Close(); - c1.Close(); - IDictionary props = connectionCf.GetCacheProperties(); - Assert.Equal(10, props["channelCacheSize"]); - Assert.Equal(5, props["connectionCacheSize"]); - Assert.Equal(2, props["openConnections"]); - Assert.Equal(1, props["idleConnections"]); - c2.Close(); - props = connectionCf.GetCacheProperties(); - Assert.Equal(2, props["idleConnections"]); - Assert.Equal(2, props["idleConnectionsHighWater"]); - int c1Port = c1.LocalPort; - int c2Port = c2.LocalPort; - Assert.StartsWith("testConnectionCache:1", (string)props[$"connectionName:{c1Port}"], StringComparison.Ordinal); - Assert.StartsWith("testConnectionCache:2", (string)props[$"connectionName:{c2Port}"], StringComparison.Ordinal); - Assert.Equal(2, props[$"idleChannelsNotTx:{c1Port}"]); - Assert.Equal(0, props[$"idleChannelsTx:{c1Port}"]); - Assert.Equal(2, props[$"idleChannelsNotTxHighWater:{c1Port}"]); - Assert.Equal(0, props[$"idleChannelsTxHighWater:{c1Port}"]); - Assert.Equal(1, props[$"idleChannelsNotTx:{c2Port}"]); - Assert.Equal(2, props[$"idleChannelsTx:{c2Port}"]); - Assert.Equal(1, props[$"idleChannelsNotTxHighWater:{c2Port}"]); - Assert.Equal(2, props[$"idleChannelsTxHighWater:{c2Port}"]); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/CachingConnectionFactoryIntegrationTests.cs b/src/Messaging/test/RabbitMQ.Test/Connection/CachingConnectionFactoryIntegrationTests.cs deleted file mode 100644 index 574a03c20a..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/CachingConnectionFactoryIntegrationTests.cs +++ /dev/null @@ -1,354 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; -using static Steeltoe.Messaging.RabbitMQ.Connection.CachingConnectionFactory; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -[Trait("Category", "Integration")] -public sealed class CachingConnectionFactoryIntegrationTests : IDisposable -{ - private const string CFIntegrationConnectionName = "cfIntegrationTestConnectionName"; - public const string CFIntegrationTestQueue = "cfIntegrationTest"; - - private readonly CachingConnectionFactory _connectionFactory; - - public CachingConnectionFactoryIntegrationTests() - { - _connectionFactory = new CachingConnectionFactory("localhost") - { - ServiceName = CFIntegrationConnectionName - }; - } - - [Fact] - public void TestCachedConnections() - { - _connectionFactory.CacheMode = CachingMode.Connection; - _connectionFactory.ConnectionCacheSize = 5; - - var connections = new List - { - _connectionFactory.CreateConnection(), - _connectionFactory.CreateConnection() - }; - - Assert.NotSame(connections[0], connections[1]); - connections.Add(_connectionFactory.CreateConnection()); - connections.Add(_connectionFactory.CreateConnection()); - connections.Add(_connectionFactory.CreateConnection()); - connections.Add(_connectionFactory.CreateConnection()); - HashSet allocatedConnections = _connectionFactory.AllocatedConnections; - Assert.Equal(6, allocatedConnections.Count); - - foreach (ChannelCachingConnectionProxy c in allocatedConnections) - { - c.Close(); - } - - LinkedList idleConnections = _connectionFactory.IdleConnections; - Assert.Equal(6, idleConnections.Count); - connections.Clear(); - connections.Add(_connectionFactory.CreateConnection()); - connections.Add(_connectionFactory.CreateConnection()); - allocatedConnections = _connectionFactory.AllocatedConnections; - Assert.Equal(6, allocatedConnections.Count); - idleConnections = _connectionFactory.IdleConnections; - Assert.Equal(4, idleConnections.Count); - - foreach (IConnection c in connections) - { - c.Close(); - } - } - - [Fact] - public void TestCachedConnectionsChannelLimit() - { - _connectionFactory.CacheMode = CachingMode.Connection; - _connectionFactory.ConnectionCacheSize = 2; - _connectionFactory.ChannelCacheSize = 1; - _connectionFactory.ChannelCheckoutTimeout = 10; - - var connections = new List - { - _connectionFactory.CreateConnection(), - _connectionFactory.CreateConnection() - }; - - var channels = new List - { - connections[0].CreateChannel() - }; - - try - { - channels.Add(connections[0].CreateChannel()); - throw new Exception("Exception expected"); - } - catch (RabbitTimeoutException) - { - // Ignore - } - - channels.Add(connections[1].CreateChannel()); - - try - { - channels.Add(connections[1].CreateChannel()); - throw new Exception("Exception expected"); - } - catch (RabbitTimeoutException) - { - // Ignore - } - - channels[0].Close(); - channels[1].Close(); - - channels.Add(connections[0].CreateChannel()); - channels.Add(connections[1].CreateChannel()); - - Assert.Same(channels[2], channels[0]); - Assert.Same(channels[3], channels[1]); - channels[2].Close(); - channels[3].Close(); - - foreach (IConnection c in connections) - { - c.Close(); - } - } - - [Fact] - public void TestCachedConnectionsAndChannels() - { - _connectionFactory.CacheMode = CachingMode.Connection; - _connectionFactory.ConnectionCacheSize = 1; - _connectionFactory.ChannelCacheSize = 3; - - var connections = new List - { - _connectionFactory.CreateConnection(), - _connectionFactory.CreateConnection() - }; - - HashSet allocatedConnections = _connectionFactory.AllocatedConnections; - Assert.Equal(2, allocatedConnections.Count); - Assert.NotSame(connections[0], connections[1]); - var channels = new List(); - - for (int i = 0; i < 5; i++) - { - channels.Add(connections[0].CreateChannel()); - channels.Add(connections[1].CreateChannel()); - channels.Add(connections[0].CreateChannel(true)); - channels.Add(connections[1].CreateChannel(true)); - } - - Dictionary> cachedChannels = _connectionFactory.AllocatedConnectionNonTransactionalChannels; - Assert.Empty(cachedChannels[(ChannelCachingConnectionProxy)connections[0]]); - Assert.Empty(cachedChannels[(ChannelCachingConnectionProxy)connections[1]]); - - Dictionary> cachedTxChannels = _connectionFactory.AllocatedConnectionTransactionalChannels; - Assert.Empty(cachedTxChannels[(ChannelCachingConnectionProxy)connections[0]]); - Assert.Empty(cachedTxChannels[(ChannelCachingConnectionProxy)connections[1]]); - - foreach (RC.IModel c in channels) - { - c.Close(); - } - - Assert.Equal(3, cachedChannels[(ChannelCachingConnectionProxy)connections[0]].Count); - Assert.Equal(3, cachedChannels[(ChannelCachingConnectionProxy)connections[1]].Count); - Assert.Equal(3, cachedTxChannels[(ChannelCachingConnectionProxy)connections[0]].Count); - Assert.Equal(3, cachedTxChannels[(ChannelCachingConnectionProxy)connections[1]].Count); - - for (int i = 0; i < 3; i++) - { - Assert.Equal(channels[i * 4], connections[0].CreateChannel()); - Assert.Equal(channels[i * 4 + 1], connections[1].CreateChannel()); - Assert.Equal(channels[i * 4 + 2], connections[0].CreateChannel(true)); - Assert.Equal(channels[i * 4 + 3], connections[1].CreateChannel(true)); - } - - cachedChannels = _connectionFactory.AllocatedConnectionNonTransactionalChannels; - Assert.Empty(cachedChannels[(ChannelCachingConnectionProxy)connections[0]]); - Assert.Empty(cachedChannels[(ChannelCachingConnectionProxy)connections[1]]); - - cachedTxChannels = _connectionFactory.AllocatedConnectionTransactionalChannels; - Assert.Empty(cachedTxChannels[(ChannelCachingConnectionProxy)connections[0]]); - Assert.Empty(cachedTxChannels[(ChannelCachingConnectionProxy)connections[1]]); - - foreach (RC.IModel c in channels) - { - c.Close(); - } - - foreach (IConnection c in connections) - { - c.Close(); - } - - Assert.Equal(3, cachedChannels[(ChannelCachingConnectionProxy)connections[0]].Count); - Assert.Empty(cachedChannels[(ChannelCachingConnectionProxy)connections[1]]); - Assert.Equal(3, cachedTxChannels[(ChannelCachingConnectionProxy)connections[0]].Count); - Assert.Empty(cachedTxChannels[(ChannelCachingConnectionProxy)connections[1]]); - - allocatedConnections = _connectionFactory.AllocatedConnections; - Assert.Equal(2, allocatedConnections.Count); - IDictionary props = _connectionFactory.GetCacheProperties(); - Assert.Equal(1, props["openConnections"]); - - IConnection connection = _connectionFactory.CreateConnection(); - RC.IConnection rabbitConnection = connection.Connection; - rabbitConnection.Close(); - - RC.IModel channel = connection.CreateChannel(); - allocatedConnections = _connectionFactory.AllocatedConnections; - Assert.Equal(2, allocatedConnections.Count); - props = _connectionFactory.GetCacheProperties(); - Assert.Equal(1, props["openConnections"]); - - channel.Close(); - connection.Close(); - - allocatedConnections = _connectionFactory.AllocatedConnections; - Assert.Equal(2, allocatedConnections.Count); - props = _connectionFactory.GetCacheProperties(); - Assert.Equal(1, props["openConnections"]); - } - - [Fact] - public void TestSendAndReceiveFromVolatileQueue() - { - var template = new RabbitTemplate(_connectionFactory); - var admin = new RabbitAdmin(_connectionFactory); - IQueue queue = admin.DeclareQueue(); - template.ConvertAndSend(queue.QueueName, "message"); - string result = template.ReceiveAndConvert(queue.QueueName); - Assert.Equal("message", result); - } - - [Fact] - public void TestReceiveFromNonExistentVirtualHost() - { - _connectionFactory.VirtualHost = "non-existent"; - var template = new RabbitTemplate(_connectionFactory); - Assert.Throws(() => template.ReceiveAndConvert("foo")); - } - - [Fact] - public void TestSendAndReceiveFromVolatileQueueAfterImplicitRemoval() - { - var template = new RabbitTemplate(_connectionFactory); - var admin = new RabbitAdmin(_connectionFactory); - IQueue queue = admin.DeclareQueue(); - template.ConvertAndSend(queue.QueueName, "message"); - _connectionFactory.ResetConnection(); - Assert.Throws(() => template.ReceiveAndConvert(queue.QueueName)); - } - - [Fact] - public void TestMixTransactionalAndNonTransactional() - { - var template1 = new RabbitTemplate(_connectionFactory); - var template2 = new RabbitTemplate(_connectionFactory); - template1.IsChannelTransacted = true; - - var admin = new RabbitAdmin(_connectionFactory); - IQueue queue = admin.DeclareQueue(); - template1.ConvertAndSend(queue.QueueName, "message"); - string result = template2.ReceiveAndConvert(queue.QueueName); - Assert.Equal("message", result); - - Assert.Throws(() => template2.Execute(c => - { - c.TxRollback(); - return null; - })); - } - - [Fact] - public void TestHardErrorAndReconnectNoAuto() - { - var template = new RabbitTemplate(_connectionFactory); - var admin = new RabbitAdmin(_connectionFactory); - var queue = new Queue(CFIntegrationTestQueue); - admin.DeclareQueue(queue); - string route = queue.QueueName; - var latch = new CountdownEvent(1); - - try - { - template.Execute(channel => - { - channel.ModelShutdown += (_, args) => - { - latch.Signal(); - throw new ShutdownSignalException(args); - }; - - string tag = RC.IModelExensions.BasicConsume(channel, route, false, new RC.DefaultBasicConsumer(channel)); - string result = RC.IModelExensions.BasicConsume(channel, route, false, tag, new RC.DefaultBasicConsumer(channel)); - throw new Exception($"Expected Exception, got: {result}"); - }); - - throw new Exception("Expected AmqpIOException"); - } - catch (RabbitIOException) - { - // Intentionally left empty. - } - - template.ConvertAndSend(route, "message"); - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - string result = template.ReceiveAndConvert(route); - Assert.Equal("message", result); - result = template.ReceiveAndConvert(route); - Assert.Null(result); - admin.DeleteQueue(CFIntegrationTestQueue); - } - - [Fact] - public void TestConnectionName() - { - var connection = _connectionFactory.CreateConnection() as ChannelCachingConnectionProxy; - RC.IConnection rabbitConnection = connection.Target.Connection; - Assert.StartsWith(CFIntegrationConnectionName, rabbitConnection.ClientProvidedName, StringComparison.Ordinal); - } - - [Fact] - public void TestDestroy() - { - IConnection connection1 = _connectionFactory.CreateConnection(); - _connectionFactory.Destroy(); - IConnection connection2 = _connectionFactory.CreateConnection(); - Assert.Same(connection1, connection2); - - _connectionFactory.Dispose(); - Assert.Throws(() => _connectionFactory.CreateConnection()); - } - - [Fact] - public void TestChannelMax() - { - _connectionFactory.RabbitConnectionFactory.RequestedChannelMax = 1; - IConnection connection = _connectionFactory.CreateConnection(); - connection.CreateChannel(true); - Assert.Throws(() => connection.CreateChannel()); - } - - public void Dispose() - { - _connectionFactory.Destroy(); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/CachingConnectionFactoryTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/CachingConnectionFactoryTest.cs deleted file mode 100644 index ebbb089371..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/CachingConnectionFactoryTest.cs +++ /dev/null @@ -1,1835 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using RabbitMQ.Client.Events; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -public sealed class CachingConnectionFactoryTest : AbstractConnectionFactoryTest -{ - [Fact] - public void TestWithConnectionFactoryDefaults() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChanel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChanel.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChanel.Setup(c => c.IsOpen).Returns(true); - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object); - IConnection con = ccf.CreateConnection(); - RC.IModel channel = con.CreateChannel(); - channel.Close(); // should be ignored, and placed into channel cache. - con.Close(); // should be ignored - IConnection con2 = ccf.CreateConnection(); - - // Will retrieve same channel object that was just put into channel cache - RC.IModel channel2 = con2.CreateChannel(); - channel2.Close(); // should be ignored - con2.Close(); // should be ignored - - Assert.Same(con, con2); - Assert.Same(channel, channel2); - mockConnection.Verify(c => c.Close(), Times.Never); - mockChanel.Verify(c => c.Close(), Times.Never); - } - - [Fact] - public void TestPublisherConnection() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChanel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChanel.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChanel.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object); - IConnection con = ccf.PublisherConnectionFactory.CreateConnection(); - RC.IModel channel = con.CreateChannel(); - channel.Close(); // should be ignored, and placed into channel cache. - con.Close(); // should be ignored - IConnection con2 = ccf.PublisherConnectionFactory.CreateConnection(); - - // Will retrieve same channel object that was just put into channel cache - RC.IModel channel2 = con2.CreateChannel(); - channel2.Close(); // should be ignored - con2.Close(); // should be ignored - - Assert.Same(con, con2); - Assert.Same(channel, channel2); - mockConnection.Verify(c => c.Close(), Times.Never); - mockChanel.Verify(c => c.Close(), Times.Never); - } - - [Fact] - public void TestWithConnectionFactoryCacheSize() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - var mockChanel2 = new Mock(); - var mockTxChanel = new Mock(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockConnection.SetupSequence(c => c.CreateModel()).Returns(mockChannel1.Object).Returns(mockChanel2.Object).Returns(mockTxChanel.Object); - - mockChannel1.Setup(c => c.BasicGet("foo", false)).Returns(new RC.BasicGetResult(0, false, null, null, 1, null, null)); - mockChanel2.Setup(c => c.BasicGet("foo", false)).Returns(new RC.BasicGetResult(0, false, null, null, 1, null, null)); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - mockChanel2.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 2 - }; - - IConnection con = ccf.CreateConnection(); - RC.IModel channel1 = con.CreateChannel(); - RC.IModel channel2 = con.CreateChannel(); - var txChannel = (IChannelProxy)con.CreateChannel(true); - Assert.True(txChannel.IsTransactional); - mockTxChanel.Verify(c => c.TxSelect(), Times.Once); - txChannel.Close(); - channel1.BasicGet("foo", true); - channel2.BasicGet("bar", true); - channel1.Close(); // should be ignored, and add last into channel cache. - channel2.Close(); // should be ignored, and add last into channel cache. - - RC.IModel ch1 = con.CreateChannel(); // remove first entry in cache - RC.IModel ch2 = con.CreateChannel(); // remove first entry in cache - - Assert.NotSame(ch1, ch2); - Assert.Same(ch1, channel1); - Assert.Same(ch2, channel2); - - ch1.Close(); - ch2.Close(); - - mockConnection.Verify(c => c.CreateModel(), Times.Exactly(3)); - - con.Close(); - mockConnection.Verify(c => c.Close(), Times.Never); - mockChannel1.Verify(c => c.Close(), Times.Never); - mockChanel2.Verify(c => c.Close(), Times.Never); - } - - [Fact] - public void TestCacheSizeExceeded() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - var mockChanel2 = new Mock(); - var mockChanel3 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.SetupSequence(c => c.CreateModel()).Returns(mockChannel1.Object).Returns(mockChanel2.Object).Returns(mockChanel3.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - - mockChannel1.Setup(c => c.IsOpen).Returns(true); - mockChanel2.Setup(c => c.IsOpen).Returns(true); - mockChanel3.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1 - }; - - IConnection con = ccf.CreateConnection(); - - RC.IModel channel1 = con.CreateChannel(); - - // cache size is 1, but the other connection is not released yet so this - // creates a new one - RC.IModel channel2 = con.CreateChannel(); - Assert.NotSame(channel1, channel2); - - // should be ignored, and added last into channel cache. - channel1.Close(); - - // should be physically closed - channel2.Close(); - - // remove first entry in cache (channel1) - RC.IModel ch1 = con.CreateChannel(); - - // create a new channel - RC.IModel ch2 = con.CreateChannel(); - - Assert.NotSame(ch1, ch2); - Assert.Same(channel1, ch1); - Assert.NotSame(channel2, ch2); - - ch1.Close(); - ch2.Close(); - - mockConnection.Verify(c => c.CreateModel(), Times.Exactly(3)); - - con.Close(); - mockConnection.Verify(c => c.Close(), Times.Never); - mockChannel1.Verify(c => c.Close(), Times.Never); - mockChanel2.Verify(c => c.Close(), Times.AtLeastOnce); - mockChanel3.Verify(c => c.Close(), Times.AtLeastOnce); - } - - [Fact] - public void TestCheckoutLimit() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1, - ChannelCheckoutTimeout = 10 - }; - - IConnection con = ccf.CreateConnection(); - - RC.IModel channel1 = con.CreateChannel(); - - try - { - con.CreateChannel(); - throw new Exception("Exception expected"); - } - catch (RabbitTimeoutException) - { - // Intentionally left empty. - } - - // should be ignored, and added last into channel cache. - channel1.Close(); - - // remove first entry in cache (channel1) - RC.IModel ch1 = con.CreateChannel(); - - Assert.Same(channel1, ch1); - ch1.Close(); - mockConnection.Verify(c => c.CreateModel(), Times.Once); - con.Close(); // should be ignored - - mockConnection.Verify(c => c.Close(), Times.Never); - mockChannel1.Verify(c => c.Close(), Times.Never); - - ccf.Destroy(); - } - - [Fact] - public void TestCheckoutLimitWithFailures() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - - var brokerDown = new AtomicBoolean(false); - - mockConnectionFactory.SetupSequence(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object) - .Throws(new RabbitConnectException(null)) // Happens when broker down - .Returns(mockConnection.Object); - - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - - mockConnection.Setup(c => c.IsOpen).Returns(() => !brokerDown.Value); - mockChannel1.Setup(c => c.IsOpen).Returns(() => !brokerDown.Value); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1, - ChannelCheckoutTimeout = 10 - }; - - IConnection con = ccf.CreateConnection(); // .Returns(mockConnection.Object) - - RC.IModel channel1 = con.CreateChannel(); - - try - { - con.CreateChannel(); - throw new Exception("Exception expected"); - } - catch (RabbitTimeoutException) - { - // Intentionally left empty. - } - - channel1.Close(); - RC.IModel ch1 = con.CreateChannel(); - Assert.Same(channel1, ch1); - - ch1.Close(); - - // Connection will report not open, will try to create new one - brokerDown.Value = true; - - try - { - // .Throws(new AmqpConnectException(null)) thrown - con.CreateChannel(); - throw new Exception("Exception expected"); - } - catch (RabbitConnectException) - { - // Intentionally left empty. - } - - brokerDown.Value = true; - - // Will try to create new connection and will succeed - ch1 = con.CreateChannel(); - ch1.Close(); - - ccf.Destroy(); - } - - [Fact] - public async Task TestConnectionLimit() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object, false, CachingConnectionFactory.CachingMode.Connection) - { - ChannelCacheSize = 1, - ConnectionLimit = 1, - ChannelCheckoutTimeout = 10 - }; - - IConnection con1 = ccf.CreateConnection(); // .Returns(mockConnection.Object) - - try - { - ccf.CreateConnection(); - throw new Exception("Exception expected"); - } - catch (RabbitTimeoutException) - { - // Intentionally left empty. - } - - // should be ignored, and added to cache - con1.Close(); - IConnection con2 = ccf.CreateConnection(); - Assert.Same(con1, con2); - - var latch2 = new CountdownEvent(1); - var latch1 = new CountdownEvent(1); - var connection = new AtomicReference(); - - ccf.ChannelCheckoutTimeout = 30_000; - - _ = Task.Run(() => - { - latch1.Signal(); - connection.Value = ccf.CreateConnection(); - latch2.Signal(); - }); - - Assert.True(latch1.Wait(TimeSpan.FromSeconds(10))); - await Task.Delay(100); - con2.Close(); - - Assert.True(latch2.Wait(TimeSpan.FromSeconds(10))); - Assert.Same(con2, connection.Value); - ccf.Destroy(); - } - - [Fact] - public void TestCheckoutsWithRefreshedConnectionModeChannel() - { - TestCheckoutsWithRefreshedConnectionGuts(CachingConnectionFactory.CachingMode.Channel); - } - - [Fact] - public void TestCheckoutsWithRefreshedConnectionModeConnection() - { - TestCheckoutsWithRefreshedConnectionGuts(CachingConnectionFactory.CachingMode.Connection); - } - - [Fact] - public void TestCheckoutLimitWithRelease() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1, - ChannelCheckoutTimeout = 10_000 - }; - - IConnection con = ccf.CreateConnection(); - var channelOne = new AtomicReference(); - var latch = new CountdownEvent(1); - - _ = Task.Run(async () => - { - RC.IModel channel1 = con.CreateChannel(); - latch.Signal(); - channelOne.Value = channel1; - - try - { - await Task.Delay(100); - channel1.Close(); - } - catch (Exception) - { - // Ignore - } - }); - - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - RC.IModel channel2 = con.CreateChannel(); - Assert.Same(channelOne.Value, channel2); - - channel2.Close(); - - mockConnection.Verify(c => c.Close(), Times.Never); - mockChannel1.Verify(c => c.Close(), Times.Never); - - ccf.Destroy(); - } - - [Fact] - public async Task TestCheckoutLimitWithPublisherConfirmsLogical() - { - await TestCheckoutLimitWithPublisherConfirmsAsync(false); - } - - [Fact] - public async Task TestCheckoutLimitWithPublisherConfirmsPhysical() - { - await TestCheckoutLimitWithPublisherConfirmsAsync(true); - } - - [Fact] - public void TestCheckoutLimitWithPublisherConfirmsLogicalAlreadyCloses() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - var mockProperties = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - - var open = new AtomicBoolean(true); - mockChannel1.Setup(c => c.IsOpen).Returns(() => open.Value); - mockChannel1.Setup(c => c.CreateBasicProperties()).Returns(mockProperties.Object); - mockChannel1.Setup(c => c.NextPublishSeqNo).Returns(1); - - mockChannel1.Setup(c => c.BasicPublish(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback(() => open.Value = false); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1, - ChannelCheckoutTimeout = 1, - PublisherConfirmType = CachingConnectionFactory.ConfirmType.Correlated - }; - - var rabbitTemplate = new RabbitTemplate(ccf); - rabbitTemplate.ConvertAndSend("foo", "bar"); - open.Value = true; - rabbitTemplate.ConvertAndSend("foo", "bar"); - - mockChannel1.Verify(c => c.BasicPublish(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), - Times.Exactly(2)); - } - - [Fact] - public void TestReleaseWithForcedPhysicalClose() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1, - ChannelCheckoutTimeout = 10 - }; - - IConnection con = ccf.CreateConnection(); - - RC.IModel channel1 = con.CreateChannel(); - - Assert.Single(ccf.CheckoutPermits.Values); - SemaphoreSlim slim = ccf.CheckoutPermits.Values.Single(); - Assert.Equal(0, slim.CurrentCount); - - channel1.Close(); - con.Close(); - Assert.Equal(1, slim.CurrentCount); - - channel1 = con.CreateChannel(); - RabbitUtils.SetPhysicalCloseRequired(channel1, true); - Assert.Equal(0, slim.CurrentCount); - - channel1.Close(); - RabbitUtils.SetPhysicalCloseRequired(channel1, false); - con.Close(); - - mockChannel1.Verify(c => c.Close()); - mockConnection.Verify(c => c.Close(), Times.Never); - Assert.Equal(1, slim.CurrentCount); - - ccf.Destroy(); - Assert.Equal(1, slim.CurrentCount); - } - - [Fact] - public void TestDoubleLogicalClose() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1, - ChannelCheckoutTimeout = 10 - }; - - IConnection con = ccf.CreateConnection(); - - RC.IModel channel1 = con.CreateChannel(); - - Assert.Single(ccf.CheckoutPermits.Values); - SemaphoreSlim slim = ccf.CheckoutPermits.Values.Single(); - Assert.Equal(0, slim.CurrentCount); - - channel1.Close(); - Assert.Equal(1, slim.CurrentCount); - - channel1.Close(); // double close of proxy - Assert.Equal(1, slim.CurrentCount); - - con.Close(); - - mockChannel1.Verify(c => c.Close(), Times.Never); - mockConnection.Verify(c => c.Close(), Times.Never); - - ccf.Destroy(); - Assert.Equal(1, slim.CurrentCount); - } - - [Fact] - public void TestCacheSizeExceededAfterClose() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - var mockChannel2 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.SetupSequence(c => c.CreateModel()).Returns(mockChannel1.Object).Returns(mockChannel2.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - mockChannel2.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1 - }; - - IConnection con = ccf.CreateConnection(); - - RC.IModel channel1 = con.CreateChannel(); - channel1.Close(); - - RC.IModel channel2 = con.CreateChannel(); - channel2.Close(); - - Assert.Same(channel1, channel2); - - RC.IModel ch1 = con.CreateChannel(); // remove first entry in cache - - // (channel1) - RC.IModel ch2 = con.CreateChannel(); // create new channel - - Assert.NotSame(ch1, ch2); - Assert.Same(ch1, channel1); - Assert.NotSame(ch2, channel2); - - ch1.Close(); - ch2.Close(); - - mockConnection.Verify(c => c.CreateModel(), Times.Exactly(2)); - con.Close(); // should be ignored - - mockConnection.Verify(c => c.Close(), Times.Never); - mockChannel1.Verify(c => c.Close(), Times.Never); - mockChannel2.Verify(c => c.Close(), Times.AtLeastOnce()); - } - - [Fact] - public void TestTransactionalAndNonTransactionalChannelsSegregated() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - var mockChannel2 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.SetupSequence(c => c.CreateModel()).Returns(mockChannel1.Object).Returns(mockChannel2.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - mockChannel2.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1 - }; - - IConnection con = ccf.CreateConnection(); - RC.IModel channel1 = con.CreateChannel(true); - channel1.TxSelect(); - channel1.Close(); // should be ignored, and add last into channel cache. - - // When a channel is created as non-transactional we should create a new one. - RC.IModel channel2 = con.CreateChannel(); - channel2.Close(); // should be ignored, and add last into channel cache. - Assert.NotSame(channel1, channel2); - - RC.IModel ch1 = con.CreateChannel(true); // remove first entry in cache (channel1) - RC.IModel ch2 = con.CreateChannel(); // create new channel - - Assert.NotSame(ch1, ch2); - Assert.Same(channel1, ch1); - Assert.Same(channel2, ch2); - - ch1.Close(); - ch2.Close(); - - mockConnection.Verify(c => c.CreateModel(), Times.Exactly(2)); - con.Close(); // should be ignored - - mockConnection.Verify(c => c.Close(), Times.Never); - mockChannel1.Verify(c => c.Close(), Times.Never); - mockChannel2.Verify(c => c.Close(), Times.Never); - - Assert.Single(ccf.CachedChannelsNonTransactional); - Assert.Single(ccf.CachedChannelsTransactional); - } - - [Fact] - public void TestWithConnectionFactoryDestroy() - { - var mockConnectionFactory = new Mock(); - var mockConnection1 = new Mock(); - var mockConnection2 = new Mock(); - - var mockChannel1 = new Mock(); - var mockChannel2 = new Mock(); - var mockChannel3 = new Mock(); - - mockConnectionFactory.SetupSequence(f => f.CreateConnection(It.IsAny())).Returns(mockConnection1.Object).Returns(mockConnection2.Object); - - mockConnection1.SetupSequence(c => c.CreateModel()).Returns(mockChannel1.Object).Returns(mockChannel2.Object); - mockConnection1.Setup(c => c.IsOpen).Returns(true); - - mockConnection2.SetupSequence(c => c.CreateModel()).Returns(mockChannel3.Object); - mockConnection2.Setup(c => c.IsOpen).Returns(true); - - mockChannel1.Setup(c => c.IsOpen).Returns(true); - mockChannel2.Setup(c => c.IsOpen).Returns(true); - mockChannel3.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 2 - }; - - IConnection con = ccf.CreateConnection(); - - // This will return a proxy that suppresses calls to close - RC.IModel channel1 = con.CreateChannel(); - RC.IModel channel2 = con.CreateChannel(); - - // Should be ignored, and add last into channel cache. - channel1.Close(); - channel2.Close(); - - // remove first entry in cache (channel1) - RC.IModel ch1 = con.CreateChannel(); - - // remove first entry in cache (channel2) - RC.IModel ch2 = con.CreateChannel(); - - Assert.Same(channel1, ch1); - Assert.Same(channel2, ch2); - - RC.IModel target1 = ((IChannelProxy)ch1).TargetChannel; - RC.IModel target2 = ((IChannelProxy)ch2).TargetChannel; - - Assert.NotSame(target1, target2); - - ch1.Close(); - ch2.Close(); - con.Close(); // should be ignored - - var asProxy = con as CachingConnectionFactory.ChannelCachingConnectionProxy; - RC.IConnection conDelegate = asProxy.TargetConnection.Connection; - - ccf.Destroy(); - - mockConnection1.Verify(c => c.CreateModel(), Times.Exactly(2)); - mockConnection1.Verify(c => c.Close(It.IsAny())); - - mockChannel2.Verify(c => c.Close()); - - IConnection con1 = ccf.CreateConnection(); - - var asProxy1 = con1 as CachingConnectionFactory.ChannelCachingConnectionProxy; - RC.IConnection conDelegate1 = asProxy1.TargetConnection.Connection; - Assert.NotSame(conDelegate, conDelegate1); - - RC.IModel channel3 = con.CreateChannel(); - - Assert.NotSame(channel1, channel3); - Assert.NotSame(channel2, channel3); - } - - [Fact] - public void TestWithChannelListener() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - - var called = new AtomicInteger(0); - AbstractConnectionFactory connectionFactory = CreateConnectionFactory(mockConnectionFactory.Object); - - connectionFactory.SetChannelListeners(new List - { - new TestWithChannelListenerListener(called) - }); - - ((CachingConnectionFactory)connectionFactory).ChannelCacheSize = 1; - - IConnection con = connectionFactory.CreateConnection(); - RC.IModel channel = con.CreateChannel(); - Assert.Equal(1, called.Value); - - channel.Close(); - con.Close(); - - mockConnection.Verify(c => c.Close(), Times.Never); - connectionFactory.CreateConnection(); - con.CreateChannel(); - Assert.Equal(1, called.Value); - - connectionFactory.Destroy(); - mockConnection.Verify(c => c.Close(It.IsAny())); - mockConnectionFactory.Verify(c => c.CreateConnection(It.IsAny())); - } - - [Fact] - public void TestWithConnectionListener() - { - var mockConnectionFactory = new Mock(); - var mockConnection1 = new Mock(); - mockConnection1.Setup(c => c.ToString()).Returns("conn1"); - var mockConnection2 = new Mock(); - mockConnection2.Setup(c => c.ToString()).Returns("conn2"); - var mockChannel1 = new Mock(); - mockConnectionFactory.SetupSequence(f => f.CreateConnection(It.IsAny())).Returns(mockConnection1.Object).Returns(mockConnection2.Object); - mockConnection1.Setup(c => c.IsOpen).Returns(true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - mockConnection1.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - mockConnection2.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - - var created = new AtomicReference(); - var closed = new AtomicReference(); - var timesClosed = new AtomicInteger(0); - AbstractConnectionFactory connectionFactory = CreateConnectionFactory(mockConnectionFactory.Object); - connectionFactory.AddConnectionListener(new TestWithConnectionListenerListener(created, closed, timesClosed)); - ((CachingConnectionFactory)connectionFactory).ChannelCacheSize = 1; - IConnection con = connectionFactory.CreateConnection(); - RC.IModel channel = con.CreateChannel(); - Assert.Same(created.Value, con); - channel.Close(); - - con.Close(); - mockConnection1.Verify(c => c.Close(), Times.Never); - - IConnection same = connectionFactory.CreateConnection(); - channel = con.CreateChannel(); - Assert.Same(same, con); - channel.Close(); - - var asProxy = con as CachingConnectionFactory.ChannelCachingConnectionProxy; - RC.IConnection conDelegate = asProxy.TargetConnection.Connection; - - mockConnection1.Setup(c => c.IsOpen).Returns(false); - mockChannel1.Setup(c => c.IsOpen).Returns(false); - - channel.BasicCancel("foo"); - channel.Close(); - Assert.Equal(1, timesClosed.Value); - - IConnection notSame = connectionFactory.CreateConnection(); - - var asProxy1 = notSame as CachingConnectionFactory.ChannelCachingConnectionProxy; - RC.IConnection conDelegate1 = asProxy1.TargetConnection.Connection; - Assert.NotSame(conDelegate, conDelegate1); - Assert.Same(closed.Value, con); - Assert.Same(created.Value, notSame); - - connectionFactory.Destroy(); - mockConnection2.Verify(c => c.Close(It.IsAny()), Times.AtLeastOnce); - Assert.Same(closed.Value, notSame); - Assert.Equal(2, timesClosed.Value); - - mockConnectionFactory.Verify(f => f.CreateConnection(It.IsAny()), Times.Exactly(2)); - } - - [Fact] - public void TestWithConnectionFactoryCachedConnection() - { - var mockConnectionFactory = new Mock(); - var mockConnections = new List>(); - var mockChannels = new List>(); - - var connectionNumber = new AtomicInteger(); - var channelNumber = new AtomicInteger(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Callback(() => - { - var connection = new Mock(); - - connection.Setup(c => c.CreateModel()).Callback(() => - { - var channel = new Mock(); - channel.Setup(c => c.IsOpen).Returns(true); - int channelNum = channelNumber.IncrementAndGet(); - channel.Setup(c => c.ToString()).Returns($"mockChannel{connectionNumber}:{channelNum}"); - mockChannels.Add(channel); - }).Returns(() => mockChannels[channelNumber.Value - 1].Object); - - int connectionNum = connectionNumber.IncrementAndGet(); - connection.Setup(c => c.ToString()).Returns($"mockConnection{connectionNum}"); - connection.Setup(c => c.IsOpen).Returns(true); - mockConnections.Add(connection); - }).Returns(() => mockConnections[connectionNumber.Value - 1].Object); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object, false, CachingConnectionFactory.CachingMode.Connection); - - Assert.Empty(ccf.AllocatedConnections); - Assert.Empty(ccf.IdleConnections); - - var createNotification = new AtomicReference(); - var closedNotification = new AtomicReference(); - - ccf.SetConnectionListeners(new List - { - new TestWithConnectionFactoryCachedConnectionListener(createNotification, closedNotification) - }); - - IConnection con1 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[0].Object, con1); - Assert.Single(ccf.AllocatedConnections); - Assert.Empty(ccf.IdleConnections); - - Assert.NotNull(createNotification.Value); - RC.IConnection val = createNotification.GetAndSet(null); - Assert.Same(val, mockConnections[0].Object); - - RC.IModel channel1 = con1.CreateChannel(); - VerifyChannelIs(mockChannels[0].Object, channel1); - channel1.Close(); - - // AMQP-358 - mockChannels[0].Verify(c => c.Close(), Times.Never); - - con1.Close(); - mockConnections[0].Verify(c => c.Close(), Times.Never); - Assert.Single(ccf.AllocatedConnections); - Assert.Single(ccf.IdleConnections); - Assert.Null(closedNotification.Value); - - // Will retrieve same connection that was just put into cache, and reuse single channel from cache as well - IConnection con2 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[0].Object, con2); - RC.IModel channel2 = con2.CreateChannel(); - VerifyChannelIs(mockChannels[0].Object, channel2); - channel2.Close(); - mockChannels[0].Verify(c => c.Close(), Times.Never); - con2.Close(); - mockConnections[0].Verify(c => c.Close(), Times.Never); - Assert.Single(ccf.AllocatedConnections); - Assert.Single(ccf.IdleConnections); - Assert.Null(createNotification.Value); - - // Now check for multiple connections/channels - con1 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[0].Object, con1); - con2 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[1].Object, con2); - channel1 = con1.CreateChannel(); - VerifyChannelIs(mockChannels[0].Object, channel1); - channel2 = con2.CreateChannel(); - VerifyChannelIs(mockChannels[1].Object, channel2); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Empty(ccf.IdleConnections); - Assert.NotNull(createNotification.Value); - val = createNotification.GetAndSet(null); - Assert.Same(val, mockConnections[1].Object); - - // put mock1 in cache - channel1.Close(); - mockChannels[1].Verify(c => c.Close(), Times.Never); - con1.Close(); - mockConnections[0].Verify(c => c.Close(), Times.Never); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Single(ccf.IdleConnections); - Assert.Null(closedNotification.Value); - - IConnection con3 = ccf.CreateConnection(); - Assert.Null(createNotification.Value); - VerifyConnectionIs(mockConnections[0].Object, con3); - RC.IModel channel3 = con3.CreateChannel(); - VerifyChannelIs(mockChannels[0].Object, channel3); - - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Empty(ccf.IdleConnections); - - channel2.Close(); - con2.Close(); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Single(ccf.IdleConnections); - channel3.Close(); - con3.Close(); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Equal(2, ccf.IdleConnections.Count); - Assert.Equal(1, ccf.CountOpenConnections()); - - // Cache size is 1; con3 (mock1) should have been a real close. - // con2 (mock2) should still be in the cache. - mockConnections[0].Verify(c => c.Close(30_000)); - Assert.NotNull(closedNotification.Value); - val = closedNotification.GetAndSet(null); - Assert.Same(val, mockConnections[0].Object); - mockChannels[1].Verify(c => c.Close(), Times.Never); - mockConnections[1].Verify(c => c.Close(30_000), Times.Never); - - VerifyConnectionIs(mockConnections[1].Object, ccf.IdleConnections.First.Value); - - // Now a closed cached connection - mockConnections[1].Setup(c => c.IsOpen).Returns(false); - mockChannels[1].Setup(c => c.IsOpen).Returns(false); - con3 = ccf.CreateConnection(); - Assert.NotNull(closedNotification.Value); - val = closedNotification.GetAndSet(null); - Assert.Same(mockConnections[1].Object, val); - VerifyConnectionIs(mockConnections[2].Object, con3); - Assert.NotNull(createNotification.Value); - val = createNotification.GetAndSet(null); - Assert.Same(mockConnections[2].Object, val); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Single(ccf.IdleConnections); - Assert.Equal(1, ccf.CountOpenConnections()); - channel3 = con3.CreateChannel(); - VerifyChannelIs(mockChannels[2].Object, channel3); - channel3.Close(); - con3.Close(); - Assert.Null(closedNotification.Value); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Equal(2, ccf.IdleConnections.Count); - Assert.Equal(1, ccf.CountOpenConnections()); - - // Now a closed cached connection when creating a channel - con3 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[2].Object, con3); - Assert.Null(createNotification.Value); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Single(ccf.IdleConnections); - mockConnections[2].Setup(c => c.IsOpen).Returns(false); - channel3 = con3.CreateChannel(); - val = closedNotification.GetAndSet(null); - Assert.NotNull(val); - val = createNotification.GetAndSet(null); - Assert.NotNull(val); - VerifyChannelIs(mockChannels[3].Object, channel3); - channel3.Close(); - con3.Close(); - Assert.Null(closedNotification.Value); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Equal(2, ccf.IdleConnections.Count); - Assert.Equal(1, ccf.CountOpenConnections()); - - ccf.Destroy(); - Assert.NotNull(closedNotification.Value); - mockConnections[3].Verify(c => c.Close(30_000)); - } - - [Fact] - public void TestWithConnectionFactoryCachedConnectionAndChannels() - { - var mockConnectionFactory = new Mock(); - var mockConnections = new List>(); - var mockChannels = new List>(); - - var connectionNumber = new AtomicInteger(); - var channelNumber = new AtomicInteger(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Callback(() => - { - var connection = new Mock(); - - connection.Setup(c => c.CreateModel()).Callback(() => - { - var channel = new Mock(); - channel.Setup(c => c.IsOpen).Returns(true); - int channelNum = channelNumber.IncrementAndGet(); - channel.Setup(c => c.ToString()).Returns($"mockChannel{connectionNumber}:{channelNum}"); - mockChannels.Add(channel); - }).Returns(() => mockChannels[channelNumber.Value - 1].Object); - - int connectionNum = connectionNumber.IncrementAndGet(); - connection.Setup(c => c.ToString()).Returns($"mockConnection{connectionNum}"); - connection.Setup(c => c.IsOpen).Returns(true); - mockConnections.Add(connection); - }).Returns(() => mockConnections[connectionNumber.Value - 1].Object); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object, false, CachingConnectionFactory.CachingMode.Connection) - { - ConnectionCacheSize = 2, - ChannelCacheSize = 2 - }; - - Assert.Empty(ccf.AllocatedConnections); - Assert.Empty(ccf.IdleConnections); - - var createNotification = new AtomicReference(); - var closedNotification = new AtomicReference(); - - ccf.SetConnectionListeners(new List - { - new TestWithConnectionFactoryCachedConnectionListener(createNotification, closedNotification) - }); - - IConnection con1 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[0].Object, con1); - Assert.Single(ccf.AllocatedConnections); - Assert.Empty(ccf.IdleConnections); - - Assert.NotNull(createNotification.Value); - RC.IConnection val = createNotification.GetAndSet(null); - Assert.Same(val, mockConnections[0].Object); - - RC.IModel channel1 = con1.CreateChannel(); - VerifyChannelIs(mockChannels[0].Object, channel1); - channel1.Close(); - - // AMQP-358 - mockChannels[0].Verify(c => c.Close(), Times.Never); - - con1.Close(); - mockConnections[0].Verify(c => c.Close(), Times.Never); - Assert.Single(ccf.AllocatedConnections); - Assert.Single(ccf.IdleConnections); - var con1Proxy = con1 as CachingConnectionFactory.ChannelCachingConnectionProxy; - Assert.Single(ccf.AllocatedConnectionNonTransactionalChannels[con1Proxy]); - Assert.Null(closedNotification.Value); - - // Will retrieve same connection that was just put into cache, and reuse single channel from cache as well - IConnection con2 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[0].Object, con2); - RC.IModel channel2 = con2.CreateChannel(); - VerifyChannelIs(mockChannels[0].Object, channel2); - channel2.Close(); - mockChannels[0].Verify(c => c.Close(), Times.Never); - con2.Close(); - mockConnections[0].Verify(c => c.Close(), Times.Never); - Assert.Single(ccf.AllocatedConnections); - Assert.Single(ccf.IdleConnections); - Assert.Null(createNotification.Value); - - // Now check for multiple connections/channels - con1 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[0].Object, con1); - con2 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[1].Object, con2); - channel1 = con1.CreateChannel(); - VerifyChannelIs(mockChannels[0].Object, channel1); - channel2 = con2.CreateChannel(); - VerifyChannelIs(mockChannels[1].Object, channel2); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Empty(ccf.IdleConnections); - Assert.NotNull(createNotification.Value); - val = createNotification.GetAndSet(null); - Assert.Same(val, mockConnections[1].Object); - - // put mock1 in cache - channel1.Close(); - mockChannels[1].Verify(c => c.Close(), Times.Never); - con1.Close(); - mockConnections[0].Verify(c => c.Close(), Times.Never); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Single(ccf.IdleConnections); - Assert.Null(closedNotification.Value); - - IConnection con3 = ccf.CreateConnection(); - Assert.Null(createNotification.Value); - VerifyConnectionIs(mockConnections[0].Object, con3); - RC.IModel channel3 = con3.CreateChannel(); - VerifyChannelIs(mockChannels[0].Object, channel3); - - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Empty(ccf.IdleConnections); - - channel2.Close(); - con2.Close(); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Single(ccf.IdleConnections); - channel3.Close(); - con3.Close(); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Equal(2, ccf.IdleConnections.Count); - con1Proxy = con1 as CachingConnectionFactory.ChannelCachingConnectionProxy; - Assert.Single(ccf.AllocatedConnectionNonTransactionalChannels[con1Proxy]); - var con2Proxy = con2 as CachingConnectionFactory.ChannelCachingConnectionProxy; - Assert.Single(ccf.AllocatedConnectionNonTransactionalChannels[con2Proxy]); - - // Cache size is 1; con3 (mock1) should have been a real close. - // con2 (mock2) should still be in the cache. - mockConnections[0].Verify(c => c.Close(30_000), Times.Never); - Assert.Null(closedNotification.Value); - mockChannels[1].Verify(c => c.Close(), Times.Never); - mockConnections[1].Verify(c => c.Close(30_000), Times.Never); - - Assert.Equal(2, ccf.IdleConnections.Count); - using LinkedList.Enumerator idleEnumerator = ccf.IdleConnections.GetEnumerator(); - Assert.True(idleEnumerator.MoveNext()); - VerifyConnectionIs(mockConnections[1].Object, idleEnumerator.Current); - Assert.True(idleEnumerator.MoveNext()); - VerifyConnectionIs(mockConnections[0].Object, idleEnumerator.Current); - - // Now a closed cached connection - mockConnections[1].Setup(c => c.IsOpen).Returns(false); - con3 = ccf.CreateConnection(); - Assert.NotNull(closedNotification.Value); - val = closedNotification.GetAndSet(null); - Assert.Same(mockConnections[1].Object, val); - VerifyConnectionIs(mockConnections[0].Object, con3); - Assert.Null(createNotification.Value); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Single(ccf.IdleConnections); - channel3 = con3.CreateChannel(); - VerifyChannelIs(mockChannels[0].Object, channel3); - channel3.Close(); - con3.Close(); - Assert.Null(closedNotification.Value); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Equal(2, ccf.IdleConnections.Count); - - // Now a closed cached connection when creating a channel - con3 = ccf.CreateConnection(); - VerifyConnectionIs(mockConnections[0].Object, con3); - Assert.Null(createNotification.Value); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Single(ccf.IdleConnections); - mockConnections[0].Setup(c => c.IsOpen).Returns(false); - channel3 = con3.CreateChannel(); - val = closedNotification.GetAndSet(null); - Assert.NotNull(val); - val = createNotification.GetAndSet(null); - Assert.NotNull(val); - - VerifyChannelIs(mockChannels[2].Object, channel3); - channel3.Close(); - con3.Close(); - Assert.Null(closedNotification.Value); - Assert.Equal(2, ccf.AllocatedConnections.Count); - Assert.Equal(2, ccf.IdleConnections.Count); - - IConnection con4 = ccf.CreateConnection(); - Assert.Same(con3, con4); - Assert.Single(ccf.IdleConnections); - RC.IModel channelA = con4.CreateChannel(); - RC.IModel channelB = con4.CreateChannel(); - RC.IModel channelC = con4.CreateChannel(); - channelA.Close(); - var con4Proxy = con4 as CachingConnectionFactory.ChannelCachingConnectionProxy; - Assert.Single(ccf.AllocatedConnectionNonTransactionalChannels[con4Proxy]); - channelB.Close(); - con4Proxy = con4 as CachingConnectionFactory.ChannelCachingConnectionProxy; - Assert.Equal(2, ccf.AllocatedConnectionNonTransactionalChannels[con4Proxy].Count); - channelC.Close(); - con4Proxy = con4 as CachingConnectionFactory.ChannelCachingConnectionProxy; - Assert.Equal(2, ccf.AllocatedConnectionNonTransactionalChannels[con4Proxy].Count); - - ccf.Destroy(); - Assert.NotNull(closedNotification.Value); - mockConnections[0].Verify(c => c.Close(30_000)); - mockConnections[1].Verify(c => c.Close(30_000)); - mockConnections[2].Verify(c => c.Close(30_000)); - } - - [Fact] - public void TestWithConnectionFactoryCachedConnectionIdleAreClosed() - { - var mockConnectionFactory = new Mock(); - var mockConnections = new List>(); - var mockChannels = new List>(); - - var connectionNumber = new AtomicInteger(); - var channelNumber = new AtomicInteger(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Callback(() => - { - var connection = new Mock(); - - connection.Setup(c => c.CreateModel()).Callback(() => - { - var channel = new Mock(); - channel.Setup(c => c.IsOpen).Returns(true); - int channelNum = channelNumber.IncrementAndGet(); - channel.Setup(c => c.ToString()).Returns($"mockChannel{connectionNumber}:{channelNum}"); - mockChannels.Add(channel); - }).Returns(() => mockChannels[channelNumber.Value - 1].Object); - - int connectionNum = connectionNumber.IncrementAndGet(); - connection.Setup(c => c.ToString()).Returns($"mockConnection{connectionNum}"); - connection.Setup(c => c.IsOpen).Returns(true); - mockConnections.Add(connection); - }).Returns(() => mockConnections[connectionNumber.Value - 1].Object); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object, false, CachingConnectionFactory.CachingMode.Connection) - { - ConnectionCacheSize = 5 - }; - - Assert.Empty(ccf.AllocatedConnections); - Assert.Empty(ccf.IdleConnections); - - IConnection conn1 = ccf.CreateConnection(); - IConnection conn2 = ccf.CreateConnection(); - IConnection conn3 = ccf.CreateConnection(); - Assert.Equal(3, ccf.AllocatedConnections.Count); - Assert.Empty(ccf.IdleConnections); - conn1.Close(); - conn2.Close(); - conn3.Close(); - Assert.Equal(3, ccf.AllocatedConnections.Count); - Assert.Equal(3, ccf.IdleConnections.Count); - - mockConnections[0].Setup(c => c.IsOpen).Returns(false); - mockConnections[1].Setup(c => c.IsOpen).Returns(false); - IConnection conn4 = ccf.CreateConnection(); - Assert.Equal(3, ccf.AllocatedConnections.Count); - Assert.Equal(2, ccf.IdleConnections.Count); - Assert.Same(conn4, conn3); - conn4.Close(); - Assert.Equal(3, ccf.AllocatedConnections.Count); - Assert.Equal(3, ccf.IdleConnections.Count); - Assert.Equal(1, ccf.CountOpenConnections()); - - ccf.Destroy(); - Assert.Equal(3, ccf.AllocatedConnections.Count); - Assert.Equal(3, ccf.IdleConnections.Count); - Assert.Equal(0, ccf.CountOpenConnections()); - } - - [Fact] - public void TestConsumerChannelPhysicallyClosedWhenNotIsOpen() - { - TestConsumerChannelPhysicallyClosedWhenNotIsOpenGuts(false); - } - - [Fact] - public void TestConsumerChannelWithPubConfPhysicallyClosedWhenNotIsOpen() - { - TestConsumerChannelPhysicallyClosedWhenNotIsOpenGuts(true); - } - - [Fact(Skip = "Can't Mock sealed class: RC.ConnectionFactory")] - public void SetAddressesEmpty() - { - var mock = new Mock(); - - var ccf = new CachingConnectionFactory(mock.Object) - { - Host = "abc" - }; - - ccf.SetAddresses(string.Empty); - ccf.CreateConnection(); - mock.VerifyGet(f => f.AutomaticRecoveryEnabled); - mock.VerifySet(f => f.HostName = "abc"); - mock.Verify(f => f.CreateConnection(It.IsAny())); - } - - [Fact(Skip = "Can't Mock sealed class: RC.ConnectionFactory")] - public void SetAddressesOneHost() - { - var mock = new Mock(); - IList captured = null; - mock.Setup(f => f.CreateConnection(It.IsAny>())).Callback, string>((arg1, _) => captured = arg1); - var ccf = new CachingConnectionFactory(mock.Object); - ccf.SetAddresses("mq1"); - ccf.CreateConnection(); - mock.VerifyGet(f => f.AutomaticRecoveryEnabled); - mock.Verify(f => f.CreateConnection(It.IsAny>(), It.IsAny())); - Assert.NotNull(captured); - Assert.Equal("mq1", captured[0].HostName); - } - - [Fact(Skip = "Can't Mock sealed class: RC.ConnectionFactory")] - public void SetAddressesTwoHosts() - { - var mock = new Mock(); - IList captured = null; - mock.Setup(f => f.CreateConnection(It.IsAny>())).Callback, string>((arg1, _) => captured = arg1); - mock.Setup(f => f.AutomaticRecoveryEnabled).Returns(true); - var ccf = new CachingConnectionFactory(mock.Object); - ccf.SetAddresses("mq1,mq2"); - ccf.CreateConnection(); - mock.VerifyGet(f => f.AutomaticRecoveryEnabled); - mock.VerifySet(f => f.AutomaticRecoveryEnabled = false); - mock.Verify(f => f.CreateConnection(It.IsAny>(), It.IsAny())); - Assert.NotNull(captured); - Assert.Equal("mq1", captured[0].HostName); - Assert.Equal("mq2", captured[1].HostName); - } - - [Fact] - public void SetUri() - { - var uri = new Uri("amqp://localhost:1234/%2f"); - var mock = new Mock(); - - var ccf = new CachingConnectionFactory(mock.Object) - { - Uri = uri - }; - - ccf.CreateConnection(); - mock.VerifySet(f => f.Uri = uri); - mock.Verify(f => f.CreateConnection(It.IsAny())); - } - - [Fact] - public void TestChannelCloseIdempotency() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.SetupSequence(c => c.IsOpen).Returns(true).Returns(false); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object); - IConnection con = ccf.CreateConnection(); - RC.IModel channel1 = con.CreateChannel(); - channel1.Close(); // should be ignored, and placed into channel cache. - channel1.Close(); // physically closed, so remove from the cache. - channel1.Close(); // physically closed and removed from the cache before, so void "close". - RC.IModel channel2 = con.CreateChannel(); - Assert.NotSame(channel1, channel2); - } - - [Fact] - public void TestOrderlyShutDown() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - PublisherConfirmType = CachingConnectionFactory.ConfirmType.Correlated - }; - - var pccMock = new Mock(); - pccMock.Setup(p => p.IsOpen).Returns(true); - var asyncClosingLatch = new CountdownEvent(1); - pccMock.Setup(p => p.WaitForConfirmsOrDie(It.IsAny())).Callback(() => asyncClosingLatch.Signal()); - var closeLatch = new CountdownEvent(1); - ccf.PublisherCallbackChannelFactory = new TestOrderlyShutdownPublisherCallbackChannelFactory(pccMock); - - pccMock.Setup(p => p.Close()).Callback(() => - { - closeLatch.Signal(); - }); - - RC.IModel channel = ccf.CreateConnection().CreateChannel(); - - Task.Run(() => - { - RabbitUtils.SetPhysicalCloseRequired(channel, true); - - try - { - channel.Close(); - } - catch (Exception) - { - // ignore - } - }); - - Assert.True(asyncClosingLatch.Wait(TimeSpan.FromSeconds(10))); - ccf.Destroy(); - Assert.True(closeLatch.Wait(TimeSpan.FromSeconds(10))); - } - - [Fact] - public void TestFirstConnectionDoesNotWait() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object, false, CachingConnectionFactory.CachingMode.Connection) - { - ChannelCheckoutTimeout = 60_000 - }; - - long t1 = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - ccf.CreateConnection(); - Assert.True(DateTimeOffset.Now.ToUnixTimeMilliseconds() - t1 < 30_000); - } - - [Fact] - public void TestShuffle() - { - var captors = new List>(); - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny>())) - .Callback>(arg1 => captors.Add(arg1)).Returns(mockConnection.Object); - - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object, false, CachingConnectionFactory.CachingMode.Connection); - ccf.SetAddresses("host1:5672,host2:5672,host3:5672"); - ccf.ShuffleAddresses = true; - - for (int i = 0; i < 100; i++) - { - ccf.CreateConnection(); - } - - ccf.Destroy(); - List firstAddress = captors.SelectMany(e => e).Select(e => e.HostName).Distinct().ToList(); - firstAddress.Sort(); - Assert.Equal(3, firstAddress.Count); - Assert.Equal("host1", firstAddress[0]); - Assert.Equal("host2", firstAddress[1]); - Assert.Equal("host3", firstAddress[2]); - } - - [Fact] - public void ConfirmsSimple() - { - var mockConnectionFactory = new Mock(); - - var cf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - PublisherConfirmType = CachingConnectionFactory.ConfirmType.None - }; - - Assert.False(cf.IsSimplePublisherConfirms); - cf.PublisherConfirmType = CachingConnectionFactory.ConfirmType.Simple; - Assert.True(cf.IsSimplePublisherConfirms); - Assert.True(cf.PublisherConnectionFactory.IsSimplePublisherConfirms); - cf.PublisherConfirmType = CachingConnectionFactory.ConfirmType.None; - Assert.False(cf.IsSimplePublisherConfirms); - Assert.False(cf.PublisherConnectionFactory.IsSimplePublisherConfirms); - } - - [Fact] - public void ConfirmsCorrelated() - { - var mockConnectionFactory = new Mock(); - - var cf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - PublisherConfirmType = CachingConnectionFactory.ConfirmType.None - }; - - Assert.False(cf.IsPublisherConfirms); - cf.PublisherConfirmType = CachingConnectionFactory.ConfirmType.Correlated; - Assert.True(cf.IsPublisherConfirms); - Assert.True(cf.PublisherConnectionFactory.IsPublisherConfirms); - cf.PublisherConfirmType = CachingConnectionFactory.ConfirmType.None; - Assert.False(cf.IsPublisherConfirms); - Assert.False(cf.PublisherConnectionFactory.IsPublisherConfirms); - } - - protected override AbstractConnectionFactory CreateConnectionFactory(RC.IConnectionFactory connectionFactory) - { - return new CachingConnectionFactory(connectionFactory); - } - - private void TestConsumerChannelPhysicallyClosedWhenNotIsOpenGuts(bool confirms) - { - try - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel1.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object); - - if (confirms) - { - ccf.PublisherConfirmType = CachingConnectionFactory.ConfirmType.Correlated; - } - - IConnection con = ccf.CreateConnection(); - RC.IModel channel = con.CreateChannel(); - RabbitUtils.SetPhysicalCloseRequired(channel, true); - mockChannel1.Setup(c => c.IsOpen).Returns(true); - var physicalCloseLatch = new CountdownEvent(1); - - mockChannel1.Setup(c => c.Close()).Callback(() => physicalCloseLatch.Signal()); - channel.Close(); - RabbitUtils.SetPhysicalCloseRequired(channel, false); - con.Close(); // should be ignored - - Assert.True(physicalCloseLatch.Wait(TimeSpan.FromSeconds(10))); - } - catch (Exception) - { - // Ignore - } - } - - private void VerifyChannelIs(RC.IModel mockChannel, RC.IModel channel) - { - var proxy = (IChannelProxy)channel; - Assert.Same(proxy.TargetChannel, mockChannel); - } - - private void VerifyConnectionIs(RC.IConnection mockConnection, object con) - { - var asProxy = con as CachingConnectionFactory.ChannelCachingConnectionProxy; - RC.IConnection conDelegate = asProxy.TargetConnection.Connection; - Assert.Same(mockConnection, conDelegate); - } - - private async Task TestCheckoutLimitWithPublisherConfirmsAsync(bool physicalClose) - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - var mockProperties = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(mockProperties.Object); - var confirmsLatch = new CountdownEvent(1); - - mockChannel.Setup(c => c.WaitForConfirmsOrDie(It.IsAny())).Callback(() => - { - confirmsLatch.Wait(TimeSpan.FromSeconds(10)); - }); - - mockChannel.SetupAdd(c => c.BasicAcks += It.IsAny>()); - - mockChannel.Setup(c => c.NextPublishSeqNo).Returns(1); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object) - { - ChannelCacheSize = 1, - ChannelCheckoutTimeout = 1, - PublisherConfirmType = CachingConnectionFactory.ConfirmType.Correlated - }; - - IConnection con = ccf.CreateConnection(); - var rabbitTemplate = new RabbitTemplate(ccf); - - if (physicalClose) - { - RC.IModel channel1 = con.CreateChannel(); - RabbitUtils.SetPhysicalCloseRequired(channel1, true); - channel1.Close(); - } - else - { - rabbitTemplate.ConvertAndSend("foo", "bar"); // pending confirm - } - - Assert.Throws(() => con.CreateChannel()); - int n = 0; - - if (physicalClose) - { - confirmsLatch.Signal(); - RC.IModel channel2 = null; - - while (channel2 == null && n++ < 100) - { - try - { - channel2 = con.CreateChannel(); - } - catch (Exception) - { - await Task.Delay(100); - } - } - - Assert.NotNull(channel2); - } - else - { - mockChannel.Raise(m => m.BasicAcks += null, new BasicAckEventArgs - { - DeliveryTag = 1, - Multiple = false - }); - - bool ok = false; - - while (!ok && n++ < 100) - { - try - { - rabbitTemplate.ConvertAndSend("foo", "bar"); - ok = true; - } - catch (Exception) - { - await Task.Delay(100); - } - } - - Assert.True(ok); - } - } - - private void TestCheckoutsWithRefreshedConnectionGuts(CachingConnectionFactory.CachingMode mode) - { - var mockConnectionFactory = new Mock(); - var mockConnection1 = new Mock(); - var mockConnection2 = new Mock(); - var mockChannel1 = new Mock(); - var mockChanel2 = new Mock(); - var mockChanel3 = new Mock(); - var mockChanel4 = new Mock(); - mockConnectionFactory.SetupSequence(f => f.CreateConnection(It.IsAny())).Returns(mockConnection1.Object).Returns(mockConnection2.Object); - mockConnection1.SetupSequence(c => c.CreateModel()).Returns(mockChannel1.Object).Returns(mockChanel2.Object); - mockConnection1.Setup(c => c.IsOpen).Returns(true); - mockConnection2.SetupSequence(c => c.CreateModel()).Returns(mockChanel3.Object).Returns(mockChanel4.Object); - mockConnection2.Setup(c => c.IsOpen).Returns(true); - - mockChannel1.Setup(c => c.IsOpen).Returns(true); - mockChanel2.Setup(c => c.IsOpen).Returns(true); - mockChanel3.Setup(c => c.IsOpen).Returns(true); - mockChanel4.Setup(c => c.IsOpen).Returns(true); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object, false, mode) - { - ChannelCacheSize = 2, - ChannelCheckoutTimeout = 10 - }; - - ccf.AddConnectionListener(new TestCheckoutsWithRefreshedConnectionGutsListener()); - IConnection con = ccf.CreateConnection(); - RC.IModel channel1 = con.CreateChannel(); - - Assert.Single(ccf.CheckoutPermits.Values); - SemaphoreSlim slim = ccf.CheckoutPermits.Values.Single(); - Assert.Equal(1, slim.CurrentCount); - - channel1.Close(); - con.Close(); - - Assert.Equal(2, slim.CurrentCount); - - mockConnection1.Setup(c => c.IsOpen).Returns(false); - mockChannel1.Setup(c => c.IsOpen).Returns(false); - mockChanel2.Setup(c => c.IsOpen).Returns(false); - - con.CreateChannel().Close(); - con = ccf.CreateConnection(); - con.CreateChannel().Close(); - con.CreateChannel().Close(); - con.CreateChannel().Close(); - con.CreateChannel().Close(); - con.CreateChannel().Close(); - - mockConnection1.Verify(c => c.CreateModel(), Times.Once); - mockConnection2.Verify(c => c.CreateModel(), Times.Exactly(2)); - - con.Close(); - mockConnection2.Verify(c => c.Close(), Times.Never); - - Assert.Equal(2, slim.CurrentCount); - - ccf.Destroy(); - - Assert.Equal(2, slim.CurrentCount); - } - - private sealed class TestOrderlyShutdownPublisherCallbackChannelFactory : IPublisherCallbackChannelFactory - { - private readonly Mock _pccMock; - - public TestOrderlyShutdownPublisherCallbackChannelFactory(Mock pccMock) - { - _pccMock = pccMock; - } - - public IPublisherCallbackChannel CreateChannel(RC.IModel channel) - { - return _pccMock.Object; - } - } - - private sealed class TestWithConnectionFactoryCachedConnectionListener : IConnectionListener - { - private readonly AtomicReference _createNotification; - private readonly AtomicReference _closedNotification; - - public TestWithConnectionFactoryCachedConnectionListener(AtomicReference createNotification, - AtomicReference closedNotification) - { - _createNotification = createNotification; - _closedNotification = closedNotification; - } - - public void OnClose(IConnection connection) - { - Assert.Null(_closedNotification.Value); - var asProxy = connection as CachingConnectionFactory.ChannelCachingConnectionProxy; - RC.IConnection conDelegate = asProxy.TargetConnection.Connection; - _closedNotification.Value = conDelegate; - } - - public void OnCreate(IConnection connection) - { - Assert.Null(_createNotification.Value); - var asProxy = connection as CachingConnectionFactory.ChannelCachingConnectionProxy; - RC.IConnection conDelegate = asProxy.TargetConnection.Connection; - _createNotification.Value = conDelegate; - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - } - } - - private sealed class TestWithConnectionListenerListener : IConnectionListener - { - private readonly AtomicReference _created; - private readonly AtomicReference _closed; - private readonly AtomicInteger _timesClosed; - - public TestWithConnectionListenerListener(AtomicReference created, AtomicReference closed, AtomicInteger timesClosed) - { - _created = created; - _closed = closed; - _timesClosed = timesClosed; - } - - public void OnClose(IConnection connection) - { - _closed.Value = connection; - _timesClosed.GetAndIncrement(); - } - - public void OnCreate(IConnection connection) - { - _created.Value = connection; - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - } - } - - private sealed class TestWithChannelListenerListener : IChannelListener - { - private readonly AtomicInteger _atomicInteger; - - public TestWithChannelListenerListener(AtomicInteger atomicInteger) - { - _atomicInteger = atomicInteger; - } - - public void OnCreate(RC.IModel channel, bool transactional) - { - _atomicInteger.IncrementAndGet(); - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - throw new NotImplementedException(); - } - } - - private sealed class TestCheckoutsWithRefreshedConnectionGutsListener : IConnectionListener - { - public void OnClose(IConnection connection) - { - } - - public void OnCreate(IConnection connection) - { - connection.CreateChannel().Close(); - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/ConnectionFactoryLifecycleTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/ConnectionFactoryLifecycleTest.cs deleted file mode 100644 index 5bd1ac72d4..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/ConnectionFactoryLifecycleTest.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using RabbitMQ.Client.Events; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Xunit; -using static Steeltoe.Messaging.RabbitMQ.Connection.CachingConnectionFactory; -using IConnection = RabbitMQ.Client.IConnection; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -[Trait("Category", "Integration")] -public sealed class ConnectionFactoryLifecycleTest : AbstractTest -{ - [Fact] - public async Task TestConnectionFactoryAvailableDuringStop() - { - ServiceCollection services = CreateContainer(); - services.AddRabbitConnectionFactory((_, f) => f.Host = "localhost"); - services.AddRabbitAdmin(); - services.AddSingleton(); - services.AddSingleton(p => p.GetService()); - - MyLifecycle myLifecycle; - CachingConnectionFactory cf; - - await using (ServiceProvider provider = services.BuildServiceProvider(true)) - { - var hostService = provider.GetRequiredService(); - await hostService.StartAsync(default); - myLifecycle = provider.GetService(); - cf = provider.GetService() as CachingConnectionFactory; - } - - Assert.NotNull(myLifecycle); - Assert.False(myLifecycle.IsRunning); - Assert.NotNull(cf); - Assert.True(cf.Stopped); - Assert.Throws(() => cf.CreateConnection()); - } - - [Fact] - public async Task TestBlockedConnection() - { - ServiceCollection services = CreateContainer(); - - services.AddRabbitConnectionFactory((_, f) => - { - f.Host = "localhost"; - f.ServiceName = "TestBlockedConnection"; - }); - - services.AddRabbitAdmin(); - await using ServiceProvider provider = services.BuildServiceProvider(true); - - var hostService = provider.GetRequiredService(); - await hostService.StartAsync(default); - var blockedConnectionLatch = new CountdownEvent(1); - var unblockedConnectionLatch = new CountdownEvent(1); - var cf = provider.GetService() as CachingConnectionFactory; - var connection = cf.CreateConnection() as ChannelCachingConnectionProxy; - var listener = new TestBlockedListener(blockedConnectionLatch, unblockedConnectionLatch); - connection.AddBlockedListener(listener); - IConnection amqConnection = connection.Target.Connection; - amqConnection.HandleConnectionBlocked("Test connection blocked"); - Assert.True(blockedConnectionLatch.Wait(TimeSpan.FromSeconds(10))); - amqConnection.HandleConnectionUnblocked(); - Assert.True(unblockedConnectionLatch.Wait(TimeSpan.FromSeconds(10))); - } - - public sealed class TestBlockedListener : IBlockedListener - { - private readonly CountdownEvent _blockedConnectionLatch; - private readonly CountdownEvent _unblockedConnectionLatch; - - public TestBlockedListener(CountdownEvent blockedConnectionLatch, CountdownEvent unblockedConnectionLatch) - { - _blockedConnectionLatch = blockedConnectionLatch; - _unblockedConnectionLatch = unblockedConnectionLatch; - } - - public void HandleBlocked(object sender, ConnectionBlockedEventArgs args) - { - _blockedConnectionLatch.Signal(); - } - - public void HandleUnblocked(object sender, EventArgs args) - { - _unblockedConnectionLatch.Signal(); - } - } - - public sealed class MyLifecycle : ISmartLifecycle - { - private readonly RabbitAdmin _admin; - private readonly IQueue _queue = new AnonymousQueue(); - private volatile bool _running; - - public bool IsRunning => _running; - - public int Phase => 0; - - public bool IsAutoStartup => true; - - public MyLifecycle(IConnectionFactory cf) - { - _admin = new RabbitAdmin(cf); - } - - public Task StartAsync() - { - _running = true; - _admin.DeclareQueue(_queue); - return Task.CompletedTask; - } - - public Task StopAsync() - { - // Prior to the fix for AMQP-546, this threw an exception and - // running was not reset. - _admin.DeleteQueue(_queue.QueueName); - _running = false; - return Task.CompletedTask; - } - - public async Task StopAsync(Action callback) - { - await StopAsync(); - callback(); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/ConnectionFactoryUtilsTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/ConnectionFactoryUtilsTest.cs deleted file mode 100644 index d3501dcae5..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/ConnectionFactoryUtilsTest.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Common.Transaction; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -public sealed class ConnectionFactoryUtilsTest -{ - [Fact] - public void TestResourceHolder() - { - var h1 = new RabbitResourceHolder(); - var h2 = new RabbitResourceHolder(); - var connectionFactory = new Mock(); - TransactionSynchronizationManager.SetActualTransactionActive(true); - ConnectionFactoryUtils.BindResourceToTransaction(h1, connectionFactory.Object, true); - Assert.Same(h1, ConnectionFactoryUtils.BindResourceToTransaction(h2, connectionFactory.Object, true)); - TransactionSynchronizationManager.Clear(); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/PublisherCallbackChannelTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/PublisherCallbackChannelTest.cs deleted file mode 100644 index 4b7400b867..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/PublisherCallbackChannelTest.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -public sealed class PublisherCallbackChannelTest -{ - [Fact] - public void ShutdownWhileCreate() - { - var mockChannel = new Mock(); - var npe = new AtomicBoolean(); - - mockChannel.SetupAdd(m => m.ModelShutdown += It.IsAny>()).Callback>(handler => - { - try - { - handler.Invoke(null, new RC.ShutdownEventArgs(RC.ShutdownInitiator.Peer, RabbitUtils.NotFound, string.Empty)); - } - catch (NullReferenceException) - { - npe.Value = true; - } - }); - - var channel = new PublisherCallbackChannel(mockChannel.Object); - Assert.False(npe.Value); - channel.Close(); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/RabbitReconnectProblemTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/RabbitReconnectProblemTest.cs deleted file mode 100644 index c03e34fd8b..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/RabbitReconnectProblemTest.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Xunit; -using Xunit.Abstractions; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -public sealed class RabbitReconnectProblemTest -{ - private readonly ITestOutputHelper _output; - - public RabbitReconnectProblemTest(ITestOutputHelper output) - { - _output = output; - } - - [Fact(Skip = "Requires manual intervention")] - public async Task SurviveAReconnect() - { - var myQueue = new Queue("my-queue"); - - var cf = new RC.ConnectionFactory - { - Uri = new Uri("amqp://localhost") - }; - - var ccf = new CachingConnectionFactory(cf) - { - ChannelCacheSize = 2, - ChannelCheckoutTimeout = 2000 - }; - - var admin = new RabbitAdmin(ccf); - admin.DeclareQueue(myQueue); - var template = new RabbitTemplate(ccf); - CheckIt(template, 0, myQueue.ActualName); - - int i = 1; - - while (i < 45) - { - // While in this loop, stop and start the broker - // The CCF should reconnect and the receives in - // CheckIt should stop throwing exceptions - // The available permits should always be == 2. - await Task.Delay(2000); - CheckIt(template, i++, myQueue.ActualName); - using Dictionary.ValueCollection.Enumerator values = ccf.CheckoutPermits.Values.GetEnumerator(); - values.MoveNext(); - int availablePermits = values.Current.CurrentCount; - _output.WriteLine("Permits after test: " + availablePermits); - Assert.Equal(2, availablePermits); - } - } - - private void CheckIt(RabbitTemplate template, int counter, string name) - { - try - { - _output.WriteLine("#" + counter); - template.Receive(name); - _output.WriteLine("Ok"); - } - catch (Exception e) - { - _output.WriteLine("Failed: " + e.Message); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/RoutingConnectionFactoryTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/RoutingConnectionFactoryTest.cs deleted file mode 100644 index a8923e7c7f..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/RoutingConnectionFactoryTest.cs +++ /dev/null @@ -1,353 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -public sealed class RoutingConnectionFactoryTest -{ - [Fact] - public void TestAbstractRoutingConnectionFactory() - { - var connectionFactory1 = new Mock(); - var connectionFactory2 = new Mock(); - - var factories = new Dictionary - { - { true, connectionFactory1.Object }, - { false, connectionFactory2.Object } - }; - - var defaultConnectionFactory = new Mock(); - - var lookupFlag = new AtomicBoolean(true); - var count = new AtomicInteger(); - - var connectionFactory = new TestAbstractRoutingConnectionFactoryFactory(lookupFlag, count) - { - DefaultTargetConnectionFactory = defaultConnectionFactory.Object - }; - - connectionFactory.SetTargetConnectionFactories(factories); - - for (int i = 0; i < 5; i++) - { - connectionFactory.CreateConnection(); - } - - connectionFactory1.Verify(f => f.CreateConnection(), Times.Exactly(2)); - connectionFactory2.Verify(f => f.CreateConnection()); - defaultConnectionFactory.Verify(f => f.CreateConnection(), Times.Exactly(2)); - } - - [Fact] - public async Task TestSimpleRoutingConnectionFactory() - { - var connectionFactory1 = new Mock(); - var connectionFactory2 = new Mock(); - - var factories = new Dictionary - { - { "foo", connectionFactory1.Object }, - { "bar", connectionFactory2.Object } - }; - - var connectionFactory = new SimpleRoutingConnectionFactory(); - connectionFactory.SetTargetConnectionFactories(factories); - var tasks = new List(); - - for (int i = 0; i < 3; i++) - { - int count = i; - - Task task = Task.Run(() => - { - SimpleResourceHolder.Bind(connectionFactory, count % 2 == 0 ? "foo" : "bar"); - connectionFactory.CreateConnection(); - SimpleResourceHolder.Unbind(connectionFactory); - }); - - tasks.Add(task); - } - - await Task.WhenAll(tasks).WaitAsync(TimeSpan.FromSeconds(10)); - - connectionFactory1.Verify(f => f.CreateConnection(), Times.Exactly(2)); - connectionFactory2.Verify(f => f.CreateConnection()); - } - - [Fact] - public void TestGetAddAndRemoveOperationsForTargetConnectionFactories() - { - var targetConnectionFactory = new Mock(); - var routingFactory = new TestNullReturningFactory(); - Assert.Null(routingFactory.GetTargetConnectionFactory("1")); - routingFactory.AddTargetConnectionFactory("1", targetConnectionFactory.Object); - Assert.Equal(targetConnectionFactory.Object, routingFactory.GetTargetConnectionFactory("1")); - Assert.Null(routingFactory.GetTargetConnectionFactory("2")); - IConnectionFactory removedConnectionFactory = routingFactory.RemoveTargetConnectionFactory("1"); - Assert.Equal(targetConnectionFactory.Object, removedConnectionFactory); - Assert.Null(routingFactory.GetTargetConnectionFactory("1")); - } - - [Fact] - public void TestAddTargetConnectionFactoryAddsExistingConnectionListenersToConnectionFactory() - { - var routingFactory = new TestNullReturningFactory(); - routingFactory.AddConnectionListener(new Mock().Object); - routingFactory.AddConnectionListener(new Mock().Object); - var targetConnectionFactory = new Mock(); - routingFactory.AddTargetConnectionFactory("1", targetConnectionFactory.Object); - targetConnectionFactory.Verify(f => f.AddConnectionListener(It.IsAny()), Times.Exactly(2)); - } - - [Fact] - public async Task TestAbstractRoutingConnectionFactoryWithListenerContainer() - { - var connectionFactory1 = new Mock(); - var connectionFactory2 = new Mock(); - var defaultConnectionFactory = new Mock(); - var connection1 = new Mock(); - var connection2 = new Mock(); - var defaultConnection = new Mock(); - var channel1 = new Mock(); - var channel2 = new Mock(); - var defaultChannel = new Mock(); - - connectionFactory1.SetupSequence(f => f.CreateConnection()).Returns(connection1.Object); - - connectionFactory2.SetupSequence(f => f.CreateConnection()).Returns(connection1.Object).Returns(connection2.Object); - - defaultConnectionFactory.SetupSequence(f => f.CreateConnection()).Returns(defaultConnection.Object); - - connection1.Setup(c => c.IsOpen).Returns(true); - connection1.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel1.Object); - - connection2.Setup(c => c.IsOpen).Returns(true); - connection2.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel2.Object); - - defaultConnection.Setup(c => c.IsOpen).Returns(true); - defaultConnection.Setup(c => c.CreateChannel(It.IsAny())).Returns(defaultChannel.Object); - - channel1.Setup(c => c.IsOpen).Returns(true); - channel2.Setup(c => c.IsOpen).Returns(true); - defaultChannel.Setup(c => c.IsOpen).Returns(true); - - var factories = new Dictionary - { - { "[baz]", connectionFactory1.Object }, - { "[foo,bar]", connectionFactory2.Object } - }; - - var connectionFactory = new SimpleRoutingConnectionFactory - { - LenientFallback = true, - DefaultTargetConnectionFactory = defaultConnectionFactory.Object - }; - - connectionFactory.SetTargetConnectionFactories(factories); - - var container = new DirectMessageListenerContainer(null, connectionFactory); - container.SetQueueNames("foo", "bar"); - container.Initialize(); - await container.StartAsync(); - - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - - connectionFactory1.Verify(f => f.CreateConnection(), Times.Never); - connectionFactory2.Verify(f => f.CreateConnection(), Times.Exactly(2)); - defaultConnectionFactory.Verify(f => f.CreateConnection(), Times.Once); // Checks connection - - connectionFactory1.Invocations.Clear(); - connectionFactory2.Invocations.Clear(); - defaultConnectionFactory.Invocations.Clear(); - - container.SetQueueNames("baz"); - connectionFactory1.Verify(f => f.CreateConnection()); - connectionFactory2.Verify(f => f.CreateConnection(), Times.Never); - defaultConnectionFactory.Verify(f => f.CreateConnection(), Times.Never); - - connectionFactory1.Invocations.Clear(); - connectionFactory2.Invocations.Clear(); - defaultConnectionFactory.Invocations.Clear(); - - container.SetQueueNames("qux"); - connectionFactory1.Verify(f => f.CreateConnection(), Times.Never); - connectionFactory2.Verify(f => f.CreateConnection(), Times.Never); - defaultConnectionFactory.Verify(f => f.CreateConnection()); - - await container.StopAsync(); - } - - [Fact] - public async Task TestWithDmlcAndConnectionListener() - { - var connectionFactory1 = new Mock(); - var connection1 = new Mock(); - var channel1 = new Mock(); - - var factories = new Dictionary - { - { "xxx[foo]", connectionFactory1.Object } - }; - - var connectionFactory = new SimpleRoutingConnectionFactory(); - - connection1.Setup(c => c.IsOpen).Returns(true); - connection1.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel1.Object); - - channel1.Setup(c => c.IsOpen).Returns(true); - - var connectionMakerKey1 = new AtomicReference(); - var latch = new CountdownEvent(1); - - connectionFactory1.Setup(f => f.CreateConnection()).Returns(connection1.Object).Callback(() => - { - connectionMakerKey1.Value = connectionFactory.DetermineCurrentLookupKey(); - latch.Signal(); - }); - - connectionFactory.SetTargetConnectionFactories(factories); - var connectionMakerKey2 = new AtomicReference(); - - var container = new TestDirectMessageListenerContainer(connectionFactory, connectionMakerKey2); - container.SetQueueNames("foo"); - container.LookupKeyQualifier = "xxx"; - container.ShutdownTimeout = 10; - container.Initialize(); - await container.StartAsync(); - - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); // Container started - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - - await container.StopAsync(); - Assert.Equal("xxx[foo]", connectionMakerKey1.Value); - Assert.Equal("xxx[foo]", connectionMakerKey2.Value); - } - - [Fact] - public async Task TestWithDrtDmlcAndConnectionListenerExistingRfk() - { - var connectionFactory1 = new Mock(); - var connection1 = new Mock(); - var channel1 = new Mock(); - - var factories = new Dictionary - { - { "xxx[foo]", connectionFactory1.Object }, - { "xxx[amq.rabbitmq.reply-to]", connectionFactory1.Object } - }; - - var connectionFactory = new SimpleRoutingConnectionFactory(); - SimpleResourceHolder.Bind(connectionFactory, "foo"); - - connection1.Setup(c => c.IsOpen).Returns(true); - connection1.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel1.Object); - - channel1.Setup(c => c.IsOpen).Returns(true); - - var connectionMakerKey1 = new AtomicReference(); - var latch = new CountdownEvent(1); - - connectionFactory1.Setup(f => f.CreateConnection()).Returns(connection1.Object).Callback(() => - { - connectionMakerKey1.Value = connectionFactory.DetermineCurrentLookupKey(); - latch.Signal(); - }); - - connectionFactory.SetTargetConnectionFactories(factories); - var connectionMakerKey2 = new AtomicReference(); - - var container = new TestDirectReplyToMessageListenerContainer(connectionFactory, connectionMakerKey2) - { - LookupKeyQualifier = "xxx", - ShutdownTimeout = 10 - }; - - container.Initialize(); - await container.StartAsync(); - - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); // Container started - - DirectReplyToMessageListenerContainer.ChannelHolder channelHolder = container.GetChannelHolder(); - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - container.ReleaseConsumerFor(channelHolder, true, "test"); - await container.StopAsync(); - - Assert.Equal("xxx[amq.rabbitmq.reply-to]", connectionMakerKey1.Value); - Assert.Equal("xxx[amq.rabbitmq.reply-to]", connectionMakerKey2.Value); - Assert.Equal("foo", SimpleResourceHolder.Unbind(connectionFactory)); - } - - private sealed class TestNullReturningFactory : AbstractRoutingConnectionFactory - { - public override string ServiceName { get; set; } = "TestNullReturningFactory"; - - public override object DetermineCurrentLookupKey() - { - return null; - } - } - - private sealed class TestDirectReplyToMessageListenerContainer : DirectReplyToMessageListenerContainer - { - private readonly AtomicReference _connectionMakerKey; - private readonly SimpleRoutingConnectionFactory _simpleFactory; - - public TestDirectReplyToMessageListenerContainer(SimpleRoutingConnectionFactory connectionFactory, AtomicReference connectionMakerKey) - : base(null, connectionFactory) - { - _connectionMakerKey = connectionMakerKey; - _simpleFactory = connectionFactory; - } - - protected override void RedeclareElementsIfNecessary() - { - _connectionMakerKey.Value = _simpleFactory.DetermineCurrentLookupKey(); - } - } - - private sealed class TestDirectMessageListenerContainer : DirectMessageListenerContainer - { - private readonly AtomicReference _connectionMakerKey2; - private readonly SimpleRoutingConnectionFactory _simpleFactory; - - public TestDirectMessageListenerContainer(SimpleRoutingConnectionFactory connectionFactory, AtomicReference connectionMakerKey2) - : base(null, connectionFactory) - { - _connectionMakerKey2 = connectionMakerKey2; - _simpleFactory = connectionFactory; - } - - protected override void RedeclareElementsIfNecessary() - { - _connectionMakerKey2.Value = _simpleFactory.DetermineCurrentLookupKey(); - } - } - - private sealed class TestAbstractRoutingConnectionFactoryFactory : AbstractRoutingConnectionFactory - { - private readonly AtomicBoolean _lookupFlag; - private readonly AtomicInteger _count; - - public override string ServiceName { get; set; } = "TestAbstractRoutingConnectionFactoryFactory"; - - public TestAbstractRoutingConnectionFactoryFactory(AtomicBoolean lookupFlag, AtomicInteger count) - { - _lookupFlag = lookupFlag; - _count = count; - } - - public override object DetermineCurrentLookupKey() - { - return _count.IncrementAndGet() > 3 ? null : (bool?)_lookupFlag.GetAndSet(!_lookupFlag.Value); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/SSLConnectionTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/SSLConnectionTest.cs deleted file mode 100644 index 713e97dd45..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/SSLConnectionTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -public sealed class SslConnectionTest -{ -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/SingleConnectionFactory.cs b/src/Messaging/test/RabbitMQ.Test/Connection/SingleConnectionFactory.cs deleted file mode 100644 index 17b7287bfe..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/SingleConnectionFactory.cs +++ /dev/null @@ -1,241 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -public class SingleConnectionFactory : AbstractConnectionFactory -{ - public const string DefaultServiceName = "scFactory"; - - private readonly object _connectionMonitor = new(); - - public SharedConnectionProxy Connection { get; private set; } - - public SingleConnectionFactory(ILoggerFactory loggerFactory) - : this((string)null, loggerFactory) - { - } - - public SingleConnectionFactory(int port, ILoggerFactory loggerFactory) - : this(null, port, loggerFactory) - { - } - - public SingleConnectionFactory(string hostname, ILoggerFactory loggerFactory) - : this(hostname, RabbitOptions.DefaultPort, loggerFactory) - { - } - - public SingleConnectionFactory(string hostname, int port, ILoggerFactory loggerFactory) - : base(new RC.ConnectionFactory(), loggerFactory) - { - if (string.IsNullOrEmpty(hostname)) - { - hostname = GetDefaultHostName(); - } - - Host = hostname; - Port = port; - ServiceName = DefaultServiceName; - } - - public SingleConnectionFactory(Uri uri, ILoggerFactory loggerFactory) - : base(new RC.ConnectionFactory(), loggerFactory) - { - Uri = uri; - ServiceName = DefaultServiceName; - } - - public SingleConnectionFactory(RC.IConnectionFactory rabbitConnectionFactory, ILoggerFactory loggerFactory) - : base(rabbitConnectionFactory, loggerFactory) - { - ServiceName = DefaultServiceName; - } - - public override void SetConnectionListeners(List listeners) - { - base.SetConnectionListeners(listeners); - - // If the connection is already alive we assume that the new listeners want to be notified - if (Connection != null) - { - ConnectionListener.OnCreate(Connection); - } - } - - public override void AddConnectionListener(IConnectionListener connectionListener) - { - base.AddConnectionListener(connectionListener); - - // If the connection is already alive we assume that the new listener wants to be notified - if (Connection != null) - { - connectionListener.OnCreate(Connection); - } - } - - public override IConnection CreateConnection() - { - lock (_connectionMonitor) - { - if (Connection == null) - { - IConnection target = DoCreateConnection(); - Connection = new SharedConnectionProxy(this, target); - - // invoke the listener *after* this.connection is assigned - ConnectionListener.OnCreate(target); - } - } - - return Connection; - } - - public override void Destroy() - { - lock (_connectionMonitor) - { - if (Connection != null) - { - Connection.Destroy(); - Connection = null; - } - } - } - - public override string ToString() - { - return $"SingleConnectionFactory [host={Host}, port={Port}]"; - } - - protected IConnection DoCreateConnection() - { - IConnection connection = CreateBareConnection(); - return connection; - } - - public sealed class SharedConnectionProxy : IConnectionProxy - { - private readonly ILogger _logger; - private readonly SingleConnectionFactory _factory; - private readonly object _lock = new(); - - public IConnection Target { get; set; } - - public bool IsOpen => Target != null && Target.IsOpen; - - public IConnection TargetConnection => Target; - - public int LocalPort - { - get - { - IConnection target = Target; - - if (target != null) - { - return target.LocalPort; - } - - return 0; - } - } - - public RC.IConnection Connection - { - get - { - var asSimple = Target as SimpleConnection; - return asSimple.Connection; - } - } - - internal SharedConnectionProxy(SingleConnectionFactory factory, IConnection target, ILogger logger = null) - { - _logger = logger; - _factory = factory; - Target = target; - } - - public RC.IModel CreateChannel(bool transactional = false) - { - if (!IsOpen) - { - lock (_lock) - { - if (!IsOpen) - { - _logger?.LogDebug("Detected closed connection. Opening a new one before creating Channel."); - Target = _factory.CreateBareConnection(); - _factory.ConnectionListener.OnCreate(Target); - } - } - } - - RC.IModel channel = Target.CreateChannel(transactional); - _factory.ChannelListener.OnCreate(channel, transactional); - return channel; - } - - public void AddBlockedListener(IBlockedListener listener) - { - Target.AddBlockedListener(listener); - } - - public bool RemoveBlockedListener(IBlockedListener listener) - { - return Target.RemoveBlockedListener(listener); - } - - public void Close() - { - } - - public void Destroy() - { - if (Target != null) - { - _factory.ConnectionListener.OnClose(Target); - RabbitUtils.CloseConnection(Target); - } - - Target = null; - } - - public override int GetHashCode() - { - return Target?.GetHashCode() ?? 0; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not SharedConnectionProxy other || GetType() != obj.GetType()) - { - return false; - } - - return Equals(Target, other.Target); - } - - public override string ToString() - { - return $"Shared Rabbit Connection: {Target}"; - } - - public void Dispose() - { - Destroy(); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Connection/SingleConnectionFactoryTest.cs b/src/Messaging/test/RabbitMQ.Test/Connection/SingleConnectionFactoryTest.cs deleted file mode 100644 index cf57031374..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Connection/SingleConnectionFactoryTest.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Connection; - -public sealed class SingleConnectionFactoryTest : AbstractConnectionFactoryTest -{ - [Fact] - public void TestWithChannelListener() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - - var called = new AtomicInteger(0); - AbstractConnectionFactory connectionFactory = CreateConnectionFactory(mockConnectionFactory.Object); - var listener = new TestListener(called); - - connectionFactory.SetChannelListeners(new List - { - listener - }); - - IConnection con = connectionFactory.CreateConnection(); - RC.IModel channel = con.CreateChannel(); - Assert.Equal(1, called.Value); - - channel.Close(); - con.Close(); - - mockConnection.Verify(c => c.Close(), Times.Never); - - con = connectionFactory.CreateConnection(); - con.CreateChannel(); - Assert.Equal(2, called.Value); - - connectionFactory.Destroy(); - mockConnection.Verify(c => c.Close(It.IsAny()), Times.AtLeastOnce); - mockConnectionFactory.Verify(c => c.CreateConnection(It.IsAny())); - } - - protected override AbstractConnectionFactory CreateConnectionFactory(RC.IConnectionFactory connectionFactory) - { - var scf = new SingleConnectionFactory(connectionFactory, null); - return scf; - } - - private sealed class TestListener : IChannelListener - { - private readonly AtomicInteger _called; - - public TestListener(AtomicInteger called) - { - _called = called; - } - - public void OnCreate(RC.IModel channel, bool transactional) - { - _called.IncrementAndGet(); - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/AddressTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/AddressTest.cs deleted file mode 100644 index 3723cff634..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/AddressTest.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -public sealed class AddressTest -{ - [Fact] - public void ToStringCheck() - { - var address = new Address("my-exchange", "routing-key"); - const string replyToUri = "my-exchange/routing-key"; - Assert.Equal(replyToUri, address.ToString()); - } - - [Fact] - public void Parse() - { - const string replyToUri = "direct://my-exchange/routing-key"; - var address = new Address(replyToUri); - Assert.Equal("my-exchange", address.ExchangeName); - Assert.Equal("routing-key", address.RoutingKey); - } - - [Fact] - public void ParseUnstructuredWithRoutingKeyOnly() - { - var address = new Address("my-routing-key"); - Assert.Equal("my-routing-key", address.RoutingKey); - Assert.Equal("/my-routing-key", address.ToString()); - - address = new Address("/foo"); - Assert.Equal("foo", address.RoutingKey); - Assert.Equal("/foo", address.ToString()); - - address = new Address("bar/baz"); - Assert.Equal("bar", address.ExchangeName); - Assert.Equal("baz", address.RoutingKey); - Assert.Equal("bar/baz", address.ToString()); - } - - [Fact] - public void ParseWithoutRoutingKey() - { - var address = new Address("fanout://my-exchange"); - Assert.Equal("my-exchange", address.ExchangeName); - Assert.Equal(string.Empty, address.RoutingKey); - Assert.Equal("my-exchange/", address.ToString()); - } - - [Fact] - public void ParseWithDefaultExchangeAndRoutingKey() - { - var address = new Address("direct:///routing-key"); - Assert.Equal(string.Empty, address.ExchangeName); - Assert.Equal("routing-key", address.RoutingKey); - Assert.Equal("/routing-key", address.ToString()); - } - - [Fact] - public void TestEmpty() - { - var address = new Address("/"); - Assert.Equal(string.Empty, address.ExchangeName); - Assert.Equal(string.Empty, address.RoutingKey); - Assert.Equal("/", address.ToString()); - } - - [Fact] - public void TestDirectReplyTo() - { - const string replyTo = $"{Address.AmqRabbitMQReplyTo}.ab/cd/ef"; - var headers = new MessageHeaders(); - RabbitHeaderAccessor props = RabbitHeaderAccessor.GetMutableAccessor(headers); - props.ReplyTo = replyTo; - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props.MessageHeaders); - Address address = message.Headers.ReplyToAddress(); - Assert.Equal(string.Empty, address.ExchangeName); - Assert.Equal(replyTo, address.RoutingKey); - address = props.ReplyToAddress; - Assert.Equal(string.Empty, address.ExchangeName); - Assert.Equal(replyTo, address.RoutingKey); - } - - [Fact] - public void TestEquals() - { - Assert.Equal(new Address("foo/bar"), new Address("foo/bar")); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/BatchingRabbitTemplateTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/BatchingRabbitTemplateTest.cs deleted file mode 100644 index 2f5517c81c..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/BatchingRabbitTemplateTest.cs +++ /dev/null @@ -1,784 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Globalization; -using System.IO.Compression; -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; -using Xunit; -using Xunit.Abstractions; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -[Trait("Category", "Integration")] -public sealed class BatchingRabbitTemplateTest : IDisposable -{ - public const string Route = "test.queue.BatchingRabbitTemplateTests"; - private readonly CachingConnectionFactory _connectionFactory; - private readonly ITestOutputHelper _testOutputHelper; - - public BatchingRabbitTemplateTest(ITestOutputHelper testOutputHelper) - { - _connectionFactory = new CachingConnectionFactory("localhost"); - var admin = new RabbitAdmin(_connectionFactory); - admin.DeclareQueue(new Queue(Route)); - _testOutputHelper = testOutputHelper; - } - - [Fact] - public void TestSimpleBatch() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar")); - template.Send(string.Empty, Route, message); - IMessage received = Receive(template); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])received.Payload)); - } - - [Fact] - public void TestSimpleBatchTimeout() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 50); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - template.Send(string.Empty, Route, message); - IMessage received = Receive(template); - Assert.Equal("foo", Encoding.UTF8.GetString((byte[])received.Payload)); - } - - [Fact] - public void TestSimpleBatchTimeoutMultiple() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 50); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - template.Send(string.Empty, Route, message); - template.Send(string.Empty, Route, message); - IMessage received = Receive(template); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003foo", Encoding.UTF8.GetString((byte[])received.Payload)); - } - - [Fact] - public void TestSimpleBatchBufferLimit() - { - var batchingStrategy = new SimpleBatchingStrategy(2, 8, 50); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar")); - template.Send(string.Empty, Route, message); - IMessage received = Receive(template); - Assert.Equal("foo", Encoding.UTF8.GetString((byte[])received.Payload)); - received = Receive(template); - Assert.Equal("bar", Encoding.UTF8.GetString((byte[])received.Payload)); - } - - [Fact] - public void TestSimpleBatchBufferLimitMultiple() - { - var batchingStrategy = new SimpleBatchingStrategy(2, 15, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - template.Send(string.Empty, Route, message); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar")); - template.Send(string.Empty, Route, message); - template.Send(string.Empty, Route, message); - IMessage received = Receive(template); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003foo", Encoding.UTF8.GetString((byte[])received.Payload)); - received = Receive(template); - Assert.Equal("\u0000\u0000\u0000\u0003bar\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])received.Payload)); - } - - [Fact] - public void TestSimpleBatchBiggerThanBufferLimit() - { - var batchingStrategy = new SimpleBatchingStrategy(2, 2, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar")); - template.Send(string.Empty, Route, message); - IMessage received = Receive(template); - Assert.Equal("foo", Encoding.UTF8.GetString((byte[])received.Payload)); - received = Receive(template); - Assert.Equal("bar", Encoding.UTF8.GetString((byte[])received.Payload)); - } - - [Fact] - public void TestSimpleBatchBiggerThanBufferLimitMultiple() - { - var batchingStrategy = new SimpleBatchingStrategy(2, 6, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("f")); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar")); - template.Send(string.Empty, Route, message); - IMessage received = Receive(template); - Assert.Equal("f", Encoding.UTF8.GetString((byte[])received.Payload)); - received = Receive(template); - Assert.Equal("bar", Encoding.UTF8.GetString((byte[])received.Payload)); - } - - [Fact] - public void TestSimpleBatchTwoEqualBufferLimit() - { - var batchingStrategy = new SimpleBatchingStrategy(10, 14, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar")); - template.Send(string.Empty, Route, message); - IMessage received = Receive(template); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])received.Payload)); - } - - [Fact] - public async Task TestDebatchByContainer() - { - ServiceProvider provider = new ServiceCollection().BuildServiceProvider(true); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - var received = new List(); - var latch = new CountdownEvent(2); - var context = new GenericApplicationContext(provider, configurationRoot); - context.ServiceExpressionResolver = new StandardServiceExpressionResolver(); - var container = new DirectMessageListenerContainer(context, _connectionFactory); - container.SetQueueNames(Route); - var lastInBatch = new List(); - var batchSize = new AtomicInteger(); - container.MessageListener = new TestDebatchListener(received, lastInBatch, batchSize, latch); - container.Initialize(); - await container.StartAsync(); - - try - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar")); - template.Send(string.Empty, Route, message); - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - Assert.Equal(2, received.Count); - Assert.Equal("foo", Encoding.UTF8.GetString((byte[])received[0].Payload)); - Assert.Equal(3, received[0].Headers.ContentLength()); - Assert.False(lastInBatch[0]); - - Assert.Equal("bar", Encoding.UTF8.GetString((byte[])received[1].Payload)); - Assert.Equal(3, received[1].Headers.ContentLength()); - Assert.True(lastInBatch[1]); - Assert.Equal(2, batchSize.Value); - } - finally - { - await container.StopAsync(); - container.Dispose(); - } - } - - [Fact] - public async Task TestDebatchByContainerPerformance() - { - ServiceProvider provider = new ServiceCollection().BuildServiceProvider(true); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - var received = new List(); - const int count = 10_000; - var latch = new CountdownEvent(count); - var context = new GenericApplicationContext(provider, configurationRoot); - context.ServiceExpressionResolver = new StandardServiceExpressionResolver(); - var container = new DirectMessageListenerContainer(context, _connectionFactory); - container.SetQueueNames(Route); - container.MessageListener = new TestDebatchListener(received, null, null, latch); - container.PrefetchCount = 1000; - container.BatchingStrategy = new SimpleBatchingStrategy(1000, int.MaxValue, 30000); - container.Initialize(); - await container.StartAsync(); - - try - { - var batchingStrategy = new SimpleBatchingStrategy(1000, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(new MessageHeaders()); - accessor.DeliveryMode = MessageDeliveryMode.NonPersistent; - IMessage message = Message.Create(new byte[256], accessor.MessageHeaders); - var watch = new Stopwatch(); - watch.Start(); - - for (int i = 0; i < count; i++) - { - template.Send(string.Empty, Route, message); - } - - Assert.True(latch.Wait(TimeSpan.FromSeconds(60))); - watch.Stop(); - _testOutputHelper.WriteLine(watch.ElapsedMilliseconds.ToString(CultureInfo.InvariantCulture)); - Assert.Equal(count, received.Count); - } - finally - { - await container.StopAsync(); - container.Dispose(); - } - } - - [Fact] - public async Task TestDebatchByContainerBadMessageRejected() - { - ServiceProvider provider = new ServiceCollection().BuildServiceProvider(true); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - var context = new GenericApplicationContext(provider, configurationRoot); - context.ServiceExpressionResolver = new StandardServiceExpressionResolver(); - var container = new DirectMessageListenerContainer(context, _connectionFactory); - container.SetQueueNames(Route); - var listener = new EmptyListener(); - container.MessageListener = listener; - var errorHandler = new TestConditionalRejectingErrorHandler(); - container.ErrorHandler = errorHandler; - container.Initialize(); - await container.StartAsync(); - - try - { - var template = new RabbitTemplate - { - ConnectionFactory = _connectionFactory - }; - - var headers = new MessageHeaders(new Dictionary - { - { RabbitMessageHeaders.SpringBatchFormat, RabbitMessageHeaders.BatchFormatLengthHeader4 } - }); - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("\u0000\u0000\u0000\u0004foo"), headers); - template.Send(string.Empty, Route, message); - await Task.Delay(1000); - Assert.Equal(0, listener.Count); - Assert.True(errorHandler.HandleErrorCalled); - } - finally - { - await container.StopAsync(); - container.Dispose(); - } - } - - [Fact] - public void TestSimpleBatchGZipped() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var gZipPostProcessor = new GZipPostProcessor(); - Assert.Equal(CompressionLevel.Fastest, gZipPostProcessor.Level); - template.SetBeforePublishPostProcessors(gZipPostProcessor); - var props = new MessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Equal("gzip", result.Headers.ContentEncoding()); - var unzipper = new GUnzipPostProcessor(); - IMessage unzip = unzipper.PostProcessMessage(result); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])unzip.Payload)); - } - - [Fact] - public void TestSimpleBatchGZippedUsingAdd() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var gZipPostProcessor = new GZipPostProcessor(); - Assert.Equal(CompressionLevel.Fastest, gZipPostProcessor.Level); - template.AddBeforePublishPostProcessors(gZipPostProcessor); - var props = new MessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Equal("gzip", result.Headers.ContentEncoding()); - var unzipper = new GUnzipPostProcessor(); - IMessage unzip = unzipper.PostProcessMessage(result); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])unzip.Payload)); - } - - [Fact] - public void TestSimpleBatchGZippedUsingAddAndRemove() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var gZipPostProcessor = new GZipPostProcessor(); - Assert.Equal(CompressionLevel.Fastest, gZipPostProcessor.Level); - template.AddBeforePublishPostProcessors(gZipPostProcessor); - var headerPostProcessor = new HeaderPostProcessor(); - template.AddBeforePublishPostProcessors(headerPostProcessor); - template.RemoveBeforePublishPostProcessor(headerPostProcessor); - var props = new MessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Equal("gzip", result.Headers.ContentEncoding()); - var unzipper = new GUnzipPostProcessor(); - IMessage unzip = unzipper.PostProcessMessage(result); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])unzip.Payload)); - Assert.Null(unzip.Headers.Get("someHeader")); - } - - [Fact] - public void TestSimpleBatchGZippedConfiguredUnzipper() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var gZipPostProcessor = new GZipPostProcessor - { - Level = CompressionLevel.Optimal - }; - - Assert.Equal(CompressionLevel.Optimal, gZipPostProcessor.Level); - template.SetBeforePublishPostProcessors(gZipPostProcessor); - template.SetAfterReceivePostProcessors(new GUnzipPostProcessor()); - var props = new MessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Null(result.Headers.ContentEncoding()); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])result.Payload)); - } - - [Fact] - public void TestSimpleBatchGZippedConfiguredUnzipperUsingAdd() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var gZipPostProcessor = new GZipPostProcessor - { - Level = CompressionLevel.Optimal - }; - - Assert.Equal(CompressionLevel.Optimal, gZipPostProcessor.Level); - template.AddBeforePublishPostProcessors(gZipPostProcessor); - template.AddAfterReceivePostProcessors(new GUnzipPostProcessor()); - var props = new MessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Null(result.Headers.ContentEncoding()); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])result.Payload)); - } - - [Fact] - public void TestSimpleBatchGZippedWithEncoding() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var gZipPostProcessor = new GZipPostProcessor(); - template.SetBeforePublishPostProcessors(gZipPostProcessor); - - var accessor = new RabbitHeaderAccessor(new MessageHeaders()) - { - ContentEncoding = "foo" - }; - - IMessageHeaders props = accessor.ToMessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Equal("gzip:foo", result.Headers.ContentEncoding()); - var unzipper = new GUnzipPostProcessor(); - IMessage unzip = unzipper.PostProcessMessage(result); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])unzip.Payload)); - } - - [Fact] - public async Task TestSimpleBatchGZippedWithEncodingInflated() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var gZipPostProcessor = new GZipPostProcessor(); - template.SetBeforePublishPostProcessors(gZipPostProcessor); - template.SetAfterReceivePostProcessors(new DelegatingDecompressingPostProcessor()); - - var accessor = new RabbitHeaderAccessor(new MessageHeaders()) - { - ContentEncoding = "foo" - }; - - IMessageHeaders props = accessor.ToMessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - await Task.Delay(100); - byte[] output = template.ReceiveAndConvert(Route); - Assert.NotNull(output); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString(output)); - } - - [Fact] - public void TestSimpleBatchZippedBestCompression() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var zipPostProcessor = new ZipPostProcessor - { - Level = CompressionLevel.Optimal - }; - - template.SetBeforePublishPostProcessors(zipPostProcessor); - var accessor = new RabbitHeaderAccessor(new MessageHeaders()); - IMessageHeaders props = accessor.ToMessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Equal("zip", result.Headers.ContentEncoding()); - var unzipper = new UnzipPostProcessor(); - IMessage unzip = unzipper.PostProcessMessage(result); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])unzip.Payload)); - } - - [Fact] - public void TestSimpleBatchZippedWithEncoding() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var zipPostProcessor = new ZipPostProcessor - { - Level = CompressionLevel.Optimal - }; - - template.SetBeforePublishPostProcessors(zipPostProcessor); - - var accessor = new RabbitHeaderAccessor(new MessageHeaders()) - { - ContentEncoding = "foo" - }; - - IMessageHeaders props = accessor.ToMessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Equal("zip:foo", result.Headers.ContentEncoding()); - var unzipper = new UnzipPostProcessor(); - IMessage unzip = unzipper.PostProcessMessage(result); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])unzip.Payload)); - } - - [Fact] - public void TestSimpleBatchDeflater() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var deflaterPostProcessor = new DeflaterPostProcessor - { - Level = CompressionLevel.Optimal - }; - - template.SetBeforePublishPostProcessors(deflaterPostProcessor); - var accessor = new RabbitHeaderAccessor(new MessageHeaders()); - IMessageHeaders props = accessor.ToMessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Equal("deflate", result.Headers.ContentEncoding()); - var unzipper = new InflaterPostProcessor(); - IMessage unzip = unzipper.PostProcessMessage(result); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])unzip.Payload)); - } - - [Fact] - public void TestSimpleBatchDeflaterFastestCompression() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var deflaterPostProcessor = new DeflaterPostProcessor - { - Level = CompressionLevel.Fastest - }; - - template.SetBeforePublishPostProcessors(deflaterPostProcessor); - var accessor = new RabbitHeaderAccessor(new MessageHeaders()); - IMessageHeaders props = accessor.ToMessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Equal("deflate", result.Headers.ContentEncoding()); - var unzipper = new InflaterPostProcessor(); - IMessage unzip = unzipper.PostProcessMessage(result); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])unzip.Payload)); - } - - [Fact] - public void TestSimpleBatchDeflaterWithEncoding() - { - var batchingStrategy = new SimpleBatchingStrategy(2, int.MaxValue, 30000); - - var template = new BatchingRabbitTemplate(batchingStrategy) - { - ConnectionFactory = _connectionFactory - }; - - var deflaterPostProcessor = new DeflaterPostProcessor - { - Level = CompressionLevel.Fastest - }; - - template.SetBeforePublishPostProcessors(deflaterPostProcessor); - - var accessor = new RabbitHeaderAccessor(new MessageHeaders()) - { - ContentEncoding = "foo" - }; - - IMessageHeaders props = accessor.ToMessageHeaders(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props); - template.Send(string.Empty, Route, message); - message = Message.Create(Encoding.UTF8.GetBytes("bar"), props); - template.Send(string.Empty, Route, message); - IMessage result = Receive(template); - Assert.Equal("deflate:foo", result.Headers.ContentEncoding()); - var unzipper = new InflaterPostProcessor(); - IMessage unzip = unzipper.PostProcessMessage(result); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", Encoding.UTF8.GetString((byte[])unzip.Payload)); - } - - public void Dispose() - { - var admin = new RabbitAdmin(_connectionFactory); - admin.DeleteQueue(Route); - _connectionFactory.Dispose(); - } - - private IMessage Receive(BatchingRabbitTemplate template) - { - IMessage message = template.Receive(Route); - int n = 0; - - while (n++ < 200 && message == null) - { - Thread.Sleep(50); - message = template.Receive(Route); - } - - Assert.NotNull(message); - return message; - } - - private sealed class HeaderPostProcessor : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return PostProcessMessage(message); - } - - public IMessage PostProcessMessage(IMessage message) - { - RabbitHeaderAccessor headers = RabbitHeaderAccessor.GetMutableAccessor(message); - headers.SetHeader("someHeader", "someValue"); - return message; - } - } - - private sealed class TestConditionalRejectingErrorHandler : ConditionalRejectingErrorHandler - { - public bool HandleErrorCalled { get; private set; } - - public override bool HandleError(Exception exception) - { - HandleErrorCalled = true; - return base.HandleError(exception); - } - } - - private sealed class EmptyListener : IMessageListener - { - public int Count { get; private set; } - - public AcknowledgeMode ContainerAckMode { get; set; } - - public void OnMessage(IMessage message) - { - Count++; - } - - public void OnMessageBatch(List messages) - { - } - } - - private sealed class TestDebatchListener : IMessageListener - { - public List Received { get; } - public List LastInBatch { get; } - public AtomicInteger BatchSize { get; } - public CountdownEvent Latch { get; } - - public AcknowledgeMode ContainerAckMode { get; set; } - - public TestDebatchListener(List received, List lastInBatch, AtomicInteger batchSize, CountdownEvent latch) - { - Received = received; - LastInBatch = lastInBatch; - BatchSize = batchSize; - Latch = latch; - } - - public void OnMessage(IMessage message) - { - Received.Add(message); - - if (LastInBatch != null) - { - LastInBatch.Add(message.Headers.LastInBatch().HasValue && message.Headers.LastInBatch().Value); - } - - if (BatchSize != null) - { - BatchSize.Value = message.Headers.Get(RabbitMessageHeaders.BatchSize); - } - - Latch.Signal(); - } - - public void OnMessageBatch(List messages) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/FixedReplyQueueDeadLetterTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/FixedReplyQueueDeadLetterTest.cs deleted file mode 100644 index cc4dd35434..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/FixedReplyQueueDeadLetterTest.cs +++ /dev/null @@ -1,303 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Net; -using System.Net.Http.Headers; -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -[Trait("Category", "Integration")] -public sealed class FixedReplyQueueDeadLetterTest : IClassFixture -{ - private readonly ServiceProvider _provider; - private readonly FixedReplyStartupFixture _fixture; - - public FixedReplyQueueDeadLetterTest(FixedReplyStartupFixture fix) - { - _fixture = fix; - _provider = _fixture.Provider; - } - - [Fact] - public void Test() - { - RabbitTemplate template = _provider.GetRabbitTemplate("fixedReplyQRabbitTemplate"); - var deadListener = _provider.GetService(); - Assert.Null(template.ConvertSendAndReceive("foo")); - Assert.True(deadListener.Latch.Wait(TimeSpan.FromSeconds(10))); - } - - [Fact] - public async Task TestQueueArgs1() - { - IConfiguration configuration = await GetQueueConfigurationAsync("all.args.1"); - IConfigurationSection arguments = configuration.GetSection("arguments"); - Assert.Equal(1000, arguments.GetValue("x-message-ttl")); - Assert.Equal(200000, arguments.GetValue("x-expires")); - Assert.Equal(42, arguments.GetValue("x-max-length")); - Assert.Equal("reject-publish", arguments.GetValue("x-overflow")); - Assert.Equal("reply.dlx", arguments.GetValue("x-dead-letter-exchange")); - Assert.Equal("reply.dlrk", arguments.GetValue("x-dead-letter-routing-key")); - Assert.Equal(4, arguments.GetValue("x-max-priority")); - Assert.Equal("lazy", arguments.GetValue("x-queue-mode")); - Assert.Equal("min-masters", arguments.GetValue("x-queue-master-locator")); - Assert.True(arguments.GetValue("x-single-active-consumer")); - } - - [Fact] - public async Task TestQueueArgs2() - { - IConfiguration configuration = await GetQueueConfigurationAsync("all.args.2"); - IConfigurationSection arguments = configuration.GetSection("arguments"); - Assert.Equal(1000, arguments.GetValue("x-message-ttl")); - Assert.Equal(200000, arguments.GetValue("x-expires")); - Assert.Equal(42, arguments.GetValue("x-max-length")); - Assert.Equal(10000, arguments.GetValue("x-max-length-bytes")); - Assert.Equal("drop-head", arguments.GetValue("x-overflow")); - Assert.Equal("reply.dlx", arguments.GetValue("x-dead-letter-exchange")); - Assert.Equal("reply.dlrk", arguments.GetValue("x-dead-letter-routing-key")); - Assert.Equal(4, arguments.GetValue("x-max-priority")); - Assert.Equal("lazy", arguments.GetValue("x-queue-mode")); - Assert.Equal("client-local", arguments.GetValue("x-queue-master-locator")); - } - - [Fact] - public async Task TestQueueArgs3() - { - IConfiguration configuration = await GetQueueConfigurationAsync("all.args.3"); - IConfigurationSection arguments = configuration.GetSection("arguments"); - Assert.Equal(1000, arguments.GetValue("x-message-ttl")); - Assert.Equal(200000, arguments.GetValue("x-expires")); - Assert.Equal(42, arguments.GetValue("x-max-length")); - Assert.Equal(10000, arguments.GetValue("x-max-length-bytes")); - Assert.Equal("reject-publish", arguments.GetValue("x-overflow")); - Assert.Equal("reply.dlx", arguments.GetValue("x-dead-letter-exchange")); - Assert.Equal("reply.dlrk", arguments.GetValue("x-dead-letter-routing-key")); - Assert.Equal(4, arguments.GetValue("x-max-priority")); - Assert.Equal("lazy", arguments.GetValue("x-queue-mode")); - Assert.Equal("random", arguments.GetValue("x-queue-master-locator")); - - IConfiguration exchangeConfig = await GetExchangeConfigurationAsync("dlx.test.requestEx"); - IConfigurationSection arguments2 = exchangeConfig.GetSection("arguments"); - Assert.Equal("alternate", arguments2.GetValue("alternate-exchange")); - } - - [Fact] - public async Task TestQuorumArgs() - { - IConfiguration configuration = await GetQueueConfigurationAsync("test.quorum"); - IConfigurationSection arguments = configuration.GetSection("arguments"); - Assert.Equal(10, arguments.GetValue("x-delivery-limit")); - Assert.Equal("quorum", arguments.GetValue("x-queue-type")); - } - - private async Task GetQueueConfigurationAsync(string queueName) - { - var client = new HttpClient(); - byte[] authToken = Encoding.ASCII.GetBytes("guest:guest"); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)); - - HttpResponseMessage result = await client.GetAsync(new Uri($"http://localhost:15672/api/queues/%3F/{queueName}")); - int n = 0; - - while (n++ < 100 && result.StatusCode == HttpStatusCode.NotFound) - { - await Task.Delay(100); - result = await client.GetAsync(new Uri($"http://localhost:15672/api/queues/%2F/{queueName}")); - } - - Assert.True(n < 100); - Assert.Equal(HttpStatusCode.OK, result.StatusCode); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddJsonStream(await result.Content.ReadAsStreamAsync()).Build(); - return configurationRoot; - } - - private async Task GetExchangeConfigurationAsync(string exchangeName) - { - var client = new HttpClient(); - byte[] authToken = Encoding.ASCII.GetBytes("guest:guest"); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)); - - HttpResponseMessage result = await client.GetAsync(new Uri($"http://localhost:15672/api/exchanges/%2F/{exchangeName}")); - - Assert.Equal(HttpStatusCode.OK, result.StatusCode); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddJsonStream(await result.Content.ReadAsStreamAsync()).Build(); - return configurationRoot; - } - - public sealed class FixedReplyStartupFixture : IDisposable - { - private readonly IServiceCollection _services; - - public ServiceProvider Provider { get; set; } - - public FixedReplyStartupFixture() - { - _services = CreateContainer(); - Provider = _services.BuildServiceProvider(true); - Provider.GetRequiredService().StartAsync(default).GetAwaiter().GetResult(); - } - - private ServiceCollection CreateContainer(IConfiguration configuration = null) - { - var services = new ServiceCollection(); - configuration ??= new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configuration); - services.AddRabbitHostingServices(); - services.AddRabbitDefaultMessageConverter(); - - services.AddRabbitConnectionFactory(); - - IQueue requestQueue = QueueBuilder.NonDurable("dlx.test.requestQ").AutoDelete().Build(); - services.AddRabbitQueue(requestQueue); - - IQueue replyQueue = QueueBuilder.NonDurable("dlx.test.replyQ").AutoDelete().WithArgument("x-dead-letter-exchange", "reply.dlx").Build(); - services.AddRabbitQueue(replyQueue); - - IQueue dlq = QueueBuilder.NonDurable("dlx.test.DLQ").AutoDelete().Build(); - services.AddRabbitQueue(dlq); - - var ex = ExchangeBuilder.DirectExchange("dlx.test.requestEx").Durable(false).AutoDelete().Alternate("alternate").Build() as DirectExchange; - services.AddRabbitExchange(ex); - - var dlx = new DirectExchange("reply.dlx", false, true); - services.AddRabbitExchange(dlx); - - IQueue allArgs1 = QueueBuilder.NonDurable("all.args.1").Ttl(1000).Expires(200000).MaxLength(42).MaxLengthBytes(10000) - .Overflow(QueueBuilder.OverFlow.RejectPublish).DeadLetterExchange("reply.dlx").DeadLetterRoutingKey("reply.dlrk").MaxPriority(4).Lazy() - .Masterlocator(QueueBuilder.MasterLocator.MinMasters).SingleActiveConsumer().Build(); - - services.AddRabbitQueue(allArgs1); - - IQueue allArgs2 = QueueBuilder.NonDurable("all.args.2").Ttl(1000).Expires(200000).MaxLength(42).MaxLengthBytes(10000) - .Overflow(QueueBuilder.OverFlow.DropHead).DeadLetterExchange("reply.dlx").DeadLetterRoutingKey("reply.dlrk").MaxPriority(4).Lazy() - .Masterlocator(QueueBuilder.MasterLocator.ClientLocal).Build(); - - services.AddRabbitQueue(allArgs2); - - IQueue allArgs3 = QueueBuilder.NonDurable("all.args.3").Ttl(1000).Expires(200000).MaxLength(42).MaxLengthBytes(10000) - .Overflow(QueueBuilder.OverFlow.RejectPublish).DeadLetterExchange("reply.dlx").DeadLetterRoutingKey("reply.dlrk").MaxPriority(4).Lazy() - .Masterlocator(QueueBuilder.MasterLocator.Random).Build(); - - services.AddRabbitQueue(allArgs3); - - IQueue quorum = QueueBuilder.Durable("test.quorum").Quorum().DeliveryLimit(10).Build(); - services.AddRabbitQueue(quorum); - - IBinding dlBinding = BindingBuilder.Bind(dlq).To(dlx).With(replyQueue.QueueName); - services.AddRabbitBinding(dlBinding); - - IBinding binding = BindingBuilder.Bind(requestQueue).To(ex).With("dlx.reply.test"); - services.AddRabbitBinding(binding); - - // Add a container "replyListenerContainer" - services.AddSingleton(p => - { - IApplicationContext context = p.GetApplicationContext(); - var factory = p.GetService(); - var logFactory = p.GetService(); - IQueue queue = p.GetRabbitQueue(replyQueue.QueueName); - RabbitTemplate template = p.GetRabbitTemplate("fixedReplyQRabbitTemplate"); - var container = new DirectMessageListenerContainer(context, factory, "replyListenerContainer", logFactory); - container.SetQueues(queue); - container.MessageListener = template; - return container; - }); - - // Add a container "serviceListenerContainer" - services.AddSingleton(p => - { - IApplicationContext context = p.GetApplicationContext(); - var factory = p.GetService(); - var logFactory = p.GetService(); - IQueue queue = p.GetRabbitQueue(requestQueue.QueueName); - var container = new DirectMessageListenerContainer(context, factory, "serviceListenerContainer", logFactory); - container.SetQueues(queue); - var pojoListener = p.GetService(); - container.MessageListener = new MessageListenerAdapter(context, pojoListener, p.GetService>()); - return container; - }); - - // Add a container "dlListenerContainer" - services.AddSingleton(p => - { - IApplicationContext context = p.GetApplicationContext(); - var factory = p.GetService(); - var logFactory = p.GetService(); - IQueue q = p.GetRabbitQueue(dlq.QueueName); - var container = new DirectMessageListenerContainer(context, factory, "dlListenerContainer", logFactory); - container.SetQueues(q); - var deadListener = p.GetService(); - container.MessageListener = new MessageListenerAdapter(context, deadListener, p.GetService>()); - return container; - }); - - services.AddRabbitAdmin(); - - // Add RabbitTemplate named fixedReplyQRabbitTemplate - services.AddRabbitTemplate((_, t) => - { - t.DefaultSendDestination = new RabbitDestination(ex.ExchangeName, "dlx.reply.test"); - t.ReplyAddress = replyQueue.QueueName; - t.ReplyTimeout = 1; - t.ServiceName = "fixedReplyQRabbitTemplate"; - }); - - services.AddSingleton(); - services.AddSingleton(); - - return services; - } - - public void Dispose() - { - RabbitAdmin admin = Provider.GetRabbitAdmin(); - admin.DeleteQueue("all.args.1"); - admin.DeleteQueue("all.args.2"); - admin.DeleteQueue("all.args.3"); - admin.DeleteQueue("test.quorum"); - Provider.Dispose(); - } - } - - public sealed class PojoListener - { - public string HandleMessage(string foo) - { - Thread.Sleep(500); - return foo.ToUpperInvariant(); - } - } - - public sealed class DeadListener - { - public CountdownEvent Latch { get; set; } = new(1); - - public void HandleMessage(string foo) - { - Latch.Signal(); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/MessageHeaderTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/MessageHeaderTest.cs deleted file mode 100644 index 3ce240008e..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/MessageHeaderTest.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -public sealed class MessageHeaderTest -{ - [Fact] - public void TestReplyTo() - { - var properties = new RabbitHeaderAccessor - { - ReplyTo = "foo/bar" - }; - - Assert.Equal("bar", properties.ReplyToAddress.RoutingKey); - } - - [Fact] - public void TestReplyToNullByDefault() - { - var properties = new RabbitHeaderAccessor(); - Assert.Null(properties.ReplyTo); - Assert.Null(properties.ReplyToAddress); - } - - [Fact] - public void TestDelayHeader() - { - var properties = new RabbitHeaderAccessor(); - const int delay = 100; - properties.Delay = delay; - IMessageHeaders headers = properties.ToMessageHeaders(); - Assert.Equal(delay, headers.Get(RabbitHeaderAccessor.XDelay)); - properties.Delay = null; - headers = properties.ToMessageHeaders(); - Assert.False(headers.ContainsKey(RabbitHeaderAccessor.XDelay)); - } - - [Fact] - public void TestContentLengthSet() - { - var properties = new RabbitHeaderAccessor - { - ContentLength = 1L - }; - - Assert.True(properties.IsContentLengthSet); - } - - [Fact] - public void TesNoNullPointerInEquals() - { - var mp = new RabbitHeaderAccessor - { - LeaveMutable = true - }; - - var mp2 = new RabbitHeaderAccessor - { - LeaveMutable = true - }; - - Assert.True(mp.MessageHeaders.Equals(mp2.MessageHeaders)); - } - - [Fact] - public void TesNoNullPointerInHashCode() - { - var messageList = new HashSet - { - new() - }; - - Assert.Single(messageList); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/MessageTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/MessageTest.cs deleted file mode 100644 index f7a00717ed..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/MessageTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -public sealed class MessageTest -{ - [Fact] - public void ToStringForEmptyMessageBody() - { - IMessage message = Message.Create(Array.Empty(), new MessageHeaders()); - Assert.NotNull(message.ToString()); - } - - [Fact(Skip = "Standard ContentType header missing")] - public void ProperEncoding() - { - IMessage message = Message.Create(EncodingUtils.Utf16.GetBytes("ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"), new MessageHeaders()); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MessageHeaders.ContentTypeJson; - accessor.ContentEncoding = "UTF-16"; - Assert.Contains("ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP", message.ToString(), StringComparison.Ordinal); - } - - [Fact] - public void ToStringForNullMessageProperties() - { - IMessage message = Message.Create(Array.Empty(), null); - Assert.NotNull(message.ToString()); - } - - [Fact] - public void ToStringForNonStringMessageBody() - { - IMessage message = Message.Create(default(DateTime), null); - Assert.NotNull(message.ToString()); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitAdminDeclarationTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitAdminDeclarationTest.cs deleted file mode 100644 index a4b0426956..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitAdminDeclarationTest.cs +++ /dev/null @@ -1,598 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -public sealed class RabbitAdminDeclarationTest : IClassFixture -{ - private readonly RabbitAdminDeclarationTestStartupFixture _fixture; - - public RabbitAdminDeclarationTest(RabbitAdminDeclarationTestStartupFixture fix) - { - _fixture = fix; - } - - [Fact] - public void TestUnconditional() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - - var cf = new Mock(); - var conn = new Mock(); - var channel = new Mock(); - cf.Setup(f => f.CreateConnection()).Returns(conn.Object); - cf.SetupGet(f => f.ServiceName).Returns(CachingConnectionFactory.DefaultServiceName); - conn.Setup(c => c.CreateChannel(false)).Returns(channel.Object); - conn.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - - channel.Setup(c => c.QueueDeclare("foo", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(() => new RC.QueueDeclareOk("foo", 0, 0)); - - var listener = new AtomicReference(); - cf.Setup(f => f.AddConnectionListener(It.IsAny())).Callback(l => listener.Value = l); - var queue = new Queue("foo"); - services.AddRabbitQueue(queue); - var exchange = new DirectExchange("bar"); - services.AddRabbitExchange(exchange); - var binding = new Binding("baz", "foo", Binding.DestinationType.Queue, "bar", "foo", null); - services.AddRabbitBinding(binding); - ServiceProvider provider = services.BuildServiceProvider(true); - IApplicationContext context = provider.GetApplicationContext(); - _ = new RabbitAdmin(context, cf.Object); - Assert.NotNull(listener.Value); - listener.Value.OnCreate(conn.Object); - channel.Verify(c => c.QueueDeclare("foo", true, false, false, It.IsAny>())); - channel.Verify(c => c.ExchangeDeclare("bar", "direct", true, false, It.IsAny>())); - channel.Verify(c => c.QueueBind("foo", "bar", "foo", It.IsAny>())); - } - - [Fact] - public void TestNoDeclareWithCachedConnections() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - - var mockConnectionFactory = new Mock(); - - var mockConnections = new List(); - var mockChannels = new List(); - var connectionNumber = new AtomicInteger(-1); - var channelNumber = new AtomicInteger(-1); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Callback(() => - { - var connection = new Mock(); - int connectionNum = connectionNumber.IncrementAndGet(); - mockConnections.Add(connection.Object); - connection.Setup(c => c.IsOpen).Returns(true); - connection.Setup(c => c.ToString()).Returns($"mockConnection{connectionNum}"); - - connection.Setup(c => c.CreateModel()).Callback(() => - { - var channel = new Mock(); - mockChannels.Add(channel.Object); - channel.Setup(c => c.IsOpen).Returns(true); - int channelNum = channelNumber.IncrementAndGet(); - channel.Setup(c => c.ToString()).Returns($"mockChannel{channelNum}"); - }).Returns(() => mockChannels[channelNumber.Value]); - }).Returns(() => mockConnections[connectionNumber.Value]); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object, false, CachingConnectionFactory.CachingMode.Connection); - var queue = new Queue("foo"); - services.AddRabbitQueue(queue); - ServiceProvider provider = services.BuildServiceProvider(true); - IApplicationContext context = provider.GetApplicationContext(); - _ = new RabbitAdmin(context, ccf); - ccf.CreateConnection().Close(); - ccf.Destroy(); - Assert.Empty(mockChannels); - } - - [Fact] - public void TestUnconditionalWithExplicitFactory() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - - var cf = new Mock(); - var conn = new Mock(); - var channel = new Mock(); - cf.Setup(f => f.CreateConnection()).Returns(conn.Object); - cf.SetupGet(f => f.ServiceName).Returns(CachingConnectionFactory.DefaultServiceName); - conn.Setup(c => c.CreateChannel(false)).Returns(channel.Object); - conn.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - - channel.Setup(c => c.QueueDeclare("foo", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(() => new RC.QueueDeclareOk("foo", 0, 0)); - - var listener = new AtomicReference(); - cf.Setup(f => f.AddConnectionListener(It.IsAny())).Callback(l => listener.Value = l); - var queue = new Queue("foo"); - services.AddRabbitQueue(queue); - var exchange = new DirectExchange("bar"); - services.AddRabbitExchange(exchange); - var binding = new Binding("baz", "foo", Binding.DestinationType.Queue, "bar", "foo", null); - services.AddRabbitBinding(binding); - ServiceProvider provider = services.BuildServiceProvider(true); - IApplicationContext context = provider.GetApplicationContext(); - var admin = new RabbitAdmin(context, cf.Object); - - queue.SetAdminsThatShouldDeclare(admin); - exchange.SetAdminsThatShouldDeclare(admin); - binding.SetAdminsThatShouldDeclare(admin); - - Assert.NotNull(listener.Value); - listener.Value.OnCreate(conn.Object); - channel.Verify(c => c.QueueDeclare("foo", true, false, false, It.IsAny>())); - channel.Verify(c => c.ExchangeDeclare("bar", "direct", true, false, It.IsAny>())); - channel.Verify(c => c.QueueBind("foo", "bar", "foo", It.IsAny>())); - } - - [Fact] - public void TestSkipBecauseDifferentFactory() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - - var cf = new Mock(); - var conn = new Mock(); - var channel = new Mock(); - cf.Setup(f => f.CreateConnection()).Returns(conn.Object); - cf.SetupGet(f => f.ServiceName).Returns(CachingConnectionFactory.DefaultServiceName); - conn.Setup(c => c.CreateChannel(false)).Returns(channel.Object); - conn.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - - channel.Setup(c => c.QueueDeclare("foo", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(() => new RC.QueueDeclareOk("foo", 0, 0)); - - var listener = new AtomicReference(); - cf.Setup(f => f.AddConnectionListener(It.IsAny())).Callback(l => listener.Value = l); - - var queue = new Queue("foo"); - services.AddRabbitQueue(queue); - var exchange = new DirectExchange("bar"); - services.AddRabbitExchange(exchange); - var binding = new Binding("baz", "foo", Binding.DestinationType.Queue, "bar", "foo", null); - services.AddRabbitBinding(binding); - ServiceProvider provider = services.BuildServiceProvider(true); - IApplicationContext context = provider.GetApplicationContext(); - - _ = new RabbitAdmin(context, cf.Object); - var other = new RabbitAdmin(cf.Object); - - queue.SetAdminsThatShouldDeclare(other); - exchange.SetAdminsThatShouldDeclare(other); - binding.SetAdminsThatShouldDeclare(other); - - Assert.NotNull(listener.Value); - listener.Value.OnCreate(conn.Object); - channel.Verify(c => c.QueueDeclare("foo", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()), Times.Never); - channel.Verify(c => c.ExchangeDeclare("bar", "direct", It.IsAny(), It.IsAny(), It.IsAny>()), Times.Never); - channel.Verify(c => c.QueueBind("foo", "bar", "foo", It.IsAny>()), Times.Never); - } - - [Fact] - public void TestSkipBecauseShouldNotDeclare() - { - var services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configurationRoot); - services.AddRabbitHostingServices(); - - var cf = new Mock(); - var conn = new Mock(); - var channel = new Mock(); - cf.Setup(f => f.CreateConnection()).Returns(conn.Object); - cf.SetupGet(f => f.ServiceName).Returns(CachingConnectionFactory.DefaultServiceName); - conn.Setup(c => c.CreateChannel(false)).Returns(channel.Object); - conn.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - - channel.Setup(c => c.QueueDeclare("foo", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(() => new RC.QueueDeclareOk("foo", 0, 0)); - - var listener = new AtomicReference(); - cf.Setup(f => f.AddConnectionListener(It.IsAny())).Callback(l => listener.Value = l); - - var queue = new Queue("foo") - { - ShouldDeclare = false - }; - - services.AddRabbitQueue(queue); - - var exchange = new DirectExchange("bar") - { - ShouldDeclare = false - }; - - services.AddRabbitExchange(exchange); - - var binding = new Binding("baz", "foo", Binding.DestinationType.Queue, "bar", "foo", null) - { - ShouldDeclare = false - }; - - services.AddRabbitBinding(binding); - ServiceProvider provider = services.BuildServiceProvider(true); - IApplicationContext context = provider.GetApplicationContext(); - - _ = new RabbitAdmin(context, cf.Object); - - Assert.NotNull(listener.Value); - listener.Value.OnCreate(conn.Object); - channel.Verify(c => c.QueueDeclare("foo", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()), Times.Never); - channel.Verify(c => c.ExchangeDeclare("bar", "direct", It.IsAny(), It.IsAny(), It.IsAny>()), Times.Never); - channel.Verify(c => c.QueueBind("foo", "bar", "foo", It.IsAny>()), Times.Never); - } - - [Fact] - public void TestContainerConfig() - { - _fixture.Listener1.Value.OnCreate(_fixture.Conn1.Object); - _fixture.Channel1.Verify(c => c.QueueDeclare("foo", true, false, false, It.IsAny>())); - _fixture.Channel1.Verify(c => c.QueueDeclare("baz", true, false, false, It.IsAny>()), Times.Never); - _fixture.Channel1.Verify(c => c.QueueDeclare("qux", true, false, false, It.IsAny>())); - _fixture.Channel1.Verify(c => c.ExchangeDeclare("bar", "direct", true, false, It.IsAny>())); - _fixture.Channel1.Verify(c => c.QueueBind("foo", "bar", "foo", It.IsAny>())); - - _fixture.Listener2.Value.OnCreate(_fixture.Conn2.Object); - - _fixture.Channel2.Verify(c => c.QueueDeclare("foo", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()), - Times.Never); - - _fixture.Channel2.Verify(c => c.QueueDeclare("baz", true, false, false, It.IsAny>()), Times.Never); - _fixture.Channel2.Verify(c => c.QueueDeclare("qux", true, false, false, It.IsAny>())); - - _fixture.Channel2.Verify(c => c.ExchangeDeclare("bar", "direct", It.IsAny(), It.IsAny(), It.IsAny>()), - Times.Never); - - _fixture.Channel2.Verify(c => c.QueueBind("foo", "bar", "foo", It.IsAny>()), Times.Never); - - _fixture.Listener3.Value.OnCreate(_fixture.Conn3.Object); - - _fixture.Channel3.Verify(c => c.QueueDeclare("foo", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()), - Times.Never); - - var args = new Dictionary - { - { "added.by.customizer.1", true }, - { "added.by.customizer.2", true } - }; - - _fixture.Channel3.Verify(c => c.QueueDeclare("baz", true, false, false, args)); - _fixture.Channel3.Verify(c => c.QueueDeclare("qux", true, false, false, It.IsAny>()), Times.Never); - - _fixture.Channel3.Verify(c => c.ExchangeDeclare("bar", "direct", It.IsAny(), It.IsAny(), It.IsAny>()), - Times.Never); - - _fixture.Channel3.Verify(c => c.QueueBind("foo", "bar", "foo", It.IsAny>()), Times.Never); - } - - [Fact] - public void TestAddRemove() - { - var queue = new Queue("foo"); - var cf = new Mock(); - var admin1 = new RabbitAdmin(cf.Object); - var admin2 = new RabbitAdmin(cf.Object); - queue.SetAdminsThatShouldDeclare(admin1, admin2); - Assert.Equal(2, queue.DeclaringAdmins.Count); - queue.SetAdminsThatShouldDeclare(admin1); - Assert.Single(queue.DeclaringAdmins); - - queue.SetAdminsThatShouldDeclare(null); - - Assert.Empty(queue.DeclaringAdmins); - queue.SetAdminsThatShouldDeclare(admin1, admin2); - Assert.Equal(2, queue.DeclaringAdmins.Count); - queue.SetAdminsThatShouldDeclare(); - Assert.Empty(queue.DeclaringAdmins); - queue.SetAdminsThatShouldDeclare(admin1, admin2); - Assert.Equal(2, queue.DeclaringAdmins.Count); - queue.SetAdminsThatShouldDeclare(null); - Assert.Empty(queue.DeclaringAdmins); - queue.SetAdminsThatShouldDeclare(admin1, admin2); - Assert.Equal(2, queue.DeclaringAdmins.Count); - queue.SetAdminsThatShouldDeclare(null); - Assert.Empty(queue.DeclaringAdmins); - Assert.Throws(() => queue.SetAdminsThatShouldDeclare(null, admin1)); - } - - public sealed class RabbitAdminDeclarationTestStartupFixture : IDisposable - { - private readonly IServiceCollection _services; - - public ServiceProvider Provider { get; set; } - - public Mock Conn1 { get; set; } - - public Mock Conn2 { get; set; } - - public Mock Conn3 { get; set; } - - public Mock Channel1 { get; set; } - - public Mock Channel2 { get; set; } - - public Mock Channel3 { get; set; } - - public AtomicReference Listener1 { get; set; } = new(); - - public AtomicReference Listener2 { get; set; } = new(); - - public AtomicReference Listener3 { get; set; } = new(); - - public RabbitAdminDeclarationTestStartupFixture() - { - _services = CreateContainer(); - Provider = _services.BuildServiceProvider(true); - Provider.GetRequiredService().StartAsync(default).GetAwaiter().GetResult(); - } - - private ServiceCollection CreateContainer(IConfiguration configuration = null) - { - var services = new ServiceCollection(); - configuration ??= new ConfigurationBuilder().Build(); - - services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - services.AddSingleton(configuration); - services.AddRabbitHostingServices(); - - // ConnectionFactory cf1 - services.AddSingleton(_ => - { - var mockConnectionFactory = new Mock(); - Conn1 = new Mock(); - Channel1 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(Conn1.Object); - mockConnectionFactory.SetupGet(f => f.ServiceName).Returns("cf1"); - Conn1.Setup(c => c.CreateChannel(false)).Returns(Channel1.Object); - Conn1.Setup(c => c.IsOpen).Returns(true); - Channel1.Setup(c => c.IsOpen).Returns(true); - var queueName = new AtomicReference(); - - Channel1 - .Setup(c => c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>())) - .Callback>((a1, _, _, _, _) => queueName.Value = a1) - .Returns(() => new RC.QueueDeclareOk(queueName.Value, 0, 0)); - - mockConnectionFactory.Setup(f => f.AddConnectionListener(It.IsAny())) - .Callback(l => Listener1.Value = l); - - return mockConnectionFactory.Object; - }); - - // ConnectionFactory cf2 - services.AddSingleton(_ => - { - var mockConnectionFactory = new Mock(); - Conn2 = new Mock(); - Channel2 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(Conn2.Object); - mockConnectionFactory.SetupGet(f => f.ServiceName).Returns("cf2"); - Conn2.Setup(c => c.CreateChannel(false)).Returns(Channel2.Object); - Conn2.Setup(c => c.IsOpen).Returns(true); - Channel2.Setup(c => c.IsOpen).Returns(true); - var queueName = new AtomicReference(); - - Channel2 - .Setup(c => c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>())) - .Callback>((a1, _, _, _, _) => queueName.Value = a1) - .Returns(() => new RC.QueueDeclareOk(queueName.Value, 0, 0)); - - mockConnectionFactory.Setup(f => f.AddConnectionListener(It.IsAny())) - .Callback(l => Listener2.Value = l); - - return mockConnectionFactory.Object; - }); - - // ConnectionFactory cf3 - services.AddSingleton(_ => - { - var mockConnectionFactory = new Mock(); - Conn3 = new Mock(); - Channel3 = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(Conn3.Object); - mockConnectionFactory.SetupGet(f => f.ServiceName).Returns("cf3"); - Conn3.Setup(c => c.CreateChannel(false)).Returns(Channel3.Object); - Conn3.Setup(c => c.IsOpen).Returns(true); - Channel3.Setup(c => c.IsOpen).Returns(true); - var queueName = new AtomicReference(); - - Channel3 - .Setup(c => c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>())) - .Callback>((a1, _, _, _, _) => queueName.Value = a1) - .Returns(() => new RC.QueueDeclareOk(queueName.Value, 0, 0)); - - mockConnectionFactory.Setup(f => f.AddConnectionListener(It.IsAny())) - .Callback(l => Listener3.Value = l); - - return mockConnectionFactory.Object; - }); - - services.AddSingleton(p => - { - IApplicationContext context = p.GetApplicationContext(); - var cf1 = context.GetService("cf1"); - - var admin = new RabbitAdmin(context, cf1, p.GetService>()) - { - ServiceName = "admin1" - }; - - return admin; - }); - - services.AddSingleton(p => - { - IApplicationContext context = p.GetApplicationContext(); - return context.GetService("admin1"); - }); - - services.AddSingleton(p => - { - IApplicationContext context = p.GetApplicationContext(); - var cf2 = context.GetService("cf2"); - - var admin = new RabbitAdmin(context, cf2, p.GetService>()) - { - ServiceName = "admin2" - }; - - return admin; - }); - - services.AddSingleton(p => - { - IApplicationContext context = p.GetApplicationContext(); - return context.GetService("admin2"); - }); - - services.AddSingleton(p => - { - IApplicationContext context = p.GetApplicationContext(); - var cf3 = context.GetService("cf3"); - - var admin = new RabbitAdmin(context, cf3, p.GetService>()) - { - ExplicitDeclarationsOnly = true, - ServiceName = "admin3" - }; - - return admin; - }); - - services.AddSingleton(p => - { - IApplicationContext context = p.GetApplicationContext(); - return context.GetService("admin3"); - }); - - var queueFoo = new Queue("foo"); - queueFoo.SetAdminsThatShouldDeclare("admin1"); - services.AddRabbitQueue(queueFoo); - - var queueBaz = new Queue("baz"); - queueBaz.SetAdminsThatShouldDeclare("admin3"); - services.AddRabbitQueue(queueBaz); - - var queueQux = new Queue("qux"); - services.AddRabbitQueue(queueQux); - - var exchange = new DirectExchange("bar"); - exchange.SetAdminsThatShouldDeclare("admin1"); - exchange.IsInternal = true; - services.AddRabbitExchange(exchange); - - var binding = new Binding("foo.binding", "foo", Binding.DestinationType.Queue, "bar", "foo", null); - binding.SetAdminsThatShouldDeclare("admin1"); - services.AddRabbitBinding(binding); - services.AddSingleton(); - services.AddSingleton(); - - return services; - } - - public void Dispose() - { - Provider.Dispose(); - } - - private sealed class Customizer1 : IDeclarableCustomizer - { - public IDeclarable Apply(IDeclarable declarable) - { - if (declarable is IQueue queue && queue.QueueName == "baz") - { - queue.AddArgument("added.by.customizer.1", true); - } - - return declarable; - } - } - - private sealed class Customizer2 : IDeclarableCustomizer - { - public IDeclarable Apply(IDeclarable declarable) - { - if (declarable is IQueue queue && queue.QueueName == "baz") - { - queue.AddArgument("added.by.customizer.2", true); - } - - return declarable; - } - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitAdminIntegrationTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitAdminIntegrationTest.cs deleted file mode 100644 index 5399412e47..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitAdminIntegrationTest.cs +++ /dev/null @@ -1,427 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Net; -using System.Net.Http.Headers; -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.Support; -using Xunit; -using static Steeltoe.Messaging.RabbitMQ.Configuration.Binding; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -[Trait("Category", "Integration")] -public sealed class RabbitAdminIntegrationTest : IDisposable -{ - private readonly ServiceCollection _services; - private ServiceProvider _provider; - - public RabbitAdminIntegrationTest() - { - _services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - - _services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - _services.AddSingleton(configurationRoot); - _services.AddRabbitHostingServices(); - _services.AddRabbitConnectionFactory((_, f) => f.Host = "localhost"); - _services.AddRabbitAdmin((_, a) => a.AutoStartup = true); - } - - [Fact] - public void TestStartupWithLazyDeclaration() - { - var queue = new Queue("test.queue"); - _services.AddRabbitQueue(queue); - _provider = _services.BuildServiceProvider(true); - - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - - // A new connection is initialized so the queue is declared - Assert.True(rabbitAdmin.DeleteQueue(queue.QueueName)); - } - - [Fact] - public void TestDoubleDeclarationOfExclusiveQueue() - { - _services.AddRabbitConnectionFactory("connectionFactory1", (_, f) => - { - f.Host = "localhost"; - }); - - _services.AddRabbitConnectionFactory("connectionFactory2", (_, f) => - { - f.Host = "localhost"; - }); - - _provider = _services.BuildServiceProvider(true); - - var queue = new Queue("test.queue", false, true, true); - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - rabbitAdmin.DeleteQueue(queue.QueueName); - - IApplicationContext context = _provider.GetApplicationContext(); - var admin1 = new RabbitAdmin(context, context.GetService("connectionFactory1")); - admin1.DeclareQueue(queue); - - try - { - var admin2 = new RabbitAdmin(context, context.GetService("connectionFactory2")); - Assert.Throws(() => admin2.DeclareQueue(queue)); - } - finally - { - var cf1 = context.GetService("connectionFactory1"); - var cf2 = context.GetService("connectionFactory2"); - cf1.Destroy(); - cf2.Destroy(); - } - } - - [Fact] - public void TestQueueWithAutoDelete() - { - var queue = new Queue("test.queue", false, true, true); - _services.AddRabbitQueue(queue); - _provider = _services.BuildServiceProvider(true); - - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - rabbitAdmin.Initialize(); - Assert.True(QueueExists(queue)); - - IConnectionFactory cf = _provider.GetRabbitConnectionFactory(); - cf.Destroy(); - Assert.False(QueueExists(queue)); - - cf.CreateConnection(); - Assert.True(QueueExists(queue)); - - Assert.True(rabbitAdmin.DeleteQueue(queue.QueueName)); - Assert.False(QueueExists(queue)); - } - - [Fact] - public void TestQueueWithoutAutoDelete() - { - var queue = new Queue("test.queue", false, false, false); - _services.AddRabbitQueue(queue); - _provider = _services.BuildServiceProvider(true); - - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - rabbitAdmin.Initialize(); - Assert.True(QueueExists(queue)); - - IConnectionFactory cf = _provider.GetRabbitConnectionFactory(); - cf.Destroy(); - Assert.True(QueueExists(queue)); - - cf.CreateConnection(); - Assert.True(QueueExists(queue)); - - Assert.True(rabbitAdmin.DeleteQueue(queue.QueueName)); - Assert.False(QueueExists(queue)); - - cf.Destroy(); - } - - [Fact] - public void TestQueueWithoutName() - { - _provider = _services.BuildServiceProvider(true); - var queue = new Queue(string.Empty, true, false, true); - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - string generatedName = rabbitAdmin.DeclareQueue(queue); - - Assert.Equal(string.Empty, queue.QueueName); - var queueWithGeneratedName = new Queue(generatedName, true, false, true); - Assert.True(QueueExists(queueWithGeneratedName)); - - IConnectionFactory cf = _provider.GetRabbitConnectionFactory(); - cf.Destroy(); - Assert.True(QueueExists(queueWithGeneratedName)); - - cf.CreateConnection(); - Assert.True(QueueExists(queueWithGeneratedName)); - - Assert.True(rabbitAdmin.DeleteQueue(generatedName)); - Assert.False(QueueExists(queueWithGeneratedName)); - - cf.Destroy(); - } - - [Fact] - public void TestDeleteExchangeWithDefaultExchange() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - bool result = rabbitAdmin.DeleteExchange(string.Empty); - Assert.True(result); - } - - [Fact] - public async Task TestDeleteExchangeWithInternalOption() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - const string exchangeName = "test.exchange.internal"; - - AbstractExchange exchange = new DirectExchange(exchangeName) - { - IsInternal = true - }; - - rabbitAdmin.DeclareExchange(exchange); - IConfiguration exchange2 = await GetExchangeAsync(exchangeName); - Assert.Equal("direct", exchange2.GetValue("type")); - - rabbitAdmin.DeleteExchange(exchangeName); - } - - [Fact] - public void TestDeclareBindingWithDefaultExchangeImplicitBinding() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - var exchange = new DirectExchange(string.Empty); - const string queueName = "test.queue"; - var queue = new Queue(queueName, false, false, false); - rabbitAdmin.DeclareQueue(queue); - var binding = new Binding("mybinding", queueName, DestinationType.Queue, exchange.ExchangeName, queueName, null); - rabbitAdmin.DeclareBinding(binding); - - // Pass by virtue of RabbitMQ not firing a 403 reply code for both exchange and binding declaration - Assert.True(QueueExists(queue)); - } - - [Fact] - public void TestSpringWithDefaultExchangeImplicitBinding() - { - var exchange = new DirectExchange(string.Empty); - _services.AddRabbitExchange(exchange); - const string queueName = "test.queue"; - var queue = new Queue(queueName, false, false, false); - _services.AddRabbitQueue(queue); - var binding = new Binding("mybinding", queueName, DestinationType.Queue, exchange.ExchangeName, queueName, null); - _services.AddRabbitBinding(binding); - _provider = _services.BuildServiceProvider(true); - - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - rabbitAdmin.Initialize(); - - // Pass by virtue of RabbitMQ not firing a 403 reply code for both exchange and binding declaration - Assert.True(QueueExists(queue)); - } - - [Fact] - public void TestDeclareBindingWithDefaultExchangeNonImplicitBinding() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - - var exchange = new DirectExchange(string.Empty); - - const string queueName = "test.queue"; - var queue = new Queue(queueName, false, false, false); - rabbitAdmin.DeclareQueue(queue); - - var binding = new Binding("mybinding", queueName, DestinationType.Queue, exchange.ExchangeName, "test.routingKey", null); - var ex = Assert.Throws(() => rabbitAdmin.DeclareBinding(binding)); - Exception cause = ex; - Exception rootCause = null; - - while (cause != null) - { - rootCause = cause; - cause = cause.InnerException; - } - - Assert.Contains("code=403", rootCause.Message, StringComparison.Ordinal); - Assert.Contains("operation not permitted on the default exchange", rootCause.Message, StringComparison.Ordinal); - } - - [Fact] - public void TestSpringWithDefaultExchangeNonImplicitBinding() - { - var exchange = new DirectExchange(string.Empty); - _services.AddRabbitExchange(exchange); - const string queueName = "test.queue"; - var queue = new Queue(queueName, false, false, false); - _services.AddRabbitQueue(queue); - var binding = new Binding("baz", queueName, DestinationType.Queue, exchange.ExchangeName, "test.routingKey", null); - _services.AddRabbitBinding(binding); - _provider = _services.BuildServiceProvider(true); - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - rabbitAdmin.RetryTemplate = null; - var ex = Assert.Throws(() => rabbitAdmin.DeclareBinding(binding)); - Exception cause = ex; - Exception rootCause = null; - - while (cause != null) - { - rootCause = cause; - cause = cause.InnerException; - } - - Assert.Contains("code=403", rootCause.Message, StringComparison.Ordinal); - Assert.Contains("operation not permitted on the default exchange", rootCause.Message, StringComparison.Ordinal); - } - - [Fact] - public void TestQueueDeclareBad() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - rabbitAdmin.IgnoreDeclarationExceptions = true; - var queue = new AnonymousQueue(); - Assert.Equal(queue.QueueName, rabbitAdmin.DeclareQueue(queue)); - var queue2 = new Queue(queue.QueueName); - Assert.Null(rabbitAdmin.DeclareQueue(queue2)); - rabbitAdmin.DeleteQueue(queue2.QueueName); - } - - [Fact] - public async Task TestDeclareDelayedExchange() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin rabbitAdmin = _provider.GetRabbitAdmin(); - - var exchange = new DirectExchange("test.delayed.exchange") - { - IsDelayed = true - }; - - var queue = new Queue(Guid.NewGuid().ToString(), true, false, false); - string exchangeName = exchange.ExchangeName; - var binding = new Binding("baz", queue.QueueName, DestinationType.Queue, exchangeName, queue.QueueName, null); - - try - { - rabbitAdmin.DeclareExchange(exchange); - } - catch (RabbitIOException e) - { - if (RabbitUtils.IsExchangeDeclarationFailure(e)) - { - Exception inner = e.InnerException; - - if (inner.Message.Contains("exchange type 'x-delayed-message'", StringComparison.Ordinal)) - { - return; // Broker doesn't support? - } - } - - throw; - } - - rabbitAdmin.DeclareQueue(queue); - rabbitAdmin.DeclareBinding(binding); - IConnectionFactory cf = _provider.GetRabbitConnectionFactory(); - var pp = new TestPostProcessor(); - - var template = new RabbitTemplate(cf) - { - ReceiveTimeout = 10000 - }; - - template.ConvertAndSend(exchangeName, queue.QueueName, "foo", pp); - RabbitHeaderAccessor headers = RabbitHeaderAccessor.GetMutableAccessor(new MessageHeaders()); - headers.Delay = 500; - IMessage send = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes("foo")).SetHeaders(headers).Build(); - template.Send(exchangeName, queue.QueueName, send); - long t1 = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - IMessage received = template.Receive(queue.QueueName); - Assert.NotNull(received); - int? delay = received.Headers.ReceivedDelay(); - Assert.NotNull(delay); - Assert.Equal(500, delay.Value); - received = template.Receive(queue.QueueName); - Assert.NotNull(received); - delay = received.Headers.ReceivedDelay(); - Assert.NotNull(delay); - Assert.Equal(1000, delay.Value); - long t2 = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - long dif = t2 - t1; - Assert.InRange(dif, 950, 1250); - IConfiguration configuration = await GetExchangeAsync(exchangeName); - Assert.Equal("direct", configuration.GetValue("x-delayed-type") ?? configuration.GetValue("arguments:x-delayed-type")); - Assert.Equal("x-delayed-message", configuration.GetValue("type")); - } - - public void Dispose() - { - RabbitAdmin admin = _provider.GetRabbitAdmin(); - admin?.DeleteQueue("test.queue"); - - _provider.Dispose(); - } - - private async Task GetExchangeAsync(string exchangeName) - { - var client = new HttpClient(); - byte[] authToken = Encoding.ASCII.GetBytes("guest:guest"); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)); - - HttpResponseMessage result = await client.GetAsync(new Uri($"http://localhost:15672/api/exchanges/%2F/{exchangeName}")); - - Assert.Equal(HttpStatusCode.OK, result.StatusCode); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddJsonStream(await result.Content.ReadAsStreamAsync()).Build(); - return configurationRoot; - } - - private bool QueueExists(Queue queue) - { - var cf = new RC.ConnectionFactory - { - HostName = "localhost" - }; - - RC.IConnection connection = cf.CreateConnection(); - RC.IModel channel = connection.CreateModel(); - - try - { - RC.QueueDeclareOk result = channel.QueueDeclarePassive(queue.QueueName); - return result != null; - } - catch (Exception e) - { - return e.Message.Contains("RESOURCE_LOCKED", StringComparison.Ordinal); - } - finally - { - connection.Close(); - } - } - - private sealed class TestPostProcessor : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return PostProcessMessage(message); - } - - public IMessage PostProcessMessage(IMessage message) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.Delay = 1000; - return message; - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitAdminTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitAdminTest.cs deleted file mode 100644 index 60e80e8c7e..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitAdminTest.cs +++ /dev/null @@ -1,443 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Net; -using System.Net.Http.Headers; -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Test.Connection; -using Xunit; -using static Steeltoe.Messaging.RabbitMQ.Configuration.Binding; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -[Trait("Category", "Integration")] -public sealed class RabbitAdminTest : AbstractTest -{ - [Fact] - public void TestSettingOfNullConnectionFactory() - { - const IConnectionFactory connectionFactory = null; - Assert.Throws(() => new RabbitAdmin(connectionFactory)); - } - - [Fact] - public void TestFailOnFirstUseWithMissingBroker() - { - ServiceCollection serviceCollection = CreateContainer(); - serviceCollection.AddLogging(); - serviceCollection.AddRabbitQueue(new Queue("foo")); - - serviceCollection.AddRabbitConnectionFactory((_, f) => - { - f.Host = "localhost"; - f.Port = 434343; - }); - - ServiceProvider provider = serviceCollection.BuildServiceProvider(true); - var applicationContext = provider.GetService(); - var connectionFactory = applicationContext.GetService(); - - var rabbitAdmin = new RabbitAdmin(applicationContext, connectionFactory) - { - AutoStartup = true - }; - - Assert.Throws(() => rabbitAdmin.DeclareQueue()); - connectionFactory.Destroy(); - } - - [Fact] - public async Task TestGetQueueProperties() - { - ServiceCollection serviceCollection = CreateContainer(); - serviceCollection.AddLogging(); - - serviceCollection.AddRabbitConnectionFactory((_, f) => - { - f.Host = "localhost"; - }); - - ServiceProvider provider = serviceCollection.BuildServiceProvider(true); - var applicationContext = provider.GetService(); - var connectionFactory = applicationContext.GetService(); - var rabbitAdmin = new RabbitAdmin(applicationContext, connectionFactory); - string queueName = $"test.properties.{DateTimeOffset.Now.ToUnixTimeMilliseconds()}"; - - try - { - rabbitAdmin.DeclareQueue(new Queue(queueName)); - var template = new RabbitTemplate(connectionFactory); - template.ConvertAndSend(queueName, "foo"); - int n = 0; - - while (n++ < 100 && MessageCount(rabbitAdmin, queueName) == 0) - { - await Task.Delay(100); - } - - Assert.True(n < 100); - RC.IModel channel = connectionFactory.CreateConnection().CreateChannel(); - var consumer = new RC.DefaultBasicConsumer(channel); - RC.IModelExensions.BasicConsume(channel, queueName, true, consumer); - n = 0; - - while (n++ < 100 && MessageCount(rabbitAdmin, queueName) > 0) - { - await Task.Delay(100); - } - - Assert.True(n < 100); - - Dictionary props = rabbitAdmin.GetQueueProperties(queueName); - Assert.True(props.TryGetValue(RabbitAdmin.QueueConsumerCount, out object consumerCount)); - Assert.Equal(1U, consumerCount); - channel.Close(); - } - finally - { - rabbitAdmin.DeleteQueue(queueName); - connectionFactory.Destroy(); - } - } - - [Fact] - public void TestTemporaryLogs() - { - ServiceCollection serviceCollection = CreateContainer(); - serviceCollection.AddLogging(); - serviceCollection.AddRabbitQueue(new Queue("testq.nonDur", false, false, false)); - serviceCollection.AddRabbitQueue(new Queue("testq.ad", true, false, true)); - serviceCollection.AddRabbitQueue(new Queue("testq.excl", true, true, false)); - serviceCollection.AddRabbitQueue(new Queue("testq.all", false, true, true)); - serviceCollection.AddRabbitExchange(new DirectExchange("testex.nonDur", false, false)); - serviceCollection.AddRabbitExchange(new DirectExchange("testex.ad", true, true)); - serviceCollection.AddRabbitExchange(new DirectExchange("testex.all", false, true)); - - serviceCollection.AddRabbitConnectionFactory((_, f) => - { - f.Host = "localhost"; - }); - - ServiceProvider provider = serviceCollection.BuildServiceProvider(true); - var applicationContext = provider.GetService(); - var connectionFactory = applicationContext.GetService(); - - var logs = new List(); - var mockLogger = new Mock(); - - mockLogger.Setup(l => l.Log(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - (Func)It.IsAny())).Callback(new InvocationAction(invocation => - { - logs.Add(invocation.Arguments[2].ToString()); - })); - - var rabbitAdmin = new RabbitAdmin(applicationContext, connectionFactory, mockLogger.Object); - - try - { - connectionFactory.CreateConnection().Close(); - logs.Sort(); - Assert.NotEmpty(logs); - Assert.Contains("(testex.ad), durable:True, auto-delete:True", logs[0], StringComparison.Ordinal); - Assert.Contains("(testex.all), durable:False, auto-delete:True", logs[1], StringComparison.Ordinal); - Assert.Contains("(testex.nonDur), durable:False, auto-delete:False", logs[2], StringComparison.Ordinal); - Assert.Contains("(testq.ad) durable:True, auto-delete:True, exclusive:False", logs[3], StringComparison.Ordinal); - Assert.Contains("(testq.all) durable:False, auto-delete:True, exclusive:True", logs[4], StringComparison.Ordinal); - Assert.Contains("(testq.excl) durable:True, auto-delete:False, exclusive:True", logs[5], StringComparison.Ordinal); - Assert.Contains("(testq.nonDur) durable:False, auto-delete:False, exclusive:False", logs[6], StringComparison.Ordinal); - } - finally - { - CleanQueuesAndExchanges(rabbitAdmin); - connectionFactory.Destroy(); - } - } - - [Fact] - public async Task TestMultiEntities() - { - var serviceCollection = new ServiceCollection(); - serviceCollection.AddSingleton(new ConfigurationBuilder().Build()); - serviceCollection.AddRabbitServices(); - serviceCollection.AddRabbitAdmin(); - var e1 = new DirectExchange("e1", false, true); - serviceCollection.AddRabbitExchange(e1); - var q1 = new Queue("q1", false, false, true); - serviceCollection.AddRabbitQueue(q1); - IBinding binding = BindingBuilder.Bind(q1).To(e1).With("k1"); - serviceCollection.AddRabbitBinding(binding); - var es = new Declarables("es", new DirectExchange("e2", false, true), new DirectExchange("e3", false, true)); - serviceCollection.AddSingleton(es); - var qs = new Declarables("qs", new Queue("q2", false, false, true), new Queue("q3", false, false, true)); - serviceCollection.AddSingleton(qs); - - var bs = new Declarables("bs", new Binding("b1", "q2", DestinationType.Queue, "e2", "k2", null), - new Binding("b2", "q3", DestinationType.Queue, "e3", "k3", null)); - - serviceCollection.AddSingleton(bs); - - var ds = new Declarables("ds", new DirectExchange("e4", false, true), new Queue("q4", false, false, true), - new Binding("b3", "q4", DestinationType.Queue, "e4", "k4", null)); - - serviceCollection.AddSingleton(ds); - - await using (ServiceProvider provider = serviceCollection.BuildServiceProvider(true)) - { - RabbitAdmin admin = provider.GetRabbitAdmin(); - RabbitTemplate template = admin.RabbitTemplate; - template.ConvertAndSend("e1", "k1", "foo"); - template.ConvertAndSend("e2", "k2", "bar"); - template.ConvertAndSend("e3", "k3", "baz"); - template.ConvertAndSend("e4", "k4", "qux"); - Assert.Equal("foo", template.ReceiveAndConvert("q1")); - Assert.Equal("bar", template.ReceiveAndConvert("q2")); - Assert.Equal("baz", template.ReceiveAndConvert("q3")); - Assert.Equal("qux", template.ReceiveAndConvert("q4")); - admin.DeleteQueue("q1"); - admin.DeleteQueue("q2"); - admin.DeleteQueue("q3"); - admin.DeleteQueue("q4"); - admin.DeleteExchange("e1"); - admin.DeleteExchange("e2"); - admin.DeleteExchange("e3"); - admin.DeleteExchange("e4"); - } - - await using (ServiceProvider provider = serviceCollection.BuildServiceProvider(true)) - { - var ctx = provider.GetService(); - var mixedDeclarables = ctx.GetService("ds"); - Assert.NotNull(mixedDeclarables); - IEnumerable queues = mixedDeclarables.GetDeclarablesByType(); - Assert.Single(queues); - Assert.Equal("q4", queues.Single().QueueName); - IEnumerable exchanges = mixedDeclarables.GetDeclarablesByType(); - Assert.Single(exchanges); - Assert.Equal("e4", exchanges.Single().ExchangeName); - IEnumerable bindings = mixedDeclarables.GetDeclarablesByType(); - Assert.Single(bindings); - Assert.Equal("q4", bindings.Single().Destination); - } - } - - [Fact] - public void TestAvoidHangAMQP_508() - { - var cf = new CachingConnectionFactory("localhost"); - var admin = new RabbitAdmin(cf); - byte[] bytes = new byte[300]; - string longName = Encoding.UTF8.GetString(bytes).Replace('\u0000', 'x'); - - try - { - admin.DeclareQueue(new Queue(longName)); - throw new Exception("expected exception"); - } - catch (Exception) - { - // Ignore - } - - const string goodName = "foobar"; - admin.DeclareQueue(new Queue(goodName)); - Assert.Null(admin.GetQueueProperties(longName)); - Assert.NotNull(admin.GetQueueProperties(goodName)); - admin.DeleteQueue(goodName); - cf.Destroy(); - } - - [Fact] - public void TestIgnoreDeclarationExceptionsTimeout() - { - var rabbitConnectionFactory = new Mock(); - var toBeThrown = new TimeoutException("test"); - rabbitConnectionFactory.Setup(c => c.CreateConnection(It.IsAny())).Throws(toBeThrown); - var ccf = new CachingConnectionFactory(rabbitConnectionFactory.Object); - - var admin = new RabbitAdmin(ccf) - { - IgnoreDeclarationExceptions = true - }; - - admin.DeclareQueue(new AnonymousQueue("test")); - DeclarationExceptionEvent lastEvent = admin.LastDeclarationExceptionEvent; - Assert.Same(admin, lastEvent.Source); - Assert.Same(toBeThrown, lastEvent.Exception.InnerException); - Assert.IsType(lastEvent.Declarable); - - admin.DeclareQueue(); - lastEvent = admin.LastDeclarationExceptionEvent; - Assert.Same(admin, lastEvent.Source); - Assert.Same(toBeThrown, lastEvent.Exception.InnerException); - Assert.Null(lastEvent.Declarable); - - admin.DeclareExchange(new DirectExchange("foo")); - lastEvent = admin.LastDeclarationExceptionEvent; - Assert.Same(admin, lastEvent.Source); - Assert.Same(toBeThrown, lastEvent.Exception.InnerException); - Assert.IsType(lastEvent.Declarable); - - admin.DeclareBinding(new Binding("foo", "foo", DestinationType.Queue, "bar", "baz", null)); - lastEvent = admin.LastDeclarationExceptionEvent; - Assert.Same(admin, lastEvent.Source); - Assert.Same(toBeThrown, lastEvent.Exception.InnerException); - Assert.IsType(lastEvent.Declarable); - } - - [Fact] - public void TestWithinInvoke() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - - var channel1 = new Mock(); - var channel2 = new Mock(); - - connection.SetupSequence(c => c.CreateChannel(false)).Returns(channel1.Object).Returns(channel2.Object); - var declareOk = new RC.QueueDeclareOk("foo", 0, 0); - - channel1.Setup(c => c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(declareOk); - - var template = new RabbitTemplate(connectionFactory.Object); - var admin = new RabbitAdmin(template); - - template.Invoke(_ => - { - admin.DeclareQueue(); - admin.DeclareQueue(); - admin.DeclareQueue(); - admin.DeclareQueue(); - return null; - }); - - connection.Verify(c => c.CreateChannel(false), Times.Once); - - channel1.Verify(c => c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()), - Times.Exactly(4)); - - channel1.Verify(c => c.Close(), Times.Once); - channel2.VerifyNoOtherCalls(); - } - - [Fact] - public void TestRetry() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - connection.Setup(c => c.IsOpen).Returns(true); - connectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(connection.Object); - - var channel1 = new Mock(); - channel1.Setup(c => c.IsOpen).Returns(true); - connection.Setup(c => c.CreateModel()).Returns(channel1.Object); - - channel1.Setup(c => c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Throws(); - - var ccf = new CachingConnectionFactory(connectionFactory.Object); - - var rtt = new PollyRetryTemplate(new Dictionary(), 3, true, 1, 1, 1); - ServiceCollection serviceCollection = CreateContainer(); - serviceCollection.AddSingleton(ccf); - - serviceCollection.AddRabbitAdmin((_, a) => - { - a.RetryTemplate = rtt; - }); - - var foo = new AnonymousQueue("foo"); - serviceCollection.AddRabbitQueue(foo); - ServiceProvider provider = serviceCollection.BuildServiceProvider(true); - _ = provider.GetRabbitAdmin(); - Assert.Throws(() => ccf.CreateConnection()); - - channel1.Verify(c => c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()), - Times.Exactly(3)); - } - - [Fact] - public async Task TestMasterLocator() - { - var factory = new RC.ConnectionFactory - { - Uri = new Uri("amqp://guest:guest@localhost:5672/") - }; - - var cf = new CachingConnectionFactory(factory); - var admin = new RabbitAdmin(cf); - var queue = new AnonymousQueue(); - admin.DeclareQueue(queue); - var client = new HttpClient(); - byte[] authToken = Encoding.ASCII.GetBytes("guest:guest"); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)); - - HttpResponseMessage result = await client.GetAsync(new Uri($"http://localhost:15672/api/queues/%3F/{queue.QueueName}")); - int n = 0; - - while (n++ < 100 && result.StatusCode == HttpStatusCode.NotFound) - { - await Task.Delay(100); - result = await client.GetAsync(new Uri($"http://localhost:15672/api/queues/%2F/{queue.QueueName}")); - } - - Assert.Equal(HttpStatusCode.OK, result.StatusCode); - string content = await result.Content.ReadAsStringAsync(); - Assert.Contains("x-queue-master-locator", content, StringComparison.Ordinal); - Assert.Contains("client-local", content, StringComparison.Ordinal); - - queue = new AnonymousQueue - { - MasterLocator = null - }; - - admin.DeclareQueue(queue); - - result = await client.GetAsync(new Uri($"http://localhost:15672/api/queues/%3F/{queue.QueueName}")); - n = 0; - - while (n++ < 100 && result.StatusCode == HttpStatusCode.NotFound) - { - await Task.Delay(100); - result = await client.GetAsync(new Uri($"http://localhost:15672/api/queues/%2F/{queue.QueueName}")); - } - - Assert.Equal(HttpStatusCode.OK, result.StatusCode); - content = await result.Content.ReadAsStringAsync(); - Assert.DoesNotContain("x-queue-master-locator", content, StringComparison.Ordinal); - Assert.DoesNotContain("client-local", content, StringComparison.Ordinal); - cf.Destroy(); - } - - private void CleanQueuesAndExchanges(RabbitAdmin rabbitAdmin) - { - rabbitAdmin.DeleteQueue("testq.nonDur"); - rabbitAdmin.DeleteQueue("testq.ad"); - rabbitAdmin.DeleteQueue("testq.excl"); - rabbitAdmin.DeleteQueue("testq.all"); - rabbitAdmin.DeleteExchange("testex.nonDur"); - rabbitAdmin.DeleteExchange("testex.ad"); - rabbitAdmin.DeleteExchange("testex.all"); - } - - private uint MessageCount(RabbitAdmin rabbitAdmin, string queueName) - { - QueueInformation info = rabbitAdmin.GetQueueInfo(queueName); - Assert.NotNull(info); - return info.MessageCount; - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitBindingIntegrationTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitBindingIntegrationTest.cs deleted file mode 100644 index 96cf1a2bb7..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitBindingIntegrationTest.cs +++ /dev/null @@ -1,305 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Steeltoe.Messaging.RabbitMQ.Util; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -[Trait("Category", "Integration")] -public sealed class RabbitBindingIntegrationTest : IDisposable -{ - private const string QueueName = "test.queue.RabbitBindingIntegrationTests"; - private readonly Queue _queue = new(QueueName); - private readonly ServiceCollection _services; - private ServiceProvider _provider; - - public RabbitBindingIntegrationTest() - { - _services = new ServiceCollection(); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - - _services.AddLogging(b => - { - b.AddDebug(); - b.AddConsole(); - }); - - _services.AddSingleton(configurationRoot); - _services.AddRabbitHostingServices(); - _services.AddRabbitDefaultMessageConverter(); - _services.AddRabbitConnectionFactory((_, f) => f.Host = "localhost"); - _services.AddRabbitAdmin((_, a) => a.AutoStartup = true); - _services.AddRabbitTemplate(); - _services.AddRabbitQueue(_queue); - } - - [Fact] - public void TestSendAndReceiveWithTopicSingleCallback() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin admin = _provider.GetRabbitAdmin(); - var exchange = new TopicExchange("topic"); - admin.DeclareExchange(exchange); - RabbitTemplate template = _provider.GetRabbitTemplate(); - template.DefaultSendDestination = new RabbitDestination(exchange.ExchangeName, string.Empty); - IBinding binding = BindingBuilder.Bind(_queue).To(exchange).With("*.end"); - admin.DeclareBinding(binding); - - try - { - template.Execute(_ => - { - BlockingQueueConsumer consumer = CreateConsumer(template.ConnectionFactory); - string tag = consumer.GetConsumerTags()[0]; - Assert.NotNull(tag); - template.ConvertAndSend("foo", "message"); - - try - { - string result = GetResult(consumer, false); - Assert.Null(result); - template.ConvertAndSend("foo.end", "message"); - result = GetResult(consumer, true); - Assert.Equal("message", result); - } - finally - { - consumer.Channel.BasicCancel(tag); - } - }); - } - finally - { - Assert.True(admin.DeleteExchange("topic")); - } - } - - [Fact] - public void TestSendAndReceiveWithNonDefaultExchange() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin admin = _provider.GetRabbitAdmin(); - var exchange = new TopicExchange("topic"); - admin.DeclareExchange(exchange); - RabbitTemplate template = _provider.GetRabbitTemplate(); - IBinding binding = BindingBuilder.Bind(_queue).To(exchange).With("*.end"); - admin.DeclareBinding(binding); - - try - { - template.Execute(_ => - { - BlockingQueueConsumer consumer = CreateConsumer(template.ConnectionFactory); - string tag = consumer.GetConsumerTags()[0]; - Assert.NotNull(tag); - template.ConvertAndSend("topic", "foo", "message"); - - try - { - string result = GetResult(consumer, false); - Assert.Null(result); - template.ConvertAndSend("topic", "foo.end", "message"); - result = GetResult(consumer, true); - Assert.Equal("message", result); - } - finally - { - consumer.Channel.BasicCancel(tag); - } - }); - } - finally - { - Assert.True(admin.DeleteExchange("topic")); - } - } - - [Fact] - public void TestSendAndReceiveWithTopicConsumeInBackground() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin admin = _provider.GetRabbitAdmin(); - var exchange = new TopicExchange("topic"); - admin.DeclareExchange(exchange); - RabbitTemplate template = _provider.GetRabbitTemplate(); - template.DefaultSendDestination = new RabbitDestination(exchange.ExchangeName, string.Empty); - IBinding binding = BindingBuilder.Bind(_queue).To(exchange).With("*.end"); - admin.DeclareBinding(binding); - - var cachingConnectionFactory = new CachingConnectionFactory("localhost"); - - var template1 = new RabbitTemplate(cachingConnectionFactory) - { - DefaultSendDestination = new RabbitDestination(exchange.ExchangeName, string.Empty) - }; - - BlockingQueueConsumer consumer = template1.Execute(_ => - { - BlockingQueueConsumer consumer1 = CreateConsumer(template1.ConnectionFactory); - string tag = consumer1.GetConsumerTags()[0]; - Assert.NotNull(tag); - - return consumer1; - }); - - template1.ConvertAndSend("foo", "message"); - string result = GetResult(consumer, false); - Assert.Null(result); - - template1.ConvertAndSend("foo.end", "message"); - result = GetResult(consumer, true); - Assert.Equal("message", result); - - consumer.Stop(); - admin.DeleteExchange("topic"); - cachingConnectionFactory.Destroy(); - } - - [Fact] - public void TestSendAndReceiveWithTopicTwoCallbacks() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin admin = _provider.GetRabbitAdmin(); - var exchange = new TopicExchange("topic"); - admin.DeclareExchange(exchange); - IBinding binding = BindingBuilder.Bind(_queue).To(exchange).With("*.end"); - admin.DeclareBinding(binding); - - RabbitTemplate template = _provider.GetRabbitTemplate(); - template.DefaultSendDestination = new RabbitDestination(exchange.ExchangeName, string.Empty); - - try - { - template.Execute(_ => - { - BlockingQueueConsumer consumer = CreateConsumer(template.ConnectionFactory); - string tag = consumer.GetConsumerTags()[0]; - Assert.NotNull(tag); - - try - { - template.ConvertAndSend("foo", "message"); - string result = GetResult(consumer, false); - Assert.Null(result); - } - finally - { - consumer.Stop(); - } - }); - - template.Execute(_ => - { - BlockingQueueConsumer consumer = CreateConsumer(template.ConnectionFactory); - string tag = consumer.GetConsumerTags()[0]; - Assert.NotNull(tag); - - try - { - template.ConvertAndSend("foo.end", "message"); - string result = GetResult(consumer, true); - Assert.Equal("message", result); - } - finally - { - consumer.Stop(); - } - }); - } - finally - { - Assert.True(admin.DeleteExchange("topic")); - } - } - - [Fact] - public void TestSendAndReceiveWithFanOut() - { - _provider = _services.BuildServiceProvider(true); - RabbitAdmin admin = _provider.GetRabbitAdmin(); - var exchange = new FanOutExchange("fanout"); - admin.DeclareExchange(exchange); - admin.DeclareBinding(BindingBuilder.Bind(_queue).To(exchange)); - - RabbitTemplate template = _provider.GetRabbitTemplate(); - template.DefaultSendDestination = new RabbitDestination(exchange.ExchangeName, string.Empty); - - try - { - template.Execute(_ => - { - BlockingQueueConsumer consumer = CreateConsumer(template.ConnectionFactory); - string tag = consumer.GetConsumerTags()[0]; - Assert.NotNull(tag); - - try - { - template.ConvertAndSend("message"); - string result = GetResult(consumer, true); - Assert.Equal("message", result); - } - finally - { - consumer.Stop(); - } - }); - } - finally - { - admin.DeleteExchange("fanout"); - } - } - - public void Dispose() - { - RabbitAdmin admin = _provider.GetRabbitAdmin(); - admin.DeleteQueue(QueueName); - _provider.Dispose(); - } - - private string GetResult(BlockingQueueConsumer consumer, bool expected) - { - IMessage response = consumer.NextMessage(expected ? 2000 : 100); - - if (response == null) - { - return null; - } - - return new SimpleMessageConverter().FromMessage(response); - } - - private BlockingQueueConsumer CreateConsumer(IConnectionFactory connectionFactory) - { - var consumer = new BlockingQueueConsumer(connectionFactory, new DefaultMessageHeadersConverter(), new ActiveObjectCounter(), - AcknowledgeMode.Auto, true, 1, null, _queue.QueueName); - - consumer.Start(); - - int n = 0; - - while (n++ < 100) - { - if (consumer.CurrentConsumers().Count > 0) - { - break; - } - - Thread.Sleep(100); - } - - return consumer; - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitDestinationTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitDestinationTest.cs deleted file mode 100644 index 765e8502d7..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitDestinationTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Core; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -public sealed class RabbitDestinationTest -{ - [Fact] - public void TestConvertRabbitDestinationToString() - { - var d = new RabbitDestination("foo", "bar"); - RabbitDestination str = d; - Assert.Equal("foo/bar", str); - RabbitDestination returned = ReceiveStringReturnDestination(str); - Assert.Equal("foo", returned.ExchangeName); - Assert.Equal("bar", returned.RoutingKey); - Assert.Equal("bar", returned.QueueName); - - d = new RabbitDestination("bar"); - str = d; - Assert.Equal("bar", str); - returned = ReceiveString2ReturnDestination(str); - Assert.Equal(string.Empty, returned.ExchangeName); - Assert.Equal("bar", returned.RoutingKey); - Assert.Equal("bar", returned.QueueName); - } - - [Fact] - public void TestConvertStringToRabbitDestination() - { - RabbitDestination d = "foo/bar"; - Assert.Equal("foo", d.ExchangeName); - Assert.Equal("bar", d.RoutingKey); - Assert.Equal("bar", d.QueueName); - - string returned = ReceiveDestinationReturnString(d); - Assert.Equal("foo/bar", returned); - - d = "bar"; - Assert.Equal(string.Empty, d.ExchangeName); - Assert.Equal("bar", d.RoutingKey); - Assert.Equal("bar", d.QueueName); - - returned = ReceiveDestination2ReturnString(d); - Assert.Equal("bar", returned); - - d = "/bar"; - Assert.Equal(string.Empty, d.ExchangeName); - Assert.Equal("bar", d.RoutingKey); - Assert.Equal("bar", d.QueueName); - - returned = ReceiveDestination2ReturnString(d); - Assert.Equal("bar", returned); - } - - public RabbitDestination ReceiveStringReturnDestination(string destination) - { - Assert.Equal("foo/bar", destination); - return destination; - } - - public RabbitDestination ReceiveString2ReturnDestination(string destination) - { - Assert.Equal("bar", destination); - return destination; - } - - public string ReceiveDestinationReturnString(RabbitDestination destination) - { - Assert.Equal("foo/bar", destination); - return destination; - } - - public string ReceiveDestination2ReturnString(RabbitDestination destination) - { - Assert.Equal("bar", destination); - return destination; - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateDirectReplyToContainerIntegrationPubCFTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateDirectReplyToContainerIntegrationPubCFTest.cs deleted file mode 100644 index 89286d90e9..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateDirectReplyToContainerIntegrationPubCFTest.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -[Trait("Category", "Integration")] -public sealed class RabbitTemplateDirectReplyToContainerIntegrationPubCFTest : RabbitTemplateDirectReplyToContainerIntegrationTest -{ - protected override RabbitTemplate CreateSendAndReceiveRabbitTemplate(IConnectionFactory connectionFactory) - { - RabbitTemplate srTemplate = base.CreateSendAndReceiveRabbitTemplate(connectionFactory); - srTemplate.UsePublisherConnection = true; - return srTemplate; - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateDirectReplyToContainerIntegrationTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateDirectReplyToContainerIntegrationTest.cs deleted file mode 100644 index 04afb664c2..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateDirectReplyToContainerIntegrationTest.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -[Trait("Category", "Integration")] -public class RabbitTemplateDirectReplyToContainerIntegrationTest : RabbitTemplateIntegrationTest -{ - [Fact] - public void ChannelReleasedOnTimeout() - { - using var connectionFactory = new CachingConnectionFactory("localhost"); - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(connectionFactory); - rabbitTemplate.ReplyTimeout = 1; - var exception = new AtomicReference(); - var latch = new CountdownEvent(1); - rabbitTemplate.ReplyErrorHandler = new TestErrorHandler(exception, latch); - object reply = rabbitTemplate.ConvertSendAndReceive(Route, "foo"); - Assert.Null(reply); - Dictionary directReplyToContainers = rabbitTemplate.DirectReplyToContainers; - - DirectReplyToMessageListenerContainer container = rabbitTemplate.UsePublisherConnection - ? directReplyToContainers[connectionFactory.PublisherConnectionFactory] - : directReplyToContainers[connectionFactory]; - - Assert.Empty(container.InUseConsumerChannels); - Assert.Same(rabbitTemplate.ReplyErrorHandler, container.ErrorHandler); - IMessage replyMessage = Message.Create(Encoding.UTF8.GetBytes("foo"), new MessageHeaders()); - - var ex = Assert.Throws(() => rabbitTemplate.OnMessage(replyMessage)); - Assert.Contains("No correlation header in reply", ex.Message, StringComparison.Ordinal); - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(replyMessage); - accessor.CorrelationId = "foo"; - - ex = Assert.Throws(() => rabbitTemplate.OnMessage(replyMessage)); - Assert.Contains("Reply received after timeout", ex.Message, StringComparison.Ordinal); - - _ = Task.Run(() => - { - IMessage message = rabbitTemplate.Receive(Route, 10000); - Assert.NotNull(message); - rabbitTemplate.Send(message.Headers.ReplyTo(), replyMessage); - return message; - }); - - while (rabbitTemplate.Receive(Route, 100) != null) - { - // Intentionally left empty. - } - - reply = rabbitTemplate.ConvertSendAndReceive(Route, "foo"); - Assert.Null(reply); - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - Assert.IsType(exception.Value); - var listException = exception.Value as ListenerExecutionFailedException; - Assert.Contains("Reply received after timeout", exception.Value.InnerException.Message, StringComparison.Ordinal); - Assert.Equal(replyMessage.Payload, listException.FailedMessage.Payload); - Assert.Empty(container.InUseConsumerChannels); - } - - protected override RabbitTemplate CreateSendAndReceiveRabbitTemplate(IConnectionFactory connectionFactory) - { - RabbitTemplate rabbitTemplate = base.CreateSendAndReceiveRabbitTemplate(connectionFactory); - rabbitTemplate.UseDirectReplyToContainer = true; - rabbitTemplate.ServiceName = $"{nameof(RabbitTemplateDirectReplyToContainerIntegrationTest)}.SendReceiveRabbitTemplate"; - return rabbitTemplate; - } - - private sealed class TestErrorHandler : IErrorHandler - { - public string ServiceName { get; set; } = nameof(TestErrorHandler); - - public AtomicReference AtomicReference { get; } - - public CountdownEvent Latch { get; } - - public TestErrorHandler(AtomicReference atomicReference, CountdownEvent latch) - { - AtomicReference = atomicReference; - Latch = latch; - } - - public bool HandleError(Exception exception) - { - AtomicReference.Value = exception; - Latch.Signal(); - return true; - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateIntegrationPubCFTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateIntegrationPubCFTest.cs deleted file mode 100644 index 718de187ac..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateIntegrationPubCFTest.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -public sealed class RabbitTemplateIntegrationPubCFTest : RabbitTemplateIntegrationTest -{ - public RabbitTemplateIntegrationPubCFTest() - { - Template.UsePublisherConnection = true; - RoutingTemplate.UsePublisherConnection = true; - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateIntegrationTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateIntegrationTest.cs deleted file mode 100644 index d7f931190c..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateIntegrationTest.cs +++ /dev/null @@ -1,1821 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Runtime.Serialization.Formatters.Binary; -using System.Text; -using Microsoft.Extensions.Logging; -using Moq; -using RabbitMQ.Client.Events; -using RabbitMQ.Client.Exceptions; -using Steeltoe.Common.Expression.Internal.Spring.Common; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Transaction; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; -using Steeltoe.Messaging.RabbitMQ.Test.Connection; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -[Trait("Category", "Integration")] -public abstract class RabbitTemplateIntegrationTest : IDisposable -{ - public const string Route = "test.queue.RabbitTemplateIntegrationTests"; - public const string ReplyQueueName = "test.reply.queue.RabbitTemplateIntegrationTests"; - - private readonly CachingConnectionFactory _connectionFactory; - - protected RabbitTemplate Template { get; private set; } - protected RabbitTemplate RoutingTemplate { get; } - protected RabbitAdmin Admin { get; } - protected Mock ConnectionFactory1 { get; } - protected Mock ConnectionFactory2 { get; } - protected Mock DefaultConnectionFactory { get; } - - protected RabbitTemplateIntegrationTest() - { - _connectionFactory = new CachingConnectionFactory("localhost") - { - IsPublisherReturns = true - }; - - Template = new RabbitTemplate(_connectionFactory) - { - ReplyTimeout = 10000 - }; - - Template.SendConnectionFactorySelectorExpression = new LiteralExpression("foo"); - var adminCf = new CachingConnectionFactory("localhost"); - Admin = new RabbitAdmin(adminCf); - Admin.DeclareQueue(new Queue(Route)); - Admin.DeclareQueue(new Queue(ReplyQueueName)); - - RoutingTemplate = new RabbitTemplate(); - - RoutingTemplate.SendConnectionFactorySelectorExpression = new SpelExpressionParser().ParseExpression("Headers['cfKey']"); - var routingConnFactory = new SimpleRoutingConnectionFactory(); - ConnectionFactory1 = new Mock(); - ConnectionFactory2 = new Mock(); - DefaultConnectionFactory = new Mock(); - routingConnFactory.AddTargetConnectionFactory("foo", ConnectionFactory1.Object); - routingConnFactory.AddTargetConnectionFactory("bar", ConnectionFactory2.Object); - RoutingTemplate.ConnectionFactory = routingConnFactory; - } - - [Fact] - public async Task TestChannelCloseInTx() - { - _connectionFactory.IsPublisherReturns = false; - RC.IModel channel = _connectionFactory.CreateConnection().CreateChannel(true); - var holder = new RabbitResourceHolder(channel, true); - TransactionSynchronizationManager.BindResource(_connectionFactory, holder); - - try - { - Template.IsChannelTransacted = true; - Template.ConvertAndSend(Route, "foo"); - Template.ConvertAndSend(Guid.NewGuid().ToString(), Route, "xxx"); - int n = 0; - - while (n++ < 100 && channel.IsOpen) - { - await Task.Delay(100); - } - - Assert.False(channel.IsOpen); - - try - { - Template.ConvertAndSend(Route, "bar"); - throw new Exception("Expected exception"); - } - catch (RabbitUncategorizedException e) - { - if (e.InnerException is InvalidOperationException) - { - Assert.Contains("Channel closed during transaction", e.InnerException.Message, StringComparison.Ordinal); - } - else - { - throw new Exception("Expected InvalidOperationException"); - } - } - catch (RabbitConnectException e) - { - Assert.IsType(e.InnerException); - } - } - finally - { - TransactionSynchronizationManager.UnbindResource(_connectionFactory); - channel.Close(); - } - } - - [Fact] - public void TestTemplateUsesPublisherConnectionUnlessInTx() - { - _connectionFactory.Destroy(); - Template.UsePublisherConnection = true; - Template.ConvertAndSend("dummy", "foo"); - Assert.Null(_connectionFactory.Connection.Target); - Assert.NotNull(((CachingConnectionFactory)_connectionFactory.PublisherConnectionFactory).Connection.Target); - _connectionFactory.Destroy(); - Assert.Null(_connectionFactory.Connection.Target); - Assert.Null(((CachingConnectionFactory)_connectionFactory.PublisherConnectionFactory).Connection.Target); - RC.IModel channel = _connectionFactory.CreateConnection().CreateChannel(true); - Assert.NotNull(_connectionFactory.Connection.Target); - var holder = new RabbitResourceHolder(channel, true); - TransactionSynchronizationManager.BindResource(_connectionFactory, holder); - - try - { - Template.IsChannelTransacted = true; - Template.ConvertAndSend("dummy", "foo"); - Assert.NotNull(_connectionFactory.Connection.Target); - Assert.Null(((CachingConnectionFactory)_connectionFactory.PublisherConnectionFactory).Connection.Target); - } - finally - { - TransactionSynchronizationManager.UnbindResource(_connectionFactory); - channel.Close(); - } - } - - [Fact] - public async Task TestReceiveNonBlocking() - { - Template.ConvertAndSend(Route, "nonblock"); - int n = 0; - string o = Template.ReceiveAndConvert(Route); - - while (n++ < 100 && o == null) - { - await Task.Delay(100); - o = Template.ReceiveAndConvert(Route); - } - - Assert.NotNull(o); - Assert.Equal("nonblock", o); - Assert.Null(Template.Receive(Route)); - } - - [Fact] - public void TestReceiveConsumerCanceled() - { - using var connectionFactory = new MockSingleConnectionFactory("localhost"); - - Template = new RabbitTemplate(connectionFactory) - { - ReceiveTimeout = 10000 - }; - - Assert.Throws(() => Template.Receive(Route)); - } - - [Fact] - public void TestReceiveBlocking() - { - Template.ConvertAndSend(Route, "block"); - IMessage received = Template.Receive(Route, 10000); - Assert.NotNull(received); - Assert.Equal("block", EncodingUtils.Utf8.GetString((byte[])received.Payload)); - - Template.ReceiveTimeout = 0; - Assert.Null(Template.Receive(Route)); - } - - [Fact] - public void TestReceiveBlockingNoTimeout() - { - Template.ConvertAndSend(Route, "blockNoTO"); - string o = Template.ReceiveAndConvert(Route, -1); - Assert.NotNull(o); - Assert.Equal("blockNoTO", o); - Template.ReceiveTimeout = 1; // test the no message after timeout path - - try - { - Assert.Null(Template.Receive(Route)); - } - catch (ConsumeOkNotReceivedException) - { - // we're expecting no result, this could happen, depending on timing. - } - } - - [Fact] - public void TestReceiveTimeoutRequeue() - { - try - { - Assert.Null(Template.ReceiveAndConvert(Route, 10)); - } - catch (ConsumeOkNotReceivedException) - { - // empty - race for consumeOk - } - - Assert.Empty(_connectionFactory.CachedChannelsNonTransactional); - } - - [Fact] - public void TestReceiveBlockingTx() - { - Template.ConvertAndSend(Route, "blockTX"); - Template.IsChannelTransacted = true; - Template.ReceiveTimeout = 10000; - string o = Template.ReceiveAndConvert(Route); - Assert.NotNull(o); - Assert.Equal("blockTX", o); - Template.ReceiveTimeout = 0; - Assert.Null(Template.Receive(Route)); - } - - [Fact] - public void TestReceiveBlockingGlobalTx() - { - Template.ConvertAndSend(Route, "blockGTXNoTO"); - RabbitResourceHolder resourceHolder = ConnectionFactoryUtils.GetTransactionalResourceHolder(Template.ConnectionFactory, true); - TransactionSynchronizationManager.SetActualTransactionActive(true); - ConnectionFactoryUtils.BindResourceToTransaction(resourceHolder, Template.ConnectionFactory, true); - Template.ReceiveTimeout = -1; - Template.IsChannelTransacted = true; - string o = Template.ReceiveAndConvert(Route); - resourceHolder.CommitAll(); - resourceHolder.CloseAll(); - Assert.Same(resourceHolder, TransactionSynchronizationManager.UnbindResource(Template.ConnectionFactory)); - Assert.NotNull(o); - Assert.Equal("blockGTXNoTO", o); - Template.ReceiveTimeout = 0; - Assert.Null(Template.Receive(Route)); - } - - [Fact] - public void TestSendToNonExistentAndThenReceive() - { - // If transacted then the commit fails on send, so we get a nice synchronous exception - Template.IsChannelTransacted = true; - - try - { - Template.ConvertAndSend(string.Empty, "no.such.route", "message"); - } - catch (RabbitException) - { - // Intentionally left empty. - } - - // Now send the real message, and all should be well... - Template.ConvertAndSend(Route, "message"); - string result = Template.ReceiveAndConvert(Route); - Assert.Equal("message", result); - result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestSendAndReceiveWithPostProcessor() - { - Template.ConvertAndSend(Route, (object)"message", new PostProcessor1()); - Template.SetAfterReceivePostProcessors(new PostProcessor2()); - string result = Template.ReceiveAndConvert(Route); - Assert.Equal("message", result); - result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestSendAndReceive() - { - Template.ConvertAndSend(Route, "message"); - string result = Template.ReceiveAndConvert(Route); - Assert.Equal("message", result); - result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestSendAndReceiveUndeliverable() - { - Template.Mandatory = true; - var ex = Assert.Throws(() => Template.ConvertSendAndReceive($"{Route}xxxxxx", "undeliverable")); - byte[] body = ex.ReturnedMessage.Payload as byte[]; - Assert.NotNull(body); - Assert.Equal("undeliverable", EncodingUtils.Utf8.GetString(body)); - Assert.Contains(ex.ReplyText, "NO_ROUTE", StringComparison.Ordinal); - Assert.Empty(Template.ReplyHolder); - } - - [Fact] - public void TestSendAndReceiveTransacted() - { - Template.IsChannelTransacted = true; - Template.ConvertAndSend(Route, "message"); - string result = Template.ReceiveAndConvert(Route); - Assert.Equal("message", result); - result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestSendAndReceiveTransactedWithUncachedConnection() - { - var singleConnectionFactory = new SingleConnectionFactory("localhost", null); - - var rabbitTemplate = new RabbitTemplate(singleConnectionFactory) - { - IsChannelTransacted = true - }; - - rabbitTemplate.ConvertAndSend(Route, "message"); - string result = rabbitTemplate.ReceiveAndConvert(Route); - Assert.Equal("message", result); - result = rabbitTemplate.ReceiveAndConvert(Route); - Assert.Null(result); - singleConnectionFactory.Destroy(); - } - - [Fact] - public void TestSendAndReceiveTransactedWithImplicitRollback() - { - Template.IsChannelTransacted = true; - Template.ConvertAndSend(Route, "message"); - - // Rollback of manual receive is implicit because the channel is - // closed... - var ex = Assert.Throws(() => Template.Execute(c => - { - c.BasicGet(Route, false); - c.BasicRecover(true); - throw new PlannedException(); - })); - - Assert.IsType(ex.InnerException); - string result = Template.ReceiveAndConvert(Route); - Assert.Equal("message", result); - result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestSendAndReceiveInCallback() - { - Template.ConvertAndSend(Route, "message"); - var messagePropertiesConverter = new DefaultMessageHeadersConverter(); - - string result = Template.Execute(c => - { - RC.BasicGetResult response = c.BasicGet(Route, false); - - IMessageHeaders props = messagePropertiesConverter.ToMessageHeaders(response.BasicProperties, - new Envelope(response.DeliveryTag, response.Redelivered, response.Exchange, response.RoutingKey), EncodingUtils.Utf8); - - c.BasicAck(response.DeliveryTag, false); - return new SimpleMessageConverter().FromMessage(Message.Create(response.Body, props)); - }); - - Assert.Equal("message", result); - result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestReceiveInExternalTransaction() - { - Template.ConvertAndSend(Route, "message"); - Template.IsChannelTransacted = true; - string result = new TransactionTemplate(new TestTransactionManager()).Execute(_ => Template.ReceiveAndConvert(Route)); - Assert.Equal("message", result); - result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestReceiveInExternalTransactionWithRollback() - { - // Makes receive (and send in principle) transactional - Template.IsChannelTransacted = true; - Template.ConvertAndSend(Route, "message"); - - Assert.Throws(() => - { - new TransactionTemplate(new TestTransactionManager()).Execute(_ => - { - string result = Template.ReceiveAndConvert(Route); - Assert.NotNull(result); - throw new PlannedException(); - }); - }); - - string result = Template.ReceiveAndConvert(Route); - Assert.Equal("message", result); - result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestReceiveInExternalTransactionWithNoRollback() - { - // Makes receive non-transactional - Template.IsChannelTransacted = false; - Template.ConvertAndSend(Route, "message"); - - Assert.Throws(() => - { - new TransactionTemplate(new TestTransactionManager()).Execute(_ => - { - Template.ReceiveAndConvert(Route); - throw new PlannedException(); - }); - }); - - // No rollback - string result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestSendInExternalTransaction() - { - Template.IsChannelTransacted = true; - - new TransactionTemplate(new TestTransactionManager()).Execute(_ => - { - Template.ConvertAndSend(Route, "message"); - }); - - string result = Template.ReceiveAndConvert(Route); - Assert.Equal("message", result); - result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public void TestSendInExternalTransactionWithRollback() - { - // Makes receive non-transactional - Template.IsChannelTransacted = true; - - Assert.Throws(() => - { - new TransactionTemplate(new TestTransactionManager()).Execute(_ => - { - Template.ConvertAndSend(Route, "message"); - throw new PlannedException(); - }); - }); - - // No rollback - string result = Template.ReceiveAndConvert(Route); - Assert.Null(result); - } - - [Fact] - public async Task TestAtomicSendAndReceive() - { - using var cachingConnectionFactory = new CachingConnectionFactory("localhost"); - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(cachingConnectionFactory); - rabbitTemplate.DefaultSendDestination = new RabbitDestination(string.Empty, Route); - rabbitTemplate.DefaultReceiveDestination = new RabbitDestination(Route); - - Task task = Task.Run(async () => - { - IMessage message = null; - - for (int i = 0; i < 10; i++) - { - message = rabbitTemplate.Receive(); - - if (message != null) - { - break; - } - - await Task.Delay(100); - } - - Assert.NotNull(message); - rabbitTemplate.Send(message.Headers.ReplyTo(), message); - return message; - }); - - IMessage message = Message.Create(EncodingUtils.Utf8.GetBytes("test-message"), new MessageHeaders()); - rabbitTemplate.SendAndReceive(message); - - IMessage receiveResult = await task.WaitAsync(TimeSpan.FromSeconds(10)); - Assert.NotNull(receiveResult); - } - - [Fact] - public async Task TestAtomicSendAndReceiveUserCorrelation() - { - using var cachingConnectionFactory = new CachingConnectionFactory("localhost"); - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(cachingConnectionFactory); - rabbitTemplate.DefaultSendDestination = new RabbitDestination(string.Empty, Route); - rabbitTemplate.DefaultReceiveDestination = new RabbitDestination(Route); - var remoteCorrelationId = new AtomicReference(); - - Task receiveTask = Task.Run(() => - { - IMessage message = rabbitTemplate.Receive(10000); - Assert.NotNull(message); - remoteCorrelationId.Value = message.Headers.CorrelationId(); - rabbitTemplate.Send(message.Headers.ReplyTo(), message); - return message; - }); - - var rabbitAdmin = new RabbitAdmin(cachingConnectionFactory); - IQueue replyQueue = rabbitAdmin.DeclareQueue(); - rabbitTemplate.ReplyAddress = replyQueue.QueueName; - rabbitTemplate.UserCorrelationId = true; - rabbitTemplate.ReplyTimeout = 10000; - var container = new DirectMessageListenerContainer(null, cachingConnectionFactory); - container.SetQueues(replyQueue); - container.MessageListener = rabbitTemplate; - container.Initialize(); - await container.StartAsync(); - - var headers = new RabbitHeaderAccessor(new MessageHeaders()) - { - CorrelationId = "myCorrelationId" - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("test-message"), headers.MessageHeaders); - IMessage reply = rabbitTemplate.SendAndReceive(message); - - IMessage receiveResult = await receiveTask.WaitAsync(TimeSpan.FromSeconds(1)); - Assert.Equal("test-message", Encoding.UTF8.GetString((byte[])receiveResult.Payload)); - Assert.NotNull(reply); - Assert.Equal("myCorrelationId", remoteCorrelationId.Value); - Assert.Equal("test-message", Encoding.UTF8.GetString((byte[])reply.Payload)); - reply = rabbitTemplate.Receive(); - Assert.Null(reply); - await rabbitTemplate.StopAsync(); - await container.StopAsync(); - cachingConnectionFactory.Destroy(); - } - - [Fact] - public async Task TestAtomicSendAndReceiveWithRoutingKey() - { - using var cachingConnectionFactory = new CachingConnectionFactory("localhost"); - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(cachingConnectionFactory); - - // Set up a consumer to respond to our producer - Task receiveTask = Task.Run(async () => - { - IMessage message = null; - - for (int i = 0; i < 10; i++) - { - message = rabbitTemplate.Receive(Route); - - if (message != null) - { - break; - } - - await Task.Delay(100); - } - - Assert.NotNull(message); - rabbitTemplate.Send(message.Headers.ReplyTo(), message); - return message; - }); - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("test-message"), new MessageHeaders()); - IMessage reply = rabbitTemplate.SendAndReceive(Route, message); - - IMessage receiveResult = await receiveTask.WaitAsync(TimeSpan.FromSeconds(1)); - Assert.Equal("test-message", Encoding.UTF8.GetString((byte[])receiveResult.Payload)); - Assert.NotNull(reply); - Assert.Equal("test-message", Encoding.UTF8.GetString((byte[])reply.Payload)); - reply = rabbitTemplate.Receive(Route); - Assert.Null(reply); - await rabbitTemplate.StopAsync(); - cachingConnectionFactory.Destroy(); - } - - [Fact] - public async Task TestAtomicSendAndReceiveWithExchangeAndRoutingKey() - { - using var cachingConnectionFactory = new CachingConnectionFactory("localhost"); - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(cachingConnectionFactory); - - // Set up a consumer to respond to our producer - Task receiveTask = Task.Run(async () => - { - IMessage message = null; - - for (int i = 0; i < 10; i++) - { - message = rabbitTemplate.Receive(Route); - - if (message != null) - { - break; - } - - await Task.Delay(100); - } - - Assert.NotNull(message); - rabbitTemplate.Send(message.Headers.ReplyTo(), message); - return message; - }); - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("test-message"), new MessageHeaders()); - IMessage reply = rabbitTemplate.SendAndReceive(string.Empty, Route, message); - - IMessage receiveResult = await receiveTask.WaitAsync(TimeSpan.FromSeconds(1)); - Assert.Equal("test-message", Encoding.UTF8.GetString((byte[])receiveResult.Payload)); - Assert.NotNull(reply); - Assert.Equal("test-message", Encoding.UTF8.GetString((byte[])reply.Payload)); - reply = rabbitTemplate.Receive(Route); - Assert.Null(reply); - await rabbitTemplate.StopAsync(); - cachingConnectionFactory.Destroy(); - } - - [Fact] - public async Task TestAtomicSendAndReceiveWithConversion() - { - using var cachingConnectionFactory = new CachingConnectionFactory("localhost"); - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(cachingConnectionFactory); - rabbitTemplate.RoutingKey = Route; - rabbitTemplate.DefaultReceiveQueue = Route; - - // Set up a consumer to respond to our producer - Task receiveTask = Task.Run(async () => - { - IMessage message = null; - - for (int i = 0; i < 10; i++) - { - message = rabbitTemplate.Receive(Route); - - if (message != null) - { - break; - } - - await Task.Delay(100); - } - - Assert.NotNull(message); - rabbitTemplate.Send(message.Headers.ReplyTo(), message); - return rabbitTemplate.MessageConverter.FromMessage(message); - }); - - rabbitTemplate.ConvertSendAndReceive("message"); - - string receiveResult = await receiveTask.WaitAsync(TimeSpan.FromSeconds(1)); - Assert.Equal("message", receiveResult); - string result = rabbitTemplate.ReceiveAndConvert(); - Assert.Null(result); - await rabbitTemplate.StopAsync(); - cachingConnectionFactory.Destroy(); - } - - [Fact] - public async Task TestAtomicSendAndReceiveWithConversionUsingRoutingKey() - { - // Set up a consumer to respond to our producer - Task receiveTask = Task.Run(async () => - { - IMessage message = null; - - for (int i = 0; i < 10; i++) - { - message = Template.Receive(Route); - - if (message != null) - { - break; - } - - await Task.Delay(100); - } - - Assert.NotNull(message); - Template.Send(message.Headers.ReplyTo(), message); - return Template.MessageConverter.FromMessage(message); - }); - - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(_connectionFactory); - string result = rabbitTemplate.ConvertSendAndReceive(Route, "message"); - - string receiveResult = await receiveTask.WaitAsync(TimeSpan.FromSeconds(1)); - Assert.Equal("message", receiveResult); - Assert.Equal("message", result); - - result = rabbitTemplate.ReceiveAndConvert(Route); - Assert.Null(result); - await rabbitTemplate.StopAsync(); - } - - [Fact] - public async Task TestAtomicSendAndReceiveWithConversionUsingExchangeAndRoutingKey() - { - // Set up a consumer to respond to our producer - Task receiveTask = Task.Run(async () => - { - IMessage message = null; - - for (int i = 0; i < 10; i++) - { - message = Template.Receive(Route); - - if (message != null) - { - break; - } - - await Task.Delay(100); - } - - Assert.NotNull(message); - Template.Send(message.Headers.ReplyTo(), message); - return Template.MessageConverter.FromMessage(message); - }); - - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(_connectionFactory); - string result = rabbitTemplate.ConvertSendAndReceive(string.Empty, Route, "message"); - - string receiveResult = await receiveTask.WaitAsync(TimeSpan.FromSeconds(1)); - Assert.Equal("message", receiveResult); - Assert.Equal("message", result); - - result = rabbitTemplate.ReceiveAndConvert(Route); - Assert.Null(result); - await rabbitTemplate.StopAsync(); - } - - [Fact] - public async Task TestAtomicSendAndReceiveWithConversionAndMessagePostProcessor() - { - using var cachingConnectionFactory = new CachingConnectionFactory("localhost"); - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(cachingConnectionFactory); - rabbitTemplate.RoutingKey = Route; - rabbitTemplate.DefaultReceiveQueue = Route; - - // Set up a consumer to respond to our producer - Task receiveTask = Task.Run(async () => - { - IMessage message = null; - - for (int i = 0; i < 10; i++) - { - message = rabbitTemplate.Receive(); - - if (message != null) - { - break; - } - - await Task.Delay(100); - } - - Assert.NotNull(message); - rabbitTemplate.Send(message.Headers.ReplyTo(), message); - return rabbitTemplate.MessageConverter.FromMessage(message); - }); - - string result = rabbitTemplate.ConvertSendAndReceive((object)"message", new PostProcessor3()); - - string receiveResult = await receiveTask.WaitAsync(TimeSpan.FromSeconds(1)); - Assert.Equal("MESSAGE", receiveResult); - Assert.Equal("MESSAGE", result); - - result = rabbitTemplate.ReceiveAndConvert(); - Assert.Null(result); - await rabbitTemplate.StopAsync(); - cachingConnectionFactory.Destroy(); - } - - [Fact] - public async Task TestAtomicSendAndReceiveWithConversionAndMessagePostProcessorUsingRoutingKey() - { - // Set up a consumer to respond to our producer - Task receiveTask = Task.Run(async () => - { - IMessage message = null; - - for (int i = 0; i < 10; i++) - { - message = Template.Receive(Route); - - if (message != null) - { - break; - } - - await Task.Delay(100); - } - - Assert.NotNull(message); - Template.Send(message.Headers.ReplyTo(), message); - return Template.MessageConverter.FromMessage(message); - }); - - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(_connectionFactory); - string result = rabbitTemplate.ConvertSendAndReceive(Route, (object)"message", new PostProcessor3()); - - string receiveResult = await receiveTask.WaitAsync(TimeSpan.FromSeconds(1)); - Assert.Equal("MESSAGE", receiveResult); - Assert.Equal("MESSAGE", result); - - result = rabbitTemplate.ReceiveAndConvert(Route); - Assert.Null(result); - await rabbitTemplate.StopAsync(); - } - - [Fact] - public async Task TestAtomicSendAndReceiveWithConversionAndMessagePostProcessorUsingExchangeAndRoutingKey() - { - // Set up a consumer to respond to our producer - Task receiveTask = Task.Run(async () => - { - IMessage message = null; - - for (int i = 0; i < 10; i++) - { - message = Template.Receive(Route); - - if (message != null) - { - break; - } - - await Task.Delay(100); - } - - Assert.NotNull(message); - Template.Send(message.Headers.ReplyTo(), message); - return Template.MessageConverter.FromMessage(message); - }); - - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(_connectionFactory); - string result = rabbitTemplate.ConvertSendAndReceive(string.Empty, Route, "message", new PostProcessor3()); - - string receiveResult = await receiveTask.WaitAsync(TimeSpan.FromSeconds(1)); - Assert.Equal("MESSAGE", receiveResult); - Assert.Equal("MESSAGE", result); - - result = rabbitTemplate.ReceiveAndConvert(Route); - Assert.Null(result); - await rabbitTemplate.StopAsync(); - } - - [Fact] - public void TestReceiveAndReplyNonStandardCorrelationNotBytes() - { - Template.DefaultReceiveQueue = Route; - Template.RoutingKey = Route; - - var headers = new MessageHeaders(new Dictionary - { - { "baz", "bar" } - }); - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), headers); - Template.Send(Route, message); - Template.CorrelationKey = "baz"; - bool received = Template.ReceiveAndReply(_ => Message.Create(Encoding.UTF8.GetBytes("fuz"), new MessageHeaders())); - Assert.True(received); - IMessage message2 = Template.Receive(); - Assert.NotNull(message2); - Assert.Equal("bar", message2.Headers.Get("baz")); - } - - [Fact] - public async Task TestReceiveAndReplyBlocking() - { - await TestReceiveAndReplyAsync(10000); - } - - [Fact] - public async Task TestReceiveAndReplyNonBlocking() - { - await TestReceiveAndReplyAsync(0); - } - - [Fact] - public async Task TestSymmetricalReceiveAndReply() - { - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(_connectionFactory); - rabbitTemplate.DefaultReceiveQueue = Route; - rabbitTemplate.RoutingKey = Route; - rabbitTemplate.ReplyAddress = ReplyQueueName; - rabbitTemplate.ReplyTimeout = 20000; - rabbitTemplate.ReceiveTimeout = 20000; - - var container = new DirectMessageListenerContainer(); - container.ConnectionFactory = rabbitTemplate.ConnectionFactory; - container.SetQueueNames(ReplyQueueName); - container.MessageListener = rabbitTemplate; - await container.StartAsync(); - - const int count = 10; - var results = new ConcurrentDictionary(); - rabbitTemplate.CorrelationKey = "CorrelationKey"; - var tasks = new List(); - - for (int i = 0; i < count; i++) - { - tasks.Add(Task.Run(() => - { - double request = Random.Shared.NextDouble() * 100; - object reply = rabbitTemplate.ConvertSendAndReceive(request); - results.TryAdd(request, reply); - })); - } - - for (int i = 0; i < count; i++) - { - tasks.Add(Task.Run(() => - { - double request = Random.Shared.NextDouble() * 100; - - var messageHeaders = new RabbitHeaderAccessor(new MessageHeaders()) - { - ContentType = MessageHeaders.ContentTypeDotNetSerializedObject - }; - -#pragma warning disable SYSLIB0011 // Type or member is obsolete - var formatter = new BinaryFormatter(); - using var requestStream = new MemoryStream(512); - - formatter.Serialize(requestStream, request); - byte[] bytes = requestStream.ToArray(); - IMessage reply = rabbitTemplate.SendAndReceive(Message.Create(bytes, messageHeaders.MessageHeaders)); - - using var replyStream = new MemoryStream((byte[])reply.Payload); - object obj = formatter.Deserialize(replyStream); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - results.TryAdd(request, obj); - })); - } - - var receiveCount = new AtomicInteger(); - long start = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - - do - { - rabbitTemplate.ReceiveAndReply(payload => - { - receiveCount.IncrementAndGet(); - return payload * 3; - }); - - if (DateTimeOffset.Now.ToUnixTimeMilliseconds() > start + 20000) - { - throw new Exception("Something wrong with RabbitMQ"); - } - } - while (receiveCount.Value < count * 2); - - await Task.WhenAll(tasks).WaitAsync(TimeSpan.FromSeconds(10)); - - await container.StopAsync(); - Assert.Equal(count * 2, results.Count); - - foreach (KeyValuePair entry in results) - { - Assert.Equal(entry.Value, entry.Key * 3); - } - - string messageId = Guid.NewGuid().ToString(); - - var messageProperties = new RabbitHeaderAccessor - { - MessageId = messageId, - ContentType = MessageHeaders.ContentTypeTextPlain, - ReplyTo = ReplyQueueName - }; - - rabbitTemplate.Send(Message.Create(Encoding.UTF8.GetBytes("test"), messageProperties.MessageHeaders)); - rabbitTemplate.ReceiveAndReply(str => str.ToUpperInvariant()); - - Template.ReceiveTimeout = 20000; - IMessage result = Template.Receive(ReplyQueueName); - Assert.NotNull(result); - Assert.Equal("TEST", Encoding.UTF8.GetString((byte[])result.Payload)); - Assert.Equal(messageId, result.Headers.CorrelationId()); - await rabbitTemplate.StopAsync(); - } - - [Fact] - public async Task TestSendAndReceiveFastImplicit() - { - await SendAndReceiveFastGutsAsync(false, false, false); - } - - [Fact] - public async Task TestSendAndReceiveFastExplicit() - { - await SendAndReceiveFastGutsAsync(false, true, false); - } - - [Fact] - public async Task TestSendAndReceiveNeverFast() - { - await SendAndReceiveFastGutsAsync(true, false, true); - } - - [Fact] - public async Task TestSendAndReceiveNeverFastWitReplyQueue() - { - await SendAndReceiveFastGutsAsync(true, true, false); - } - - [Fact] - public async Task TestReplyCompressionWithContainer() - { - var container = new DirectMessageListenerContainer(); - container.ConnectionFactory = Template.ConnectionFactory; - container.SetQueueNames(Route); - var messageListener = new MessageListenerAdapter(null, new TestMessageHandlerString()); - messageListener.SetBeforeSendReplyPostProcessors(new GZipPostProcessor()); - container.MessageListener = messageListener; - container.Initialize(); - await container.StartAsync(); - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(Template.ConnectionFactory); - - try - { - var props = new RabbitHeaderAccessor - { - ContentType = "text/plain" - }; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo"), props.MessageHeaders); - IMessage reply = rabbitTemplate.SendAndReceive(string.Empty, Route, message); - Assert.NotNull(reply); - Assert.Equal("gzip:utf-8", reply.Headers.ContentEncoding()); - var unzipper = new GUnzipPostProcessor(); - reply = unzipper.PostProcessMessage(reply); - Assert.Equal("FOO", Encoding.UTF8.GetString((byte[])reply.Payload)); - } - finally - { - await rabbitTemplate.StopAsync(); - await container.StopAsync(); - } - } - - [Fact] - public void TestRouting() - { - var connection1 = new Mock(); - var channel1 = new Mock(); - var properties1 = new Mock(); - ConnectionFactory1.Setup(f => f.CreateConnection()).Returns(connection1.Object); - connection1.Setup(c => c.CreateChannel(false)).Returns(channel1.Object); - connection1.Setup(c => c.IsOpen).Returns(true); - channel1.Setup(c => c.IsOpen).Returns(true); - channel1.Setup(c => c.CreateBasicProperties()).Returns(properties1.Object); - - var testPp = new TestPostProcessor("foo"); - RoutingTemplate.ConvertAndSend("exchange", "routingKey", "xyz", testPp); - channel1.Verify(c => c.BasicPublish(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); - - var connection2 = new Mock(); - var channel2 = new Mock(); - var properties2 = new Mock(); - ConnectionFactory2.Setup(f => f.CreateConnection()).Returns(connection2.Object); - connection2.Setup(c => c.CreateChannel(false)).Returns(channel2.Object); - connection2.Setup(c => c.IsOpen).Returns(true); - channel2.Setup(c => c.IsOpen).Returns(true); - channel2.Setup(c => c.CreateBasicProperties()).Returns(properties2.Object); - var testPp2 = new TestPostProcessor("bar"); - RoutingTemplate.ConvertAndSend("exchange", "routingKey", "xyz", testPp2); - channel1.Verify(c => c.BasicPublish(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); - } - - [Fact] - public void TestSendInGlobalTransactionCommit() - { - TestSendInGlobalTransactionGuts(false); - string result = Template.ReceiveAndConvert(Route); - Assert.Equal("message", result); - Assert.Null(Template.Receive(Route)); - } - - [Fact] - public void TestSendInGlobalTransactionRollback() - { - TestSendInGlobalTransactionGuts(true); - Assert.Null(Template.Receive(Route)); - } - - [Fact] - public void TestSendToMissingExchange() - { - var shutdownLatch = new CountdownEvent(1); - var shutdown = new AtomicReference(); - var testListener = new TestChannelListener(shutdown, shutdownLatch); - _connectionFactory.AddChannelListener(testListener); - - var connLatch = new CountdownEvent(1); - var testListener2 = new TestConnectionListener(shutdown, connLatch); - _connectionFactory.AddConnectionListener(testListener2); - - Template.ConvertAndSend(Guid.NewGuid().ToString(), "foo", "bar"); - Assert.True(shutdownLatch.Wait(TimeSpan.FromSeconds(10))); - Template.IsChannelTransacted = true; - - try - { - Template.ConvertAndSend(Guid.NewGuid().ToString(), "foo", "bar"); - throw new Exception("Expected exception"); - } - catch (RabbitException) - { - RC.ShutdownEventArgs shutdownArgs = shutdown.Value.Args; - Assert.Equal(60, shutdownArgs.ClassId); - Assert.Equal(40, shutdownArgs.MethodId); - Assert.Equal(404, shutdownArgs.ReplyCode); - Assert.Contains("NOT_FOUND", shutdownArgs.ReplyText, StringComparison.Ordinal); - } - - var signal = new RC.ShutdownEventArgs(RC.ShutdownInitiator.Library, 320, "CONNECTION_FORCED", 10, 0); - _connectionFactory.ConnectionShutdownCompleted(this, signal); - - Assert.True(connLatch.Wait(TimeSpan.FromSeconds(10))); - Assert.Equal(10, shutdown.Value.Args.ClassId); - Assert.Contains("CONNECTION_FORCED", shutdown.Value.Args.ReplyText, StringComparison.Ordinal); - Assert.Equal(320, shutdown.Value.Args.ReplyCode); - } - - [Fact] - public void TestInvoke() - { - Template.Invoke(t => - { - t.Execute(c => - { - t.Execute(chan => - { - Assert.Same(c, chan); - return null; - }); - - return null; - }); - - return null; - }); - - Assert.Null(Template.DedicatedChannels.Value); - } - - [Fact] - public void WaitForConfirms() - { - _connectionFactory.PublisherConfirmType = CachingConnectionFactory.ConfirmType.Correlated; - - var messages = new List - { - "foo", - "bar" - }; - - bool result = Template.Invoke(t => - { - messages.ForEach(m => t.ConvertAndSend(string.Empty, Route, m)); - t.WaitForConfirmsOrDie(10_000); - return true; - }); - - Assert.True(result); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - Admin.DeleteQueue(Route); - Admin.DeleteQueue(ReplyQueueName); - Admin.ConnectionFactory.Dispose(); - Template.Dispose(); - _connectionFactory.Dispose(); - } - } - - protected virtual RabbitTemplate CreateSendAndReceiveRabbitTemplate(IConnectionFactory connectionFactory) - { - var rabbitTemplate = new RabbitTemplate(connectionFactory) - { - UseDirectReplyToContainer = false - }; - - return rabbitTemplate; - } - - private void TestSendInGlobalTransactionGuts(bool rollback) - { - Template.IsChannelTransacted = true; - var tt = new TransactionTemplate(new TestTransactionManager()); - - tt.Execute(_ => - { - Template.ConvertAndSend(Route, "message"); - - if (rollback) - { - var adapter = new TestTransactionSynchronizationAdapter(); - TransactionSynchronizationManager.RegisterSynchronization(adapter); - } - }); - } - - private async Task SendAndReceiveFastGutsAsync(bool tempQueue, bool setDirectReplyToExplicitly, bool expectUsedTemp) - { - using RabbitTemplate rabbitTemplate = CreateSendAndReceiveRabbitTemplate(_connectionFactory); - - try - { - rabbitTemplate.Execute(channel => - { - channel.QueueDeclarePassive(Address.AmqRabbitMQReplyTo); - }); - - rabbitTemplate.UseTemporaryReplyQueues = tempQueue; - - if (setDirectReplyToExplicitly) - { - rabbitTemplate.ReplyAddress = Address.AmqRabbitMQReplyTo; - } - - var container = new DirectMessageListenerContainer(); - container.ConnectionFactory = rabbitTemplate.ConnectionFactory; - container.SetQueueNames(Route); - var replyToWas = new AtomicReference(); - var handler = new TestMessageHandler(replyToWas); - - var messageListenerAdapter = new MessageListenerAdapter(null, handler) - { - MessageConverter = null - }; - - container.MessageListener = messageListenerAdapter; - await container.StartAsync(); - rabbitTemplate.DefaultReceiveQueue = Route; - rabbitTemplate.RoutingKey = Route; - string result = rabbitTemplate.ConvertSendAndReceive("foo"); - await container.StopAsync(); - Assert.Equal("FOO", result); - - if (expectUsedTemp) - { - Assert.False(replyToWas.Value.StartsWith(Address.AmqRabbitMQReplyTo, StringComparison.Ordinal)); - } - else - { - Assert.StartsWith(Address.AmqRabbitMQReplyTo, replyToWas.Value, StringComparison.Ordinal); - } - } - catch (Exception e) - { - Assert.Contains("404", e.InnerException.InnerException.Message, StringComparison.Ordinal); - } - finally - { - await rabbitTemplate.StopAsync(); - } - } - - private async Task TestReceiveAndReplyAsync(int timeout) - { - Template.DefaultReceiveQueue = Route; - Template.RoutingKey = Route; - Template.ConvertAndSend(Route, "test"); - Template.ReceiveTimeout = timeout; - - bool received = ReceiveAndReply(); - int n = 0; - - while (timeout == 0 && !received && n++ < 100) - { - await Task.Delay(100); - received = ReceiveAndReply(); - } - - Assert.True(received); - - IMessage receive = Template.Receive(); - Assert.NotNull(receive); - Assert.Equal("bar", receive.Headers.Get("foo")); - - Template.ConvertAndSend(Route, 1); - received = Template.ReceiveAndReply(Route, payload => payload + 1); - Assert.True(received); - - int result = Template.ReceiveAndConvert(Route); - Assert.Equal(2, result); - - Template.ConvertAndSend(Route, 2); - received = Template.ReceiveAndReply(Route, payload => payload * 2); - Assert.True(received); - - result = Template.ReceiveAndConvert(Route); - Assert.Equal(4, result); - - received = false; - - if (timeout > 0) - { - Template.ReceiveTimeout = 1; - } - - try - { - received = Template.ReceiveAndReply(message => message); - } - catch (ConsumeOkNotReceivedException) - { - // we're expecting no result, this could happen, depending on timing. - } - - Assert.False(received); - - Template.ConvertAndSend(Route, "test"); - Template.ReceiveTimeout = timeout; - received = Template.ReceiveAndReply(_ => null); - Assert.True(received); - - Template.ReceiveTimeout = 0; - IMessage result2 = Template.Receive(); - Assert.Null(result2); - - Template.ConvertAndSend(Route, "TEST"); - Template.ReceiveTimeout = timeout; - - received = Template.ReceiveAndReply(message => - { - var messageProperties = new RabbitHeaderAccessor(new MessageHeaders()) - { - ContentType = message.Headers.ContentType() - }; - - messageProperties.SetHeader("testReplyTo", new Address(string.Empty, Route)); - return Message.Create(message.Payload, messageProperties.MessageHeaders, message.Payload.GetType()); - }, (_, reply) => reply.Headers.Get
("testReplyTo")); - - Assert.True(received); - string result3 = Template.ReceiveAndConvert(Route); - Assert.Equal("TEST", result3); - - Template.ReceiveTimeout = 0; - Assert.Null(Template.Receive(Route)); - - Template.IsChannelTransacted = true; - - Template.ConvertAndSend(Route, "TEST"); - Template.ReceiveTimeout = timeout; - var payloadReference = new AtomicReference(); - var transactionTemplate = new TransactionTemplate(new TestTransactionManager()); - - string result4 = transactionTemplate.Execute(_ => - { - bool received1 = Template.ReceiveAndReply(payload => - { - payloadReference.Value = payload; - return null; - }); - - Assert.True(received1); - return payloadReference.Value; - }); - - Assert.Equal("TEST", result4); - Template.ReceiveTimeout = 0; - Assert.Null(Template.Receive(Route)); - - Template.ConvertAndSend(Route, "TEST"); - Template.ReceiveTimeout = timeout; - - try - { - transactionTemplate = new TransactionTemplate(new TestTransactionManager()); - - transactionTemplate.Execute(_ => - { - Template.ReceiveAndReply(message => message, (_, _) => throw new PlannedException()); - }); - } - catch (Exception e) - { - Assert.IsType(e.InnerException); - } - - Assert.Equal("TEST", Template.ReceiveAndConvert(Route)); - Template.ReceiveTimeout = 0; - Assert.Null(Template.ReceiveAndConvert(Route)); - - Template.ConvertAndSend("test"); - Template.ReceiveTimeout = timeout; - - try - { - Template.ReceiveAndReply(_ => null); - throw new Exception("Should have throw Exception"); - } - catch (Exception e) - { - Assert.IsType(e.InnerException); - } - } - - private bool ReceiveAndReply() - { - return Template.ReceiveAndReply(message => - { - RabbitHeaderAccessor.GetMutableAccessor(message).SetHeader("foo", "bar"); - return message; - }); - } - - private sealed class TestConnectionListener : IConnectionListener - { - private readonly AtomicReference _shutdown; - private readonly CountdownEvent _connLatch; - - public TestConnectionListener(AtomicReference shutdown, CountdownEvent connLatch) - { - _shutdown = shutdown; - _connLatch = connLatch; - } - - public void OnClose(IConnection connection) - { - } - - public void OnCreate(IConnection connection) - { - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - _shutdown.Value = new ShutdownSignalException(args); - _connLatch.Signal(); - } - } - - private sealed class TestChannelListener : IChannelListener - { - private readonly AtomicReference _shutdown; - private readonly CountdownEvent _shutdownLatch; - - public TestChannelListener(AtomicReference shutdown, CountdownEvent shutdownLatch) - { - _shutdown = shutdown; - _shutdownLatch = shutdownLatch; - } - - public void OnCreate(RC.IModel channel, bool transactional) - { - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - _shutdown.Value = new ShutdownSignalException(args); - _shutdownLatch.Signal(); - } - } - - private sealed class TestTransactionSynchronizationAdapter : ITransactionSynchronization - { - public void AfterCommit() - { - TransactionSynchronizationUtils.TriggerAfterCompletion(AbstractTransactionSynchronization.StatusRolledBack); - } - - public void AfterCompletion(int status) - { - } - - public void BeforeCommit(bool readOnly) - { - } - - public void BeforeCompletion() - { - } - - public void Flush() - { - } - - public void Resume() - { - } - - public void Suspend() - { - } - } - - private sealed class TestPostProcessor : IMessagePostProcessor - { - public string KeyValue { get; } - - public TestPostProcessor(string keyValue) - { - KeyValue = keyValue; - } - - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.SetHeader("cfKey", KeyValue); - return message; - } - - public IMessage PostProcessMessage(IMessage message) - { - return PostProcessMessage(message, null); - } - } - - private sealed class TestMessageHandlerString - { -#pragma warning disable S1144 // Unused private types or members should be removed - public string HandleMessage(string message) - { - return message.ToUpperInvariant(); - } -#pragma warning restore S1144 // Unused private types or members should be removed - } - - private sealed class TestMessageHandler - { - private readonly AtomicReference _replyToWas; - - public TestMessageHandler(AtomicReference replyToWas) - { - _replyToWas = replyToWas; - } - -#pragma warning disable S1144 // Unused private types or members should be removed - public IMessage HandleMessage(IMessage message) - { - _replyToWas.Value = message.Headers.ReplyTo(); - return Message.Create(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString((byte[])message.Payload).ToUpperInvariant()), message.Headers); - } -#pragma warning restore S1144 // Unused private types or members should be removed - } - - private sealed class TestTransactionManager : AbstractPlatformTransactionManager - { - protected override void DoBegin(object transaction, ITransactionDefinition definition) - { - } - - protected override void DoCommit(DefaultTransactionStatus status) - { - } - - protected override object DoGetTransaction() - { - return new object(); - } - - protected override void DoRollback(DefaultTransactionStatus status) - { - } - } - - public sealed class PlannedException : Exception - { - public PlannedException() - : base("Planned") - { - } - } - - private sealed class Foo - { - public override string ToString() - { - return "FooAsAString"; - } - } - - private sealed class PostProcessor3 : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return PostProcessMessage(message); - } - - public IMessage PostProcessMessage(IMessage message) - { - try - { - byte[] newPayload = Encoding.UTF8.GetBytes(Encoding.UTF8.GetString((byte[])message.Payload).ToUpperInvariant()); - return Message.Create(newPayload, message.Headers); - } - catch (Exception e) - { - throw new RabbitException("unexpected failure in test", e); - } - } - } - - private sealed class PostProcessor2 : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return PostProcessMessage(message); - } - - public IMessage PostProcessMessage(IMessage message) - { - var strings = message.Headers.Get("strings") as List; - Assert.NotNull(strings); - Assert.Contains("1", strings); - Assert.Contains("2", strings); - var objects = message.Headers.Get("objects") as List; - Assert.NotNull(objects); - Assert.Equal("FooAsAString", objects[0]); - Assert.Equal("FooAsAString", objects[1]); - var binaryTableValue = message.Headers.Get("bytes") as RC.BinaryTableValue; - Assert.NotNull(binaryTableValue); - Assert.Equal("abc", EncodingUtils.Utf8.GetString(binaryTableValue.Bytes)); - return message; - } - } - - private sealed class PostProcessor1 : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return PostProcessMessage(message); - } - - public IMessage PostProcessMessage(IMessage message) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = "text/other"; - - accessor.SetHeader("strings", new[] - { - "1", - "2" - }); - - accessor.SetHeader("objects", new object[] - { - new Foo(), - new Foo() - }); - - accessor.SetHeader("bytes", EncodingUtils.Utf8.GetBytes("abc")); - return message; - } - } - - private sealed class MockSingleConnectionFactory : SingleConnectionFactory - { - public MockSingleConnectionFactory(string hostname) - : base(hostname, null) - { - } - - public override IConnection CreateConnection() - { - IConnection connection = base.CreateConnection(); - return new MockConnection(connection); - } - } - - private sealed class MockConnection : IConnection - { - public IConnection Delegate { get; } - - public bool IsOpen => Delegate.IsOpen; - - public int LocalPort => Delegate.LocalPort; - - public RC.IConnection Connection => Delegate.Connection; - - public MockConnection(IConnection connection) - { - Delegate = connection; - } - - public void AddBlockedListener(IBlockedListener listener) - { - Delegate.AddBlockedListener(listener); - } - - public void Close() - { - Delegate.Close(); - } - - public RC.IModel CreateChannel(bool transactional = false) - { - RC.IModel chan = Delegate.CreateChannel(transactional); - return new MockChannel(chan); - } - - public void Dispose() - { - Delegate.Dispose(); - } - - public bool RemoveBlockedListener(IBlockedListener listener) - { - return Delegate.RemoveBlockedListener(listener); - } - } - - private sealed class MockConsumer : RC.IBasicConsumer - { - public RC.IBasicConsumer Delegate { get; } - - public RC.IModel Model => Delegate.Model; - - public event EventHandler ConsumerCancelled - { - add => Delegate.ConsumerCancelled += value; - remove => Delegate.ConsumerCancelled -= value; - } - - public MockConsumer(RC.IBasicConsumer consumer) - { - Delegate = consumer; - } - - public void HandleBasicCancel(string consumerTag) - { - Delegate.HandleBasicCancel(consumerTag); - } - - public void HandleBasicCancelOk(string consumerTag) - { - Delegate.HandleBasicCancelOk(consumerTag); - } - - public void HandleBasicConsumeOk(string consumerTag) - { - Delegate.HandleBasicConsumeOk(consumerTag); - - try - { - HandleBasicCancel(consumerTag); - } - catch (Exception e) - { - throw new InvalidOperationException("HandleBasicCancel error", e); - } - } - - public void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - RC.IBasicProperties properties, byte[] body) - { - Delegate.HandleBasicDeliver(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body); - } - - public void HandleModelShutdown(object model, RC.ShutdownEventArgs reason) - { - throw new NotImplementedException(); - } - } - - private sealed class MockChannel : PublisherCallbackChannel - { - public MockChannel(RC.IModel channel, ILogger logger = null) - : base(channel, logger) - { - } - - public override string BasicConsume(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, IDictionary arguments, - RC.IBasicConsumer consumer) - { - return base.BasicConsume(queue, autoAck, consumerTag, noLocal, exclusive, arguments, new MockConsumer(consumer)); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateSimpleRoutingConnectionFactoryTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateSimpleRoutingConnectionFactoryTest.cs deleted file mode 100644 index 12cdbd0327..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateSimpleRoutingConnectionFactoryTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -[Trait("Category", "Integration")] -public sealed class RabbitTemplateSimpleRoutingConnectionFactoryTest -{ - private const string ConnectionFactoryName1 = "foo"; - private const string ConnectionFactoryName2 = "bar"; - - private RabbitTemplate RoutingTemplate { get; } - - private Mock ConnectionFactory1 { get; } - private Mock ConnectionFactory2 { get; } - private Mock DefaultConnectionFactory { get; } - - public RabbitTemplateSimpleRoutingConnectionFactoryTest() - { - RoutingTemplate = new RabbitTemplate(); - - var routingConnFactory = new SimpleRoutingConnectionFactory(); - - ConnectionFactory1 = new Mock(); - ConnectionFactory2 = new Mock(); - DefaultConnectionFactory = new Mock(); - routingConnFactory.AddTargetConnectionFactory(ConnectionFactoryName1, ConnectionFactory1.Object); - routingConnFactory.AddTargetConnectionFactory(ConnectionFactoryName2, ConnectionFactory2.Object); - - RoutingTemplate.ConnectionFactory = routingConnFactory; - } - - [Fact] - public void ConvertSendAndReceiveShouldBindToRoutingConnectionFactoriesWithSimpleResourceHolder() - { - static Mock SetupMocks(Mock cf) - { - var connection = new Mock(); - var channel = new Mock(); - cf.Setup(f => f.CreateConnection()).Returns(connection.Object); - connection.Setup(c => c.CreateChannel(false)).Returns(channel.Object); - connection.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - channel.Setup(c => c.QueueDeclarePassive(Address.AmqRabbitMQReplyTo)).Returns(() => new RC.QueueDeclareOk(Address.AmqRabbitMQReplyTo, 0, 0)); - return channel; - } - - Mock channel1 = SetupMocks(ConnectionFactory1); - Mock channel2 = SetupMocks(ConnectionFactory2); - - // act(a): send message using connection factory 1 - SimpleResourceHolder.Bind(RoutingTemplate.ConnectionFactory, ConnectionFactoryName1); - RoutingTemplate.ConvertSendAndReceive("exchFoo", "rkFoo", "msgFoo"); - SimpleResourceHolder.UnbindIfPossible(RoutingTemplate.ConnectionFactory); - - // act(b): send message using connection factory 2 - SimpleResourceHolder.Bind(RoutingTemplate.ConnectionFactory, ConnectionFactoryName2); - RoutingTemplate.ConvertSendAndReceive("exchBar", "rkBar", "msgBar"); - SimpleResourceHolder.UnbindIfPossible(RoutingTemplate.ConnectionFactory); - - // assert: both connection factories should be used - ConnectionFactory1.Verify(cf => cf.CreateConnection(), Times.AtLeastOnce); - channel1.Verify(c => c.BasicPublish("exchFoo", "rkFoo", It.IsAny(), It.IsAny(), It.IsAny())); - - ConnectionFactory2.Verify(cf => cf.CreateConnection(), Times.AtLeastOnce); - channel2.Verify(c => c.BasicPublish("exchBar", "rkBar", It.IsAny(), It.IsAny(), It.IsAny())); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateTest.cs b/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateTest.cs deleted file mode 100644 index fed53cd383..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Core/RabbitTemplateTest.cs +++ /dev/null @@ -1,538 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using RabbitMQ.Client.Exceptions; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Retry; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Common.Transaction; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Test.Connection; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Core; - -public sealed class RabbitTemplateTest -{ - [Fact] - public void ReturnConnectionAfterCommit() - { - var txTemplate = new TransactionTemplate(new TestTransactionManager()); - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - mockChannel.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - var connectionFactory = new CachingConnectionFactory(mockConnectionFactory.Object); - - var template = new RabbitTemplate(connectionFactory) - { - IsChannelTransacted = true - }; - - txTemplate.Execute(_ => - { - template.ConvertAndSend("foo", "bar"); - }); - - txTemplate.Execute(_ => - { - template.ConvertAndSend("baz", "qux"); - }); - - mockConnectionFactory.Verify(c => c.CreateConnection(It.IsAny()), Times.Once); - mockConnection.Verify(c => c.CreateModel(), Times.Once); - } - - [Fact] - public void TestConvertBytes() - { - var template = new RabbitTemplate(); - byte[] payload = EncodingUtils.GetDefaultEncoding().GetBytes("Hello, world!"); - IMessage message = template.ConvertMessageIfNecessary(payload); - Assert.Same(payload, message.Payload); - } - - [Fact] - public void TestConvertString() - { - var template = new RabbitTemplate(); - const string payload = "Hello, world!"; - IMessage message = template.ConvertMessageIfNecessary(payload); - string messageString = EncodingUtils.GetDefaultEncoding().GetString((byte[])message.Payload); - Assert.Equal(payload, messageString); - } - - [Fact] - public void TestConvertMessage() - { - var template = new RabbitTemplate(); - byte[] payload = EncodingUtils.GetDefaultEncoding().GetBytes("Hello, world!"); - IMessage input = Message.Create(payload, new MessageHeaders()); - IMessage message = template.ConvertMessageIfNecessary(input); - Assert.Same(message, input); - } - - [Fact] -#pragma warning disable S2699 // Tests should include assertions - public void DoNotHangConsumerThread() -#pragma warning restore S2699 // Tests should include assertions - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - mockChannel.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - var consumer = new AtomicReference(); - - mockChannel.Setup(c => - c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(new RC.QueueDeclareOk("foo", 0, 0)); - - mockChannel - .Setup(c => c.BasicConsume(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())) - .Callback, RC.IBasicConsumer>((_, _, _, _, _, _, arg7) => consumer.Value = arg7); - - var connectionFactory = new SingleConnectionFactory(mockConnectionFactory.Object, null); - - var template = new RabbitTemplate(connectionFactory) - { - ReplyTimeout = 1 - }; - - byte[] payload = EncodingUtils.GetDefaultEncoding().GetBytes("Hello, world!"); - IMessage input = Message.Create(payload, new MessageHeaders()); - template.DoSendAndReceiveWithTemporary("foo", "bar", input, null, default); - - // used to hang here because of the SynchronousQueue and doSendAndReceive() already exited - consumer.Value.HandleBasicDeliver("foo", 1ul, false, "foo", "bar", new MockRabbitBasicProperties(), Array.Empty()); - } - - [Fact] - public void TestRetry() - { - var mockConnectionFactory = new Mock(); - var count = new AtomicInteger(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Callback(() => count.IncrementAndGet()) - .Throws(new AuthenticationFailureException("foo")); - - var connectionFactory = new SingleConnectionFactory(mockConnectionFactory.Object, null); - - var template = new RabbitTemplate(connectionFactory) - { - RetryTemplate = new PollyRetryTemplate(new Dictionary(), 3, true, 1, 1, 1) - }; - - try - { - template.ConvertAndSend("foo", "bar", "baz"); - } - catch (RabbitAuthenticationException e) - { - Assert.Contains("foo", e.InnerException.Message, StringComparison.Ordinal); - } - - Assert.Equal(3, count.Value); - } - - [Fact] - public void TestEvaluateDirectReplyToWithConnectException() - { - var mockConnectionFactory = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Throws(new RabbitConnectException(null)); - var template = new RabbitTemplate(mockConnectionFactory.Object); - Assert.Throws(() => template.ConvertSendAndReceive("foo")); - Assert.False(template.EvaluatedFastReplyTo); - } - - [Fact] - public void TestEvaluateDirectReplyToWithIOException() - { - var mockConnectionFactory = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection()).Throws(new RabbitIOException(null)); - var template = new RabbitTemplate(mockConnectionFactory.Object); - Assert.Throws(() => template.ConvertSendAndReceive("foo")); - Assert.False(template.EvaluatedFastReplyTo); - } - - [Fact] - public void TestEvaluateDirectReplyToWithIOExceptionDeclareFailed() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - - mockChannel.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - mockChannel.Setup(c => - c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(() => new RC.QueueDeclareOk("foo", 0, 0)); - - mockChannel.Setup(c => c.QueueDeclarePassive(Address.AmqRabbitMQReplyTo)).Throws(new ShutdownSignalException( - new RC.ShutdownEventArgs(RC.ShutdownInitiator.Peer, RabbitUtils.NotFound, string.Empty, RabbitUtils.QueueClassId, RabbitUtils.DeclareMethodId))); - - var connectionFactory = new SingleConnectionFactory(mockConnectionFactory.Object, null); - - var template = new RabbitTemplate(connectionFactory) - { - ReplyTimeout = 1 - }; - - template.ConvertSendAndReceive("foo"); - Assert.True(template.EvaluatedFastReplyTo); - Assert.False(template.UsingFastReplyTo); - } - - [Fact] - public void TestEvaluateDirectReplyToOk() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockConnection.Setup(c => c.CreateModel()).Returns(mockChannel.Object); - - mockChannel.Setup(c => c.IsOpen).Returns(true); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockChannel.Setup(c => c.QueueDeclarePassive(Address.AmqRabbitMQReplyTo)).Returns(() => new RC.QueueDeclareOk(Address.AmqRabbitMQReplyTo, 0, 0)); - var connectionFactory = new SingleConnectionFactory(mockConnectionFactory.Object, null); - - var template = new RabbitTemplate(connectionFactory) - { - ReplyTimeout = 1 - }; - - template.ConvertSendAndReceive("foo"); - Assert.True(template.EvaluatedFastReplyTo); - Assert.True(template.UsingFastReplyTo); - } - - [Fact] - public void TestRecovery() - { - var mockConnectionFactory = new Mock(); - var count = new AtomicInteger(); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Callback(() => count.IncrementAndGet()) - .Throws(new AuthenticationFailureException("foo")); - - var connectionFactory = new SingleConnectionFactory(mockConnectionFactory.Object, null); - - var template = new RabbitTemplate(connectionFactory) - { - RetryTemplate = new PollyRetryTemplate(new Dictionary(), 3, true, 1, 1, 1) - }; - - var recoverInvoked = new AtomicBoolean(); - template.RecoveryCallback = new TestRecoveryRecoveryCallback(recoverInvoked); - template.ConvertAndSend("foo", "bar", "baz"); - Assert.Equal(3, count.Value); - Assert.True(recoverInvoked.Value); - } - - [Fact] - public void TestPublisherConfirmsReturnsSetup() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel = new Mock(); - mockChannel.Setup(m => m.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockConnectionFactory.Setup(f => f.IsPublisherConfirms).Returns(true); - mockConnectionFactory.Setup(f => f.IsPublisherReturns).Returns(true); - mockConnectionFactory.Setup(f => f.CreateConnection()).Returns(mockConnection.Object); - mockConnection.Setup(c => c.CreateChannel(false)).Returns(mockChannel.Object); - var template = new RabbitTemplate(mockConnectionFactory.Object); - template.ConvertAndSend("foo"); - mockChannel.Verify(c => c.AddListener(template)); - } - - [Fact] - public void TestNoListenerAllowed1() - { - var template = new RabbitTemplate(); - Assert.Throws(() => template.GetExpectedQueueNames()); - } - - [Fact] - public void TestNoListenerAllowed2() - { - var template = new RabbitTemplate - { - ReplyAddress = Address.AmqRabbitMQReplyTo - }; - - Assert.Throws(() => template.GetExpectedQueueNames()); - } - - [Fact] - public void TestNestedTxBinding() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - var mockChannel2 = new Mock(); - - mockChannel1.Setup(c => c.IsOpen).Returns(true); - mockChannel2.Setup(c => c.IsOpen).Returns(true); - mockConnection.Setup(c => c.IsOpen).Returns(true); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.SetupSequence(c => c.CreateModel()).Returns(mockChannel1.Object).Returns(mockChannel2.Object); - - mockChannel1.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockChannel2.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - mockChannel1.Setup(c => - c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(() => new RC.QueueDeclareOk("foo", 0, 0)); - - var ccf = new CachingConnectionFactory(mockConnectionFactory.Object); - - var rabbitTemplate = new RabbitTemplate(ccf) - { - IsChannelTransacted = true - }; - - var admin = new RabbitAdmin(rabbitTemplate); - var mockContext = new Mock(); - - mockContext.Setup(c => c.GetServices()).Returns(new List - { - new Queue("foo") - }); - - admin.ApplicationContext = mockContext.Object; - var templateChannel = new AtomicReference(); - var transTemplate = new TransactionTemplate(new TestTransactionManager()); - - transTemplate.Execute(_ => - { - return rabbitTemplate.Execute(c => - { - templateChannel.Value = ((IChannelProxy)c).TargetChannel; - return true; - }); - }); - - mockChannel1.Verify(c => c.TxSelect()); - - mockChannel1.Verify(c => - c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())); - - mockChannel1.Verify(c => c.TxCommit()); - Assert.Same(templateChannel.Value, mockChannel1.Object); - } - - [Fact] - public void TestShutdownWhileWaitingForReply() - { - var mockConnectionFactory = new Mock(); - var mockConnection = new Mock(); - var mockChannel1 = new Mock(); - - mockChannel1.Setup(c => c.IsOpen).Returns(true); - mockConnection.Setup(c => c.IsOpen).Returns(true); - - mockConnectionFactory.Setup(f => f.CreateConnection(It.IsAny())).Returns(mockConnection.Object); - mockConnection.SetupSequence(c => c.CreateModel()).Returns(mockChannel1.Object); - - mockChannel1.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - mockChannel1.Setup(c => - c.QueueDeclare(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(() => new RC.QueueDeclareOk("foo", 0, 0)); - - var listener = new AtomicReference>(); - var shutdownLatch = new CountdownEvent(1); - - mockChannel1.SetupAdd(m => m.ModelShutdown += It.IsAny>()).Callback>(handler => - { - listener.Value = handler; - shutdownLatch.Signal(); - }); - - var connectionFactory = new SingleConnectionFactory(mockConnectionFactory.Object, null); - - var template = new RabbitTemplate(connectionFactory) - { - ReplyTimeout = 60_000 - }; - - IMessage input = Message.Create(EncodingUtils.GetDefaultEncoding().GetBytes("Hello, world!"), new MessageHeaders()); - - Task.Run(() => - { - try - { - shutdownLatch.Wait(TimeSpan.FromSeconds(10)); - } - catch (Exception) - { - // Ignore - } - - listener.Value.Invoke(null, new RC.ShutdownEventArgs(RC.ShutdownInitiator.Peer, RabbitUtils.NotFound, string.Empty)); - }); - - try - { - template.DoSendAndReceiveWithTemporary("foo", "bar", input, null, default); - throw new Exception("Expected exception"); - } - catch (RabbitException e) - { - Exception cause = e.InnerException; - Assert.IsType(cause); - } - } - - [Fact] - public void TestAddAndRemoveBeforePublishPostProcessors() - { - var mpp1 = new DoNothingMessagePostProcessor(); - var mpp2 = new DoNothingMessagePostProcessor(); - var mpp3 = new DoNothingMessagePostProcessor(); - var rabbitTemplate = new RabbitTemplate(); - rabbitTemplate.AddBeforePublishPostProcessors(mpp1, mpp2); - rabbitTemplate.AddBeforePublishPostProcessors(mpp3); - bool removed = rabbitTemplate.RemoveBeforePublishPostProcessor(mpp1); - Assert.True(removed); - Assert.Equal(2, rabbitTemplate.BeforePublishPostProcessors.Count); - Assert.Contains(mpp2, rabbitTemplate.BeforePublishPostProcessors); - Assert.Contains(mpp3, rabbitTemplate.BeforePublishPostProcessors); - } - - [Fact] - public void TestAddAndRemoveAfterReceivePostProcessors() - { - var mpp1 = new DoNothingMessagePostProcessor(); - var mpp2 = new DoNothingMessagePostProcessor(); - var mpp3 = new DoNothingMessagePostProcessor(); - var rabbitTemplate = new RabbitTemplate(); - rabbitTemplate.AddAfterReceivePostProcessors(mpp1, mpp2); - rabbitTemplate.AddAfterReceivePostProcessors(mpp3); - bool removed = rabbitTemplate.RemoveAfterReceivePostProcessor(mpp1); - Assert.True(removed); - Assert.Equal(2, rabbitTemplate.AfterReceivePostProcessors.Count); - Assert.Contains(mpp2, rabbitTemplate.AfterReceivePostProcessors); - Assert.Contains(mpp3, rabbitTemplate.AfterReceivePostProcessors); - } - - [Fact] - public void TestPublisherConnWithInvoke() - { - var cf = new Mock(); - var pcf = new Mock(); - cf.SetupGet(c => c.PublisherConnectionFactory).Returns(pcf.Object); - - var template = new RabbitTemplate(cf.Object) - { - UsePublisherConnection = true - }; - - var mockConnection = new Mock(); - var mockChannel = new Mock(); - pcf.Setup(c => c.CreateConnection()).Returns(mockConnection.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockConnection.Setup(c => c.CreateChannel(false)).Returns(mockChannel.Object); - template.Invoke(_ => null); - pcf.Verify(c => c.CreateConnection()); - mockConnection.Verify(c => c.CreateChannel(false)); - } - - [Fact] - public void TestPublisherConnWithInvokeInTx() - { - var cf = new Mock(); - var pcf = new Mock(); - cf.SetupGet(c => c.PublisherConnectionFactory).Returns(pcf.Object); - - var template = new RabbitTemplate(cf.Object) - { - UsePublisherConnection = true, - IsChannelTransacted = true - }; - - var mockConnection = new Mock(); - var mockChannel = new Mock(); - - pcf.Setup(c => c.CreateConnection()).Returns(mockConnection.Object); - mockConnection.Setup(c => c.IsOpen).Returns(true); - mockConnection.Setup(c => c.CreateChannel(true)).Returns(mockChannel.Object); - template.Invoke(_ => null); - pcf.Verify(c => c.CreateConnection()); - mockConnection.Verify(c => c.CreateChannel(true)); - } - - private sealed class DoNothingMessagePostProcessor : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message) - { - return message; - } - - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return message; - } - } - - private sealed class TestRecoveryRecoveryCallback : IRecoveryCallback - { - public AtomicBoolean Boolean { get; } - - public TestRecoveryRecoveryCallback(AtomicBoolean boolean) - { - Boolean = boolean; - } - - public object Recover(IRetryContext context) - { - Boolean.Value = true; - return null; - } - } - - private sealed class TestTransactionManager : AbstractPlatformTransactionManager - { - protected override object DoGetTransaction() - { - return new object(); - } - - protected override void DoBegin(object transaction, ITransactionDefinition definition) - { - } - - protected override void DoCommit(DefaultTransactionStatus status) - { - } - - protected override void DoRollback(DefaultTransactionStatus status) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Extensions/RabbitServiceExtensionsTest.cs b/src/Messaging/test/RabbitMQ.Test/Extensions/RabbitServiceExtensionsTest.cs deleted file mode 100644 index e0e9820d89..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Extensions/RabbitServiceExtensionsTest.cs +++ /dev/null @@ -1,611 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Text; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Contexts; -using Steeltoe.Configuration.CloudFoundry.ServiceBinding; -using Steeltoe.Connectors.RabbitMQ; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Xunit; -using SimpleMessageConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter.SimpleMessageConverter; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Extensions; - -public sealed class RabbitServiceExtensionsTest -{ - [Fact] - public void AddRabbitTemplate_DefaultName() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitTemplate(); - ServiceProvider provider = services.BuildServiceProvider(true); - RabbitTemplate template = provider.GetRabbitTemplate(); - Assert.NotNull(template); - Assert.Equal(RabbitTemplate.DefaultServiceName, template.ServiceName); - var context = provider.GetService(); - Assert.Same(template, context.GetService(RabbitTemplate.DefaultServiceName)); - Assert.Same(template, provider.GetService()); - Assert.Same(template, context.GetService()); - } - - [Fact] - public void AddRabbitTemplate_SingleName() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitTemplate("foo"); - ServiceProvider provider = services.BuildServiceProvider(true); - RabbitTemplate template = provider.GetRabbitTemplate("foo"); - Assert.NotNull(template); - Assert.Equal("foo", template.ServiceName); - var context = provider.GetService(); - Assert.Same(template, context.GetService("foo")); - Assert.Same(template, context.GetService("foo")); - } - - [Fact] - public void AddRabbitTemplate_MultipleNames() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitTemplate(); - services.AddRabbitTemplate("foo"); - services.AddRabbitTemplate("bar"); - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - RabbitTemplate template = provider.GetRabbitTemplate(); - Assert.NotNull(template); - Assert.Equal(RabbitTemplate.DefaultServiceName, template.ServiceName); - Assert.Same(template, context.GetService(RabbitTemplate.DefaultServiceName)); - Assert.Same(template, context.GetService(RabbitTemplate.DefaultServiceName)); - RabbitTemplate template1 = provider.GetRabbitTemplate("foo"); - Assert.NotNull(template1); - Assert.Same(template1, context.GetService("foo")); - Assert.Same(template1, context.GetService("foo")); - Assert.Equal("foo", template1.ServiceName); - RabbitTemplate template2 = provider.GetRabbitTemplate("bar"); - Assert.NotNull(template2); - Assert.Same(template2, context.GetService("bar")); - Assert.Same(template2, context.GetService("bar")); - Assert.Equal("bar", template2.ServiceName); - IEnumerable all = provider.GetServices(); - Assert.Equal(3, all.Count()); - } - - [Fact] - public void AddRabbitTemplate_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - services.AddRabbitDefaultMessageConverter(); - - services.AddRabbitTemplate((_, t) => - { - t.CorrelationKey = "foobar"; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - RabbitTemplate template = provider.GetRabbitTemplate(); - Assert.NotNull(template); - Assert.Equal("foobar", template.CorrelationKey); - } - - [Fact] - public void AddRabbitAdmin_DefaultName() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitAdmin(); - ServiceProvider provider = services.BuildServiceProvider(true); - RabbitAdmin admin = provider.GetRabbitAdmin(); - Assert.NotNull(admin); - Assert.Equal(RabbitAdmin.DefaultServiceName, admin.ServiceName); - var context = provider.GetService(); - Assert.Same(admin, context.GetService(RabbitAdmin.DefaultServiceName)); - Assert.Same(admin, context.GetService(RabbitAdmin.DefaultServiceName)); - } - - [Fact] - public void AddRabbitAdmin_SingleName() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitAdmin("foo"); - ServiceProvider provider = services.BuildServiceProvider(true); - RabbitAdmin admin = provider.GetRabbitAdmin("foo"); - Assert.NotNull(admin); - Assert.Equal("foo", admin.ServiceName); - var context = provider.GetService(); - Assert.Same(admin, context.GetService("foo")); - Assert.Same(admin, context.GetService("foo")); - } - - [Fact] - public void AddRabbitAdmin_MultipleNames() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - services.AddRabbitDefaultMessageConverter(); - services.AddRabbitAdmin(); - services.AddRabbitAdmin("foo"); - services.AddRabbitAdmin("bar"); - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - RabbitAdmin admin = provider.GetRabbitAdmin(); - Assert.NotNull(admin); - Assert.Equal(RabbitAdmin.DefaultServiceName, admin.ServiceName); - Assert.Same(admin, context.GetService(RabbitAdmin.DefaultServiceName)); - Assert.Same(admin, context.GetService(RabbitAdmin.DefaultServiceName)); - RabbitAdmin admin1 = provider.GetRabbitAdmin("foo"); - Assert.NotNull(admin1); - Assert.Same(admin1, context.GetService("foo")); - Assert.Same(admin1, context.GetService("foo")); - Assert.Equal("foo", admin1.ServiceName); - RabbitAdmin admin2 = provider.GetRabbitAdmin("bar"); - Assert.NotNull(admin2); - Assert.Same(admin2, context.GetService("bar")); - Assert.Same(admin2, context.GetService("bar")); - Assert.Equal("bar", admin2.ServiceName); - - IEnumerable all = provider.GetServices(); - Assert.Equal(3, all.Count()); - } - - [Fact] - public void AddRabbitAdmin_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - services.AddRabbitDefaultMessageConverter(); - - services.AddRabbitAdmin((_, a) => - { - a.RetryDisabled = true; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - RabbitAdmin admin = provider.GetRabbitAdmin(); - Assert.NotNull(admin); - Assert.True(admin.RetryDisabled); - } - - [Fact] - public void AddRabbitQueues() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitQueues(new Queue("1"), new Queue("2")); - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - var q1 = context.GetService("1"); - var q2 = context.GetService("2"); - Assert.NotNull(q1); - Assert.NotNull(q2); - } - - [Fact] - public void AddRabbitQueue_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - - services.AddRabbitQueue("myQueue", (_, q) => - { - q.IsDurable = false; - q.ShouldDeclare = false; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - var q1 = context.GetService("myQueue"); - Assert.NotNull(q1); - Assert.Equal("myQueue", q1.QueueName); - Assert.False(q1.ShouldDeclare); - Assert.False(q1.IsDurable); - } - - [Fact] - public void AddRabbitBindings() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitBindings(new QueueBinding("1"), new ExchangeBinding("2")); - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - var b1 = context.GetService("1"); - var b2 = context.GetService("2"); - Assert.NotNull(b1); - Assert.NotNull(b2); - } - - [Fact] - public void AddRabbitBinding_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - - services.AddRabbitBinding("myBinding", Binding.DestinationType.Queue, (_, b) => - { - b.ShouldDeclare = false; - b.IgnoreDeclarationExceptions = false; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - var b1 = context.GetService("myBinding") as QueueBinding; - Assert.NotNull(b1); - Assert.Equal("myBinding", b1.BindingName); - Assert.False(b1.ShouldDeclare); - Assert.False(b1.IgnoreDeclarationExceptions); - } - - [Fact] - public void AddRabbitExchanges() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitExchanges(new DirectExchange("1"), new FanOutExchange("2")); - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - var e1 = context.GetService("1"); - var e2 = context.GetService("2"); - Assert.NotNull(e1); - Assert.NotNull(e2); - } - - [Fact] - public void AddRabbitExchange_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - - services.AddRabbitExchange("myExchange", ExchangeType.Direct, (_, e) => - { - e.IsDurable = false; - e.ShouldDeclare = false; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - var e1 = context.GetService("myExchange") as DirectExchange; - Assert.NotNull(e1); - Assert.Equal("myExchange", e1.ExchangeName); - Assert.False(e1.ShouldDeclare); - Assert.False(e1.IsDurable); - } - - [Fact] - public void AddRabbitListenerAttributeProcessor_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - - services.AddRabbitListenerAttributeProcessor((_, a) => - { - a.Charset = Encoding.UTF32; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var ap = provider.GetService() as RabbitListenerAttributeProcessor; - Assert.NotNull(ap); - Assert.Equal(Encoding.UTF32, ap.Charset); - } - - [Fact] - public void AddRabbitListenerEndpointRegistrar_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitListenerEndpointRegistry(); - - services.AddRabbitListenerEndpointRegistrar((_, r) => - { - r.ContainerFactoryServiceName = "foobar"; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var r = provider.GetService() as RabbitListenerEndpointRegistrar; - Assert.NotNull(r); - Assert.Equal("foobar", r.ContainerFactoryServiceName); - } - - [Fact] - public void AddRabbitListenerEndpointRegistry_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - - services.AddRabbitListenerEndpointRegistry((_, r) => - { - r.Phase = 100; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var r = provider.GetService() as RabbitListenerEndpointRegistry; - Assert.NotNull(r); - Assert.Equal(100, r.Phase); - } - - [Fact] - public void AddRabbitListenerContainerFactory_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - - services.AddRabbitListenerContainerFactory((_, f) => - { - f.ServiceName = "foobar"; - f.AckTimeout = 111111; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var f = provider.GetService() as DirectRabbitListenerContainerFactory; - Assert.NotNull(f); - Assert.Equal(111111, f.AckTimeout); - Assert.Equal("foobar", f.ServiceName); - var context = provider.GetService(); - Assert.Same(f, context.GetService("foobar")); - } - - [Fact] - public void AddRabbitListenerContainerFactory_MultipleNamed() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - services.AddRabbitListenerContainerFactory("foobar"); - services.AddRabbitListenerContainerFactory("barfoo"); - - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - var f1 = context.GetService("foobar"); - var f2 = context.GetService("barfoo"); - Assert.NotSame(f1, f2); - Assert.Equal("foobar", f1.ServiceName); - Assert.Equal("barfoo", f2.ServiceName); - } - - [Fact] - public void AddRabbitListenerContainerFactory_MultipleConfigure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - services.AddRabbitConnectionFactory(); - - services.AddRabbitListenerContainerFactory((_, f) => - { - f.ServiceName = "foobar"; - f.AckTimeout = 111111; - }); - - services.AddRabbitListenerContainerFactory((_, f) => - { - f.ServiceName = "barfoo"; - f.AckTimeout = 222222; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var context = provider.GetService(); - var f1 = context.GetService("foobar") as DirectRabbitListenerContainerFactory; - var f2 = context.GetService("barfoo") as DirectRabbitListenerContainerFactory; - Assert.NotSame(f1, f2); - Assert.Equal("foobar", f1.ServiceName); - Assert.Equal(111111, f1.AckTimeout); - Assert.Equal("barfoo", f2.ServiceName); - Assert.Equal(222222, f2.AckTimeout); - } - - [Fact] - public void AddRabbitContainerFactory_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - - services.AddRabbitConnectionFactory((_, f) => - { - f.Port = 9999; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var f = provider.GetService() as CachingConnectionFactory; - Assert.NotNull(f); - Assert.Equal(9999, f.Port); - } - - [Fact] - public void AddDefaultRabbitMessageConverter_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - - services.AddRabbitDefaultMessageConverter((_, c) => - { - c.CreateMessageIds = true; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var c = provider.GetService() as SimpleMessageConverter; - Assert.NotNull(c); - Assert.True(c.CreateMessageIds); - } - - [Fact] - public void AddRabbitMessageConverter_Configure() - { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationBuilder().Build()); - services.AddRabbitHostingServices(); - - services.AddRabbitMessageConverter((_, c) => - { - c.AssumeSupportedContentType = false; - }); - - ServiceProvider provider = services.BuildServiceProvider(true); - var c = provider.GetService() as JsonMessageConverter; - Assert.NotNull(c); - Assert.False(c.AssumeSupportedContentType); - } - - [Fact] - public void ConfigureRabbitOptions_Configure() - { - const string hostPrefix = "spring:rabbitmq:host"; - const string portPrefix = "spring:rabbitmq:port"; - const string usernamePrefix = "spring:rabbitmq:username"; - const string passwordPrefix = "spring:rabbitmq:password"; - var services = new ServiceCollection(); - - var appsettings = new Dictionary - { - [hostPrefix] = "this.is.test", - [portPrefix] = "12345", - [usernamePrefix] = "fakeusername", - [passwordPrefix] = "CHANGEME" - }; - - var configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddInMemoryCollection(appsettings); - IConfigurationRoot configuration = configurationBuilder.Build(); - - services.ConfigureRabbitOptions(configuration); - - ServiceProvider provider = services.BuildServiceProvider(true); - RabbitOptions rabbitOptions = provider.GetService>().Value; - - Assert.Equal(appsettings[hostPrefix], rabbitOptions.Host); - Assert.Equal(appsettings[portPrefix], rabbitOptions.Port.ToString(CultureInfo.InvariantCulture)); - Assert.Equal(appsettings[usernamePrefix], rabbitOptions.Username); - Assert.Equal(appsettings[passwordPrefix], rabbitOptions.Password); - } - - [Fact] - public void ConfigureRabbitOptions_OverrideAddressWithServiceInfo() - { - var appsettings = new Dictionary - { - ["spring:rabbitmq:username"] = "fakeusername", - ["spring:rabbitmq:password"] = "CHANGEME" - }; - - var configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddInMemoryCollection(appsettings); - configurationBuilder.AddCloudFoundryServiceBindings(new StringServiceBindingsReader(GetCloudFoundryRabbitMqConfiguration())); - configurationBuilder.ConfigureRabbitMQ(); - - IConfigurationRoot configuration = configurationBuilder.Build(); - - var services = new ServiceCollection(); - services.AddRabbitMQ(configuration); - services.AddRabbitConnectionFactory(); - services.ConfigureRabbitOptions(configuration); - - ServiceProvider serviceProvider = services.BuildServiceProvider(true); - - RabbitOptions rabbitOptions = serviceProvider.GetRequiredService>().Value; - - Assert.Equal("Dd6O1BPXUHdrmzbP", rabbitOptions.Username); - Assert.Equal("7E1LxXnlH2hhlPVt", rabbitOptions.Password); - Assert.Equal("cf_b4f8d2fa_a3ea_4e3a_a0e8_2cd040790355", rabbitOptions.VirtualHost); - Assert.Equal("Dd6O1BPXUHdrmzbP:7E1LxXnlH2hhlPVt@192.168.0.90:3306", rabbitOptions.Addresses); - } - - [Fact] - public void AddRabbitConnectionFactory_AddRabbitConnector() - { - var configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddCloudFoundryServiceBindings(new StringServiceBindingsReader(GetCloudFoundryRabbitMqConfiguration())); - configurationBuilder.ConfigureRabbitMQ(); - - IConfigurationRoot configuration = configurationBuilder.Build(); - - var services = new ServiceCollection(); - services.AddRabbitMQ(configuration); - services.AddRabbitConnectionFactory(); - - ServiceProvider serviceProvider = services.BuildServiceProvider(true); - - var rabbitConnectionFactory = serviceProvider.GetRequiredService(); - - Assert.Equal("192.168.0.90", rabbitConnectionFactory.Host); - Assert.Equal(3306, rabbitConnectionFactory.Port); - Assert.Equal("Dd6O1BPXUHdrmzbP", rabbitConnectionFactory.Username); - Assert.Equal("cf_b4f8d2fa_a3ea_4e3a_a0e8_2cd040790355", rabbitConnectionFactory.VirtualHost); - } - - private static string GetCloudFoundryRabbitMqConfiguration() - { - return @" - { - ""p-rabbitmq"": [{ - ""credentials"": { - ""protocols"": { - ""amqp"": { - ""host"": ""192.168.0.90"", - ""password"": ""7E1LxXnlH2hhlPVt"", - ""port"": 3306, - ""username"": ""Dd6O1BPXUHdrmzbP"", - ""vhost"": ""cf_b4f8d2fa_a3ea_4e3a_a0e8_2cd040790355"" - } - }, - ""ssl"": false, - }, - ""name"": ""myRabbitMQService1"", - ""tags"": [ - ""rabbitmq"" - ] - }] - }"; - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Host/MockRabbitHostedService.cs b/src/Messaging/test/RabbitMQ.Test/Host/MockRabbitHostedService.cs deleted file mode 100644 index 639abe09af..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Host/MockRabbitHostedService.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Hosting; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Host; - -public sealed class MockRabbitHostedService : IHostedService, IDisposable -{ - public int StartCount { get; internal set; } - - public int StopCount { get; internal set; } - - public int DisposeCount { get; internal set; } - - public Action StartAction { get; set; } - - public Action StopAction { get; set; } - - public Action DisposeAction { get; set; } - - public void Dispose() - { - DisposeCount++; - DisposeAction?.Invoke(); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - StartCount++; - StartAction?.Invoke(cancellationToken); - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - StopCount++; - StopAction?.Invoke(cancellationToken); - return Task.CompletedTask; - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Host/RabbitMQHostTest.cs b/src/Messaging/test/RabbitMQ.Test/Host/RabbitMQHostTest.cs deleted file mode 100644 index 22736fcc70..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Host/RabbitMQHostTest.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Connectors; -using Steeltoe.Connectors.RabbitMQ; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Hosting; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Host; - -public sealed class RabbitMQHostTest -{ - [Fact] - public void HostCanBeStarted() - { - MockRabbitHostedService hostedService; - - using (IHost host = RabbitMQHost.CreateDefaultBuilder().UseDefaultServiceProvider(options => options.ValidateScopes = true) - .ConfigureServices(svc => svc.AddSingleton()).Start()) - { - Assert.NotNull(host); - hostedService = (MockRabbitHostedService)host.Services.GetRequiredService(); - Assert.NotNull(hostedService); - Assert.Equal(1, hostedService.StartCount); - Assert.Equal(0, hostedService.StopCount); - Assert.Equal(0, hostedService.DisposeCount); - } - - Assert.Equal(1, hostedService.StartCount); - Assert.Equal(0, hostedService.StopCount); - Assert.Equal(1, hostedService.DisposeCount); - } - - [Fact] - public void HostShouldInitializeServices() - { - using IHost host = RabbitMQHost.CreateDefaultBuilder().UseDefaultServiceProvider(options => options.ValidateScopes = true).Start(); - var lifecycleProcessor = host.Services.GetRequiredService(); - var rabbitHostService = (RabbitHostService)host.Services.GetRequiredService(); - - Assert.True(lifecycleProcessor.IsRunning); - Assert.NotNull(rabbitHostService); - } - - [Fact] - public void HostShouldAddRabbitOptionsConfiguration() - { - IHostBuilder hostBuilder = RabbitMQHost.CreateDefaultBuilder().UseDefaultServiceProvider(options => options.ValidateScopes = true); - - var appSettings = new Dictionary - { - [$"{RabbitOptions.Prefix}:host"] = "ThisIsATest", - [$"{RabbitOptions.Prefix}:port"] = "1234", - [$"{RabbitOptions.Prefix}:username"] = "TestUser", - [$"{RabbitOptions.Prefix}:password"] = "TestPassword" - }; - - hostBuilder.ConfigureAppConfiguration(configBuilder => - { - configBuilder.AddInMemoryCollection(appSettings); - }); - - using IHost host = hostBuilder.Start(); - RabbitOptions rabbitOptions = host.Services.GetService>()?.Value; - - Assert.NotNull(rabbitOptions); - Assert.Equal("ThisIsATest", rabbitOptions.Host); - Assert.Equal(1234, rabbitOptions.Port); - Assert.Equal("TestUser", rabbitOptions.Username); - Assert.Equal("TestPassword", rabbitOptions.Password); - } - - [Fact] - public void HostShouldSendCommandLineArgs() - { - IHostBuilder hostBuilder = RabbitMQHost.CreateDefaultBuilder(new[] - { - "RabbitHostCommandKey=RabbitHostCommandValue" - }).UseDefaultServiceProvider(options => options.ValidateScopes = true); - - using IHost host = hostBuilder.Start(); - var configuration = host.Services.GetService(); - - Assert.Equal("RabbitHostCommandValue", configuration["RabbitHostCommandKey"]); - } - - [Fact] - public void ShouldWorkWithRabbitMQConnector() - { - IHostBuilder builder = RabbitMQHost.CreateDefaultBuilder().UseDefaultServiceProvider(options => options.ValidateScopes = true); - builder.ConfigureAppConfiguration(configurationBuilder => configurationBuilder.ConfigureRabbitMQ()); - builder.ConfigureServices((context, services) => services.AddRabbitMQ(context.Configuration)); - - using IHost host = builder.Start(); - var connectorFactory = host.Services.GetRequiredService>(); - - Assert.NotNull(connectorFactory); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/Adapters/MessageListenerAdapterTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/Adapters/MessageListenerAdapterTest.cs deleted file mode 100644 index 46469621cd..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/Adapters/MessageListenerAdapterTest.cs +++ /dev/null @@ -1,309 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Retry; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener.Adapters; - -public sealed class MessageListenerAdapterTest -{ - private readonly SimpleService _simpleService = new(); - - private readonly MessageHeaders _messageProperties; - - private MessageListenerAdapter _adapter; - - public MessageListenerAdapterTest() - { - var headers = new Dictionary - { - { MessageHeaders.ContentType, MimeTypeUtils.TextPlainValue } - }; - - _messageProperties = new MessageHeaders(headers); - _adapter = new MessageListenerAdapter(null); - } - - [Fact] - public void TestExtendedListenerAdapter() - { - var extendedAdapter = new ExtendedListenerAdapter(null); - var called = new AtomicBoolean(false); - var channelMock = new Mock(); - var testDelegate = new TestDelegate(called); - extendedAdapter.Instance = testDelegate; - extendedAdapter.ContainerAckMode = AcknowledgeMode.Manual; - byte[] bytes = EncodingUtils.GetDefaultEncoding().GetBytes("foo"); - extendedAdapter.OnMessage(Message.Create(bytes, _messageProperties), channelMock.Object); - Assert.True(called.Value); - } - - [Fact] - public void TestDefaultListenerMethod() - { - var called = new AtomicBoolean(false); - var delegate1 = new TestDelegate1(called); - _adapter.Instance = delegate1; - byte[] bytes = EncodingUtils.GetDefaultEncoding().GetBytes("foo"); - _adapter.OnMessage(Message.Create(bytes, _messageProperties), null); - Assert.True(called.Value); - } - - [Fact] - public void TestAlternateConstructor() - { - var called = new AtomicBoolean(false); - var delegate2 = new TestDelegate2(called); - _adapter = new MessageListenerAdapter(null, delegate2, nameof(TestDelegate2.MyPojoMessageMethod)); - byte[] bytes = EncodingUtils.GetDefaultEncoding().GetBytes("foo"); - _adapter.OnMessage(Message.Create(bytes, _messageProperties), null); - Assert.True(called.Value); - } - - [Fact] - public void TestExplicitListenerMethod() - { - _adapter.DefaultListenerMethod = "Handle"; - _adapter.Instance = _simpleService; - byte[] bytes = EncodingUtils.GetDefaultEncoding().GetBytes("foo"); - _adapter.OnMessage(Message.Create(bytes, _messageProperties), null); - Assert.Equal("Handle", _simpleService.Called); - } - - [Fact] - public void TestMappedListenerMethod() - { - var map = new Dictionary - { - { "foo", "Handle" }, - { "bar", "NotDefinedOnInterface" } - }; - - _adapter.DefaultListenerMethod = "AnotherHandle"; - _adapter.SetQueueOrTagToMethodName(map); - _adapter.Instance = _simpleService; - byte[] bytes = EncodingUtils.GetDefaultEncoding().GetBytes("foo"); - IMessage message = Message.Create(bytes, _messageProperties); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ConsumerQueue = "foo"; - accessor.ConsumerTag = "bar"; - _adapter.OnMessage(message, null); - Assert.Equal("Handle", _simpleService.Called); - message = Message.Create(bytes, _messageProperties); - accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ConsumerQueue = "junk"; - _adapter.OnMessage(message, null); - Assert.Equal("NotDefinedOnInterface", _simpleService.Called); - message = Message.Create(bytes, _messageProperties); - accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ConsumerTag = "junk"; - _adapter.OnMessage(message, null); - Assert.Equal("AnotherHandle", _simpleService.Called); - } - - [Fact] - public void TestReplyRetry() - { - _adapter.DefaultListenerMethod = "Handle"; - _adapter.Instance = _simpleService; - _adapter.RetryTemplate = new PollyRetryTemplate(new Dictionary(), 2, true, 1, 1, 1); - var replyMessage = new AtomicReference(); - var replyAddress = new AtomicReference
(); - var throwable = new AtomicReference(); - _adapter.RecoveryCallback = new TestRecoveryCallback(replyMessage, replyAddress, throwable); - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(_messageProperties); - accessor.ReplyTo = "foo/bar"; - var ex = new Exception(); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.BasicPublish("foo", "bar", false, It.IsAny(), It.IsAny())).Throws(ex); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - byte[] bytes = EncodingUtils.GetDefaultEncoding().GetBytes("foo"); - IMessage message = Message.Create(bytes, _messageProperties); - _adapter.OnMessage(message, mockChannel.Object); - Assert.Equal("Handle", _simpleService.Called); - Assert.NotNull(replyMessage.Value); - Assert.NotNull(replyAddress.Value); - Address address = replyAddress.Value; - Assert.Equal("foo", address.ExchangeName); - Assert.Equal("bar", address.RoutingKey); - Assert.Same(ex, throwable.Value); - } - - [Fact] - public void TestTaskReturn() - { - var called = new CountdownEvent(1); - var @delegate = new TestAsyncDelegate(); - - _adapter = new MessageListenerAdapter(null, @delegate, nameof(TestAsyncDelegate.MyPojoMessageMethodAsync)) - { - ContainerAckMode = AcknowledgeMode.Manual, - ResponseExchange = "default" - }; - - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - mockChannel.Setup(c => c.BasicAck(It.IsAny(), false)).Callback(() => called.Signal()); - byte[] bytes = EncodingUtils.GetDefaultEncoding().GetBytes("foo"); - IMessage message = Message.Create(bytes, _messageProperties); - _adapter.OnMessage(message, mockChannel.Object); - Assert.True(called.Wait(TimeSpan.FromSeconds(10))); - } - - public sealed class TestRecoveryCallback : IRecoveryCallback - { - private readonly AtomicReference _replyMessage; - private readonly AtomicReference
_replyAddress; - private readonly AtomicReference _throwable; - - public TestRecoveryCallback(AtomicReference replyMessage, AtomicReference
replyAddress, AtomicReference throwable) - { - _replyMessage = replyMessage; - _replyAddress = replyAddress; - _throwable = throwable; - } - - public object Recover(IRetryContext context) - { - _replyMessage.Value = SendRetryContextAccessor.GetMessage(context); - _replyAddress.Value = SendRetryContextAccessor.GetAddress(context); - _throwable.Value = context.LastException; - return null; - } - } - - public interface IService - { - string Handle(string input); - - string AnotherHandle(string input); - } - - public sealed class SimpleService : IService - { - public string Called { get; private set; } - - public string Handle(string input) - { - Called = "Handle"; - return $"processed{input}"; - } - - public string AnotherHandle(string input) - { - Called = "AnotherHandle"; - return $"processed{input}"; - } - - public string NotDefinedOnInterface(string input) - { - Called = "NotDefinedOnInterface"; - return $"processed{input}"; - } - } - - private sealed class TestAsyncDelegate - { - public Task MyPojoMessageMethodAsync(string input) - { - return Task.Run(() => - { - Thread.Sleep(1000); - return $"processed{input}"; - }); - } - } - - private sealed class TestDelegate2 - { - private readonly AtomicBoolean _called; - - public TestDelegate2(AtomicBoolean called) - { - _called = called; - } - - public string MyPojoMessageMethod(string input) - { - _called.Value = true; - return $"processed{input}"; - } - } - - private sealed class TestDelegate1 - { - private readonly AtomicBoolean _called; - - public TestDelegate1(AtomicBoolean called) - { - _called = called; - } - -#pragma warning disable S1144 // Unused private types or members should be removed - public string HandleMessage(string input) - { - _called.Value = true; - return $"processed{input}"; - } -#pragma warning restore S1144 // Unused private types or members should be removed - } - - private sealed class TestDelegate - { - private readonly AtomicBoolean _called; - - public TestDelegate(AtomicBoolean called) - { - _called = called; - } - -#pragma warning disable S1144 // Unused private types or members should be removed - public void HandleMessage(string input, RC.IModel channel, IMessage message) - { - Assert.NotNull(input); - Assert.NotNull(channel); - Assert.NotNull(message); - ulong deliveryTag = 0; - - if (message.Headers.DeliveryTag().HasValue) - { - deliveryTag = message.Headers.DeliveryTag().Value; - } - - channel.BasicAck(deliveryTag, false); - _called.Value = true; - } -#pragma warning restore S1144 // Unused private types or members should be removed - } - - private sealed class ExtendedListenerAdapter : MessageListenerAdapter - { - public ExtendedListenerAdapter(IApplicationContext context, ILogger logger = null) - : base(context, logger) - { - } - - protected override object[] BuildListenerArguments(object extractedMessage, RC.IModel channel, IMessage message) - { - return new[] - { - extractedMessage, - channel, - message - }; - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/Adapters/MessagingMessageListenerAdapterTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/Adapters/MessagingMessageListenerAdapterTest.cs deleted file mode 100644 index ce53867fc0..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/Adapters/MessagingMessageListenerAdapterTest.cs +++ /dev/null @@ -1,430 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Moq; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Steeltoe.Messaging.RabbitMQ.Test.Test; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener.Adapters; - -public sealed class MessagingMessageListenerAdapterTest : AbstractTest -{ - private readonly DefaultMessageHandlerMethodFactory _factory = new(); - private readonly SampleBean _sample = new(); - - [Fact] - public void BuildMessageWithStandardMessage() - { - IMessage result = RabbitMessageBuilder.WithPayload("Response").SetHeader("foo", "bar").SetHeader(RabbitMessageHeaders.Type, "msg_type") - .SetHeader(RabbitMessageHeaders.ReplyTo, "reply").Build(); - - var session = new Mock(); - MessagingMessageListenerAdapter listener = GetSimpleInstance(nameof(SampleBean.Echo), typeof(IMessage)); - IMessage replyMessage = listener.BuildMessage(session.Object, result, null); - Assert.NotNull(replyMessage); - Assert.Equal("Response", EncodingUtils.GetDefaultEncoding().GetString(replyMessage.Payload)); - Assert.Equal("msg_type", replyMessage.Headers.Type()); - Assert.Equal("reply", replyMessage.Headers.ReplyTo()); - Assert.Equal("bar", replyMessage.Headers.Get("foo")); - } - - [Fact] - public void ExceptionInListener() - { - IMessage message = MessageTestUtils.CreateTextMessage("foo"); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - MessagingMessageListenerAdapter listener = GetSimpleInstance(nameof(SampleBean.Fail), typeof(string)); - - try - { - listener.OnMessage(message, mockChannel.Object); - throw new Exception("Should have thrown an exception"); - } - catch (ListenerExecutionFailedException ex) - { - Assert.IsType(ex.InnerException); - Assert.Contains("Expected test exception", ex.InnerException.Message, StringComparison.Ordinal); - } - catch (Exception ex) - { - throw new Exception($"Should not have thrown an {ex}"); - } - } - - [Fact] - public void ExceptionInListenerBadReturnExceptionSetting() - { - IMessage message = MessageTestUtils.CreateTextMessage("foo"); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - MessagingMessageListenerAdapter listener = GetSimpleInstance("Fail", true, typeof(string)); - - try - { - listener.OnMessage(message, mockChannel.Object); - throw new Exception("Should have thrown an exception"); - } - catch (ListenerExecutionFailedException ex) - { - Assert.IsType(ex.InnerException); - Assert.Contains("Expected test exception", ex.InnerException.Message, StringComparison.Ordinal); - } - catch (Exception ex) - { - throw new Exception($"Should not have thrown an {ex}"); - } - } - - [Fact] - public void ExceptionInMultiListenerReturnException() - { - IMessage message = MessageTestUtils.CreateTextMessage("foo"); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - MessagingMessageListenerAdapter listener = - GetMultiInstance(nameof(SampleBean.Fail), nameof(SampleBean.FailWithReturn), true, typeof(string), typeof(byte[])); - - try - { - listener.OnMessage(message, mockChannel.Object); - throw new Exception("Should have thrown an exception"); - } - catch (ListenerExecutionFailedException ex) - { - Assert.IsType(ex.InnerException); - Assert.Contains("Expected test exception", ex.InnerException.Message, StringComparison.Ordinal); - } - catch (Exception ex) - { - throw new Exception($"Should not have thrown an {ex}"); - } - - message = Message.Create(new byte[] - { - 1, - 2 - }, new MessageHeaders()); - - try - { - listener.OnMessage(message, mockChannel.Object); - throw new Exception("Should have thrown an exception"); - } - catch (ReplyFailureException ex) - { - Assert.Contains("Failed to send reply", ex.Message, StringComparison.Ordinal); - } - catch (Exception ex) - { - throw new Exception($"Should not have thrown an {ex}"); - } - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ReplyTo = "foo/bar"; - listener.OnMessage(message, mockChannel.Object); - } - - [Fact] - public void ExceptionInInvocation() - { - IMessage message = MessageTestUtils.CreateTextMessage("foo"); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - MessagingMessageListenerAdapter listener = GetSimpleInstance(nameof(SampleBean.WrongParam), typeof(int)); - - try - { - listener.OnMessage(message, mockChannel.Object); - throw new Exception("Should have thrown an exception"); - } - catch (ListenerExecutionFailedException ex) - { - Assert.IsType(ex.InnerException); - } - catch (Exception ex) - { - throw new Exception($"Should not have thrown an {ex}"); - } - } - - [Fact] - public void GenericMessageTest1() - { - IMessage message = MessageTestUtils.CreateTextMessage("\"foo\""); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - MessagingMessageListenerAdapter listener = GetSimpleInstance(nameof(SampleBean.WithGenericMessageObjectType), typeof(IMessage)); - listener.MessageConverter = new JsonMessageConverter(); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MimeTypeUtils.ApplicationJsonValue; - listener.OnMessage(message, mockChannel.Object); - Assert.IsType(_sample.Payload); - } - - [Fact] - public void GenericMessageTest2() - { - IMessage message = MessageTestUtils.CreateTextMessage("{ \"foostring\" : \"bar\" }"); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - MessagingMessageListenerAdapter listener = GetSimpleInstance(nameof(SampleBean.WithGenericMessageFooType), typeof(IMessage)); - listener.MessageConverter = new JsonMessageConverter(); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MimeTypeUtils.ApplicationJsonValue; - listener.OnMessage(message, mockChannel.Object); - Assert.IsType(_sample.Payload); - } - - [Fact] - public void GenericMessageTest3() - { - IMessage message = MessageTestUtils.CreateTextMessage("\"foo\""); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - MessagingMessageListenerAdapter listener = GetSimpleInstance(nameof(SampleBean.WithNonGenericMessage), typeof(IMessage)); - listener.MessageConverter = new JsonMessageConverter(); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MimeTypeUtils.ApplicationJsonValue; - listener.OnMessage(message, mockChannel.Object); - Assert.IsType(_sample.Payload); - } - - [Fact] - public void GenericMessageTest4() - { - IMessage message = MessageTestUtils.CreateTextMessage("{ \"foo\" : \"bar\" }"); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - MessagingMessageListenerAdapter listener = - GetSimpleInstance(nameof(SampleBean.WithGenericMessageDictionaryType), typeof(IMessage>)); - - listener.MessageConverter = new JsonMessageConverter(); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MimeTypeUtils.ApplicationJsonValue; - listener.OnMessage(message, mockChannel.Object); - Assert.IsType>(_sample.Payload); - } - - [Fact] - public void BatchMessagesTest() - { - IMessage message = MessageTestUtils.CreateTextMessage("{ \"foo1\" : \"bar1\" }"); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - BatchMessagingMessageListenerAdapter listener = GetBatchInstance(nameof(SampleBean.WithMessageBatch), typeof(List>)); - listener.MessageConverter = new JsonMessageConverter(); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MimeTypeUtils.ApplicationJsonValue; - - listener.OnMessageBatch(new List - { - message - }, mockChannel.Object); - - Assert.IsType(_sample.BatchPayloads[0]); - } - - [Fact] - public void BatchTypedMessagesTest() - { - IMessage message = MessageTestUtils.CreateTextMessage("{ \"foostring\" : \"bar1\" }"); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - BatchMessagingMessageListenerAdapter listener = GetBatchInstance(nameof(SampleBean.WithTypedMessageBatch), typeof(List>)); - listener.MessageConverter = new JsonMessageConverter(); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MimeTypeUtils.ApplicationJsonValue; - - listener.OnMessageBatch(new List - { - message - }, mockChannel.Object); - - Assert.IsType(_sample.BatchPayloads[0]); - } - - [Fact] - public void BatchTypedObjectTest() - { - IMessage message = MessageTestUtils.CreateTextMessage("{ \"foostring\" : \"bar1\" }"); - var mockChannel = new Mock(); - mockChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - BatchMessagingMessageListenerAdapter listener = GetBatchInstance(nameof(SampleBean.WithFooBatch), typeof(List)); - listener.MessageConverter = new JsonMessageConverter(); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MimeTypeUtils.ApplicationJsonValue; - - listener.OnMessageBatch(new List - { - message - }, mockChannel.Object); - - Assert.IsType(_sample.BatchPayloads[0]); - } - - private MessagingMessageListenerAdapter GetSimpleInstance(string methodName, params Type[] parameterTypes) - { - MethodInfo m = typeof(SampleBean).GetMethod(methodName, parameterTypes); - return CreateInstance(m, false); - } - - private MessagingMessageListenerAdapter GetSimpleInstance(string methodName, bool returnExceptions, params Type[] parameterTypes) - { - MethodInfo m = typeof(SampleBean).GetMethod(methodName, parameterTypes); - return CreateInstance(m, returnExceptions); - } - - private MessagingMessageListenerAdapter GetMultiInstance(string methodName1, string methodName2, bool returnExceptions, Type m1ParameterType, - Type m2ParameterType) - { - MethodInfo m1 = typeof(SampleBean).GetMethod(methodName1, new[] - { - m1ParameterType - }); - - MethodInfo m2 = typeof(SampleBean).GetMethod(methodName2, new[] - { - m2ParameterType - }); - - return CreateMultiInstance(m1, m2, returnExceptions); - } - - private MessagingMessageListenerAdapter CreateMultiInstance(MethodInfo m1, MethodInfo m2, bool returnExceptions) - { - var adapter = new MessagingMessageListenerAdapter(null, null, null, returnExceptions, null); - - var methods = new List - { - _factory.CreateInvocableHandlerMethod(_sample, m1), - _factory.CreateInvocableHandlerMethod(_sample, m2) - }; - - var handler = new DelegatingInvocableHandler(methods, _sample, null, null); - adapter.HandlerAdapter = new HandlerAdapter(handler); - return adapter; - } - - private MessagingMessageListenerAdapter CreateInstance(MethodInfo m, bool returnExceptions) - { - var adapter = new MessagingMessageListenerAdapter(null, null, m, returnExceptions, null) - { - HandlerAdapter = new HandlerAdapter(_factory.CreateInvocableHandlerMethod(_sample, m)) - }; - - return adapter; - } - - private BatchMessagingMessageListenerAdapter GetBatchInstance(string methodName, params Type[] parameterTypes) - { - MethodInfo m = typeof(SampleBean).GetMethod(methodName, parameterTypes); - return CreateBatchInstance(m); - } - - private BatchMessagingMessageListenerAdapter CreateBatchInstance(MethodInfo m) - { - var adapter = new BatchMessagingMessageListenerAdapter(null, null, m, false, null, null) - { - HandlerAdapter = new HandlerAdapter(_factory.CreateInvocableHandlerMethod(_sample, m)) - }; - - return adapter; - } - -#pragma warning disable S1172 // Unused method parameters should be removed - private sealed class SampleBean - { - public object Payload { get; private set; } - public List BatchPayloads { get; private set; } - - public IMessage Echo(IMessage input) - { - return (IMessage)RabbitMessageBuilder.WithPayload(input.Payload).SetHeader(RabbitMessageHeaders.Type, "reply").Build(); - } - - public void Fail(string input) - { - throw new ArgumentException("Expected test exception"); - } - - public void WrongParam(int i) - { - throw new ArgumentException("Should not have been called"); - } - - public void WithGenericMessageObjectType(IMessage message) - { - Payload = message.Payload; - } - - public void WithGenericMessageFooType(IMessage message) - { - Payload = message.Payload; - } - - public void WithGenericMessageDictionaryType(IMessage> message) - { - Payload = message.Payload; - } - - public void WithNonGenericMessage(IMessage message) - { - Payload = message.Payload; - } - - public void WithMessageBatch(List> messageBatch) - { - BatchPayloads = new List(); - - foreach (IMessage m in messageBatch) - { - BatchPayloads.Add(EncodingUtils.GetDefaultEncoding().GetString(m.Payload)); - } - } - - public void WithTypedMessageBatch(List> messageBatch) - { - BatchPayloads = new List(); - - foreach (IMessage m in messageBatch) - { - BatchPayloads.Add(m.Payload); - } - } - - public void WithFooBatch(List messageBatch) - { - BatchPayloads = new List(); - - foreach (Foo m in messageBatch) - { - BatchPayloads.Add(m); - } - } - - public string FailWithReturn(byte[] input) - { - throw new ArgumentException("Expected test exception"); - } - } -#pragma warning restore S1172 // Unused method parameters should be removed - - private sealed class Foo - { - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/BlockingQueueConsumerIntegrationTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/BlockingQueueConsumerIntegrationTest.cs deleted file mode 100644 index be6324fc7e..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/BlockingQueueConsumerIntegrationTest.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitMQ.Client.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Util; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -[Trait("Category", "Integration")] -public sealed class BlockingQueueConsumerIntegrationTest -{ - public const string Queue1Name = "test.queue1.BlockingQueueConsumerIntegrationTests"; - public const string Queue2Name = "test.queue2.BlockingQueueConsumerIntegrationTests"; - - [Fact] - public async Task TestTransactionalLowLevel() - { - using var connectionFactory = new CachingConnectionFactory("localhost"); - var admin = new RabbitAdmin(connectionFactory); - admin.DeclareQueue(new Queue(Queue1Name)); - admin.DeclareQueue(new Queue(Queue2Name)); - var template = new RabbitTemplate(connectionFactory); - - var blockingQueueConsumer = new BlockingQueueConsumer(connectionFactory, new DefaultMessageHeadersConverter(), - new ActiveObjectCounter(), AcknowledgeMode.Auto, true, 1, null, Queue1Name, Queue2Name); - - string prefix = Guid.NewGuid().ToString(); - blockingQueueConsumer.TagStrategy = new TagStrategy(prefix); - - try - { - blockingQueueConsumer.Start(); - int n = 0; - List consumers = blockingQueueConsumer.CurrentConsumers(); - - // Wait for consumers - while (n < 100) - { - if (consumers.Count < 2) - { - n++; - await Task.Delay(100); - consumers = blockingQueueConsumer.CurrentConsumers(); - } - else - { - break; - } - } - - Assert.Equal(2, consumers.Count); - - var tags = new List - { - consumers[0].ConsumerTag, - consumers[1].ConsumerTag - }; - - Assert.Contains($"{prefix}#{Queue1Name}", tags); - Assert.Contains($"{prefix}#{Queue2Name}", tags); - blockingQueueConsumer.Stop(); - Assert.Null(template.ReceiveAndConvert(Queue1Name)); - } - finally - { - admin.DeleteQueue(Queue1Name); - admin.DeleteQueue(Queue2Name); - connectionFactory.Destroy(); - } - } - - [Fact] - public void TestAvoidHangAMQP_508() - { - using var connectionFactory = new CachingConnectionFactory("localhost"); - string longName = new('x', 300); - - var blockingQueueConsumer = new BlockingQueueConsumer(connectionFactory, new DefaultMessageHeadersConverter(), - new ActiveObjectCounter(), AcknowledgeMode.Auto, true, 1, null, longName, "foobar"); - - try - { - blockingQueueConsumer.Start(); - throw new Exception("Expected exception"); - } - catch (FatalListenerStartupException e) - { - Assert.IsType(e.InnerException); - } - finally - { - connectionFactory.Destroy(); - } - } - - public sealed class TagStrategy : IConsumerTagStrategy - { - public string ServiceName { get; set; } = nameof(TagStrategy); - - public string Prefix { get; } - - public TagStrategy(string prefix) - { - Prefix = prefix; - } - - public string CreateConsumerTag(string queue) - { - return $"{Prefix}#{queue}"; - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/BlockingQueueConsumerTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/BlockingQueueConsumerTest.cs deleted file mode 100644 index 4d07ad3b15..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/BlockingQueueConsumerTest.cs +++ /dev/null @@ -1,237 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using RabbitMQ.Client.Exceptions; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Util; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -public sealed class BlockingQueueConsumerTest -{ - [Fact] - public void TestRequeue() - { - var ex = new Exception(); - TestRequeueOrNotDefaultYes(ex, true); - } - - [Fact] - public void TestRequeueNullException() - { - TestRequeueOrNotDefaultYes(null, true); - } - - [Fact] - public void TestDoNotRequeue() - { - TestRequeueOrNotDefaultYes(new RabbitRejectAndDoNotRequeueException("fail"), false); - } - - [Fact] - public void TestDoNotRequeueNested() - { - var ex = new Exception(string.Empty, new Exception(string.Empty, new RabbitRejectAndDoNotRequeueException("fail"))); - TestRequeueOrNotDefaultYes(ex, false); - } - - [Fact] - public void TestRequeueDefaultNot() - { - TestRequeueOrNotDefaultNo(new Exception(), false); - } - - [Fact] - public void TestRequeueNullExceptionDefaultNot() - { - TestRequeueOrNotDefaultNo(null, false); - } - - [Fact] - public void TestDoNotRequeueDefaultNot() - { - TestRequeueOrNotDefaultNo(new RabbitRejectAndDoNotRequeueException("fail"), false); - } - - [Fact] - public void TestDoNotRequeueNestedDefaultNot() - { - var ex = new Exception(string.Empty, new Exception(string.Empty, new RabbitRejectAndDoNotRequeueException("fail"))); - TestRequeueOrNotDefaultNo(ex, false); - } - - [Fact] - public void TestDoRequeueStoppingDefaultNot() - { - TestRequeueOrNotDefaultNo(new MessageRejectedWhileStoppingException(), true); - } - - [Fact] - public void TestPrefetchIsSetOnFailedPassiveDeclaration() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - var channel = new Mock(); - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - connection.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - var throws = new AtomicBoolean(false); - - channel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Callback(arg => throws.Value = arg != "good").Returns(() => - { - if (throws.Value) - { - throw new OperationInterruptedException(new RC.ShutdownEventArgs(RC.ShutdownInitiator.Peer, 0, "Expected")); - } - - return new RC.QueueDeclareOk("any", 0, 0); - }); - - channel.Setup(c => c.BasicConsume(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())).Returns("consumerTag"); - - var blockingQueueConsumer = new BlockingQueueConsumer(connectionFactory.Object, new DefaultMessageHeadersConverter(), - new ActiveObjectCounter(), AcknowledgeMode.Auto, true, 20, null, "good", "bad") - { - DeclarationRetries = 1, - RetryDeclarationInterval = 10, - FailedDeclarationRetryInterval = 10 - }; - - blockingQueueConsumer.Start(); - channel.Verify(c => c.BasicQos(It.IsAny(), 20, It.IsAny())); - blockingQueueConsumer.Stop(); - } - - [Fact] - public void TestNoLocalConsumerConfiguration() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - var channel = new Mock(); - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - connection.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - - const string queue = "testQ"; - const bool noLocal = true; - - var blockingQueueConsumer = new BlockingQueueConsumer(connectionFactory.Object, new DefaultMessageHeadersConverter(), - new ActiveObjectCounter(), AcknowledgeMode.Auto, true, 1, true, null, noLocal, false, null, queue); - - blockingQueueConsumer.Start(); - - channel.Verify(c => c.BasicConsume("testQ", AcknowledgeMode.Auto.IsAutoAck(), string.Empty, noLocal, false, It.IsAny>(), - It.IsAny())); - - blockingQueueConsumer.Stop(); - } - - [Fact] - public void TestRecoverAfterDeletedQueueAndLostConnection() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - var channel = new Mock(); - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - connection.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - var n = new AtomicInteger(); - var consumerCaptor = new AtomicReference(); - var consumerLatch = new CountdownEvent(2); - - channel.Setup(c => c.BasicConsume(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())) - .Callback, RC.IBasicConsumer>((_, _, _, _, _, _, a7) => - { - consumerCaptor.Value = a7; - consumerLatch.Signal(); - }).Returns($"consumer{n.IncrementAndGet()}"); - - channel.Setup(c => c.BasicCancel("consumer2")).Throws(new Exception("Intentional cancel fail")); - - var blockingQueueConsumer = new BlockingQueueConsumer(connectionFactory.Object, new DefaultMessageHeadersConverter(), - new ActiveObjectCounter(), AcknowledgeMode.Auto, false, 1, null, "testQ1", "testQ2"); - - var latch = new CountdownEvent(1); - - Task.Run(() => - { - blockingQueueConsumer.Start(); - - while (true) - { - try - { - blockingQueueConsumer.NextMessage(1000); - } - catch (ConsumerCancelledException) - { - latch.Signal(); - break; - } - catch (ShutdownSignalException) - { - // Intentionally left empty. - } - catch (Exception) - { - // Intentionally left empty. - } - } - }); - - Assert.True(consumerLatch.Wait(TimeSpan.FromSeconds(10))); - RC.IBasicConsumer consumer = consumerCaptor.Value; - consumer.HandleBasicCancel("consumer1"); - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - } - - private void TestRequeueOrNotDefaultYes(Exception ex, bool expectedRequeue) - { - var connectionFactory = new Mock(); - var channel = new Mock(); - - var blockingQueueConsumer = new BlockingQueueConsumer(connectionFactory.Object, new DefaultMessageHeadersConverter(), - new ActiveObjectCounter(), AcknowledgeMode.Auto, true, 1, null, "testQ"); - - TestRequeueOrNotGuts(ex, expectedRequeue, channel, blockingQueueConsumer); - } - - private void TestRequeueOrNotDefaultNo(Exception ex, bool expectedRequeue) - { - var connectionFactory = new Mock(); - var channel = new Mock(); - - var blockingQueueConsumer = new BlockingQueueConsumer(connectionFactory.Object, new DefaultMessageHeadersConverter(), - new ActiveObjectCounter(), AcknowledgeMode.Auto, true, 1, false, null, "testQ"); - - TestRequeueOrNotGuts(ex, expectedRequeue, channel, blockingQueueConsumer); - } - - private void TestRequeueOrNotGuts(Exception ex, bool expectedRequeue, Mock channel, BlockingQueueConsumer blockingQueueConsumer) - { - blockingQueueConsumer.Channel = channel.Object; - - var deliveryTags = new HashSet - { - 1UL - }; - - blockingQueueConsumer.DeliveryTags = deliveryTags; - blockingQueueConsumer.RollbackOnExceptionIfNecessary(ex); - channel.Verify(c => c.BasicNack(1UL, true, expectedRequeue)); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/BrokerDeclaredQueueNameTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/BrokerDeclaredQueueNameTest.cs deleted file mode 100644 index f89825439c..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/BrokerDeclaredQueueNameTest.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Hosting; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Xunit; -using RC = RabbitMQ.Client; -using SimpleMessageConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter.SimpleMessageConverter; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -[Trait("Category", "Integration")] -public sealed class BrokerDeclaredQueueNameTest : AbstractTest -{ - [Fact] - public async Task TestBrokerNamedQueueDmlc() - { - var latch3 = new CountdownEvent(1); - var latch4 = new CountdownEvent(2); - var message = new AtomicReference(); - ServiceCollection services = CreateContainer(); - services.AddRabbitQueue(new Queue(string.Empty, false, true, true)); - - services.AddHostedService(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.AddSingleton(p => CreateDmlcContainer(p, latch3, latch4, message)); - services.AddRabbitAdmin(); - services.AddRabbitTemplate(); - await using ServiceProvider provider = services.BuildServiceProvider(true); - - await provider.GetRequiredService().StartAsync(default); - - using var container = provider.GetRequiredService(); - using var cf = provider.GetRequiredService() as CachingConnectionFactory; - - await container.StartAsync(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); // Really wait for container to start - - var queue = provider.GetRequiredService(); - using RabbitTemplate template = provider.GetRabbitTemplate(); - string firstActualName = queue.ActualName; - message.Value = null; - template.ConvertAndSend(firstActualName, "foo"); - - Assert.True(latch3.Wait(TimeSpan.FromSeconds(10))); - string body = EncodingUtils.GetDefaultEncoding().GetString((byte[])message.Value.Payload); - Assert.Equal("foo", body); - var newConnectionLatch = new CountdownEvent(2); - var conListener = new TestConnectionListener(newConnectionLatch); - cf.AddConnectionListener(conListener); - cf.ResetConnection(); - Assert.True(newConnectionLatch.Wait(TimeSpan.FromSeconds(10))); - string secondActualName = queue.ActualName; - Assert.NotEqual(firstActualName, secondActualName); - message.Value = null; - template.ConvertAndSend(secondActualName, "bar"); - Assert.True(latch4.Wait(TimeSpan.FromSeconds(10))); - body = EncodingUtils.GetDefaultEncoding().GetString((byte[])message.Value.Payload); - Assert.Equal("bar", body); - await container.StopAsync(); - } - - private DirectMessageListenerContainer CreateDmlcContainer(IServiceProvider services, CountdownEvent latch3, CountdownEvent latch4, - AtomicReference message) - { - var cf = services.GetRequiredService(); - var ctx = services.GetRequiredService(); - var queue2 = services.GetRequiredService(); - var listener = new TestMessageListener(latch3, latch4, message); - var container = new DirectMessageListenerContainer(ctx, cf); - container.SetQueues(queue2); - container.SetupMessageListener(listener); - container.FailedDeclarationRetryInterval = 1000; - container.MissingQueuesFatal = false; - container.RecoveryInterval = 100; - container.IsAutoStartup = false; - - return container; - } - - private sealed class TestConnectionListener : IConnectionListener - { - public CountdownEvent Latch { get; } - - public TestConnectionListener(CountdownEvent latch) - { - Latch = latch; - } - - public void OnClose(IConnection connection) - { - } - - public void OnCreate(IConnection connection) - { - if (!Latch.IsSet) - { - Latch.Signal(); - } - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - } - } - - private sealed class TestMessageListener : IMessageListener - { - public AcknowledgeMode ContainerAckMode { get; set; } - - public CountdownEvent Latch1 { get; } - - public CountdownEvent Latch2 { get; } - - public AtomicReference Message { get; } - - public TestMessageListener(CountdownEvent latch1, CountdownEvent latch2, AtomicReference message) - { - Latch1 = latch1; - Latch2 = latch2; - Message = message; - } - - public void OnMessage(IMessage message) - { - Message.Value = message; - - if (!Latch1.IsSet) - { - Latch1.Signal(); - } - - if (!Latch2.IsSet) - { - Latch2.Signal(); - } - } - - public void OnMessageBatch(List messages) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/ContainerInitializationTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/ContainerInitializationTest.cs deleted file mode 100644 index 3a23dca2a1..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/ContainerInitializationTest.cs +++ /dev/null @@ -1,258 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Hosting; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Xunit; -using RC = RabbitMQ.Client; -using SimpleMessageConverter = Steeltoe.Messaging.RabbitMQ.Support.Converter.SimpleMessageConverter; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -[Trait("Category", "Integration")] -public sealed class ContainerInitializationTest : AbstractTest, IDisposable -{ - public const string TestMismatch = "test.mismatch"; - public const string TestMismatch2 = "test.mismatch2"; - - private ServiceProvider _provider; - - [Fact] - public async Task TestNoAdmin() - { - ServiceCollection services = CreateServiceCollection(); - services.AddRabbitQueue(new Queue(TestMismatch, false, false, true)); - services.AddSingleton(p => CreateMessageListenerContainer(p, TestMismatch)); - - services.AddSingleton(p => p.GetService()); - - _provider = services.BuildServiceProvider(true); - - try - { - await _provider.GetRequiredService().StartAsync(default); - } - catch (LifecycleException le) - { - Assert.IsType(le.InnerException.InnerException); - Assert.Contains("mismatchedQueuesFatal", le.InnerException.InnerException.Message, StringComparison.Ordinal); - } - } - - [Fact] - public async Task TestMismatchedQueue() - { - ServiceCollection services = CreateServiceCollection(); - services.AddSingleton(p => CreateMessageListenerContainer(p, TestMismatch)); - - services.AddSingleton(p => p.GetService()); - services.AddRabbitQueue(new Queue(TestMismatch, false, false, true)); - services.AddRabbitAdmin(); - - _provider = services.BuildServiceProvider(true); - - try - { - await _provider.GetRequiredService().StartAsync(default); - } - catch (LifecycleException le) - { - Assert.IsType(le.InnerException.InnerException); - Assert.Contains("mismatchedQueuesFatal", le.InnerException.InnerException.Message, StringComparison.Ordinal); - } - } - - [Fact] - public async Task TestMismatchedQueueDuringRestart() - { - ServiceCollection services = CreateServiceCollection(); - services.AddSingleton(p => CreateMessageListenerContainer(p, TestMismatch)); - - services.AddSingleton(p => p.GetService()); - services.AddRabbitQueue(new Queue(TestMismatch, true, false, false)); - services.AddRabbitAdmin(); - _provider = services.BuildServiceProvider(true); - - CountdownEvent[] latches = SetUpChannelLatches(_provider); - await _provider.GetRequiredService().StartAsync(default); - var container = _provider.GetService(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - - RabbitAdmin admin = _provider.GetRabbitAdmin(); - admin.RetryTemplate = null; - admin.DeleteQueue(TestMismatch); - Assert.True(latches[0].Wait(TimeSpan.FromSeconds(100))); - admin.DeclareQueue(new Queue(TestMismatch, false, false, true)); - latches[2].Signal(); - Assert.True(latches[1].Wait(TimeSpan.FromSeconds(10))); - - int n = 0; - - while (n++ < 200 && container.IsRunning) - { - await Task.Delay(100); - } - - Assert.False(container.IsRunning); - } - - [Fact] - public async Task TestMismatchedQueueDuringRestartMultiQueue() - { - ServiceCollection services = CreateServiceCollection(); - services.AddSingleton(p => CreateMessageListenerContainer(p, TestMismatch, TestMismatch2)); - - services.AddSingleton(p => p.GetService()); - services.AddRabbitQueue(new Queue(TestMismatch, true, false, false)); - services.AddRabbitQueue(new Queue(TestMismatch2, true, false, false)); - services.AddRabbitAdmin(); - _provider = services.BuildServiceProvider(true); - - CountdownEvent[] latches = SetUpChannelLatches(_provider); - await _provider.GetRequiredService().StartAsync(default); - var container = _provider.GetService(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - - RabbitAdmin admin = _provider.GetRabbitAdmin(); - admin.RetryTemplate = null; - admin.DeleteQueue(TestMismatch); - Assert.True(latches[0].Wait(TimeSpan.FromSeconds(100))); - admin.DeclareQueue(new Queue(TestMismatch, false, false, true)); - latches[2].Signal(); - Assert.True(latches[1].Wait(TimeSpan.FromSeconds(10))); - - int n = 0; - - while (n++ < 200 && container.IsRunning) - { - await Task.Delay(100); - } - - Assert.False(container.IsRunning); - } - - public void Dispose() - { - if (_provider.GetService() is RabbitAdmin admin) - { - admin.IgnoreDeclarationExceptions = true; - - try - { - admin.DeleteQueue(TestMismatch); - admin.DeleteQueue(TestMismatch2); - } - catch (Exception) - { - // Ignore - } - } - - _provider.Dispose(); - } - - private CountdownEvent[] SetUpChannelLatches(IServiceProvider context) - { - var cf = context.GetService() as CachingConnectionFactory; - var cancelLatch = new CountdownEvent(1); - var mismatchLatch = new CountdownEvent(1); - var preventContainerRedeclareQueueLatch = new CountdownEvent(1); - var listener = new TestListener(cancelLatch, mismatchLatch, preventContainerRedeclareQueueLatch); - cf.AddChannelListener(listener); - - return new[] - { - cancelLatch, - mismatchLatch, - preventContainerRedeclareQueueLatch - }; - } - - private ServiceCollection CreateServiceCollection() - { - ServiceCollection services = CreateContainer(); - - services.AddHostedService(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - return services; - } - - private DirectMessageListenerContainer CreateMessageListenerContainer(IServiceProvider services, params string[] queueNames) - { - var cf = services.GetRequiredService(); - var ctx = services.GetRequiredService(); - var listener = new TestMessageListener(); - var container = new DirectMessageListenerContainer(ctx, cf); - container.SetQueueNames(queueNames); - container.SetupMessageListener(listener); - container.MismatchedQueuesFatal = true; - - return container; - } - - private sealed class TestListener : IShutDownChannelListener - { - private readonly CountdownEvent _cancelLatch; - private readonly CountdownEvent _mismatchLatch; - private readonly CountdownEvent _preventContainerRedeclareQueueLatch; - - public TestListener(CountdownEvent cancelLatch, CountdownEvent mismatchLatch, CountdownEvent preventContainerRedeclareQueueLatch) - { - _cancelLatch = cancelLatch; - _mismatchLatch = mismatchLatch; - _preventContainerRedeclareQueueLatch = preventContainerRedeclareQueueLatch; - } - - public void OnCreate(RC.IModel channel, bool transactional) - { - } - - public void OnShutDown(RC.ShutdownEventArgs args) - { - if (RabbitUtils.IsNormalChannelClose(args)) - { - _cancelLatch.Signal(); - - try - { - _preventContainerRedeclareQueueLatch.Wait(TimeSpan.FromSeconds(10)); - } - catch (Exception) - { - // Ignore - } - } - else if (RabbitUtils.IsMismatchedQueueArgs(args)) - { - _mismatchLatch.Signal(); - } - } - } - - private sealed class TestMessageListener : IMessageListener - { - public AcknowledgeMode ContainerAckMode { get; set; } - - public void OnMessage(IMessage message) - { - } - - public void OnMessageBatch(List messages) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/ContainerShutDownTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/ContainerShutDownTest.cs deleted file mode 100644 index 9b1c9e24fd..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/ContainerShutDownTest.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using RabbitMQ.Client.Impl; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Xunit; -using static Steeltoe.Messaging.RabbitMQ.Connection.CachingConnectionFactory; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -[Trait("Category", "Integration")] -public sealed class ContainerShutDownTest : AbstractTest -{ - [Fact] - public async Task TestUninterruptibleListenerDmlc() - { - using var cf = new CachingConnectionFactory("localhost"); - var admin = new RabbitAdmin(cf); - admin.DeclareQueue(new Queue("test.shutdown")); - - var container = new DirectMessageListenerContainer(null, cf) - { - ShutdownTimeout = 500 - }; - - var testEnded = new CountdownEvent(1); - SessionManager channels = null; - - try - { - container.SetQueueNames("test.shutdown"); - var latch = new CountdownEvent(1); - - var listener = new TestListener(latch, testEnded); - container.MessageListener = listener; - var connection = cf.CreateConnection() as ChannelCachingConnectionProxy; - - FieldInfo field = - typeof(global::RabbitMQ.Client.Framing.Impl.Connection).GetField("m_sessionManager", BindingFlags.Instance | BindingFlags.NonPublic); - - Assert.NotNull(field); - channels = (SessionManager)field.GetValue(connection.Target.Connection); - Assert.NotNull(channels); - - await container.StartAsync().WaitAsync(TimeSpan.FromSeconds(10)); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(1))); - - var template = new RabbitTemplate(cf); - - template.Execute(c => - { - RC.IBasicProperties properties = c.CreateBasicProperties(); - byte[] bytes = EncodingUtils.GetDefaultEncoding().GetBytes("foo"); - c.BasicPublish(string.Empty, "test.shutdown", false, properties, bytes); - RabbitUtils.SetPhysicalCloseRequired(c, false); - }); - - Assert.True(latch.Wait(TimeSpan.FromSeconds(30))); - Assert.Equal(2, channels.Count); - } - finally - { - await container.StopAsync(); - - if (channels != null) - { - Assert.Equal(1, channels.Count); - } - - container.Dispose(); - - testEnded.Signal(); - admin.DeleteQueue("test.shutdown"); - } - } - - private sealed class TestListener : IMessageListener - { - private readonly CountdownEvent _latch; - private readonly CountdownEvent _testEnded; - - public AcknowledgeMode ContainerAckMode { get; set; } - - public TestListener(CountdownEvent latch, CountdownEvent testEnded) - { - _latch = latch; - _testEnded = testEnded; - } - - public void OnMessage(IMessage message) - { - _latch.Signal(); - _testEnded.Wait(TimeSpan.FromSeconds(30)); - } - - public void OnMessageBatch(List messages) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/ContainerUtilsTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/ContainerUtilsTest.cs deleted file mode 100644 index 2c4c63b470..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/ContainerUtilsTest.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener.Support; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -public sealed class ContainerUtilsTest -{ - [Fact] - public void TestMustRequeue() - { - Assert.True(ContainerUtils.ShouldRequeue(false, new ListenerExecutionFailedException(string.Empty, new ImmediateRequeueException("requeue")))); - } - - [Fact] - public void TestMustNotRequeue() - { - Assert.False(ContainerUtils.ShouldRequeue(true, - new ListenerExecutionFailedException(string.Empty, new RabbitRejectAndDoNotRequeueException("no requeue")))); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/DirectMessageListenerContainerIntegrationTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/DirectMessageListenerContainerIntegrationTest.cs deleted file mode 100644 index 3fc39ffba3..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/DirectMessageListenerContainerIntegrationTest.cs +++ /dev/null @@ -1,197 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.RetryPolly; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using Xunit; -using Xunit.Abstractions; -using static Steeltoe.Messaging.RabbitMQ.Test.Attributes.EnableRabbitIntegrationTest; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -[Trait("Category", "Integration")] -public sealed class DirectMessageListenerContainerIntegrationTest : IDisposable -{ - public const string Q1 = "testQ1.DirectMessageListenerContainerIntegrationTests"; - public const string Q2 = "testQ2.DirectMessageListenerContainerIntegrationTests"; - public const string Eq1 = "eventTestQ1.DirectMessageListenerContainerIntegrationTests"; - public const string Eq2 = "eventTestQ2.DirectMessageListenerContainerIntegrationTests"; - public const string Dlq1 = "testDLQ1.DirectMessageListenerContainerIntegrationTests"; - - private static int _testNumber = 1; - - private readonly CachingConnectionFactory _adminCf; - - private readonly RabbitAdmin _admin; - - private readonly string _testName; - - private readonly ITestOutputHelper _output; - - public DirectMessageListenerContainerIntegrationTest(ITestOutputHelper output) - { - _adminCf = new CachingConnectionFactory("localhost"); - _admin = new RabbitAdmin(_adminCf); - _admin.DeclareQueue(new Queue(Q1)); - _admin.DeclareQueue(new Queue(Q2)); - _testName = $"DirectMessageListenerContainerIntegrationTest-{_testNumber++}"; - _output = output; - } - - [Fact] - public async Task TestDirect() - { - var cf = new CachingConnectionFactory("localhost"); - var container = new DirectMessageListenerContainer(null, cf); - container.SetQueueNames(Q1, Q2); - container.ConsumersPerQueue = 2; - var listener = new ReplyingMessageListener(); - var adapter = new MessageListenerAdapter(null, listener); - container.MessageListener = adapter; - container.ServiceName = "simple"; - container.ConsumerTagStrategy = new TestConsumerTagStrategy(_testName); - await container.StartAsync(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - var template = new RabbitTemplate(cf); - Assert.Equal("FOO", template.ConvertSendAndReceive(Q1, "foo")); - Assert.Equal("BAR", template.ConvertSendAndReceive(Q2, "bar")); - await container.StopAsync(); - Assert.True(await ConsumersOnQueueAsync(Q1, 0)); - Assert.True(await ConsumersOnQueueAsync(Q2, 0)); - Assert.True(await ActiveConsumerCountAsync(container, 0)); - Assert.Empty(container.ConsumersByQueue); - await template.StopAsync(); - cf.Destroy(); - } - - [Fact] - public async Task TestMaxAttemptsRetryOnThrow() - { - var cf = new CachingConnectionFactory("localhost"); - int maxAttempts = 3; - - var container = new DirectMessageListenerContainer(null, cf) - { - RetryTemplate = new PollyRetryTemplate(maxAttempts, 1, 1, 1), - Recoverer = new DefaultReplyRecoveryCallback() - }; - - container.SetQueueNames(Q1); - - var listener = new ThrowingMessageListener(); - var adapter = new MessageListenerAdapter(null, listener); - container.MessageListener = adapter; - container.ServiceName = "simple"; - container.ConsumerTagStrategy = new TestConsumerTagStrategy(_testName); - await container.StartAsync(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - var template = new RabbitTemplate(cf); - template.ConvertSendAndReceive(Q1, "foo"); - - Assert.Equal(maxAttempts, listener.ExceptionCounter); - - await container.StopAsync(); - await template.StopAsync(); - cf.Destroy(); - } - - public void Dispose() - { - _admin.DeleteQueue(Q1); - _admin.DeleteQueue(Q2); - _adminCf.Dispose(); - } - - private async Task ConsumersOnQueueAsync(string queue, int expected) - { - int n = 0; - int currentQueueCount = -1; - _output.WriteLine(queue + " waiting for " + expected); - - while (n++ < 600) - { - Dictionary queueProperties = _admin.GetQueueProperties(queue); - - if (queueProperties != null && queueProperties.TryGetValue(RabbitAdmin.QueueConsumerCount, out object count)) - { - currentQueueCount = (int)(uint)count; - } - - if (currentQueueCount == expected) - { - return true; - } - - await Task.Delay(100); - - _output.WriteLine(queue + " waiting for " + expected + " : " + currentQueueCount); - } - - return currentQueueCount == expected; - } - - private async Task ActiveConsumerCountAsync(DirectMessageListenerContainer container, int expected) - { - int n = 0; - List consumers = container.Consumers; - - while (n++ < 600 && consumers.Count != expected) - { - await Task.Delay(100); - } - - return consumers.Count == expected; - } - - private sealed class ReplyingMessageListener : IReplyingMessageListener - { - public string HandleMessage(string input) - { - if (input == "foo" || input == "bar") - { - return input.ToUpperInvariant(); - } - - return null; - } - } - - private sealed class ThrowingMessageListener : IReplyingMessageListener - { - public int ExceptionCounter { get; private set; } - - public ThrowingMessageListener(int exceptionCounter = 0) - { - ExceptionCounter = exceptionCounter; - } - - public string HandleMessage(string input) - { - ExceptionCounter++; - throw new InvalidOperationException("Intentional exception to test retry"); - } - } - - private sealed class TestConsumerTagStrategy : IConsumerTagStrategy - { - private readonly string _testName; - private int _n; - - public string ServiceName { get; set; } = nameof(TestConsumerTagStrategy); - - public TestConsumerTagStrategy(string testName) - { - _testName = testName; - } - - public string CreateConsumerTag(string queue) - { - return $"{queue}/{_testName}{_n++}"; - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/DirectMessageListenerContainerMockTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/DirectMessageListenerContainerMockTest.cs deleted file mode 100644 index 607024eb9d..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/DirectMessageListenerContainerMockTest.cs +++ /dev/null @@ -1,416 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -public sealed class DirectMessageListenerContainerMockTest -{ - [Fact(Skip = "Not needed, We disable autorecoverconsumers")] - public async Task TestAlwaysCancelAutoRecoverConsumer() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - var channel = new Mock(); - var rabbitChannel = new Mock(); - channel.Setup(c => c.TargetChannel).Returns(rabbitChannel.Object); - - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - - connection.Setup(c => c.IsOpen).Returns(true); - var isOpen = new AtomicBoolean(true); - channel.Setup(c => c.IsOpen).Returns(() => isOpen.Value); - rabbitChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - channel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(new RC.QueueDeclareOk("test", 0, 0)); - - channel.Setup(c => c.BasicConsume(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())).Returns("consumerTag"); - - var latch1 = new CountdownEvent(1); - var qos = new AtomicInteger(); - - channel.Setup(c => c.BasicQos(It.IsAny(), It.IsAny(), It.IsAny())).Callback((_, count, _) => - { - qos.Value = count; - latch1.Signal(); - }); - - var latch2 = new CountdownEvent(1); - channel.Setup(c => c.BasicCancel("consumerTag")).Callback(() => latch2.Signal()); - var container = new DirectMessageListenerContainer(null, connectionFactory.Object); - container.SetQueueNames("test"); - container.PrefetchCount = 2; - container.MonitorInterval = 100; - container.Initialize(); - await container.StartAsync(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - - Assert.True(latch1.Wait(TimeSpan.FromSeconds(10))); - Assert.Equal(2, qos.Value); - - isOpen.Value = false; - - Assert.True(latch2.Wait(TimeSpan.FromSeconds(10))); - await container.StopAsync(); - } - - [Fact] - public async Task TestDeferredAcks() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - var channel = new Mock(); - var rabbitChannel = new Mock(); - channel.Setup(c => c.TargetChannel).Returns(rabbitChannel.Object); - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - connection.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - rabbitChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - channel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(new RC.QueueDeclareOk("test", 0, 0)); - - var consumer = new AtomicReference(); - var latch1 = new CountdownEvent(1); - - channel.Setup(c => c.BasicConsume(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())) - .Callback, RC.IBasicConsumer>((_, _, _, _, _, _, cons) => - { - consumer.Value = cons; - cons.HandleBasicConsumeOk("consumerTag"); - latch1.Signal(); - }).Returns("consumerTag"); - - var qos = new AtomicInteger(); - - channel.Setup(c => c.BasicQos(It.IsAny(), It.IsAny(), It.IsAny())).Callback((_, count, _) => - { - qos.Value = count; - }); - - var latch2 = new CountdownEvent(2); - var latch3 = new CountdownEvent(1); - - channel.Setup(c => c.BasicAck(It.IsAny(), It.IsAny())).Callback((tag, _) => - { - if (tag == 10ul || tag == 16ul) - { - latch2.Signal(); - } - else if (tag == 17ul) - { - latch3.Signal(); - } - }); - - var latch4 = new CountdownEvent(1); - - channel.Setup(c => c.BasicNack(It.IsAny(), It.IsAny(), It.IsAny())).Callback((_, _, _) => - { - latch4.Signal(); - }); - - var container = new DirectMessageListenerContainer(null, connectionFactory.Object); - container.SetQueueNames("test"); - container.PrefetchCount = 2; - container.MonitorInterval = 100; - container.MessagesPerAck = 10; - container.AckTimeout = 100; - container.MessageListener = new TestListener(); - container.Initialize(); - await container.StartAsync(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - - Assert.True(latch1.Wait(TimeSpan.FromSeconds(10))); - Assert.Equal(10, qos.Value); - var props = new MockRabbitBasicProperties(); - - byte[] body = new byte[1]; - - for (long i = 1; i < 16; i++) - { - consumer.Value.HandleBasicDeliver("consumerTag", (ulong)i, false, string.Empty, string.Empty, props, body); - } - - await Task.Delay(200); - - consumer.Value.HandleBasicDeliver("consumerTag", 16ul, false, string.Empty, string.Empty, props, body); - - // should get 2 acks #10 and #16 (timeout) - Assert.True(latch2.Wait(TimeSpan.FromSeconds(10))); - consumer.Value.HandleBasicDeliver("consumerTag", 17ul, false, string.Empty, string.Empty, props, body); - channel.Verify(c => c.BasicAck(10ul, true)); - channel.Verify(c => c.BasicAck(16ul, true)); - - Assert.True(latch3.Wait(TimeSpan.FromSeconds(10))); - - // monitor task timeout - channel.Verify(c => c.BasicAck(17ul, true)); - consumer.Value.HandleBasicDeliver("consumerTag", 18ul, false, string.Empty, string.Empty, props, body); - consumer.Value.HandleBasicDeliver("consumerTag", 19ul, false, string.Empty, string.Empty, props, body); - Assert.True(latch4.Wait(TimeSpan.FromSeconds(10))); - - // pending acks before nack - channel.Verify(c => c.BasicAck(18ul, true)); - channel.Verify(c => c.BasicNack(19ul, true, true)); - consumer.Value.HandleBasicDeliver("consumerTag", 20ul, false, string.Empty, string.Empty, props, body); - var latch5 = new CountdownEvent(1); - - channel.Setup(c => c.BasicCancel("consumerTag")).Callback(() => - { - consumer.Value.HandleBasicCancelOk("consumerTag"); - latch5.Signal(); - }); - - await container.StopAsync(); - Assert.True(latch5.Wait(TimeSpan.FromSeconds(10))); - channel.Verify(c => c.BasicAck(20ul, true)); - } - - [Fact(Skip = "Fails because it requires use of AutoRecoveringChannel which is not implemented")] - public async Task TestRemoveQueuesWhileNotConnected() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - var channel = new Mock(); - - // Note: Spring code mocks AutorecoveringChannel - var rabbitChannel = new Mock(); - channel.Setup(c => c.TargetChannel).Returns(rabbitChannel.Object); - - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - connection.Setup(c => c.IsOpen).Returns(true); - var isOpen = new AtomicBoolean(true); - channel.Setup(c => c.IsOpen).Returns(() => isOpen.Value); - rabbitChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - var declare = new AtomicReference(); - - channel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Callback(name => declare.Value = name) - .Returns(() => new RC.QueueDeclareOk(declare.Value, 0, 0)); - - var latch1 = new CountdownEvent(2); - var latch3 = new CountdownEvent(3); - - channel.Setup(c => c.BasicConsume(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())).Callback(() => - { - if (!latch3.IsSet) - { - latch3.Signal(); - } - }).Returns("consumerTag"); - - var qos = new AtomicInteger(); - - channel.Setup(c => c.BasicQos(It.IsAny(), It.IsAny(), It.IsAny())).Callback((_, count, _) => - { - qos.Value = count; - - if (!latch1.IsSet) - { - latch1.Signal(); - } - }); - - var latch2 = new CountdownEvent(2); - - channel.Setup(c => c.BasicCancel("consumerTag")).Callback(() => - { - if (!latch2.IsSet) - { - latch2.Signal(); - } - }); - - var container = new DirectMessageListenerContainer(null, connectionFactory.Object); - container.SetQueueNames("test1", "test2"); - container.PrefetchCount = 2; - container.MonitorInterval = 100; - container.FailedDeclarationRetryInterval = 100; - container.RecoveryInterval = 100; - container.ShutdownTimeout = 1; - container.Initialize(); - await container.StartAsync(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - - Assert.True(latch1.Wait(TimeSpan.FromSeconds(10))); - Assert.Equal(2, qos.Value); - isOpen.Value = false; - container.RemoveQueueNames("test1"); - Assert.True(latch2.Wait(TimeSpan.FromSeconds(20))); // Basic Cancels from isOpen = false - isOpen.Value = true; // Consumers should restart, but only test2, - Assert.True(latch3.Wait(TimeSpan.FromSeconds(10))); - - channel.Verify( - c => c.BasicConsume("test1", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), - It.IsAny()), Times.Once()); - - channel.Verify( - c => c.BasicConsume("test2", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), - It.IsAny()), Times.Exactly(2)); - - await container.StopAsync(); - } - - [Fact] - public async Task TestMonitorCancelsAfterBadAckEvenIfChannelReportsOpen() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - var channel = new Mock(); - var rabbitChannel = new Mock(); - channel.Setup(c => c.TargetChannel).Returns(rabbitChannel.Object); - - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - connection.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - rabbitChannel.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - - channel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(new RC.QueueDeclareOk("test", 0, 0)); - - var consumer = new AtomicReference(); - var latch1 = new CountdownEvent(1); - var latch2 = new CountdownEvent(1); - - channel.Setup(c => c.BasicConsume(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())) - .Callback, RC.IBasicConsumer>((_, _, _, _, _, _, cons) => - { - consumer.Value = cons; - latch1.Signal(); - }).Returns("consumerTag"); - - channel.Setup(c => c.BasicAck(1ul, false)).Throws(new Exception("bad ack")); - - channel.Setup(c => c.BasicCancel("consumerTag")).Callback(() => - { - consumer.Value.HandleBasicCancelOk("consumerTag"); - latch2.Signal(); - }); - - var container = new DirectMessageListenerContainer(null, connectionFactory.Object); - container.SetQueueNames("test"); - container.PrefetchCount = 2; - container.MonitorInterval = 100; - container.MessageListener = new Mock().Object; - container.Initialize(); - await container.StartAsync(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - - Assert.True(latch1.Wait(TimeSpan.FromSeconds(10))); - var props = new MockRabbitBasicProperties(); - consumer.Value.HandleBasicDeliver("consumerTag", 1ul, false, string.Empty, string.Empty, props, new byte[1]); - Assert.True(latch2.Wait(TimeSpan.FromSeconds(10))); - await container.StopAsync(); - } - - [Fact] - public async Task TestMonitorCancelsAfterTargetChannelChanges() - { - var connectionFactory = new Mock(); - var connection = new Mock(); - var channel = new Mock(); - var rabbitChannel1 = new Mock(); - var rabbitChannel2 = new Mock(); - var target = new AtomicReference(rabbitChannel1.Object); - channel.Setup(c => c.TargetChannel).Returns(() => target.Value); - - connectionFactory.Setup(f => f.CreateConnection()).Returns(connection.Object); - connection.Setup(c => c.CreateChannel(It.IsAny())).Returns(channel.Object); - connection.Setup(c => c.IsOpen).Returns(true); - channel.Setup(c => c.IsOpen).Returns(true); - - rabbitChannel1.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - rabbitChannel2.Setup(c => c.CreateBasicProperties()).Returns(new MockRabbitBasicProperties()); - channel.Setup(c => c.QueueDeclarePassive(It.IsAny())).Returns(new RC.QueueDeclareOk("test", 0, 0)); - - var consumer = new AtomicReference(); - var latch1 = new CountdownEvent(1); - var latch2 = new CountdownEvent(1); - - channel.Setup(c => c.BasicConsume(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>(), It.IsAny())) - .Callback, RC.IBasicConsumer>((_, _, _, _, _, _, cons) => - { - consumer.Value = cons; - latch1.Signal(); - }).Returns("consumerTag"); - - channel.Setup(c => c.BasicCancel("consumerTag")).Callback(() => - { - consumer.Value.HandleBasicCancelOk("consumerTag"); - latch2.Signal(); - }); - - var container = new DirectMessageListenerContainer(null, connectionFactory.Object); - container.SetQueueNames("test"); - container.PrefetchCount = 2; - container.MonitorInterval = 100; - container.MessageListener = new TestListener2(target, rabbitChannel2.Object); - container.AcknowledgeMode = AcknowledgeMode.Manual; - container.Initialize(); - await container.StartAsync(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - - Assert.True(latch1.Wait(TimeSpan.FromSeconds(10))); - var props = new MockRabbitBasicProperties(); - consumer.Value.HandleBasicDeliver("consumerTag", 1ul, false, string.Empty, string.Empty, props, new byte[1]); - Assert.True(latch2.Wait(TimeSpan.FromSeconds(10))); - await container.StopAsync(); - } - - private sealed class TestListener2 : IMessageListener - { - private readonly AtomicReference _target; - private readonly RC.IModel _object; - - public AcknowledgeMode ContainerAckMode { get; set; } - - public TestListener2(AtomicReference target, RC.IModel @object) - { - _target = target; - _object = @object; - } - - public void OnMessage(IMessage message) - { - _target.Value = _object; - } - - public void OnMessageBatch(List messages) - { - throw new NotImplementedException(); - } - } - - private sealed class TestListener : IMessageListener - { - public AcknowledgeMode ContainerAckMode { get; set; } - - public void OnMessage(IMessage message) - { - if (message.Headers.DeliveryTag().Value == 19ul) - { - throw new Exception("TestNackAndPendingAcks"); - } - } - - public void OnMessageBatch(List messages) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/DirectReplyToMessageListenerContainerTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/DirectReplyToMessageListenerContainerTest.cs deleted file mode 100644 index ee6d3966a9..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/DirectReplyToMessageListenerContainerTest.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Xunit; -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -[Trait("Category", "Integration")] -public sealed class DirectReplyToMessageListenerContainerTest : IDisposable -{ - private const string TestReleaseConsumerQ = "test.release.consumer"; - private readonly CachingConnectionFactory _adminCf; - private readonly RabbitAdmin _admin; - - public DirectReplyToMessageListenerContainerTest() - { - _adminCf = new CachingConnectionFactory("localhost"); - _admin = new RabbitAdmin(_adminCf); - _admin.DeclareQueue(new Queue(TestReleaseConsumerQ)); - } - - [Fact] - public async Task TestReleaseConsumerRace() - { - using var connectionFactory = new CachingConnectionFactory("localhost"); - using var container = new DirectReplyToMessageListenerContainer(null, connectionFactory); - - var latch = new CountdownEvent(1); - container.MessageListener = new EmptyListener(); - var mockMessageListener = new MockChannelAwareMessageListener(container.MessageListener, latch); - container.SetChannelAwareMessageListener(mockMessageListener); - - byte[] fooBytes = EncodingUtils.GetDefaultEncoding().GetBytes("foo"); - byte[] barBytes = EncodingUtils.GetDefaultEncoding().GetBytes("bar"); - await container.StartAsync(); - Assert.True(container.StartedLatch.Wait(TimeSpan.FromSeconds(10))); - - DirectReplyToMessageListenerContainer.ChannelHolder channel1 = container.GetChannelHolder(); - RC.IBasicProperties props = channel1.Channel.CreateBasicProperties(); - props.ReplyTo = Address.AmqRabbitMQReplyTo; - RC.IModelExensions.BasicPublish(channel1.Channel, string.Empty, TestReleaseConsumerQ, props, fooBytes); - RC.IModel replyChannel = connectionFactory.CreateConnection().CreateChannel(); - RC.BasicGetResult request = replyChannel.BasicGet(TestReleaseConsumerQ, true); - int n = 0; - - while (n++ < 100 && request == null) - { - await Task.Delay(100); - request = replyChannel.BasicGet(TestReleaseConsumerQ, true); - } - - Assert.NotNull(request); - props = channel1.Channel.CreateBasicProperties(); - RC.IModelExensions.BasicPublish(replyChannel, string.Empty, request.BasicProperties.ReplyTo, props, barBytes); - replyChannel.Close(); - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - - DirectReplyToMessageListenerContainer.ChannelHolder channel2 = container.GetChannelHolder(); - Assert.Same(channel1.Channel, channel2.Channel); - container.ReleaseConsumerFor(channel1, false, null); // simulate race for future timeout/cancel and onMessage() - ConcurrentDictionary inUse = container.InUseConsumerChannels; - Assert.Single(inUse); - container.ReleaseConsumerFor(channel2, false, null); - Assert.Empty(inUse); - await container.StopAsync(); - connectionFactory.Destroy(); - } - - public void Dispose() - { - _admin.DeleteQueue(TestReleaseConsumerQ); - _adminCf.Dispose(); - } - - private sealed class MockChannelAwareMessageListener : IChannelAwareMessageListener - { - public IChannelAwareMessageListener MessageListener { get; } - public CountdownEvent Latch { get; } - - public AcknowledgeMode ContainerAckMode { get; set; } - - public MockChannelAwareMessageListener(IMessageListener messageListener, CountdownEvent latch) - { - MessageListener = messageListener as IChannelAwareMessageListener; - Latch = latch; - } - - public void OnMessage(IMessage message, RC.IModel channel) - { - try - { - MessageListener.OnMessage(message, channel); - } - finally - { - Latch.Signal(); - } - } - - public void OnMessage(IMessage message) - { - } - - public void OnMessageBatch(List messages, RC.IModel channel) - { - } - - public void OnMessageBatch(List messages) - { - } - } - - private sealed class EmptyListener : IMessageListener - { - public AcknowledgeMode ContainerAckMode { get; set; } - - public void OnMessage(IMessage message) - { - } - - public void OnMessageBatch(List messages) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/DlqExpiryTests.cs b/src/Messaging/test/RabbitMQ.Test/Listener/DlqExpiryTests.cs deleted file mode 100644 index 0b2593c6a3..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/DlqExpiryTests.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -[Trait("Category", "Integration")] -public sealed class DlqExpiryTests : IClassFixture -{ - private readonly ServiceProvider _provider; - - public DlqExpiryTests(DlqStartupFixture fixture) - { - _provider = fixture.Provider; - } - - [Fact] - public async Task TestExpiredDies() - { - RabbitTemplate template = _provider.GetRabbitTemplate(); - var listener = _provider.GetService(); - IApplicationContext context = _provider.GetApplicationContext(); - var queue1 = context.GetService("test.expiry.main"); - - template.ConvertAndSend(queue1.QueueName, "foo"); - Assert.True(listener.Latch.Wait(TimeSpan.FromSeconds(10))); - await Task.Delay(300); - Assert.Equal(2, listener.Counter); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/DlqStartupFixture.cs b/src/Messaging/test/RabbitMQ.Test/Listener/DlqStartupFixture.cs deleted file mode 100644 index 8e6d5d416b..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/DlqStartupFixture.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Extensions; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -public sealed class DlqStartupFixture : IDisposable -{ - private readonly IServiceCollection _services; - - public ServiceProvider Provider { get; set; } - - public DlqStartupFixture() - { - _services = CreateContainer(); - Provider = _services.BuildServiceProvider(true); - Provider.GetRequiredService().StartAsync(default).GetAwaiter().GetResult(); - } - - private ServiceCollection CreateContainer(IConfiguration configuration = null) - { - var services = new ServiceCollection(); - - configuration ??= new ConfigurationBuilder().AddInMemoryCollection(new Dictionary - { - { "spring:rabbitmq:listener:direct:PossibleAuthenticationFailureFatal", "False" } - }).Build(); - - services.AddLogging(b => - { - b.SetMinimumLevel(LogLevel.Debug); - b.AddDebug(); - b.AddConsole(); - }); - - services.ConfigureRabbitOptions(configuration); - services.AddSingleton(configuration); - services.AddRabbitHostingServices(); - services.AddRabbitJsonMessageConverter(); - services.AddRabbitMessageHandlerMethodFactory(); - services.AddRabbitListenerEndpointRegistry(); - services.AddRabbitListenerEndpointRegistrar(); - services.AddRabbitListenerAttributeProcessor(); - services.AddRabbitConnectionFactory(); - services.AddRabbitAdmin(); - - var mainQueue = new AnonymousQueue("test.expiry.main"); - mainQueue.AddArgument("x-dead-letter-exchange", string.Empty); - mainQueue.AddArgument("x-dead-letter-routing-key", mainQueue.QueueName); - - var dlq = new AnonymousQueue("test.expiry.dlq"); - dlq.AddArgument("x-dead-letter-exchange", string.Empty); - dlq.AddArgument("x-dead-letter-routing-key", "test.expiry.main"); - dlq.AddArgument("x-message-ttl", 100); - - services.AddRabbitQueues(mainQueue, dlq); - - // Add default container factory - services.AddRabbitListenerContainerFactory((_, f) => - { - f.MismatchedQueuesFatal = true; - f.AcknowledgeMode = AcknowledgeMode.Manual; - }); - - // Add doNotRequeueFactory container factory - services.AddRabbitListenerContainerFactory((_, f) => - { - f.ServiceName = "doNotRequeueFactory"; - f.MismatchedQueuesFatal = true; - f.AcknowledgeMode = AcknowledgeMode.Manual; - f.DefaultRequeueRejected = false; - }); - - services.AddSingleton(); - services.AddRabbitListeners(configuration); - services.AddRabbitTemplate(); - - return services; - } - - public void Dispose() - { - Provider.Dispose(); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/ErrorHandlerTests.cs b/src/Messaging/test/RabbitMQ.Test/Listener/ErrorHandlerTests.cs deleted file mode 100644 index b1866dddea..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/ErrorHandlerTests.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Moq; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -public sealed class ErrorHandlerTests -{ - [Fact] - public void TestFatalErrorsAreRejected() - { - var handler = new ConditionalRejectingErrorHandler(); - - handler.HandleError(new ListenerExecutionFailedException("intended", new InvalidOperationException(), - Message.Create(Encoding.UTF8.GetBytes(string.Empty)))); - - Assert.Throws(() => - handler.HandleError(new ListenerExecutionFailedException("intended", new MessageConversionException(string.Empty), - Message.Create(Encoding.UTF8.GetBytes(string.Empty))))); - - IMessage message = Message.Create(Encoding.UTF8.GetBytes(string.Empty)); - - var parameterInfo = new Mock(); - parameterInfo.Setup(p => p.Position).Returns(1); - parameterInfo.Setup(p => p.Member.ToString()).Returns("testMember"); - - Assert.Throws(() => - handler.HandleError(new ListenerExecutionFailedException("intended", - new MethodArgumentTypeMismatchException(message, parameterInfo.Object, string.Empty), message))); - - Assert.Throws(() => - handler.HandleError(new ListenerExecutionFailedException("intended", - new MethodArgumentNotValidException(message, parameterInfo.Object, string.Empty), message))); - } - - [Fact] - public void TestSimple() - { - var cause = new InvalidCastException(); - Assert.Throws(() => DoTest(cause)); - } - - [Fact] - public void TestMessagingException() - { - var cause = new MessageHandlingException(null, "test", new MessageHandlingException(null, "test", new InvalidCastException())); - Assert.Throws(() => DoTest(cause)); - } - - private void DoTest(Exception cause) - { - var handler = new ConditionalRejectingErrorHandler(); - handler.HandleError(new ListenerExecutionFailedException("test", cause, Message.Create(Array.Empty()))); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/ListenFromAutoDeleteQueueTest.cs b/src/Messaging/test/RabbitMQ.Test/Listener/ListenFromAutoDeleteQueueTest.cs deleted file mode 100644 index e4717eebd1..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/ListenFromAutoDeleteQueueTest.cs +++ /dev/null @@ -1,202 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; -using Moq; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Adapters; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -[Trait("Category", "Integration")] -public sealed class ListenFromAutoDeleteQueueTest : IDisposable -{ - public const string Exchange1 = "testContainerWithAutoDeleteQueues"; - public const string Exchange2 = "otherExchange"; - public const string Q1 = "anon"; - public const string Q2 = "anon2"; - public const string Q3 = "otherAnon"; - - private readonly DirectMessageListenerContainer _listenerContainer1; - private readonly DirectMessageListenerContainer _listenerContainer2; - private readonly DirectMessageListenerContainer _listenerContainer3; - private readonly DirectMessageListenerContainer _listenerContainer4; - - private readonly Queue _expiringQueue; - private readonly IConnectionFactory _connectionFactory; - private readonly AppendingListener _listener; - private readonly TestAdmin _containerAdmin; - - public ListenFromAutoDeleteQueueTest() - { - _connectionFactory = new CachingConnectionFactory("localhost") - { - IsPublisherReturns = true - }; - - // Container Admin - _containerAdmin = new TestAdmin(_connectionFactory); - - // Exchange - var directExchange = new DirectExchange("testContainerWithAutoDeleteQueues", true, true); - - _listenerContainer1 = new DirectMessageListenerContainer(null, _connectionFactory, "container1"); - _listenerContainer1.ConsumersPerQueue = 2; - _listenerContainer1.AddQueueNames(Q1, Q2); - _containerAdmin.DeclareExchange(directExchange); - _containerAdmin.DeclareQueue(new Queue(Q1, true, false, true)); - _containerAdmin.DeclareQueue(new Queue(Q2, true, false, true)); - _containerAdmin.DeclareBinding(new Binding("b1", Q1, Binding.DestinationType.Queue, directExchange.ExchangeName, Q1, null)); - _containerAdmin.DeclareBinding(new Binding("b2", Q2, Binding.DestinationType.Queue, directExchange.ExchangeName, Q2, null)); - - // Listener - _listener = new AppendingListener(); - var adapter = new MessageListenerAdapter(null, _listener); - _listenerContainer1.MessageListener = adapter; - _listenerContainer1.StartAsync(); - _listenerContainer1.StartedLatch.Wait(TimeSpan.FromSeconds(10)); - - // Conditional declarations - var otherExchange = new DirectExchange(Exchange2, true, true); - _containerAdmin.DeclareExchange(otherExchange); - _containerAdmin.DeclareQueue(new Queue(Q3, true, false, true)); - _containerAdmin.DeclareBinding(new Binding("b3", Q3, Binding.DestinationType.Queue, otherExchange.ExchangeName, Q3, null)); - - _listenerContainer2 = new DirectMessageListenerContainer(null, _connectionFactory, "container2"); - _listenerContainer2.IsAutoStartup = false; - _listenerContainer2.ShutdownTimeout = 50; - _listenerContainer2.AddQueueNames(Q3); - _listenerContainer2.MessageListener = adapter; - - _expiringQueue = new Queue(Guid.NewGuid().ToString(), true, false, false, new Dictionary - { - { "x-expires", 200 } - }); - - _containerAdmin.DeclareQueue(_expiringQueue); - _listenerContainer3 = new DirectMessageListenerContainer(null, _connectionFactory, "container3"); - _listenerContainer3.IsAutoStartup = false; - _listenerContainer3.ShutdownTimeout = 50; - _listenerContainer3.AddQueueNames(_expiringQueue.QueueName); - _listenerContainer3.MessageListener = adapter; - - _listenerContainer4 = new DirectMessageListenerContainer(null, _connectionFactory, "container4"); - - _listenerContainer4.IsAutoStartup = false; - _listenerContainer4.ShutdownTimeout = 50; - _listenerContainer4.AddQueueNames(Q2); - _listenerContainer4.MessageListener = adapter; - _listenerContainer4.AutoDeclare = false; - } - - [Fact] - public void TestStopStart() - { - var rabbitTemplate = new RabbitTemplate(_connectionFactory); - rabbitTemplate.ConvertAndSend(Exchange1, Q1, "foo"); - _listener.Latch.Wait(TimeSpan.FromSeconds(10)); - Assert.NotEmpty(_listener.Queue); - _listenerContainer1.StopAsync(); - _listenerContainer1.StartAsync(); - _listenerContainer1.StartedLatch.Wait(TimeSpan.FromSeconds(10)); - rabbitTemplate.ConvertAndSend(Exchange1, Q1, "foo"); - _listener.Latch.Wait(TimeSpan.FromSeconds(10)); - Assert.NotEmpty(_listener.Queue); - } - - [Fact] - public void TestStopStartConditionalDeclarations() - { - var rabbitTemplate = new RabbitTemplate(_connectionFactory); - _listenerContainer2.StartAsync(); - _listenerContainer2.StartedLatch.Wait(TimeSpan.FromSeconds(10)); - - rabbitTemplate.ConvertAndSend(Exchange2, Q3, "foo"); - _listener.Latch.Wait(TimeSpan.FromSeconds(10)); - Assert.NotEmpty(_listener.Queue); - _listenerContainer2.StopAsync(); - _listenerContainer2.StartAsync(); - _listenerContainer1.StartedLatch.Wait(TimeSpan.FromSeconds(10)); - rabbitTemplate.ConvertAndSend(Exchange2, Q3, "foo"); - _listener.Latch.Wait(TimeSpan.FromSeconds(10)); - Assert.NotEmpty(_listener.Queue); - } - - [Fact] - public void TestRedeclareXExpiresQueue() - { - var rabbitTemplate = new RabbitTemplate(_connectionFactory); - _listenerContainer3.StartAsync(); - _listenerContainer3.StartedLatch.Wait(TimeSpan.FromSeconds(10)); - rabbitTemplate.ConvertAndSend(_expiringQueue.QueueName, "foo"); - _listener.Latch.Wait(TimeSpan.FromSeconds(10)); - Assert.NotEmpty(_listener.Queue); - - _listenerContainer3.StopAsync(); - _listenerContainer3.StartAsync(); - _listenerContainer3.StartedLatch.Wait(TimeSpan.FromSeconds(10)); - - rabbitTemplate.ConvertAndSend(_expiringQueue.QueueName, "foo"); - _listener.Latch.Wait(TimeSpan.FromSeconds(10)); - Assert.NotEmpty(_listener.Queue); - } - - [Fact] - public void TestAutoDeclareFalse() - { - var rabbitTemplate = new RabbitTemplate(_connectionFactory); - rabbitTemplate.ConvertAndSend(Exchange1, Q2, "foo"); - _listener.Latch.Wait(TimeSpan.FromSeconds(10)); - Assert.NotEmpty(_listener.Queue); - - _listenerContainer4.StopAsync(); - var testAdminMock = new Mock(); - testAdminMock.Setup(m => m.Initialize()).Throws(new Exception("Should not be called!")); - _listenerContainer4.RabbitAdmin = testAdminMock.Object; - _listenerContainer4.StopAsync(); - _listenerContainer4.StartAsync(); - testAdminMock.Verify(m => m.Initialize(), Times.Never); - } - - public void Dispose() - { - _containerAdmin.DeleteQueue(Q1); - _containerAdmin.DeleteQueue(Q2); - _containerAdmin.DeleteQueue(Q3); - _containerAdmin.DeleteQueue(_expiringQueue.ActualName); - _containerAdmin.DeleteExchange("testContainerWithAutoDeleteQueues"); - _containerAdmin.DeleteExchange("otherExchange"); - _connectionFactory.Dispose(); - _listenerContainer1.Dispose(); - _listenerContainer2.Dispose(); - _listenerContainer3.Dispose(); - _listenerContainer4.Dispose(); - } - - private sealed class AppendingListener : IReplyingMessageListener - { - public ConcurrentQueue Queue { get; } = new(); - public CountdownEvent Latch { get; } = new(1); - - public string HandleMessage(string input) - { - Queue.Enqueue(input); - Latch.Signal(); - return input; - } - } - - private sealed class TestAdmin : RabbitAdmin - { - public TestAdmin(IConnectionFactory connectionFactory, ILogger logger = null) - : base(connectionFactory, logger) - { - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Listener/Listener.cs b/src/Messaging/test/RabbitMQ.Test/Listener/Listener.cs deleted file mode 100644 index c514036645..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Listener/Listener.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Attributes; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Listener; - -public sealed class Listener -{ - public CountdownEvent Latch { get; set; } = new(2); - - public int Counter { get; set; } - - [RabbitListener("test.expiry.main")] - public Task ListenAsync(string foo) - { - Latch.Signal(); - Counter++; - return Task.FromException(new MessageConversionException("test.expiry")); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/MockRabbitBasicProperties.cs b/src/Messaging/test/RabbitMQ.Test/MockRabbitBasicProperties.cs deleted file mode 100644 index 5d0fec48c2..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/MockRabbitBasicProperties.cs +++ /dev/null @@ -1,181 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RC = RabbitMQ.Client; - -namespace Steeltoe.Messaging.RabbitMQ.Test; - -public sealed class MockRabbitBasicProperties : RC.IBasicProperties -{ - int RC.IContentHeader.ProtocolClassId => 0; - - string RC.IContentHeader.ProtocolClassName => string.Empty; - - public ushort ProtocolClassId { get; } - - public string ProtocolClassName { get; } - - public string AppId { get; set; } - - public string ClusterId { get; set; } - - public string ContentEncoding { get; set; } - - public string ContentType { get; set; } - - public string CorrelationId { get; set; } - - public byte DeliveryMode { get; set; } = 0xff; - - public string Expiration { get; set; } - - public IDictionary Headers { get; set; } = new Dictionary(); - - public string MessageId { get; set; } - - public bool Persistent { get; set; } - - public byte Priority { get; set; } = 0xff; - - public string ReplyTo { get; set; } - - public RC.PublicationAddress ReplyToAddress { get; set; } - - public RC.AmqpTimestamp Timestamp { get; set; } - - public string Type { get; set; } - - public string UserId { get; set; } - - public void ClearAppId() - { - } - - public void ClearClusterId() - { - } - - public void ClearContentEncoding() - { - } - - public void ClearContentType() - { - } - - public void ClearCorrelationId() - { - } - - public void ClearDeliveryMode() - { - } - - public void ClearExpiration() - { - } - - public void ClearHeaders() - { - } - - public void ClearMessageId() - { - } - - public void ClearPriority() - { - } - - public void ClearReplyTo() - { - } - - public void ClearTimestamp() - { - } - - public void ClearType() - { - } - - public void ClearUserId() - { - } - - public bool IsAppIdPresent() - { - return AppId != null; - } - - public bool IsClusterIdPresent() - { - return ClusterId != null; - } - - public bool IsContentEncodingPresent() - { - return ContentEncoding != null; - } - - public bool IsContentTypePresent() - { - return ContentType != null; - } - - public bool IsCorrelationIdPresent() - { - return CorrelationId != null; - } - - public bool IsDeliveryModePresent() - { - return DeliveryMode != 0xff; - } - - public bool IsExpirationPresent() - { - return Expiration != null; - } - - public bool IsHeadersPresent() - { - return Headers != null; - } - - public bool IsMessageIdPresent() - { - return MessageId != null; - } - - public bool IsPriorityPresent() - { - return Priority != 0xff; - } - - public bool IsReplyToPresent() - { - return ReplyTo != null; - } - - public bool IsTimestampPresent() - { - return !default(RC.AmqpTimestamp).Equals(Timestamp); - } - - public bool IsTypePresent() - { - return Type != null; - } - - public bool IsUserIdPresent() - { - return UserId != null; - } - - public void SetPersistent(bool persistent) - { - Persistent = persistent; - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Steeltoe.Messaging.RabbitMQ.Test.csproj b/src/Messaging/test/RabbitMQ.Test/Steeltoe.Messaging.RabbitMQ.Test.csproj deleted file mode 100644 index 6076fb8214..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Steeltoe.Messaging.RabbitMQ.Test.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - net8.0;net6.0 - true - - - - - - - - - - - - - diff --git a/src/Messaging/test/RabbitMQ.Test/Support/Converter/ContentTypeDelegatingMessageConverterTest.cs b/src/Messaging/test/RabbitMQ.Test/Support/Converter/ContentTypeDelegatingMessageConverterTest.cs deleted file mode 100644 index 9b2b9aa1ed..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Support/Converter/ContentTypeDelegatingMessageConverterTest.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Support.Converter; - -public sealed class ContentTypeDelegatingMessageConverterTest -{ - [Fact] - public void TestDelegationOutbound() - { - var converter = new ContentTypeDelegatingMessageConverter(); - var messageConverter = new JsonMessageConverter(); - converter.AddDelegate("foo/bar", messageConverter); - converter.AddDelegate(MessageHeaders.ContentTypeJson, messageConverter); - - var props = new RabbitHeaderAccessor(); - - var foo = new Foo - { - FooString = "bar" - }; - - props.ContentType = "foo/bar"; - IMessage message = converter.ToMessage(foo, props.MessageHeaders); - Assert.Equal(MessageHeaders.ContentTypeJson, message.Headers.ContentType()); - Assert.Equal("{\"fooString\":\"bar\"}", Encoding.UTF8.GetString((byte[])message.Payload)); - var converted = converter.FromMessage(message); - Assert.Equal("bar", converted.FooString); - - props = new RabbitHeaderAccessor - { - ContentType = MessageHeaders.ContentTypeJson - }; - - message = converter.ToMessage(foo, props.MessageHeaders); - Assert.Equal("{\"fooString\":\"bar\"}", Encoding.UTF8.GetString((byte[])message.Payload)); - converted = converter.FromMessage(message); - Assert.Equal("bar", converted.FooString); - - converter = new ContentTypeDelegatingMessageConverter(null); // no default - props = new RabbitHeaderAccessor(); - - try - { - converter.ToMessage(foo, props.MessageHeaders); - throw new Exception("Expected exception"); - } - catch (Exception e) - { - Assert.IsType(e); - Assert.Contains("No delegate converter", e.Message, StringComparison.Ordinal); - } - } - - public sealed class Foo - { - public string FooString { get; set; } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Support/Converter/JsonMessageConverterTest.cs b/src/Messaging/test/RabbitMQ.Test/Support/Converter/JsonMessageConverterTest.cs deleted file mode 100644 index cd29d71597..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Support/Converter/JsonMessageConverterTest.cs +++ /dev/null @@ -1,342 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Support.Converter; - -public sealed class JsonMessageConverterTest -{ - private readonly JsonMessageConverter _converter; - private readonly JsonMessageConverter _jsonConverterWithDefaultType; - private readonly SimpleTrade _trade; - - public JsonMessageConverterTest() - { - _converter = new JsonMessageConverter(); - - _trade = new SimpleTrade - { - AccountName = "Acct1", - BuyRequest = true, - OrderType = "Market", - Price = 103.30M, - Quantity = 100, - RequestId = "R123", - Ticker = "VMW", - UserName = "Joe Trader" - }; - - _jsonConverterWithDefaultType = new JsonMessageConverter(); - - var classMapper = new DefaultTypeMapper - { - DefaultType = typeof(Foo) - }; - - _jsonConverterWithDefaultType.TypeMapper = classMapper; - } - - [Fact] - public void SimpleTrade() - { - IMessage message = _converter.ToMessage(_trade, new MessageHeaders()); - var marshaledTrade = _converter.FromMessage(message); - Assert.Equal(_trade, marshaledTrade); - } - - [Fact] - public void NestedBean() - { - var bar = new Bar - { - Foo = - { - Name = "spam" - } - }; - - IMessage message = _converter.ToMessage(bar, new MessageHeaders()); - - var marshaled = _converter.FromMessage(message); - Assert.Equal(bar, marshaled); - } - - [Fact] - public void Dictionary() - { - var hashtable = new Dictionary - { - { "TICKER", "VMW" }, - { "PRICE", "103.2" } - }; - - IMessage message = _converter.ToMessage(hashtable, new MessageHeaders()); - var marshaledHashtable = _converter.FromMessage>(message); - - Assert.Equal("VMW", marshaledHashtable["TICKER"]); - Assert.Equal("103.2", marshaledHashtable["PRICE"]); - } - - [Fact] - public void TestAmqp330StringArray() - { - string[] testData = - { - "test" - }; - - IMessage message = _converter.ToMessage(testData, new MessageHeaders()); - string[] result = _converter.FromMessage(message); - Assert.Single(result); - Assert.Equal("test", result[0]); - } - - [Fact] - public void TestAmqp330ObjectArray() - { - SimpleTrade[] testData = - { - _trade - }; - - IMessage message = _converter.ToMessage(testData, new MessageHeaders()); - SimpleTrade[] result = _converter.FromMessage(message); - Assert.Single(result); - Assert.Equal(_trade, result[0]); - } - - [Fact] - public void TestDefaultType() - { - byte[] bytes = Encoding.UTF8.GetBytes("{\"name\" : \"foo\" }"); - - var messageProperties = new RabbitHeaderAccessor - { - ContentType = "application/json" - }; - - IMessage message = Message.Create(bytes, messageProperties.MessageHeaders); - var converter = new JsonMessageConverter(); - - var classMapper = new DefaultTypeMapper - { - DefaultType = typeof(Foo) - }; - - converter.TypeMapper = classMapper; - object foo = converter.FromMessage(message, null); - Assert.IsType(foo); - } - - [Fact] - public void TestDefaultTypeConfig() - { - byte[] bytes = Encoding.UTF8.GetBytes("{\"name\" : \"foo\" }"); - - var messageProperties = new RabbitHeaderAccessor - { - ContentType = "application/json" - }; - - IMessage message = Message.Create(bytes, messageProperties.MessageHeaders); - object foo = _jsonConverterWithDefaultType.FromMessage(message, null); - Assert.IsType(foo); - } - - [Fact] - public void TestNoJsonContentType() - { - byte[] bytes = Encoding.UTF8.GetBytes("{\"name\" : \"foo\" }"); - var messageProperties = new MessageHeaders(); - IMessage message = Message.Create(bytes, messageProperties); - _jsonConverterWithDefaultType.AssumeSupportedContentType = false; - object foo = _jsonConverterWithDefaultType.FromMessage(message, null); - Assert.IsType(foo); - } - - [Fact(Skip = "Need to handle nested dictionaries")] - public void TestNoTypeInfo() - { - byte[] bytes = Encoding.UTF8.GetBytes("{\"name\" : { \"foo\" : \"bar\" } }"); - - var messageProperties = new RabbitHeaderAccessor - { - ContentType = "application/json" - }; - - IMessage message = Message.Create(bytes, messageProperties.MessageHeaders); - object foo = _converter.FromMessage(message, null); - Assert.IsType>(foo); - var fooDict = foo as Dictionary; - object nameObj = fooDict["name"]; - Assert.NotNull(nameObj); - Assert.IsType>(nameObj); - } - - [Fact] - public void TestInferredTypeInfo() - { - byte[] bytes = Encoding.UTF8.GetBytes("{\"name\" : \"foo\" }"); - - var messageProperties = new RabbitHeaderAccessor - { - ContentType = "application/json", - InferredArgumentType = typeof(Foo) - }; - - IMessage message = Message.Create(bytes, messageProperties.MessageHeaders); - object foo = _converter.FromMessage(message, null); - Assert.IsType(foo); - } - - [Fact] - public void TestInferredGenericTypeInfo() - { - byte[] bytes = Encoding.UTF8.GetBytes("[ {\"name\" : \"foo\" } ]"); - - var messageProperties = new RabbitHeaderAccessor - { - ContentType = "application/json", - InferredArgumentType = typeof(List) - }; - - IMessage message = Message.Create(bytes, messageProperties.MessageHeaders); - object foo = _converter.FromMessage(message, null); - Assert.IsType>(foo); - var asList = foo as List; - Assert.NotNull(asList[0]); - } - - [Fact] - public void TestInferredGenericMap1() - { - byte[] bytes = Encoding.UTF8.GetBytes("{\"qux\" : [ { \"foo\" : { \"name\" : \"bar\" } } ] }"); - - var messageProperties = new RabbitHeaderAccessor - { - ContentType = "application/json", - InferredArgumentType = typeof(Dictionary>) - }; - - IMessage message = Message.Create(bytes, messageProperties.MessageHeaders); - object foo = _converter.FromMessage(message, null); - Assert.IsType>>(foo); - var dict = foo as Dictionary>; - List list = dict["qux"]; - Assert.NotNull(list); - Assert.IsType(list[0]); - } - - [Fact] - public void TestInferredGenericMap2() - { - byte[] bytes = Encoding.UTF8.GetBytes("{\"qux\" : { \"baz\" : { \"foo\" : { \"name\" : \"bar\" } } } }"); - - var messageProperties = new RabbitHeaderAccessor - { - ContentType = "application/json", - InferredArgumentType = typeof(Dictionary>) - }; - - IMessage message = Message.Create(bytes, messageProperties.MessageHeaders); - object foo = _converter.FromMessage(message, null); - Assert.IsType>>(foo); - var dict = foo as Dictionary>; - Dictionary dict2 = dict["qux"]; - Assert.NotNull(dict2); - Assert.IsType(dict2["baz"]); - } - - [Fact] - public void TestMissingContentType() - { - byte[] bytes = Encoding.UTF8.GetBytes("{\"name\" : \"foo\" }"); - var messageProperties = new MessageHeaders(); - IMessage message = Message.Create(bytes, messageProperties); - var j2Converter = new JsonMessageConverter(); - - var typeMapper = new DefaultTypeMapper - { - DefaultType = typeof(Foo) - }; - - j2Converter.TypeMapper = typeMapper; - object foo = j2Converter.FromMessage(message, null); - Assert.IsType(foo); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(messageProperties); - accessor.ContentType = null; - - foo = j2Converter.FromMessage(message, null); - Assert.IsType(foo); - j2Converter.AssumeSupportedContentType = false; - foo = j2Converter.FromMessage(message, null); - Assert.Same(foo, bytes); - } - - public sealed class Foo - { - public string Name { get; set; } = "foo"; - - public Foo() - { - } - - public Foo(string name) - { - Name = name; - } - - public override int GetHashCode() - { - return Name?.GetHashCode() ?? 0; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not Foo other || GetType() != obj.GetType()) - { - return false; - } - - return Name == other.Name; - } - } - - public sealed class Bar - { - public string Name { get; set; } = "bar"; - - public Foo Foo { get; set; } = new(); - - public override int GetHashCode() - { - return HashCode.Combine(Name, Foo); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not Bar other || GetType() != obj.GetType()) - { - return false; - } - - return Name == other.Name && Equals(Foo, other.Foo); - } - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Support/Converter/SimpleMessageConverterTest.cs b/src/Messaging/test/RabbitMQ.Test/Support/Converter/SimpleMessageConverterTest.cs deleted file mode 100644 index b0a68a09f2..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Support/Converter/SimpleMessageConverterTest.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Support.Converter; - -public sealed class SimpleMessageConverterTest -{ - [Fact] - public void BytesAsDefaultMessageBodyType() - { - var converter = new SimpleMessageConverter(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("test"), new MessageHeaders()); - byte[] result = converter.FromMessage(message); - Assert.Equal("test", Encoding.UTF8.GetString(result)); - } - - [Fact] - public void MessageToString() - { - var converter = new SimpleMessageConverter(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("test"), new MessageHeaders()); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MessageHeaders.ContentTypeTextPlain; - string result = converter.FromMessage(message); - Assert.Equal("test", result); - } - - [Fact] - public void MessageToBytes() - { - var converter = new SimpleMessageConverter(); - - IMessage message = Message.Create(new byte[] - { - 1, - 2, - 3 - }, new MessageHeaders()); - - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(message); - accessor.ContentType = MessageHeaders.ContentTypeBytes; - byte[] result = converter.FromMessage(message); - Assert.Equal(3, result.Length); - Assert.Equal(1, result[0]); - Assert.Equal(2, result[1]); - Assert.Equal(3, result[2]); - } - - [Fact] - public void StringToMessage() - { - var converter = new SimpleMessageConverter(); - IMessage message = converter.ToMessage("test", new MessageHeaders()); - string contentType = message.Headers.ContentType(); - string contentEncoding = message.Headers.ContentEncoding(); - var encoding = Encoding.GetEncoding(contentEncoding); - string content = encoding.GetString((byte[])message.Payload); - Assert.Equal("text/plain", contentType); - Assert.Equal("test", content); - } - - [Fact] - public void BytesToMessage() - { - var converter = new SimpleMessageConverter(); - - IMessage message = converter.ToMessage(new byte[] - { - 1, - 2, - 3 - }, new MessageHeaders()); - - string contentType = message.Headers.ContentType(); - byte[] body = message.Payload as byte[]; - Assert.Equal(MessageHeaders.ContentTypeBytes, contentType); - Assert.Equal(3, body.Length); - Assert.Equal(1, body[0]); - Assert.Equal(2, body[1]); - Assert.Equal(3, body[2]); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Support/Converter/SimpleTrade.cs b/src/Messaging/test/RabbitMQ.Test/Support/Converter/SimpleTrade.cs deleted file mode 100644 index 54f84e5dca..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Support/Converter/SimpleTrade.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Test.Support.Converter; - -public sealed class SimpleTrade -{ - public string Ticker { get; set; } - - public long Quantity { get; set; } - - public decimal Price { get; set; } - - public string OrderType { get; set; } - - public string AccountName { get; set; } - - public bool BuyRequest { get; set; } - - public string UserName { get; set; } - - public string RequestId { get; set; } - - public override int GetHashCode() - { - return HashCode.Combine(AccountName, BuyRequest, OrderType, Price, Quantity, RequestId, Ticker, UserName); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not SimpleTrade other || GetType() != obj.GetType()) - { - return false; - } - -#pragma warning disable S1067 // Expressions should not be too complex - return AccountName == other.AccountName && BuyRequest == other.BuyRequest && OrderType == other.OrderType && Price == other.Price && - Quantity == other.Quantity && RequestId == other.RequestId && Ticker == other.Ticker && UserName == other.UserName; -#pragma warning restore S1067 // Expressions should not be too complex - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Support/DefaultMessageHeaderConverterTest.cs b/src/Messaging/test/RabbitMQ.Test/Support/DefaultMessageHeaderConverterTest.cs deleted file mode 100644 index 6d2e6f04a6..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Support/DefaultMessageHeaderConverterTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Messaging.RabbitMQ.Test.Support; - -public sealed class DefaultMessageHeaderConverterTest -{ -} diff --git a/src/Messaging/test/RabbitMQ.Test/Support/MessagePostProcessorUtilsTest.cs b/src/Messaging/test/RabbitMQ.Test/Support/MessagePostProcessorUtilsTest.cs deleted file mode 100644 index 2bcfc59846..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Support/MessagePostProcessorUtilsTest.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Order; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Support; - -public sealed class MessagePostProcessorUtilsTest -{ - [Fact] - public void TestOrderIng() - { - MessagePostProcessor[] pps = - { - new(), - new OrderedMessagePostProcessor().Order(3), - new OrderedMessagePostProcessor().Order(1), - new PriorityOrderedMessagePostProcessor().Order(6), - new PriorityOrderedMessagePostProcessor().Order(2) - }; - - var list = new List(); - list.AddRange(pps); - List sorted = MessagePostProcessorUtils.Sort(list); - using List.Enumerator iterator = sorted.GetEnumerator(); - - iterator.MoveNext(); - Assert.IsType(iterator.Current); - Assert.Equal(2, ((IOrdered)iterator.Current).Order); - - iterator.MoveNext(); - Assert.IsType(iterator.Current); - Assert.Equal(6, ((IOrdered)iterator.Current).Order); - - iterator.MoveNext(); - Assert.IsType(iterator.Current); - Assert.Equal(1, ((IOrdered)iterator.Current).Order); - - iterator.MoveNext(); - Assert.IsType(iterator.Current); - Assert.Equal(3, ((IOrdered)iterator.Current).Order); - - iterator.MoveNext(); - Assert.IsType(iterator.Current); - } - - private class MessagePostProcessor : IMessagePostProcessor - { - public IMessage PostProcessMessage(IMessage message) - { - return null; - } - - public IMessage PostProcessMessage(IMessage message, CorrelationData correlation) - { - return null; - } - } - - private class OrderedMessagePostProcessor : MessagePostProcessor, IOrdered - { - private int _order; - - int IOrdered.Order => _order; - - public OrderedMessagePostProcessor Order(int value) - { - _order = value; - return this; - } - } - - private sealed class PriorityOrderedMessagePostProcessor : OrderedMessagePostProcessor, IPriorityOrdered - { - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Support/RabbitHeaderAccessorTest.cs b/src/Messaging/test/RabbitMQ.Test/Support/RabbitHeaderAccessorTest.cs deleted file mode 100644 index d574b3fc69..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Support/RabbitHeaderAccessorTest.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Support; -using Xunit; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Support; - -public sealed class RabbitHeaderAccessorTest -{ - [Fact] - public void NewEmptyHeaders() - { - var accessor = new RabbitHeaderAccessor(); - Assert.Empty(accessor.ToDictionary()); - } - - [Fact] - public void ValidateAmqpHeaders() - { - var accessor = new RabbitHeaderAccessor(); - const string correlationId = "correlation-id-1234"; - long time = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - accessor.AppId = "app-id-1234"; - accessor.ClusterId = "cluster-id-1234"; - accessor.ContentEncoding = "UTF-16"; - accessor.ContentLength = 200L; - accessor.ContentType = "text/plain"; - accessor.CorrelationId = correlationId; - accessor.ReceivedDeliveryMode = MessageDeliveryMode.NonPersistent; - accessor.DeliveryTag = 555L; - accessor.Expiration = "expiration-1234"; - accessor.MessageCount = 42; - accessor.MessageId = "message-id-1234"; - accessor.Priority = 9; - accessor.ReceivedExchange = "received-exchange-1234"; - accessor.ReceivedRoutingKey = "received-routing-key-1234"; - accessor.ReceivedDelay = 1234; - accessor.Redelivered = true; - accessor.ReplyTo = "reply-to-1234"; - accessor.Timestamp = time; - accessor.Type = "type-1234"; - accessor.ReceivedUserId = "user-id-1234"; - accessor.ConsumerTag = "consumer.tag"; - accessor.ConsumerQueue = "consumer.queue"; - - IMessage message = RabbitMessageBuilder.WithPayload("test").SetHeaders(accessor).Build(); - - RabbitHeaderAccessor headerAccessor = RabbitHeaderAccessor.GetAccessor(message); - - Assert.Equal("app-id-1234", headerAccessor.AppId); - Assert.Equal("cluster-id-1234", headerAccessor.ClusterId); - Assert.Equal("UTF-16", headerAccessor.ContentEncoding); - Assert.Equal(200, headerAccessor.ContentLength); - Assert.Equal("text/plain", headerAccessor.ContentType); - Assert.Equal(correlationId, headerAccessor.CorrelationId); - Assert.Equal(MessageDeliveryMode.NonPersistent, headerAccessor.ReceivedDeliveryMode); - Assert.Equal(555ul, headerAccessor.DeliveryTag.Value); - Assert.Equal("expiration-1234", headerAccessor.Expiration); - Assert.Equal(42u, headerAccessor.MessageCount.Value); - Assert.Equal("message-id-1234", headerAccessor.MessageId); - Assert.Equal(9, headerAccessor.Priority); - Assert.Equal("received-exchange-1234", headerAccessor.ReceivedExchange); - Assert.Equal("received-routing-key-1234", headerAccessor.ReceivedRoutingKey); - Assert.Equal(1234, headerAccessor.ReceivedDelay); - Assert.Equal(true, headerAccessor.Redelivered); - Assert.Equal("reply-to-1234", headerAccessor.ReplyTo); - Assert.Equal(time, headerAccessor.Timestamp); - Assert.Equal("type-1234", headerAccessor.Type); - Assert.Equal("user-id-1234", headerAccessor.ReceivedUserId); - Assert.Equal("consumer.tag", headerAccessor.ConsumerTag); - Assert.Equal("consumer.queue", headerAccessor.ConsumerQueue); - - // Making sure replyChannel is not mixed with replyTo - Assert.Null(headerAccessor.ReplyChannel); - } - - [Fact] - public void PrioritySet() - { - IMessage message = RabbitMessageBuilder.WithPayload("payload").SetHeader(RabbitMessageHeaders.Priority, 90).Build(); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetAccessor(message); - Assert.Equal(90, accessor.Priority.Value); - } - - [Fact] - public void PriorityMustBeInteger() - { - var accessor = new RabbitHeaderAccessor(RabbitMessageBuilder.WithPayload("foo").Build()); - Assert.Throws(() => accessor.SetHeader(RabbitMessageHeaders.Priority, "foo")); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/Test/MessageTestUtils.cs b/src/Messaging/test/RabbitMQ.Test/Test/MessageTestUtils.cs deleted file mode 100644 index e258b7aff6..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/Test/MessageTestUtils.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.RabbitMQ.Support; - -namespace Steeltoe.Messaging.RabbitMQ.Test.Test; - -internal static class MessageTestUtils -{ - public static IMessage CreateTextMessage(string body, MessageHeaders properties) - { - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(properties); - accessor.ContentType = MimeTypeUtils.TextPlainValue; - return Message.Create(ToBytes(body), properties); - } - - public static IMessage CreateTextMessage(string body) - { - return CreateTextMessage(body, new MessageHeaders()); - } - - public static string ExtractText(IMessage message) - { - return EncodingUtils.GetDefaultEncoding().GetString((byte[])message.Payload); - } - - public static byte[] ToBytes(string content) - { - return EncodingUtils.GetDefaultEncoding().GetBytes(content); - } -} diff --git a/src/Messaging/test/RabbitMQ.Test/xunit.runner.json b/src/Messaging/test/RabbitMQ.Test/xunit.runner.json deleted file mode 100644 index fdeefaa456..0000000000 --- a/src/Messaging/test/RabbitMQ.Test/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "maxParallelThreads": 1, - "parallelizeTestCollections": false -} diff --git a/src/Security/src/Authentication.Mtls/MutualTlsAuthenticationHandler.cs b/src/Security/src/Authentication.Mtls/MutualTlsAuthenticationHandler.cs index 910c67d2e0..94df6058c6 100644 --- a/src/Security/src/Authentication.Mtls/MutualTlsAuthenticationHandler.cs +++ b/src/Security/src/Authentication.Mtls/MutualTlsAuthenticationHandler.cs @@ -29,8 +29,7 @@ internal sealed class MutualTlsAuthenticationHandler : AuthenticationHandler (CertificateAuthenticationEvents)base.Events; #if NET6_0 - public MutualTlsAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, - ISystemClock clock) + public MutualTlsAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) #else public MutualTlsAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) diff --git a/src/Security/test/DataProtection.Redis.Test/RedisDataProtectionBuilderExtensionsTest.cs b/src/Security/test/DataProtection.Redis.Test/RedisDataProtectionBuilderExtensionsTest.cs index 504880a0e3..980edb0936 100644 --- a/src/Security/test/DataProtection.Redis.Test/RedisDataProtectionBuilderExtensionsTest.cs +++ b/src/Security/test/DataProtection.Redis.Test/RedisDataProtectionBuilderExtensionsTest.cs @@ -123,7 +123,19 @@ private static object GetMockedConnectionMultiplexer(string? connectionString) RedisValue[] GetRedisValues(RedisKey key) { - return innerStore.TryGetValue(key!, out byte[]? data) ? [default, default, data] : [default, default, default]; + return innerStore.TryGetValue(key!, out byte[]? data) + ? + [ + default, + default, + data + ] + : + [ + default, + default, + default + ]; } RedisResult SetRedisValues(RedisKey[]? keys, RedisValue[]? values) diff --git a/src/Steeltoe.All.sln b/src/Steeltoe.All.sln index 4a958acd6c..301e0ca1f4 100644 --- a/src/Steeltoe.All.sln +++ b/src/Steeltoe.All.sln @@ -151,36 +151,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Security.Authentic EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Security.Authentication.Mtls.Test", "Security\test\Authentication.Mtls.Test\Steeltoe.Security.Authentication.Mtls.Test.csproj", "{0134F5C0-7595-497E-91BE-CCCD2290CF8E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Messaging", "Messaging", "{283EAD31-6CC1-4306-A0E8-AB60F6CB3FD8}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Messaging.Abstractions", "Messaging\src\Abstractions\Steeltoe.Messaging.Abstractions.csproj", "{082FF7F6-EC5B-4AC1-9B66-C4A6ED955371}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{EA583B9C-D8C6-422A-9FF8-157CF6A4A61E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Integration.Abstractions", "Integration\src\Abstractions\Steeltoe.Integration.Abstractions.csproj", "{AAA88EFE-706E-4DF6-830B-F7AAFB59FC26}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Stream", "Stream", "{4F1EA720-AA18-4955-A854-016B26F6CED1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream.Abstractions", "Stream\src\Abstractions\Steeltoe.Stream.Abstractions.csproj", "{807BCEA5-2EBC-4075-8EDC-E4281A751FFC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream.TestBinder", "Stream\test\TestBinder\Steeltoe.Stream.TestBinder.csproj", "{32F03DCB-AE2C-4F91-BB34-CD623C7F202B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream.MockBinder", "Stream\test\MockBinder\Steeltoe.Stream.MockBinder.csproj", "{F8E267B3-4219-415A-98BE-3F0F6650EA79}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream.StubBinder1", "Stream\test\StubBinder1\Steeltoe.Stream.StubBinder1.csproj", "{846CE4E4-C439-4B9A-8EF2-A1C8DEF5AAB3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream.StubBinder2", "Stream\test\StubBinder2\Steeltoe.Stream.StubBinder2.csproj", "{5AE1F2D6-0D9B-4F9B-A47F-6063CD964805}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Configuration.Kubernetes", "Configuration\src\Kubernetes\Steeltoe.Configuration.Kubernetes.csproj", "{75A0CE2D-9343-40DC-8542-3CE07C3913B9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Configuration.Kubernetes.Test", "Configuration\test\Kubernetes.Test\Steeltoe.Configuration.Kubernetes.Test.csproj", "{382B0AC3-5475-485B-81F6-27658570530C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Messaging.RabbitMQ", "Messaging\src\RabbitMQ\Steeltoe.Messaging.RabbitMQ.csproj", "{5D878BBC-85BA-487A-B451-15D5D0DEB52B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Messaging.RabbitMQ.Test", "Messaging\test\RabbitMQ.Test\Steeltoe.Messaging.RabbitMQ.Test.csproj", "{1B49DE9E-9A9F-42C3-BEE9-85B5D8418F94}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.RetryPolly", "Common\src\Common.RetryPolly\Steeltoe.Common.RetryPolly.csproj", "{5E65AD10-2CAD-4F01-936B-2AE05C0116CC}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.Kubernetes", "Common\src\Common.Kubernetes\Steeltoe.Common.Kubernetes.csproj", "{FD5CFD8B-B070-4FD2-B986-84205F33571F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.Kubernetes.Test", "Common\test\Common.Kubernetes.Test\Steeltoe.Common.Kubernetes.Test.csproj", "{B29554A3-724B-4D8D-B7D1-32EB53497FBC}" @@ -203,36 +177,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Management.Kuberne EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Management.Kubernetes.Test", "Management\test\Kubernetes.Test\Steeltoe.Management.Kubernetes.Test.csproj", "{5549BB0D-E426-4F4C-ABE6-611D22EB8A06}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Integration", "Integration\src\Integration\Steeltoe.Integration.csproj", "{2F43CD2B-A9C0-4281-8DA5-D7088AD60AF3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Integration.Test", "Integration\test\Integration.Test\Steeltoe.Integration.Test.csproj", "{558AFDB0-C130-49FF-99D5-F14244E3CC06}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream", "Stream\src\Stream\Steeltoe.Stream.csproj", "{19D3FF2E-0F94-4C17-AEB1-6B623535BE45}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream.Test", "Stream\test\Stream.Test\Steeltoe.Stream.Test.csproj", "{53BE0E37-067C-46C8-84FF-357B5B19197E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Messaging", "Messaging\src\Messaging\Steeltoe.Messaging.csproj", "{4F3EA150-6099-4DCF-8C42-CC86EA101A67}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Messaging.Test", "Messaging\test\Messaging.Test\Steeltoe.Messaging.Test.csproj", "{96AF536F-BCEC-46D2-BCD0-C1737418ED44}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Discovery.Client", "Discovery\src\Client\Steeltoe.Discovery.Client.csproj", "{E2BE4A3F-1C35-41ED-99D1-7630B5369942}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Discovery.Client.Test", "Discovery\test\Client.Test\Steeltoe.Discovery.Client.Test.csproj", "{C5DDEE8C-E3EF-49BC-AEEE-B35271FC512A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream.Binder.RabbitMQ", "Stream\src\BinderRabbitMQ\Steeltoe.Stream.Binder.RabbitMQ.csproj", "{D00E5CE7-3EF2-4E56-9A86-078022644262}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.Expression", "Common\src\Common.Expression\Steeltoe.Common.Expression.csproj", "{F2824B88-98BE-4061-B3BA-107CC553906D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.Expression.Test", "Common\test\Common.Expression.Test\Steeltoe.Common.Expression.Test.csproj", "{B00091A9-7BDC-4E75-9118-A5920ACF1394}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Integration.RabbitMQ", "Integration\src\RabbitMQ\Steeltoe.Integration.RabbitMQ.csproj", "{CCCA50F0-B383-453C-8ACA-08F2B8FC32E1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Integration.RabbitMQ.Test", "Integration\test\RabbitMQ.Test\Steeltoe.Integration.RabbitMQ.Test.csproj", "{09468CF2-61C1-4B83-8E1A-BD7C1321BA0A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream.Binder.RabbitMQ.Test", "Stream\test\BinderRabbitMQ.Test\Steeltoe.Stream.Binder.RabbitMQ.Test.csproj", "{92671FCC-F03E-40BE-B645-5B59F8F7F4C4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Stream.Binder.Test", "Stream\test\Binder.Test\Steeltoe.Stream.Binder.Test.csproj", "{FAFA69DA-494E-4FC8-BDD8-78AF2B834592}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Configuration.SpringBoot", "Configuration\src\SpringBoot\Steeltoe.Configuration.SpringBoot.csproj", "{D94706F7-7622-4D4C-87BD-84FB40D4E6BD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Configuration.SpringBoot.Test", "Configuration\test\SpringBoot.Test\Steeltoe.Configuration.SpringBoot.Test.csproj", "{BA38127A-DA5A-437F-9C84-5997FBE0B6A0}" @@ -243,10 +191,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Bootstrap.AutoConf EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Bootstrap.AutoConfiguration.Test", "Bootstrap\test\AutoConfiguration.Test\Steeltoe.Bootstrap.AutoConfiguration.Test.csproj", "{6499285A-88CF-426A-909D-307382B91AA3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmark", "Integration\test\Benchmark\Benchmark.csproj", "{267B24D5-1460-42D4-AEE7-438094164345}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChannelBenchmark", "Messaging\test\Benchmarks\Channel\ChannelBenchmark.csproj", "{15BDC5D8-7594-4506-B81A-A60D5DF1947C}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_build", "_build", "{656A7881-FE4F-4EE0-81C8-DA1DE6CDF29E}" ProjectSection(SolutionItems) = preProject ..\azure-pipelines.yml = ..\azure-pipelines.yml @@ -291,6 +235,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Configuration.Clou EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Bootstrap.EmptyAutoConfiguration.Test", "Bootstrap\test\EmptyAutoConfiguration.Test\Steeltoe.Bootstrap.EmptyAutoConfiguration.Test.csproj", "{C821DCC5-880F-4F85-97B1-6D9A56DAD6F0}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Management.Wavefront.Test", "Management\test\Wavefront.Test\Steeltoe.Management.Wavefront.Test.csproj", "{6D176D53-9D27-41A3-B466-D1F0E5B29A22}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Management.Prometheus.Test", "Management\test\Prometheus.Test\Steeltoe.Management.Prometheus.Test.csproj", "{7866263B-936E-4604-A821-73C46BAB1657}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -493,34 +441,6 @@ Global {0134F5C0-7595-497E-91BE-CCCD2290CF8E}.Debug|Any CPU.Build.0 = Debug|Any CPU {0134F5C0-7595-497E-91BE-CCCD2290CF8E}.Release|Any CPU.ActiveCfg = Release|Any CPU {0134F5C0-7595-497E-91BE-CCCD2290CF8E}.Release|Any CPU.Build.0 = Release|Any CPU - {082FF7F6-EC5B-4AC1-9B66-C4A6ED955371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {082FF7F6-EC5B-4AC1-9B66-C4A6ED955371}.Debug|Any CPU.Build.0 = Debug|Any CPU - {082FF7F6-EC5B-4AC1-9B66-C4A6ED955371}.Release|Any CPU.ActiveCfg = Release|Any CPU - {082FF7F6-EC5B-4AC1-9B66-C4A6ED955371}.Release|Any CPU.Build.0 = Release|Any CPU - {AAA88EFE-706E-4DF6-830B-F7AAFB59FC26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AAA88EFE-706E-4DF6-830B-F7AAFB59FC26}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AAA88EFE-706E-4DF6-830B-F7AAFB59FC26}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AAA88EFE-706E-4DF6-830B-F7AAFB59FC26}.Release|Any CPU.Build.0 = Release|Any CPU - {807BCEA5-2EBC-4075-8EDC-E4281A751FFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {807BCEA5-2EBC-4075-8EDC-E4281A751FFC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {807BCEA5-2EBC-4075-8EDC-E4281A751FFC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {807BCEA5-2EBC-4075-8EDC-E4281A751FFC}.Release|Any CPU.Build.0 = Release|Any CPU - {32F03DCB-AE2C-4F91-BB34-CD623C7F202B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32F03DCB-AE2C-4F91-BB34-CD623C7F202B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32F03DCB-AE2C-4F91-BB34-CD623C7F202B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32F03DCB-AE2C-4F91-BB34-CD623C7F202B}.Release|Any CPU.Build.0 = Release|Any CPU - {F8E267B3-4219-415A-98BE-3F0F6650EA79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F8E267B3-4219-415A-98BE-3F0F6650EA79}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F8E267B3-4219-415A-98BE-3F0F6650EA79}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F8E267B3-4219-415A-98BE-3F0F6650EA79}.Release|Any CPU.Build.0 = Release|Any CPU - {846CE4E4-C439-4B9A-8EF2-A1C8DEF5AAB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {846CE4E4-C439-4B9A-8EF2-A1C8DEF5AAB3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {846CE4E4-C439-4B9A-8EF2-A1C8DEF5AAB3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {846CE4E4-C439-4B9A-8EF2-A1C8DEF5AAB3}.Release|Any CPU.Build.0 = Release|Any CPU - {5AE1F2D6-0D9B-4F9B-A47F-6063CD964805}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5AE1F2D6-0D9B-4F9B-A47F-6063CD964805}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5AE1F2D6-0D9B-4F9B-A47F-6063CD964805}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5AE1F2D6-0D9B-4F9B-A47F-6063CD964805}.Release|Any CPU.Build.0 = Release|Any CPU {75A0CE2D-9343-40DC-8542-3CE07C3913B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75A0CE2D-9343-40DC-8542-3CE07C3913B9}.Debug|Any CPU.Build.0 = Debug|Any CPU {75A0CE2D-9343-40DC-8542-3CE07C3913B9}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -529,18 +449,6 @@ Global {382B0AC3-5475-485B-81F6-27658570530C}.Debug|Any CPU.Build.0 = Debug|Any CPU {382B0AC3-5475-485B-81F6-27658570530C}.Release|Any CPU.ActiveCfg = Release|Any CPU {382B0AC3-5475-485B-81F6-27658570530C}.Release|Any CPU.Build.0 = Release|Any CPU - {5D878BBC-85BA-487A-B451-15D5D0DEB52B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5D878BBC-85BA-487A-B451-15D5D0DEB52B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5D878BBC-85BA-487A-B451-15D5D0DEB52B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5D878BBC-85BA-487A-B451-15D5D0DEB52B}.Release|Any CPU.Build.0 = Release|Any CPU - {1B49DE9E-9A9F-42C3-BEE9-85B5D8418F94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1B49DE9E-9A9F-42C3-BEE9-85B5D8418F94}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1B49DE9E-9A9F-42C3-BEE9-85B5D8418F94}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1B49DE9E-9A9F-42C3-BEE9-85B5D8418F94}.Release|Any CPU.Build.0 = Release|Any CPU - {5E65AD10-2CAD-4F01-936B-2AE05C0116CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5E65AD10-2CAD-4F01-936B-2AE05C0116CC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5E65AD10-2CAD-4F01-936B-2AE05C0116CC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5E65AD10-2CAD-4F01-936B-2AE05C0116CC}.Release|Any CPU.Build.0 = Release|Any CPU {FD5CFD8B-B070-4FD2-B986-84205F33571F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD5CFD8B-B070-4FD2-B986-84205F33571F}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD5CFD8B-B070-4FD2-B986-84205F33571F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -585,30 +493,6 @@ Global {5549BB0D-E426-4F4C-ABE6-611D22EB8A06}.Debug|Any CPU.Build.0 = Debug|Any CPU {5549BB0D-E426-4F4C-ABE6-611D22EB8A06}.Release|Any CPU.ActiveCfg = Release|Any CPU {5549BB0D-E426-4F4C-ABE6-611D22EB8A06}.Release|Any CPU.Build.0 = Release|Any CPU - {2F43CD2B-A9C0-4281-8DA5-D7088AD60AF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F43CD2B-A9C0-4281-8DA5-D7088AD60AF3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F43CD2B-A9C0-4281-8DA5-D7088AD60AF3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F43CD2B-A9C0-4281-8DA5-D7088AD60AF3}.Release|Any CPU.Build.0 = Release|Any CPU - {558AFDB0-C130-49FF-99D5-F14244E3CC06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {558AFDB0-C130-49FF-99D5-F14244E3CC06}.Debug|Any CPU.Build.0 = Debug|Any CPU - {558AFDB0-C130-49FF-99D5-F14244E3CC06}.Release|Any CPU.ActiveCfg = Release|Any CPU - {558AFDB0-C130-49FF-99D5-F14244E3CC06}.Release|Any CPU.Build.0 = Release|Any CPU - {19D3FF2E-0F94-4C17-AEB1-6B623535BE45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {19D3FF2E-0F94-4C17-AEB1-6B623535BE45}.Debug|Any CPU.Build.0 = Debug|Any CPU - {19D3FF2E-0F94-4C17-AEB1-6B623535BE45}.Release|Any CPU.ActiveCfg = Release|Any CPU - {19D3FF2E-0F94-4C17-AEB1-6B623535BE45}.Release|Any CPU.Build.0 = Release|Any CPU - {53BE0E37-067C-46C8-84FF-357B5B19197E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {53BE0E37-067C-46C8-84FF-357B5B19197E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {53BE0E37-067C-46C8-84FF-357B5B19197E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {53BE0E37-067C-46C8-84FF-357B5B19197E}.Release|Any CPU.Build.0 = Release|Any CPU - {4F3EA150-6099-4DCF-8C42-CC86EA101A67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4F3EA150-6099-4DCF-8C42-CC86EA101A67}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4F3EA150-6099-4DCF-8C42-CC86EA101A67}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4F3EA150-6099-4DCF-8C42-CC86EA101A67}.Release|Any CPU.Build.0 = Release|Any CPU - {96AF536F-BCEC-46D2-BCD0-C1737418ED44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {96AF536F-BCEC-46D2-BCD0-C1737418ED44}.Debug|Any CPU.Build.0 = Debug|Any CPU - {96AF536F-BCEC-46D2-BCD0-C1737418ED44}.Release|Any CPU.ActiveCfg = Release|Any CPU - {96AF536F-BCEC-46D2-BCD0-C1737418ED44}.Release|Any CPU.Build.0 = Release|Any CPU {E2BE4A3F-1C35-41ED-99D1-7630B5369942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2BE4A3F-1C35-41ED-99D1-7630B5369942}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2BE4A3F-1C35-41ED-99D1-7630B5369942}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -617,34 +501,6 @@ Global {C5DDEE8C-E3EF-49BC-AEEE-B35271FC512A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C5DDEE8C-E3EF-49BC-AEEE-B35271FC512A}.Release|Any CPU.ActiveCfg = Release|Any CPU {C5DDEE8C-E3EF-49BC-AEEE-B35271FC512A}.Release|Any CPU.Build.0 = Release|Any CPU - {D00E5CE7-3EF2-4E56-9A86-078022644262}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D00E5CE7-3EF2-4E56-9A86-078022644262}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D00E5CE7-3EF2-4E56-9A86-078022644262}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D00E5CE7-3EF2-4E56-9A86-078022644262}.Release|Any CPU.Build.0 = Release|Any CPU - {F2824B88-98BE-4061-B3BA-107CC553906D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F2824B88-98BE-4061-B3BA-107CC553906D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F2824B88-98BE-4061-B3BA-107CC553906D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F2824B88-98BE-4061-B3BA-107CC553906D}.Release|Any CPU.Build.0 = Release|Any CPU - {B00091A9-7BDC-4E75-9118-A5920ACF1394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B00091A9-7BDC-4E75-9118-A5920ACF1394}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B00091A9-7BDC-4E75-9118-A5920ACF1394}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B00091A9-7BDC-4E75-9118-A5920ACF1394}.Release|Any CPU.Build.0 = Release|Any CPU - {CCCA50F0-B383-453C-8ACA-08F2B8FC32E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CCCA50F0-B383-453C-8ACA-08F2B8FC32E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CCCA50F0-B383-453C-8ACA-08F2B8FC32E1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CCCA50F0-B383-453C-8ACA-08F2B8FC32E1}.Release|Any CPU.Build.0 = Release|Any CPU - {09468CF2-61C1-4B83-8E1A-BD7C1321BA0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {09468CF2-61C1-4B83-8E1A-BD7C1321BA0A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {09468CF2-61C1-4B83-8E1A-BD7C1321BA0A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {09468CF2-61C1-4B83-8E1A-BD7C1321BA0A}.Release|Any CPU.Build.0 = Release|Any CPU - {92671FCC-F03E-40BE-B645-5B59F8F7F4C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92671FCC-F03E-40BE-B645-5B59F8F7F4C4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92671FCC-F03E-40BE-B645-5B59F8F7F4C4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92671FCC-F03E-40BE-B645-5B59F8F7F4C4}.Release|Any CPU.Build.0 = Release|Any CPU - {FAFA69DA-494E-4FC8-BDD8-78AF2B834592}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FAFA69DA-494E-4FC8-BDD8-78AF2B834592}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FAFA69DA-494E-4FC8-BDD8-78AF2B834592}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FAFA69DA-494E-4FC8-BDD8-78AF2B834592}.Release|Any CPU.Build.0 = Release|Any CPU {D94706F7-7622-4D4C-87BD-84FB40D4E6BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D94706F7-7622-4D4C-87BD-84FB40D4E6BD}.Debug|Any CPU.Build.0 = Debug|Any CPU {D94706F7-7622-4D4C-87BD-84FB40D4E6BD}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -661,14 +517,6 @@ Global {6499285A-88CF-426A-909D-307382B91AA3}.Debug|Any CPU.Build.0 = Debug|Any CPU {6499285A-88CF-426A-909D-307382B91AA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {6499285A-88CF-426A-909D-307382B91AA3}.Release|Any CPU.Build.0 = Release|Any CPU - {267B24D5-1460-42D4-AEE7-438094164345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {267B24D5-1460-42D4-AEE7-438094164345}.Debug|Any CPU.Build.0 = Debug|Any CPU - {267B24D5-1460-42D4-AEE7-438094164345}.Release|Any CPU.ActiveCfg = Release|Any CPU - {267B24D5-1460-42D4-AEE7-438094164345}.Release|Any CPU.Build.0 = Release|Any CPU - {15BDC5D8-7594-4506-B81A-A60D5DF1947C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {15BDC5D8-7594-4506-B81A-A60D5DF1947C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {15BDC5D8-7594-4506-B81A-A60D5DF1947C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {15BDC5D8-7594-4506-B81A-A60D5DF1947C}.Release|Any CPU.Build.0 = Release|Any CPU {6A064B5A-21F7-452A-AAFF-3C0A68286FDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6A064B5A-21F7-452A-AAFF-3C0A68286FDF}.Debug|Any CPU.Build.0 = Debug|Any CPU {6A064B5A-21F7-452A-AAFF-3C0A68286FDF}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -705,6 +553,14 @@ Global {C821DCC5-880F-4F85-97B1-6D9A56DAD6F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {C821DCC5-880F-4F85-97B1-6D9A56DAD6F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {C821DCC5-880F-4F85-97B1-6D9A56DAD6F0}.Release|Any CPU.Build.0 = Release|Any CPU + {6D176D53-9D27-41A3-B466-D1F0E5B29A22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D176D53-9D27-41A3-B466-D1F0E5B29A22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D176D53-9D27-41A3-B466-D1F0E5B29A22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D176D53-9D27-41A3-B466-D1F0E5B29A22}.Release|Any CPU.Build.0 = Release|Any CPU + {7866263B-936E-4604-A821-73C46BAB1657}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7866263B-936E-4604-A821-73C46BAB1657}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7866263B-936E-4604-A821-73C46BAB1657}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7866263B-936E-4604-A821-73C46BAB1657}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -759,18 +615,8 @@ Global {0FEEB397-0A2B-4E24-A5AE-B91A56303C98} = {3CBE0336-C3C8-4DC2-AE45-86FC1D1B3C25} {BC84AEA9-31BB-4D19-BC55-1B6EF6AD53FC} = {5128206A-242E-4069-AD30-910EDC40B165} {0134F5C0-7595-497E-91BE-CCCD2290CF8E} = {5128206A-242E-4069-AD30-910EDC40B165} - {082FF7F6-EC5B-4AC1-9B66-C4A6ED955371} = {283EAD31-6CC1-4306-A0E8-AB60F6CB3FD8} - {AAA88EFE-706E-4DF6-830B-F7AAFB59FC26} = {EA583B9C-D8C6-422A-9FF8-157CF6A4A61E} - {807BCEA5-2EBC-4075-8EDC-E4281A751FFC} = {4F1EA720-AA18-4955-A854-016B26F6CED1} - {32F03DCB-AE2C-4F91-BB34-CD623C7F202B} = {4F1EA720-AA18-4955-A854-016B26F6CED1} - {F8E267B3-4219-415A-98BE-3F0F6650EA79} = {4F1EA720-AA18-4955-A854-016B26F6CED1} - {846CE4E4-C439-4B9A-8EF2-A1C8DEF5AAB3} = {4F1EA720-AA18-4955-A854-016B26F6CED1} - {5AE1F2D6-0D9B-4F9B-A47F-6063CD964805} = {4F1EA720-AA18-4955-A854-016B26F6CED1} {75A0CE2D-9343-40DC-8542-3CE07C3913B9} = {4AB95F47-0C93-4C88-B87F-231262CD0E89} {382B0AC3-5475-485B-81F6-27658570530C} = {4AB95F47-0C93-4C88-B87F-231262CD0E89} - {5D878BBC-85BA-487A-B451-15D5D0DEB52B} = {283EAD31-6CC1-4306-A0E8-AB60F6CB3FD8} - {1B49DE9E-9A9F-42C3-BEE9-85B5D8418F94} = {283EAD31-6CC1-4306-A0E8-AB60F6CB3FD8} - {5E65AD10-2CAD-4F01-936B-2AE05C0116CC} = {59874241-E276-4035-B31D-14924889A1C9} {FD5CFD8B-B070-4FD2-B986-84205F33571F} = {59874241-E276-4035-B31D-14924889A1C9} {B29554A3-724B-4D8D-B7D1-32EB53497FBC} = {59874241-E276-4035-B31D-14924889A1C9} {AFD6152E-A432-4CD4-AB73-A5DC2411EAB1} = {3CBE0336-C3C8-4DC2-AE45-86FC1D1B3C25} @@ -782,27 +628,12 @@ Global {1C7DD135-D695-41E9-84C5-CEFBF595FD1D} = {31BAEBB1-696E-44A1-B1EF-0D150E6D2559} {5D8A913E-232E-43EB-B6ED-9397BCB89C6A} = {8BD7D5E5-D887-4BD7-9F42-725A8714F7BC} {5549BB0D-E426-4F4C-ABE6-611D22EB8A06} = {8BD7D5E5-D887-4BD7-9F42-725A8714F7BC} - {2F43CD2B-A9C0-4281-8DA5-D7088AD60AF3} = {EA583B9C-D8C6-422A-9FF8-157CF6A4A61E} - {558AFDB0-C130-49FF-99D5-F14244E3CC06} = {EA583B9C-D8C6-422A-9FF8-157CF6A4A61E} - {19D3FF2E-0F94-4C17-AEB1-6B623535BE45} = {4F1EA720-AA18-4955-A854-016B26F6CED1} - {53BE0E37-067C-46C8-84FF-357B5B19197E} = {4F1EA720-AA18-4955-A854-016B26F6CED1} - {4F3EA150-6099-4DCF-8C42-CC86EA101A67} = {283EAD31-6CC1-4306-A0E8-AB60F6CB3FD8} - {96AF536F-BCEC-46D2-BCD0-C1737418ED44} = {283EAD31-6CC1-4306-A0E8-AB60F6CB3FD8} {E2BE4A3F-1C35-41ED-99D1-7630B5369942} = {31BAEBB1-696E-44A1-B1EF-0D150E6D2559} {C5DDEE8C-E3EF-49BC-AEEE-B35271FC512A} = {31BAEBB1-696E-44A1-B1EF-0D150E6D2559} - {D00E5CE7-3EF2-4E56-9A86-078022644262} = {4F1EA720-AA18-4955-A854-016B26F6CED1} - {F2824B88-98BE-4061-B3BA-107CC553906D} = {59874241-E276-4035-B31D-14924889A1C9} - {B00091A9-7BDC-4E75-9118-A5920ACF1394} = {59874241-E276-4035-B31D-14924889A1C9} - {CCCA50F0-B383-453C-8ACA-08F2B8FC32E1} = {EA583B9C-D8C6-422A-9FF8-157CF6A4A61E} - {09468CF2-61C1-4B83-8E1A-BD7C1321BA0A} = {EA583B9C-D8C6-422A-9FF8-157CF6A4A61E} - {92671FCC-F03E-40BE-B645-5B59F8F7F4C4} = {4F1EA720-AA18-4955-A854-016B26F6CED1} - {FAFA69DA-494E-4FC8-BDD8-78AF2B834592} = {4F1EA720-AA18-4955-A854-016B26F6CED1} {D94706F7-7622-4D4C-87BD-84FB40D4E6BD} = {4AB95F47-0C93-4C88-B87F-231262CD0E89} {BA38127A-DA5A-437F-9C84-5997FBE0B6A0} = {4AB95F47-0C93-4C88-B87F-231262CD0E89} {38EFC635-0C56-442D-8CA3-20AEC1930D7B} = {EA9C3A73-3F31-4DC9-982C-963CE613E119} {6499285A-88CF-426A-909D-307382B91AA3} = {EA9C3A73-3F31-4DC9-982C-963CE613E119} - {267B24D5-1460-42D4-AEE7-438094164345} = {EA583B9C-D8C6-422A-9FF8-157CF6A4A61E} - {15BDC5D8-7594-4506-B81A-A60D5DF1947C} = {283EAD31-6CC1-4306-A0E8-AB60F6CB3FD8} {FC49CC5C-EFB6-4B08-AE63-05364EB27021} = {656A7881-FE4F-4EE0-81C8-DA1DE6CDF29E} {6A064B5A-21F7-452A-AAFF-3C0A68286FDF} = {4AB95F47-0C93-4C88-B87F-231262CD0E89} {0D68FC03-7FD4-4280-852B-0E620872CE22} = {4AB95F47-0C93-4C88-B87F-231262CD0E89} @@ -813,6 +644,8 @@ Global {5625E2FC-9DAD-4ADC-B9A6-6ED192E9A4F4} = {4AB95F47-0C93-4C88-B87F-231262CD0E89} {BF725F33-3361-4D9C-A1FA-27EEA79A09DA} = {4AB95F47-0C93-4C88-B87F-231262CD0E89} {C821DCC5-880F-4F85-97B1-6D9A56DAD6F0} = {EA9C3A73-3F31-4DC9-982C-963CE613E119} + {6D176D53-9D27-41A3-B466-D1F0E5B29A22} = {8BD7D5E5-D887-4BD7-9F42-725A8714F7BC} + {7866263B-936E-4604-A821-73C46BAB1657} = {8BD7D5E5-D887-4BD7-9F42-725A8714F7BC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AB0245B9-2464-47F8-BE15-D80A7A2FA965} diff --git a/src/Steeltoe.Common.slnf b/src/Steeltoe.Common.slnf index e4dd693f66..b53f744d47 100644 --- a/src/Steeltoe.Common.slnf +++ b/src/Steeltoe.Common.slnf @@ -3,16 +3,13 @@ "path": "Steeltoe.All.sln", "projects": [ "Common\\src\\Abstractions\\Steeltoe.Common.Abstractions.csproj", - "Common\\src\\Common.Expression\\Steeltoe.Common.Expression.csproj", "Common\\src\\Common.Hosting\\Steeltoe.Common.Hosting.csproj", "Common\\src\\Common.Http\\Steeltoe.Common.Http.csproj", "Common\\src\\Common.Kubernetes\\Steeltoe.Common.Kubernetes.csproj", "Common\\src\\Common.Net\\Steeltoe.Common.Net.csproj", - "Common\\src\\Common.RetryPolly\\Steeltoe.Common.RetryPolly.csproj", "Common\\src\\Common.Security\\Steeltoe.Common.Security.csproj", "Common\\src\\Common.Utils\\Steeltoe.Common.Utils.csproj", "Common\\src\\Common\\Steeltoe.Common.csproj", - "Common\\test\\Common.Expression.Test\\Steeltoe.Common.Expression.Test.csproj", "Common\\test\\Common.Hosting.Test\\Steeltoe.Common.Hosting.Test.csproj", "Common\\test\\Common.Http.Test\\Steeltoe.Common.Http.Test.csproj", "Common\\test\\Common.Kubernetes.Test\\Steeltoe.Common.Kubernetes.Test.csproj", diff --git a/src/Steeltoe.Integration.slnf b/src/Steeltoe.Integration.slnf deleted file mode 100644 index 1bc2588da2..0000000000 --- a/src/Steeltoe.Integration.slnf +++ /dev/null @@ -1,25 +0,0 @@ -{ - "solution": { - "path": "Steeltoe.All.sln", - "projects": [ - "Common\\src\\Abstractions\\Steeltoe.Common.Abstractions.csproj", - "Common\\src\\Common.Expression\\Steeltoe.Common.Expression.csproj", - "Common\\src\\Common.RetryPolly\\Steeltoe.Common.RetryPolly.csproj", - "Common\\src\\Common\\Steeltoe.Common.csproj", - "Common\\test\\Common.TestResources\\Steeltoe.Common.TestResources.csproj", - "Configuration\\src\\Abstractions\\Steeltoe.Configuration.Abstractions.csproj", - "Configuration\\src\\SpringBoot\\Steeltoe.Configuration.SpringBoot.csproj", - "Connectors\\src\\Abstractions\\Steeltoe.Connectors.Abstractions.csproj", - "Connectors\\src\\Connectors\\Steeltoe.Connectors.csproj", - "Integration\\src\\Abstractions\\Steeltoe.Integration.Abstractions.csproj", - "Integration\\src\\Integration\\Steeltoe.Integration.csproj", - "Integration\\src\\RabbitMQ\\Steeltoe.Integration.RabbitMQ.csproj", - "Integration\\test\\Benchmark\\Benchmark.csproj", - "Integration\\test\\Integration.Test\\Steeltoe.Integration.Test.csproj", - "Integration\\test\\RabbitMQ.Test\\Steeltoe.Integration.RabbitMQ.Test.csproj", - "Messaging\\src\\Abstractions\\Steeltoe.Messaging.Abstractions.csproj", - "Messaging\\src\\Messaging\\Steeltoe.Messaging.csproj", - "Messaging\\src\\RabbitMQ\\Steeltoe.Messaging.RabbitMQ.csproj" - ] - } -} \ No newline at end of file diff --git a/src/Steeltoe.Management.slnf b/src/Steeltoe.Management.slnf index 44cf07e241..d45f702048 100644 --- a/src/Steeltoe.Management.slnf +++ b/src/Steeltoe.Management.slnf @@ -25,8 +25,10 @@ "Management\\src\\Wavefront\\Steeltoe.Management.Wavefront.csproj", "Management\\test\\Endpoint.Test\\Steeltoe.Management.Endpoint.Test.csproj", "Management\\test\\Kubernetes.Test\\Steeltoe.Management.Kubernetes.Test.csproj", + "Management\\test\\Prometheus.Test\\Steeltoe.Management.Prometheus.Test.csproj", "Management\\test\\Task.Test\\Steeltoe.Management.Task.Test.csproj", - "Management\\test\\Tracing.Test\\Steeltoe.Management.Tracing.Test.csproj" + "Management\\test\\Tracing.Test\\Steeltoe.Management.Tracing.Test.csproj", + "Management\\test\\Wavefront.Test\\Steeltoe.Management.Wavefront.Test.csproj" ] } } \ No newline at end of file diff --git a/src/Steeltoe.Messaging.slnf b/src/Steeltoe.Messaging.slnf deleted file mode 100644 index ec1eaf3c84..0000000000 --- a/src/Steeltoe.Messaging.slnf +++ /dev/null @@ -1,24 +0,0 @@ -{ - "solution": { - "path": "Steeltoe.All.sln", - "projects": [ - "Common\\src\\Abstractions\\Steeltoe.Common.Abstractions.csproj", - "Common\\src\\Common.Expression\\Steeltoe.Common.Expression.csproj", - "Common\\src\\Common.RetryPolly\\Steeltoe.Common.RetryPolly.csproj", - "Common\\src\\Common\\Steeltoe.Common.csproj", - "Common\\test\\Common.TestResources\\Steeltoe.Common.TestResources.csproj", - "Configuration\\src\\Abstractions\\Steeltoe.Configuration.Abstractions.csproj", - "Configuration\\src\\CloudFoundry\\Steeltoe.Configuration.CloudFoundry.csproj", - "Configuration\\src\\SpringBoot\\Steeltoe.Configuration.SpringBoot.csproj", - "Connectors\\src\\Abstractions\\Steeltoe.Connectors.Abstractions.csproj", - "Connectors\\src\\CloudFoundry\\Steeltoe.Connectors.CloudFoundry.csproj", - "Connectors\\src\\Connectors\\Steeltoe.Connectors.csproj", - "Messaging\\src\\Abstractions\\Steeltoe.Messaging.Abstractions.csproj", - "Messaging\\src\\Messaging\\Steeltoe.Messaging.csproj", - "Messaging\\src\\RabbitMQ\\Steeltoe.Messaging.RabbitMQ.csproj", - "Messaging\\test\\Benchmarks\\Channel\\ChannelBenchmark.csproj", - "Messaging\\test\\Messaging.Test\\Steeltoe.Messaging.Test.csproj", - "Messaging\\test\\RabbitMQ.Test\\Steeltoe.Messaging.RabbitMQ.Test.csproj" - ] - } -} \ No newline at end of file diff --git a/src/Steeltoe.Stream.slnf b/src/Steeltoe.Stream.slnf deleted file mode 100644 index 63cb3cbe8b..0000000000 --- a/src/Steeltoe.Stream.slnf +++ /dev/null @@ -1,34 +0,0 @@ -{ - "solution": { - "path": "Steeltoe.All.sln", - "projects": [ - "Common\\src\\Abstractions\\Steeltoe.Common.Abstractions.csproj", - "Common\\src\\Common.Expression\\Steeltoe.Common.Expression.csproj", - "Common\\src\\Common.RetryPolly\\Steeltoe.Common.RetryPolly.csproj", - "Common\\src\\Common\\Steeltoe.Common.csproj", - "Common\\test\\Common.TestResources\\Steeltoe.Common.TestResources.csproj", - "Configuration\\src\\Abstractions\\Steeltoe.Configuration.Abstractions.csproj", - "Configuration\\src\\CloudFoundry\\Steeltoe.Configuration.CloudFoundry.csproj", - "Configuration\\src\\SpringBoot\\Steeltoe.Configuration.SpringBoot.csproj", - "Connectors\\src\\Abstractions\\Steeltoe.Connectors.Abstractions.csproj", - "Connectors\\src\\CloudFoundry\\Steeltoe.Connectors.CloudFoundry.csproj", - "Connectors\\src\\Connectors\\Steeltoe.Connectors.csproj", - "Integration\\src\\Abstractions\\Steeltoe.Integration.Abstractions.csproj", - "Integration\\src\\Integration\\Steeltoe.Integration.csproj", - "Integration\\src\\RabbitMQ\\Steeltoe.Integration.RabbitMQ.csproj", - "Messaging\\src\\Abstractions\\Steeltoe.Messaging.Abstractions.csproj", - "Messaging\\src\\Messaging\\Steeltoe.Messaging.csproj", - "Messaging\\src\\RabbitMQ\\Steeltoe.Messaging.RabbitMQ.csproj", - "Stream\\src\\Abstractions\\Steeltoe.Stream.Abstractions.csproj", - "Stream\\src\\BinderRabbitMQ\\Steeltoe.Stream.Binder.RabbitMQ.csproj", - "Stream\\src\\Stream\\Steeltoe.Stream.csproj", - "Stream\\test\\Binder.Test\\Steeltoe.Stream.Binder.Test.csproj", - "Stream\\test\\BinderRabbitMQ.Test\\Steeltoe.Stream.Binder.RabbitMQ.Test.csproj", - "Stream\\test\\MockBinder\\Steeltoe.Stream.MockBinder.csproj", - "Stream\\test\\Stream.Test\\Steeltoe.Stream.Test.csproj", - "Stream\\test\\StubBinder1\\Steeltoe.Stream.StubBinder1.csproj", - "Stream\\test\\StubBinder2\\Steeltoe.Stream.StubBinder2.csproj", - "Stream\\test\\TestBinder\\Steeltoe.Stream.TestBinder.csproj" - ] - } -} \ No newline at end of file diff --git a/src/Stream/src/Abstractions/Attributes/EnableBindingAttribute.cs b/src/Stream/src/Abstractions/Attributes/EnableBindingAttribute.cs deleted file mode 100644 index 844c89d12c..0000000000 --- a/src/Stream/src/Abstractions/Attributes/EnableBindingAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Attributes; - -[AttributeUsage(AttributeTargets.Class)] -public sealed class EnableBindingAttribute : Attribute -{ - public Type[] BindingTypes { get; set; } - - public EnableBindingAttribute(params Type[] bindingTypes) - { - BindingTypes = bindingTypes; - } -} diff --git a/src/Stream/src/Abstractions/Attributes/InputAttribute.cs b/src/Stream/src/Abstractions/Attributes/InputAttribute.cs deleted file mode 100644 index 0dc4e8ae0f..0000000000 --- a/src/Stream/src/Abstractions/Attributes/InputAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Attributes; - -/// -/// Indicates that an input binding target will be created by the framework. -/// -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] -public sealed class InputAttribute : Attribute -{ - /// - /// Gets or sets the binding target name; used as a name for binding target and as a destination name by default. - /// - public string Name { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// the binding target name. - /// - public InputAttribute(string name = null) - { - Name = name; - } -} diff --git a/src/Stream/src/Abstractions/Attributes/OutputAttribute.cs b/src/Stream/src/Abstractions/Attributes/OutputAttribute.cs deleted file mode 100644 index 862966391f..0000000000 --- a/src/Stream/src/Abstractions/Attributes/OutputAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Attributes; - -/// -/// Indicates that an output binding target will be created by the framework. -/// -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] -public sealed class OutputAttribute : Attribute -{ - /// - /// Gets or sets the binding target name; used as a name for binding target and as a destination name by default. - /// - public string Name { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// the binding target name. - /// - public OutputAttribute(string name = null) - { - Name = name; - } -} diff --git a/src/Stream/src/Abstractions/Attributes/StreamListenerAttribute.cs b/src/Stream/src/Abstractions/Attributes/StreamListenerAttribute.cs deleted file mode 100644 index eda5ff789a..0000000000 --- a/src/Stream/src/Abstractions/Attributes/StreamListenerAttribute.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Attributes; - -/// -/// Annotation that marks a method to be a listener to inputs declared via EnableBinding. -/// -[AttributeUsage(AttributeTargets.Method)] -public sealed class StreamListenerAttribute : Attribute -{ - /// - /// Gets or sets the binding target (e.g. channel). - /// - public string Target { get; set; } - - /// - /// Gets or sets the expression language condition that must be met by all items dispatched to this method. - /// - public string Condition { get; set; } - - /// - /// Gets or sets a value indicating whether to copy incoming headers to outgoing messages. - /// - public bool CopyHeaders { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// the name of the binding target (e.g. channel). - /// - /// - /// when true, copy incoming headers to any outgoing messages. - /// - public StreamListenerAttribute(string target, bool copyHeaders = true) - : this(target, null, copyHeaders) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// the name of the binding target (e.g. channel). - /// - /// - /// expression language condition that must be met by all items dispatched to this method. - /// - /// - /// when true, copy incoming headers to any outgoing messages. - /// - public StreamListenerAttribute(string target, string condition, bool copyHeaders = true) - { - Target = target; - Condition = condition; - CopyHeaders = copyHeaders; - } -} diff --git a/src/Stream/src/Abstractions/Binder/IBinder.cs b/src/Stream/src/Abstractions/Binder/IBinder.cs deleted file mode 100644 index ec28a16200..0000000000 --- a/src/Stream/src/Abstractions/Binder/IBinder.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binder; - -/// -/// A strategy interface used to bind an app interface to a logical name. The name is intended to identify a logical consumer or producer of messages. -/// This may be a queue, a channel adapter, another message channel, etc. -/// -public interface IBinder : IServiceNameAware, IDisposable -{ - /// - /// Gets the target type this binder can bind to. - /// - Type TargetType { get; } - - /// - /// Bind the target component as a message consumer to the logical entity identified by the name. - /// - /// - /// the logical identity of the message source. - /// - /// - /// the consumer group to which this consumer belongs. - /// - /// - /// the application interface to be bound as a consumer. - /// - /// - /// the consumer options. - /// - /// - /// the setup binding. - /// - IBinding BindConsumer(string name, string group, object inboundTarget, IConsumerOptions consumerOptions); - - /// - /// Bind the target component as a message producer to the logical entity identified by the name. - /// - /// - /// the logical identity of the message outbound target. - /// - /// - /// the application interface to be bound as a producer. - /// - /// - /// the producer options. - /// - /// - /// the setup binding. - /// - IBinding BindProducer(string name, object outboundTarget, IProducerOptions producerOptions); -} - -/// -/// A typed version of the strategy interface used to bind an app interface to a logical name. The name is intended to identify a logical consumer or -/// producer of messages. This may be a queue, a channel adapter, another message channel, etc. -/// -/// -/// the target type supported by the binder. -/// -public interface IBinder : IBinder -{ - /// - /// Bind the target component as a message consumer to the logical entity identified by the name. - /// - /// - /// the logical identity of the message source. - /// - /// - /// the consumer group to which this consumer belongs. - /// - /// - /// the application interface to be bound as a consumer. - /// - /// - /// the consumer options. - /// - /// - /// the setup binding. - /// - IBinding BindConsumer(string name, string group, T inboundTarget, IConsumerOptions consumerOptions); - - /// - /// Bind the target component as a message producer to the logical entity identified by the name. - /// - /// - /// the logical identity of the message outbound target. - /// - /// - /// the application interface to be bound as a producer. - /// - /// - /// the producer options. - /// - /// - /// the setup binding. - /// - IBinding BindProducer(string name, T outboundTarget, IProducerOptions producerOptions); -} diff --git a/src/Stream/src/Abstractions/Binder/IBinderFactory.cs b/src/Stream/src/Abstractions/Binder/IBinderFactory.cs deleted file mode 100644 index cb2a011397..0000000000 --- a/src/Stream/src/Abstractions/Binder/IBinderFactory.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -/// -/// A factory for creating or obtaining binders. -/// -public interface IBinderFactory // : IDisposable -{ - /// - /// Returns the binder instance associated with the given configuration name. Instance caching is a requirement, and implementations must return the same - /// instance on subsequent invocations with the same arguments. - /// - /// - /// the name of the binder in configuration. - /// - /// - /// the binder. - /// - IBinder GetBinder(string name); - - /// - /// Returns the binder instance associated with the given configuration name. Instance caching is a requirement, and implementations must return the same - /// instance on subsequent invocations with the same arguments. - /// - /// - /// the name of the binder in configuration. - /// - /// - /// the binding target type. - /// - /// - /// the binder. - /// - IBinder GetBinder(string name, Type bindableType); -} diff --git a/src/Stream/src/Abstractions/Binder/IBinderFactoryListener.cs b/src/Stream/src/Abstractions/Binder/IBinderFactoryListener.cs deleted file mode 100644 index 74876c63a5..0000000000 --- a/src/Stream/src/Abstractions/Binder/IBinderFactoryListener.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Stream.Binder; - -/// -/// A listener that can be registered with the BinderFactory that allows the registration of additional configuration. -/// -public interface IBinderFactoryListener -{ - /// - /// Applying additional capabilities to the binder after the binder context has been initialized. - /// - /// - /// the binders configuration name. - /// - /// - /// the configured service provider. - /// - void AfterBinderInitialized(string configurationName, IApplicationContext binderContext); -} diff --git a/src/Stream/src/Abstractions/Binder/IBinderType.cs b/src/Stream/src/Abstractions/Binder/IBinderType.cs deleted file mode 100644 index 66adc2dd49..0000000000 --- a/src/Stream/src/Abstractions/Binder/IBinderType.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -public interface IBinderType -{ - string Name { get; } - - string ConfigureClass { get; } - - string AssemblyPath { get; } -} diff --git a/src/Stream/src/Abstractions/Binder/IBinderTypeRegistry.cs b/src/Stream/src/Abstractions/Binder/IBinderTypeRegistry.cs deleted file mode 100644 index 6ceb013ddd..0000000000 --- a/src/Stream/src/Abstractions/Binder/IBinderTypeRegistry.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -public interface IBinderTypeRegistry -{ - IBinderType Get(string name); - - IDictionary GetAll(); -} diff --git a/src/Stream/src/Abstractions/Binder/IBinding.cs b/src/Stream/src/Abstractions/Binder/IBinding.cs deleted file mode 100644 index 44c519f0cb..0000000000 --- a/src/Stream/src/Abstractions/Binder/IBinding.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -/// -/// Represents a binding between an input or output and an adapter endpoint that connects via a Binder.The binding could be for a consumer or a producer. -/// A consumer binding represents a connection from an adapter to an input. A producer binding represents a connection from an output to an adapter. -/// -public interface IBinding : IPausable -{ - /// - /// Gets the extended info associated with the binding. - /// - IDictionary ExtendedInfo { get; } - - /// - /// Gets the name of the destination for this binding. - /// - string Name { get; } - - /// - /// Gets the name of the target for this binding (i.e., channel name). - /// - string BindingName { get; } - - /// - /// Gets a value indicating whether this binding is an input binding. - /// - bool IsInput { get; } - - /// - /// Unbinds the target component represented by this instance and stops any active components. - /// - /// - /// task to signal results. - /// - Task UnbindAsync(); -} diff --git a/src/Stream/src/Abstractions/Binder/IPartitionKeyExtractorStrategy.cs b/src/Stream/src/Abstractions/Binder/IPartitionKeyExtractorStrategy.cs deleted file mode 100644 index 1eb9bc321f..0000000000 --- a/src/Stream/src/Abstractions/Binder/IPartitionKeyExtractorStrategy.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -/// -/// Strategy for extracting a partition key from a Message. -/// -public interface IPartitionKeyExtractorStrategy : IServiceNameAware -{ - /// - /// Extract the partition key from the incoming message. - /// - /// - /// the message to process. - /// - /// the key. - object ExtractKey(IMessage message); -} diff --git a/src/Stream/src/Abstractions/Binder/IPartitionSelectorStrategy.cs b/src/Stream/src/Abstractions/Binder/IPartitionSelectorStrategy.cs deleted file mode 100644 index 54cb877578..0000000000 --- a/src/Stream/src/Abstractions/Binder/IPartitionSelectorStrategy.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Services; - -namespace Steeltoe.Stream.Binder; - -/// -/// Strategy for determining the partition to which a message should be sent. -/// -public interface IPartitionSelectorStrategy : IServiceNameAware -{ - /// - /// Determine the partition based on a key. The partitionCount is 1 greater than the maximum value of a valid partition. - /// - /// the key. - /// - /// the number of partitions. - /// - /// - /// the selected partition. - /// - int SelectPartition(object key, int partitionCount); -} diff --git a/src/Stream/src/Abstractions/Binder/IPausable.cs b/src/Stream/src/Abstractions/Binder/IPausable.cs deleted file mode 100644 index f3140fa4da..0000000000 --- a/src/Stream/src/Abstractions/Binder/IPausable.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Lifecycle; - -namespace Steeltoe.Stream.Binder; - -public interface IPausable : ILifecycle -{ - Task PauseAsync(); - - Task ResumeAsync(); -} diff --git a/src/Stream/src/Abstractions/Binder/IPollableConsumerBinder.cs b/src/Stream/src/Abstractions/Binder/IPollableConsumerBinder.cs deleted file mode 100644 index 41744f0ce5..0000000000 --- a/src/Stream/src/Abstractions/Binder/IPollableConsumerBinder.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -/// -/// A binder that supports pollable message sources. -/// -public interface IPollableConsumerBinder -{ -} - -/// -/// A binder that supports pollable message sources. -/// -/// -/// the polled consumer handler type. -/// -public interface IPollableConsumerBinder : IBinder>, IPollableConsumerBinder -{ -} diff --git a/src/Stream/src/Abstractions/Binder/IPollableMessageSource.cs b/src/Stream/src/Abstractions/Binder/IPollableMessageSource.cs deleted file mode 100644 index c87e8644e8..0000000000 --- a/src/Stream/src/Abstractions/Binder/IPollableMessageSource.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -/// -/// A pollable source that calls a message handler with a message. -/// -public interface IPollableMessageSource : IPollableSource -{ -} diff --git a/src/Stream/src/Abstractions/Binder/IPollableSource.cs b/src/Stream/src/Abstractions/Binder/IPollableSource.cs deleted file mode 100644 index a4c7ee1d23..0000000000 --- a/src/Stream/src/Abstractions/Binder/IPollableSource.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -/// -/// An abstraction which defines a mechanism to poll a consumer. -/// -public interface IPollableSource -{ - /// - /// Poll the consumer. - /// - /// - /// the handler to process message. - /// - /// - /// true if a message was handled. - /// - bool Poll(object handler); - - /// - /// Poll the consumer and convert the payload to the specified type. Throw a RequeueCurrentMessageException to force the current message to be requeued - /// in the broker(after retries are exhausted, if configured). - /// - /// - /// the handler. - /// - /// - /// the type of the payload. - /// - /// - /// true if a message was handled. - /// - bool Poll(object handler, Type type); -} - -/// -/// An abstraction which defines a mechanism to poll a consumer. -/// -/// -/// the handler type. -/// -public interface IPollableSource : IPollableSource -{ - /// - /// Poll the consumer. - /// - /// - /// the handler to process message. - /// - /// - /// true if a message was handled. - /// - bool Poll(THandler handler); - - /// - /// Poll the consumer and convert the payload to the specified type. Throw a RequeueCurrentMessageException to force the current message to be requeued - /// in the broker(after retries are exhausted, if configured). - /// - /// - /// the handler. - /// - /// - /// the type of the payload. - /// - /// - /// true if a message was handled. - /// - bool Poll(THandler handler, Type type); -} diff --git a/src/Stream/src/Abstractions/Binding/IBindable.cs b/src/Stream/src/Abstractions/Binding/IBindable.cs deleted file mode 100644 index 420a988425..0000000000 --- a/src/Stream/src/Abstractions/Binding/IBindable.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -/// -/// Marker interface for instances that can bind/unbind groups of inputs and outputs. -/// -public interface IBindable -{ - Type BindingType { get; } - - ICollection Inputs { get; } - - ICollection Outputs { get; } - - ICollection CreateAndBindInputs(IBindingService bindingService); - - ICollection CreateAndBindOutputs(IBindingService bindingService); - - void UnbindInputs(IBindingService bindingService); - - void UnbindOutputs(IBindingService bindingService); - - object GetBoundTarget(string name); - - object GetBoundInputTarget(string name); - - object GetBoundOutputTarget(string name); -} diff --git a/src/Stream/src/Abstractions/Binding/IBindingService.cs b/src/Stream/src/Abstractions/Binding/IBindingService.cs deleted file mode 100644 index d26bcab38f..0000000000 --- a/src/Stream/src/Abstractions/Binding/IBindingService.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -/// -/// Handles binding of input/output targets by delegating to an underlying Binder. -/// -public interface IBindingService -{ - ICollection BindConsumer(T inputChannel, string name); - - IBinding BindProducer(T outputChannel, string name); - - void UnbindProducers(string outputName); - - void UnbindConsumers(string inputName); -} diff --git a/src/Stream/src/Abstractions/Binding/IBindingTargetFactory.cs b/src/Stream/src/Abstractions/Binding/IBindingTargetFactory.cs deleted file mode 100644 index b1c21d0767..0000000000 --- a/src/Stream/src/Abstractions/Binding/IBindingTargetFactory.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binding; - -/// -/// Defines methods to create/configure the binding targets defined by EnableBinding. -/// -public interface IBindingTargetFactory -{ - /// - /// Checks whether a specific binding target type can be created by this factory. - /// - /// - /// the binding target type. - /// - /// - /// true if binding target can be created. - /// - bool CanCreate(Type type); - - /// - /// Create an input binding target that will be bound via a corresponding Binder. - /// - /// - /// the name of the binding target. - /// - /// - /// the binding target. - /// - object CreateInput(string name); - - /// - /// Create an output binding target that will be bound via a corresponding Binder. - /// - /// - /// the name of the binding target. - /// - /// - /// the binding target. - /// - object CreateOutput(string name); -} diff --git a/src/Stream/src/Abstractions/Configuration/HeaderMode.cs b/src/Stream/src/Abstractions/Configuration/HeaderMode.cs deleted file mode 100644 index 2b9d6fcdab..0000000000 --- a/src/Stream/src/Abstractions/Configuration/HeaderMode.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Configuration; - -/// -/// Specifies how headers are handled during message sending. -/// -public enum HeaderMode -{ - /// - /// No headers. - /// - None, - - /// - /// Native headers. - /// - Headers, - - /// - /// Headers embedded in payload e.g. kafka less than 0.11. - /// - EmbeddedHeaders -} diff --git a/src/Stream/src/Abstractions/Configuration/IBinderOptions.cs b/src/Stream/src/Abstractions/Configuration/IBinderOptions.cs deleted file mode 100644 index 16f7b7aef6..0000000000 --- a/src/Stream/src/Abstractions/Configuration/IBinderOptions.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Configuration; - -/// -/// Contains the configuration options for a binder. -/// -public interface IBinderOptions -{ - string ConfigureClass { get; } - - Dictionary Environment { get; } - - bool InheritEnvironment { get; } - - bool DefaultCandidate { get; } -} diff --git a/src/Stream/src/Abstractions/Configuration/IBindingOptions.cs b/src/Stream/src/Abstractions/Configuration/IBindingOptions.cs deleted file mode 100644 index 61f89d9d72..0000000000 --- a/src/Stream/src/Abstractions/Configuration/IBindingOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Configuration; - -/// -/// Contains the configuration options for a binding. -/// -public interface IBindingOptions -{ - string Destination { get; set; } - - string Group { get; set; } - - string ContentType { get; set; } - - string Binder { get; set; } - - IConsumerOptions Consumer { get; } - - IProducerOptions Producer { get; } -} diff --git a/src/Stream/src/Abstractions/Configuration/IConsumerOptions.cs b/src/Stream/src/Abstractions/Configuration/IConsumerOptions.cs deleted file mode 100644 index 3f90c41e45..0000000000 --- a/src/Stream/src/Abstractions/Configuration/IConsumerOptions.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Configuration; - -/// -/// Common consumer configuration options. -/// -public interface IConsumerOptions -{ - string BindingName { get; } - - bool AutoStartup { get; } - - int Concurrency { get; } - - bool IsPartitioned { get; } - - int InstanceCount { get; } - - int InstanceIndex { get; } - - List InstanceIndexList { get; } - - int MaxAttempts { get; } - - int BackOffInitialInterval { get; } - - int BackOffMaxInterval { get; } - - double BackOffMultiplier { get; } - - bool DefaultRetryable { get; } - - List RetryableExceptions { get; } - - HeaderMode HeaderMode { get; } - - bool UseNativeDecoding { get; } - - bool Multiplex { get; } - - IConsumerOptions Clone(); -} diff --git a/src/Stream/src/Abstractions/Configuration/IListenerContainerCustomizer.cs b/src/Stream/src/Abstractions/Configuration/IListenerContainerCustomizer.cs deleted file mode 100644 index e5f8a30d6e..0000000000 --- a/src/Stream/src/Abstractions/Configuration/IListenerContainerCustomizer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Configuration; - -public interface IListenerContainerCustomizer -{ - void Configure(object container, string destinationName, string group); -} diff --git a/src/Stream/src/Abstractions/Configuration/IMessageSourceCustomizer.cs b/src/Stream/src/Abstractions/Configuration/IMessageSourceCustomizer.cs deleted file mode 100644 index 86cdc9debe..0000000000 --- a/src/Stream/src/Abstractions/Configuration/IMessageSourceCustomizer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Configuration; - -public interface IMessageSourceCustomizer -{ - void Configure(object source, string destinationName, string group); -} diff --git a/src/Stream/src/Abstractions/Configuration/IProducerOptions.cs b/src/Stream/src/Abstractions/Configuration/IProducerOptions.cs deleted file mode 100644 index 3463d530a1..0000000000 --- a/src/Stream/src/Abstractions/Configuration/IProducerOptions.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Configuration; - -/// -/// Common producer configuration options. -/// -public interface IProducerOptions -{ - string BindingName { get; } - - bool AutoStartup { get; } - - string PartitionKeyExpression { get; } - - string PartitionKeyExtractorName { get; } - - string PartitionSelectorName { get; } - - string PartitionSelectorExpression { get; } - - int PartitionCount { get; } - - List RequiredGroups { get; } - - HeaderMode HeaderMode { get; } - - bool UseNativeEncoding { get; } - - bool ErrorChannelEnabled { get; } - - bool IsPartitioned { get; } - - IProducerOptions Clone(); -} diff --git a/src/Stream/src/Abstractions/Messaging/IProcessor.cs b/src/Stream/src/Abstractions/Messaging/IProcessor.cs deleted file mode 100644 index 6dc3ad22dd..0000000000 --- a/src/Stream/src/Abstractions/Messaging/IProcessor.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Messaging; - -/// -/// Bindable interface with one input and one output channel. -/// -public interface IProcessor : ISource, ISink -{ -} diff --git a/src/Stream/src/Abstractions/Messaging/ISink.cs b/src/Stream/src/Abstractions/Messaging/ISink.cs deleted file mode 100644 index 59c0323f68..0000000000 --- a/src/Stream/src/Abstractions/Messaging/ISink.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Messaging; - -/// -/// Bindable interface with one input channel. -/// -public interface ISink -{ - /// - /// Default channel name. - /// - const string InputName = "input"; - - /// - /// Gets the input channel. - /// - [Input(InputName)] - ISubscribableChannel Input { get; } -} diff --git a/src/Stream/src/Abstractions/Messaging/ISource.cs b/src/Stream/src/Abstractions/Messaging/ISource.cs deleted file mode 100644 index 5b043c5ab5..0000000000 --- a/src/Stream/src/Abstractions/Messaging/ISource.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Messaging; - -/// -/// Bindable interface with one output channel. -/// -public interface ISource -{ - /// - /// Default name of the output channel. - /// - const string OutputName = "output"; - - /// - /// Gets the output channel. - /// - [Output(OutputName)] - IMessageChannel Output { get; } -} diff --git a/src/Stream/src/Abstractions/Provisioning/IConsumerDestination.cs b/src/Stream/src/Abstractions/Provisioning/IConsumerDestination.cs deleted file mode 100644 index 7265f8d0bc..0000000000 --- a/src/Stream/src/Abstractions/Provisioning/IConsumerDestination.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Provisioning; - -/// -/// Represents a ConsumerDestination that provides the information about the destination that is physically provisioned through a provisioning provider. -/// -public interface IConsumerDestination -{ - /// - /// Gets the destination name. - /// - string Name { get; } -} diff --git a/src/Stream/src/Abstractions/Provisioning/IProducerDestination.cs b/src/Stream/src/Abstractions/Provisioning/IProducerDestination.cs deleted file mode 100644 index 4e0df3e29d..0000000000 --- a/src/Stream/src/Abstractions/Provisioning/IProducerDestination.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Provisioning; - -/// -/// Represents a ProducerDestination that provides the information about the destination that is physically provisioned through a provisioning provider. -/// -public interface IProducerDestination -{ - /// - /// Gets the destination name. - /// - string Name { get; } - - /// - /// Provides the destination name for a given partition. - /// - /// - /// the partition to find a name for. - /// - /// - /// the destination name for the partition. - /// - string GetNameForPartition(int partition); -} diff --git a/src/Stream/src/Abstractions/Provisioning/IProvisioningProvider.cs b/src/Stream/src/Abstractions/Provisioning/IProvisioningProvider.cs deleted file mode 100644 index d5c447e001..0000000000 --- a/src/Stream/src/Abstractions/Provisioning/IProvisioningProvider.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Provisioning; - -/// -/// Provisioning SPI that allows the users to provision destinations such as queues and topics. This SPI will allow the binders to be separated from any -/// provisioning concerns and only focus on setting up endpoints for sending/receiving messages. -/// -public interface IProvisioningProvider -{ - /// - /// Creates middleware destination on the physical broker for the producer to send data. The implementation is middleware-specific. - /// - /// - /// the name of the producer destination. - /// - /// - /// the producer options. - /// - /// - /// the provisioned destination. - /// - IProducerDestination ProvisionProducerDestination(string name, IProducerOptions options); - - /// - /// Creates the middleware destination on the physical broker for the consumer to consume data.The implementation is middleware-specific. - /// - /// - /// the name of the consumer destination. - /// - /// - /// the consumer group. - /// - /// - /// the consumer options. - /// - /// - /// the provisioned destination. - /// - IConsumerDestination ProvisionConsumerDestination(string name, string group, IConsumerOptions options); -} diff --git a/src/Stream/src/Abstractions/Steeltoe.Stream.Abstractions.csproj b/src/Stream/src/Abstractions/Steeltoe.Stream.Abstractions.csproj deleted file mode 100644 index a52c794258..0000000000 --- a/src/Stream/src/Abstractions/Steeltoe.Stream.Abstractions.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net8.0;net6.0 - Steeltoe.Stream - Abstractions for use with Steeltoe Stream - - true - - - - - - - - diff --git a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitBinderOptions.cs b/src/Stream/src/BinderRabbitMQ/Configuration/RabbitBinderOptions.cs deleted file mode 100644 index 68374325f1..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitBinderOptions.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.IO.Compression; -using Microsoft.Extensions.Configuration; -using Steeltoe.Common; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Configuration; - -public class RabbitBinderOptions -{ - public const string Prefix = "spring:cloud:stream:rabbit:binder"; - - public List AdminAddresses { get; set; } = new(); - - public List Nodes { get; set; } = new(); - - public CompressionLevel CompressionLevel { get; set; } - - public string ConnectionNamePrefix { get; set; } - - public RabbitBinderOptions() - { - } - - internal RabbitBinderOptions(IConfiguration configuration) - { - ArgumentGuard.NotNull(configuration); - - configuration.Bind(this); - } -} diff --git a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitBindingOptions.cs b/src/Stream/src/BinderRabbitMQ/Configuration/RabbitBindingOptions.cs deleted file mode 100644 index 05af2d2aaa..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitBindingOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder.RabbitMQ.Configuration; - -public class RabbitBindingOptions -{ - public RabbitProducerOptions Producer { get; set; } - - public RabbitConsumerOptions Consumer { get; set; } -} diff --git a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitBindingsOptions.cs b/src/Stream/src/BinderRabbitMQ/Configuration/RabbitBindingsOptions.cs deleted file mode 100644 index b23f124d13..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitBindingsOptions.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Steeltoe.Common; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Configuration; - -public class RabbitBindingsOptions -{ - public const string Prefix = "spring:cloud:stream:rabbit"; - - public Dictionary Bindings { get; set; } - - public RabbitBindingOptions Default { get; set; } - - // spring.cloud.stream.rabbit.bindings..consumer - // spring.cloud.stream.rabbit.bindings..producer - // spring.cloud.stream.rabbit.default.consumer NOTE: Different from Spring - // spring.cloud.stream.rabbit.default.producer NOTE: Different from Spring - public RabbitBindingsOptions() - { - PostProcess(); - } - - public RabbitBindingsOptions(IConfiguration configuration) - { - ArgumentGuard.NotNull(configuration); - - configuration.Bind(this); - PostProcess(); - } - - public RabbitConsumerOptions GetRabbitConsumerOptions(string binding) - { - RabbitConsumerOptions results = Default.Consumer; - - if (binding == null) - { - return results; - } - - Bindings.TryGetValue(binding, out RabbitBindingOptions options); - - if (options != null && options.Consumer != null) - { - results = options.Consumer; - } - - return results; - } - - public RabbitProducerOptions GetRabbitProducerOptions(string binding) - { - RabbitProducerOptions results = Default.Producer; - Bindings.TryGetValue(binding, out RabbitBindingOptions options); - - if (options != null && options.Producer != null) - { - results = options.Producer; - } - - return results; - } - - internal void PostProcess() - { - Default ??= new RabbitBindingOptions(); - Default.Consumer ??= new RabbitConsumerOptions(); - Default.Consumer.PostProcess(); - Default.Producer ??= new RabbitProducerOptions(); - Default.Producer.PostProcess(); - Bindings ??= new Dictionary(); - - foreach (KeyValuePair binding in Bindings) - { - RabbitBindingOptions bo = binding.Value; - - if (bo.Consumer != null) - { - bo.Consumer.PostProcess(Default.Consumer); - } - - if (bo.Producer != null) - { - bo.Producer.PostProcess(Default.Producer); - } - } - } -} diff --git a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitCommonOptions.cs b/src/Stream/src/BinderRabbitMQ/Configuration/RabbitCommonOptions.cs deleted file mode 100644 index 4b8881de0e..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitCommonOptions.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder.RabbitMQ.Configuration; - -public class RabbitCommonOptions -{ - public const string DeadLetterExchangeName = "DLX"; - - public string ExchangeType { get; set; } - - public bool? DeclareExchange { get; set; } - - public bool? ExchangeDurable { get; set; } - - public bool? ExchangeAutoDelete { get; set; } - - public bool? DelayedExchange { get; set; } - - public bool? QueueNameGroupOnly { get; set; } - - public bool? BindQueue { get; set; } - - public string BindingRoutingKey { get; set; } - - public string BindingRoutingKeyDelimiter { get; set; } - - public int? Ttl { get; set; } - - public int? Expires { get; set; } - - public int? MaxLength { get; set; } - - public int? MaxLengthBytes { get; set; } - - public int? MaxPriority { get; set; } - - public string DeadLetterQueueName { get; set; } - - public string DeadLetterExchange { get; set; } - - public string DeadLetterExchangeType { get; set; } - - public bool? DeclareDlx { get; set; } = true; - - public string DeadLetterRoutingKey { get; set; } - - public int? DlqTtl { get; set; } - - public int? DlqExpires { get; set; } - - public int? DlqMaxLength { get; set; } - - public int? DlqMaxLengthBytes { get; set; } - - public int? DlqMaxPriority { get; set; } - - public string DlqDeadLetterExchange { get; set; } - - public string DlqDeadLetterRoutingKey { get; set; } - - public bool? AutoBindDlq { get; set; } - - public string Prefix { get; set; } - - public bool? Lazy { get; set; } - - public bool? DlqLazy { get; set; } - - public string OverflowBehavior { get; set; } - - public string DlqOverflowBehavior { get; set; } - - public Dictionary QueueBindingArguments { get; set; } - - public Dictionary DlqBindingArguments { get; set; } - - public QuorumConfig Quorum { get; set; } - - public QuorumConfig DlqQuorum { get; set; } - - public bool? SingleActiveConsumer { get; set; } - - public bool? DlqSingleActiveConsumer { get; set; } - - internal void PostProcess(RabbitCommonOptions defaultOptions = null) - { - ExchangeType ??= defaultOptions != null ? defaultOptions.ExchangeType : Steeltoe.Messaging.RabbitMQ.Configuration.ExchangeType.Topic; - DeclareExchange ??= defaultOptions != null ? defaultOptions.DeclareExchange : true; - ExchangeDurable ??= defaultOptions != null ? defaultOptions.ExchangeDurable : true; - ExchangeAutoDelete ??= defaultOptions != null ? defaultOptions.ExchangeAutoDelete : false; - DelayedExchange ??= defaultOptions != null ? defaultOptions.DelayedExchange : false; - QueueNameGroupOnly ??= defaultOptions != null ? defaultOptions.QueueNameGroupOnly : false; - BindQueue ??= defaultOptions != null ? defaultOptions.BindQueue : true; - BindingRoutingKey ??= defaultOptions?.BindingRoutingKey; - BindingRoutingKeyDelimiter ??= defaultOptions?.BindingRoutingKeyDelimiter; - Ttl ??= defaultOptions?.Ttl; - Expires ??= defaultOptions?.Expires; - MaxLength ??= defaultOptions?.MaxLength; - MaxLengthBytes ??= defaultOptions?.MaxLengthBytes; - MaxPriority ??= defaultOptions?.MaxPriority; - DeadLetterQueueName ??= defaultOptions?.DeadLetterQueueName; - DeadLetterExchange ??= defaultOptions?.DeadLetterExchange; - - DeadLetterExchangeType ??= - defaultOptions != null ? defaultOptions.DeadLetterExchangeType : Steeltoe.Messaging.RabbitMQ.Configuration.ExchangeType.Direct; - - DeclareDlx ??= defaultOptions != null ? defaultOptions.DeclareDlx : true; - DeadLetterRoutingKey ??= defaultOptions?.DeadLetterRoutingKey; - DlqTtl ??= defaultOptions?.DlqTtl; - DlqExpires ??= defaultOptions?.DlqExpires; - DlqMaxLength ??= defaultOptions?.DlqMaxLength; - DlqMaxLengthBytes ??= defaultOptions?.DlqMaxLengthBytes; - DlqMaxPriority ??= defaultOptions?.DlqMaxPriority; - DlqDeadLetterExchange ??= defaultOptions?.DlqDeadLetterExchange; - DlqDeadLetterRoutingKey ??= defaultOptions?.DlqDeadLetterRoutingKey; - AutoBindDlq ??= defaultOptions != null ? defaultOptions.AutoBindDlq : false; - - Prefix ??= defaultOptions != null ? defaultOptions.Prefix : string.Empty; - Lazy ??= defaultOptions != null ? defaultOptions.Lazy : false; - DlqLazy ??= defaultOptions != null ? defaultOptions.DlqLazy : false; - OverflowBehavior ??= defaultOptions?.OverflowBehavior; - DlqOverflowBehavior ??= defaultOptions?.DlqOverflowBehavior; - SingleActiveConsumer ??= defaultOptions != null ? defaultOptions.SingleActiveConsumer : false; - DlqSingleActiveConsumer ??= defaultOptions != null ? defaultOptions.DlqSingleActiveConsumer : false; - - Quorum ??= defaultOptions != null - ? defaultOptions.Quorum - : new QuorumConfig - { - Enabled = false - }; - - DlqQuorum ??= defaultOptions != null - ? defaultOptions.DlqQuorum - : new QuorumConfig - { - Enabled = false - }; - - QueueBindingArguments ??= defaultOptions != null ? defaultOptions.QueueBindingArguments : new Dictionary(); - - DlqBindingArguments ??= defaultOptions != null ? defaultOptions.DlqBindingArguments : new Dictionary(); - } - - public class QuorumConfig - { - public bool? Enabled { get; set; } - - public int? InitialQuorumSize { get; set; } - - public int? DeliveryLimit { get; set; } - } -} diff --git a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitConsumerOptions.cs b/src/Stream/src/BinderRabbitMQ/Configuration/RabbitConsumerOptions.cs deleted file mode 100644 index ad8d61cfe7..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitConsumerOptions.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using RabbitConfiguration = Steeltoe.Messaging.RabbitMQ.Configuration; -using RabbitCore = Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Configuration; - -public class RabbitConsumerOptions : RabbitCommonOptions -{ - public bool? Transacted { get; set; } - - public RabbitCore.AcknowledgeMode? AcknowledgeMode { get; set; } - - public int? MaxConcurrency { get; set; } - - public int? Prefetch { get; set; } - - public int? BatchSize { get; set; } - - public bool? DurableSubscription { get; set; } - - public bool? RepublishToDlq { get; set; } - - public RabbitCore.MessageDeliveryMode? RepublishDeliveryMode { get; set; } - - public bool? RequeueRejected { get; set; } - - public List HeaderPatterns { get; set; } - - public int? RecoveryInterval { get; set; } - - public bool? Exclusive { get; set; } - - public bool? MissingQueuesFatal { get; set; } - - public int? QueueDeclarationRetries { get; set; } - - public long? FailedDeclarationRetryInterval { get; set; } - - public string ConsumerTagPrefix { get; set; } - - public int? FrameMaxHeadroom { get; set; } - - public RabbitConfiguration.ContainerType? ContainerType { get; set; } = RabbitConfiguration.ContainerType.Direct; - - public string AnonymousGroupPrefix { get; set; } - - public bool? IsEnableBatching { get; set; } - - internal void PostProcess(RabbitConsumerOptions defaultOptions = null) - { - Transacted ??= defaultOptions?.Transacted ?? false; - AcknowledgeMode ??= defaultOptions?.AcknowledgeMode ?? RabbitCore.AcknowledgeMode.Auto; - MaxConcurrency ??= defaultOptions?.MaxConcurrency ?? 1; - Prefetch ??= defaultOptions?.Prefetch ?? 1; - BatchSize ??= defaultOptions?.BatchSize ?? 1; - DurableSubscription ??= defaultOptions?.DurableSubscription ?? true; - RepublishToDlq ??= defaultOptions?.RepublishToDlq ?? true; - RepublishDeliveryMode ??= defaultOptions?.RepublishDeliveryMode ?? RabbitCore.MessageDeliveryMode.Persistent; - RequeueRejected ??= defaultOptions?.RequeueRejected ?? false; - - HeaderPatterns ??= defaultOptions?.HeaderPatterns ?? new List - { - "*" - }; - - RecoveryInterval ??= defaultOptions?.RecoveryInterval ?? 5000; - Exclusive ??= defaultOptions?.Exclusive ?? false; - MissingQueuesFatal ??= defaultOptions?.MissingQueuesFatal ?? false; - QueueDeclarationRetries ??= defaultOptions?.QueueDeclarationRetries; - FailedDeclarationRetryInterval ??= defaultOptions?.FailedDeclarationRetryInterval; - ConsumerTagPrefix ??= defaultOptions?.ConsumerTagPrefix; - FrameMaxHeadroom ??= defaultOptions?.FrameMaxHeadroom ?? 20_000; - AnonymousGroupPrefix ??= defaultOptions?.AnonymousGroupPrefix ?? "anonymous."; - IsEnableBatching ??= defaultOptions?.IsEnableBatching ?? false; - - base.PostProcess(); - } -} diff --git a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitProducerOptions.cs b/src/Stream/src/BinderRabbitMQ/Configuration/RabbitProducerOptions.cs deleted file mode 100644 index 6b995d5c65..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Configuration/RabbitProducerOptions.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.RabbitMQ.Core; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Configuration; - -public class RabbitProducerOptions : RabbitCommonOptions -{ - public bool? Compress { get; set; } - - public bool? BatchingEnabled { get; set; } - - public int? BatchSize { get; set; } - - public int? BatchBufferLimit { get; set; } - - public int? BatchTimeout { get; set; } - - // Do we need this? - public bool? DurableSubscription { get; set; } - - public bool? Exclusive { get; set; } - - public long? FailedDeclarationRetryInterval { get; set; } - - // end - public bool? Transacted { get; set; } - - public MessageDeliveryMode? DeliveryMode { get; set; } - - public List HeaderPatterns { get; set; } - - public string DelayExpression { get; set; } - - public string RoutingKeyExpression { get; set; } - - public string ConfirmAckChannel { get; set; } - - public bool? UseConfirmHeader { get; set; } - - internal void PostProcess(RabbitProducerOptions defaultOptions = null) - { - Compress ??= defaultOptions != null ? defaultOptions.Compress : false; - BatchingEnabled ??= defaultOptions?.BatchingEnabled ?? false; - BatchSize ??= defaultOptions?.BatchSize ?? 100; - BatchBufferLimit ??= defaultOptions?.BatchBufferLimit ?? 10000; - BatchTimeout ??= defaultOptions?.BatchTimeout ?? 5000; - Transacted ??= defaultOptions?.Transacted ?? false; - DeliveryMode ??= defaultOptions?.DeliveryMode ?? MessageDeliveryMode.Persistent; - - HeaderPatterns ??= defaultOptions?.HeaderPatterns ?? new List - { - "*" - }; - - DelayExpression ??= defaultOptions?.DelayExpression; - RoutingKeyExpression ??= defaultOptions?.RoutingKeyExpression; - ConfirmAckChannel ??= defaultOptions?.ConfirmAckChannel; - UseConfirmHeader ??= defaultOptions?.UseConfirmHeader ?? false; - - base.PostProcess(defaultOptions); - } -} diff --git a/src/Stream/src/BinderRabbitMQ/Properties/AssemblyInfo.cs b/src/Stream/src/BinderRabbitMQ/Properties/AssemblyInfo.cs deleted file mode 100644 index c9803d6412..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Steeltoe.Stream.Binder.RabbitMQ.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Stream.Binder.Test")] diff --git a/src/Stream/src/BinderRabbitMQ/Provisioning/RabbitExchangeQueueProvisioner.cs b/src/Stream/src/BinderRabbitMQ/Provisioning/RabbitExchangeQueueProvisioner.cs deleted file mode 100644 index 3f5e7522d4..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Provisioning/RabbitExchangeQueueProvisioner.cs +++ /dev/null @@ -1,770 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Provisioning; -using static Steeltoe.Messaging.RabbitMQ.Configuration.Binding; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Provisioning; - -public class RabbitExchangeQueueProvisioner : IProvisioningProvider -{ - private const string GroupIndexDelimiter = "."; - private readonly IApplicationContext _autoDeclareContext; - private readonly ILogger _logger; - - private bool _notOurAdminException = true; // Should be set by onApplicationEvent - - private RabbitAdmin Admin { get; } - - private List Customizers { get; } - - private RabbitBindingsOptions Options { get; } - - public RabbitExchangeQueueProvisioner(IConnectionFactory connectionFactory, IOptionsMonitor bindingsOptions, - IApplicationContext applicationContext, ILogger logger) - : this(connectionFactory, new List(), bindingsOptions, applicationContext, logger) - { - } - - public RabbitExchangeQueueProvisioner(IConnectionFactory connectionFactory, List customizers, - IOptionsMonitor bindingsOptions, IApplicationContext applicationContext, ILogger logger) - { - Admin = new RabbitAdmin(applicationContext, connectionFactory, logger); - - _autoDeclareContext = applicationContext; - _logger = logger; - Admin.ApplicationContext = _autoDeclareContext; - Admin.Initialize(); - Customizers = customizers; - Options = bindingsOptions.CurrentValue; - } - - public static string ConstructDlqName(string name) - { - return $"{name}.dlq"; - } - - public static string ApplyPrefix(string prefix, string name) - { - return prefix + name; - } - - public IProducerDestination ProvisionProducerDestination(string name, IProducerOptions options) - { - RabbitProducerOptions producerProperties = Options.GetRabbitProducerOptions(options.BindingName); - - string exchangeName = ApplyPrefix(producerProperties.Prefix, name); - IExchange exchange = BuildExchange(producerProperties, exchangeName); - - if (producerProperties.DeclareExchange.Value) - { - DeclareExchange(exchangeName, exchange); - } - - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = null; - - foreach (string requiredGroupName in options.RequiredGroups) - { - string baseQueueName = producerProperties.QueueNameGroupOnly.Value ? requiredGroupName : $"{exchangeName}.{requiredGroupName}"; - - if (!options.IsPartitioned) - { - AutoBindDlq(baseQueueName, baseQueueName, producerProperties); - - if (producerProperties.BindQueue.Value) - { - var queue = new Queue(baseQueueName, true, false, false, GetQueueArgs(baseQueueName, producerProperties, false)); - DeclareQueue(baseQueueName, queue); - List routingKeys = BindingRoutingKeys(producerProperties); - - if (routingKeys == null || routingKeys.Count == 0) - { - binding = NotPartitionedBinding(exchange, queue, null, producerProperties); - } - else - { - foreach (string routingKey in routingKeys) - { - binding = NotPartitionedBinding(exchange, queue, routingKey, producerProperties); - } - } - } - } - else - { - // if the stream is partitioned, create one queue for each target partition for the default group - for (int i = 0; i < options.PartitionCount; i++) - { - string partitionSuffix = $"-{i}"; - string partitionQueueName = baseQueueName + partitionSuffix; - AutoBindDlq(baseQueueName, baseQueueName + partitionSuffix, producerProperties); - - if (producerProperties.BindQueue.Value) - { - var queue = new Queue(partitionQueueName, true, false, false, GetQueueArgs(partitionQueueName, producerProperties, false)); - DeclareQueue(queue.QueueName, queue); - string prefix = producerProperties.Prefix; - string destination = string.IsNullOrEmpty(prefix) ? exchangeName : exchangeName.Substring(prefix.Length); - List routingKeys = BindingRoutingKeys(producerProperties); - - if (routingKeys == null || routingKeys.Count == 0) - { - binding = PartitionedBinding(destination, exchange, queue, null, producerProperties, i); - } - else - { - foreach (string routingKey in routingKeys) - { - binding = PartitionedBinding(destination, exchange, queue, routingKey, producerProperties, i); - } - } - } - } - } - } - - return new RabbitProducerDestination(exchange, binding); - } - - public IConsumerDestination ProvisionConsumerDestination(string name, string group, IConsumerOptions options) - { - IConsumerDestination consumerDestination; - - if (!options.Multiplex) - { - consumerDestination = DoProvisionConsumerDestination(name, group, options); - } - else - { - var consumerDestinationNames = new List(); - IEnumerable trimmed = name.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()); - - foreach (string destination in trimmed) - { - if (options.IsPartitioned && options.InstanceIndexList.Count > 0) - { - foreach (int index in options.InstanceIndexList) - { - var temporaryOptions = options.Clone() as ConsumerOptions; - temporaryOptions.InstanceIndex = index; - consumerDestinationNames.Add(DoProvisionConsumerDestination(destination, group, temporaryOptions).Name); - } - } - else - { - consumerDestinationNames.Add(DoProvisionConsumerDestination(destination, group, options).Name); - } - } - - consumerDestination = new RabbitConsumerDestination(string.Join(',', consumerDestinationNames), null); - } - - return consumerDestination; - } - - public void CleanAutoDeclareContext(IConsumerDestination destination, IConsumerOptions consumerProperties) - { - lock (_autoDeclareContext) - { - destination.Name.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList().ForEach(name => - { - name = name.Trim(); - RemoveSingleton($"{name}.binding"); - RemoveSingleton(name); - string dlq = $"{name}.dlq"; - RemoveSingleton($"{dlq}.binding"); - RemoveSingleton(dlq); - }); - } - } - - protected virtual string GetGroupedName(string name, string group) - { - return name + GroupIndexDelimiter + (!string.IsNullOrEmpty(group) ? group : "default"); - } - - private IConsumerDestination DoProvisionConsumerDestination(string name, string group, IConsumerOptions options) - { - RabbitConsumerOptions consumerProperties = Options.GetRabbitConsumerOptions(options.BindingName); - bool anonymous = string.IsNullOrEmpty(group); - Base64UrlNamingStrategy anonQueueNameGenerator = null; - - if (anonymous) - { - anonQueueNameGenerator = new Base64UrlNamingStrategy(consumerProperties.AnonymousGroupPrefix ?? string.Empty); - } - - string baseQueueName; - - if (consumerProperties.QueueNameGroupOnly.GetValueOrDefault()) - { - baseQueueName = anonymous ? anonQueueNameGenerator.GenerateName() : group; - } - else - { - baseQueueName = GetGroupedName(name, anonymous ? anonQueueNameGenerator.GenerateName() : group); - } - - string prefix = consumerProperties.Prefix; - string exchangeName = ApplyPrefix(prefix, name); - IExchange exchange = BuildExchange(consumerProperties, exchangeName); - - if (consumerProperties.DeclareExchange.GetValueOrDefault()) - { - DeclareExchange(exchangeName, exchange); - } - - string queueName = ApplyPrefix(prefix, baseQueueName); - bool partitioned = !anonymous && options.IsPartitioned; - bool durable = !anonymous && consumerProperties.DurableSubscription.Value; - Queue queue; - - if (anonymous) - { - string anonQueueName = queueName; - queue = new AnonymousQueue(new GivenNamingStrategy(() => anonQueueName), GetQueueArgs(queueName, consumerProperties, false)); - } - else - { - if (partitioned) - { - string partitionSuffix = $"-{options.InstanceIndex}"; - queueName += partitionSuffix; - } - - queue = durable - ? new Queue(queueName, true, false, false, GetQueueArgs(queueName, consumerProperties, false)) - : new Queue(queueName, false, false, true, GetQueueArgs(queueName, consumerProperties, false)); - } - - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = null; - - if (consumerProperties.BindQueue.GetValueOrDefault()) - { - DeclareQueue(queueName, queue); - List routingKeys = BindingRoutingKeys(consumerProperties); - - if (routingKeys == null || routingKeys.Count == 0) - { - binding = DeclareConsumerBindings(name, null, options, exchange, partitioned, queue); - } - else - { - foreach (string routingKey in routingKeys) - { - binding = DeclareConsumerBindings(name, routingKey, options, exchange, partitioned, queue); - } - } - } - - if (durable) - { - AutoBindDlq(ApplyPrefix(consumerProperties.Prefix, baseQueueName), queueName, consumerProperties); - } - - return new RabbitConsumerDestination(queue.QueueName, binding); - } - - private Steeltoe.Messaging.RabbitMQ.Configuration.IBinding DeclareConsumerBindings(string name, string routingKey, IConsumerOptions options, - IExchange exchange, bool partitioned, Queue queue) - { - RabbitConsumerOptions consumerProperties = Options.GetRabbitConsumerOptions(options.BindingName); - - if (partitioned) - { - return PartitionedBinding(name, exchange, queue, routingKey, consumerProperties, options.InstanceIndex); - } - - return NotPartitionedBinding(exchange, queue, routingKey, consumerProperties); - } - - private Steeltoe.Messaging.RabbitMQ.Configuration.IBinding PartitionedBinding(string destination, IExchange exchange, Queue queue, string rk, - RabbitCommonOptions extendedProperties, int index) - { - string bindingKey = rk ?? destination; - - bindingKey += $"-{index}"; - var arguments = new Dictionary(); - - foreach (KeyValuePair entry in extendedProperties.QueueBindingArguments) - { - arguments.Add(entry.Key, entry.Value); - } - - switch (exchange) - { - case TopicExchange topic: - { - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = BindingBuilder.Bind(queue).To(topic).With(bindingKey); - DeclareBinding(queue.QueueName, binding); - return binding; - } - - case DirectExchange direct: - { - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = BindingBuilder.Bind(queue).To(direct).With(bindingKey); - DeclareBinding(queue.QueueName, binding); - return binding; - } - - case FanOutExchange: - throw new ProvisioningException("A fan-out exchange is not appropriate for partitioned apps"); - case HeadersExchange: - { - var binding = new Steeltoe.Messaging.RabbitMQ.Configuration.Binding($"{queue.QueueName}.{exchange.ExchangeName}.binding", queue.QueueName, - DestinationType.Queue, exchange.ExchangeName, string.Empty, arguments); - - DeclareBinding(queue.QueueName, binding); - return binding; - } - - default: - throw new ProvisioningException($"Cannot bind to a {exchange.Type} exchange"); - } - } - - private Steeltoe.Messaging.RabbitMQ.Configuration.IBinding NotPartitionedBinding(IExchange exchange, Queue queue, string rk, - RabbitCommonOptions extendedProperties) - { - string routingKey = rk ?? "#"; - - var arguments = new Dictionary(); - - foreach (KeyValuePair entry in extendedProperties.QueueBindingArguments) - { - arguments.Add(entry.Key, entry.Value); - } - - switch (exchange) - { - case TopicExchange topic: - { - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = BindingBuilder.Bind(queue).To(topic).With(routingKey); - DeclareBinding(queue.QueueName, binding); - return binding; - } - - case DirectExchange direct: - { - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = BindingBuilder.Bind(queue).To(direct).With(routingKey); - DeclareBinding(queue.QueueName, binding); - return binding; - } - - case FanOutExchange fanOut: - { - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = BindingBuilder.Bind(queue).To(fanOut); - DeclareBinding(queue.QueueName, binding); - return binding; - } - - case HeadersExchange: - { - var binding = new Steeltoe.Messaging.RabbitMQ.Configuration.Binding($"{queue.QueueName}.{exchange.ExchangeName}.binding", queue.QueueName, - DestinationType.Queue, exchange.ExchangeName, string.Empty, arguments); - - DeclareBinding(queue.QueueName, binding); - return binding; - } - - default: - throw new ProvisioningException($"Cannot bind to a {exchange.Type} exchange"); - } - } - - private List BindingRoutingKeys(RabbitCommonOptions extendedProperties) - { - /* - * When the delimiter is null, we get a String[1] containing the original. - */ - string delimiter = extendedProperties.BindingRoutingKeyDelimiter; - - if (delimiter == null) - { - if (extendedProperties.BindingRoutingKey == null) - { - return null; - } - - return new List - { - extendedProperties.BindingRoutingKey.Trim() - }; - } - - if (extendedProperties.BindingRoutingKey == null) - { - return null; - } - - IEnumerable trimmed = extendedProperties.BindingRoutingKey.Split(delimiter, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()); - return new List(trimmed); - } - - private void AutoBindDlq(string baseQueueName, string routingKey, RabbitCommonOptions properties) - { - bool autoBindDlq = properties.AutoBindDlq.Value; - - _logger.LogDebug("autoBindDLQ={autoBindDLQ} for: {queueName}", autoBindDlq, baseQueueName); - - if (autoBindDlq) - { - string dlqName = properties.DeadLetterQueueName ?? ConstructDlqName(baseQueueName); - - var dlq = new Queue(dlqName, true, false, false, GetQueueArgs(dlqName, properties, true)); - DeclareQueue(dlqName, dlq); - string dlxName = GetDeadLetterExchangeName(properties); - - if (properties.DeclareDlx.Value) - { - DeclareExchange(dlxName, new ExchangeBuilder(dlxName, properties.DeadLetterExchangeType).Durable(true).Build()); - } - - var arguments = new Dictionary(); - - properties.DlqBindingArguments?.ToList().ForEach(entry => arguments.Add(entry.Key, entry.Value)); - - string dlRoutingKey = properties.DeadLetterRoutingKey ?? routingKey; - string dlBindingName = $"{dlq.QueueName}.{dlxName}.{dlRoutingKey}.binding"; - - var dlqBinding = - new Steeltoe.Messaging.RabbitMQ.Configuration.Binding(dlBindingName, dlq.QueueName, DestinationType.Queue, dlxName, dlRoutingKey, arguments); - - DeclareBinding(dlqName, dlqBinding); - - if (properties is RabbitConsumerOptions options && options.RepublishToDlq.Value) - { - /* - * Also bind with the base queue name when republishToDlq is used, which does not know about partitioning - */ - string bindingName = $"{dlq.QueueName}.{dlxName}.{baseQueueName}.binding"; - - DeclareBinding(dlqName, - new Steeltoe.Messaging.RabbitMQ.Configuration.Binding(bindingName, dlq.QueueName, DestinationType.Queue, dlxName, baseQueueName, - arguments)); - } - } - } - - private string GetDeadLetterExchangeName(RabbitCommonOptions properties) - { - if (properties.DeadLetterExchange == null) - { - return properties.Prefix + RabbitCommonOptions.DeadLetterExchangeName; - } - - return properties.DeadLetterExchange; - } - - private void DeclareQueue(string beanName, Queue queueArg) - { - Queue queue = queueArg; - - foreach (IDeclarableCustomizer customizer in Customizers) - { - queue = (Queue)customizer.Apply(queue); - } - - try - { - Admin.DeclareQueue(queue); - } - catch (RabbitConnectException e) - { - _logger.LogDebug(e, "Declaration of queue: {queueName} deferred - connection not available", queue.QueueName); - } - catch (Exception e) - { - if (_notOurAdminException) - { - _notOurAdminException = false; - throw; - } - - _logger.LogDebug(e, "Declaration of queue: {queueName} deferred", queue.QueueName); - } - - AddToAutoDeclareContext(beanName, queue); - } - - private Dictionary GetQueueArgs(string queueName, RabbitCommonOptions properties, bool isDlq) - { - var args = new Dictionary(); - - if (!isDlq) - { - if (properties.AutoBindDlq.Value) - { - string dlx = properties.DeadLetterExchange ?? ApplyPrefix(properties.Prefix, "DLX"); - - args.Add("x-dead-letter-exchange", dlx); - - string dlRk = properties.DeadLetterRoutingKey ?? queueName; - - args.Add("x-dead-letter-routing-key", dlRk); - } - } - else - { - if (properties.DlqDeadLetterExchange != null) - { - args.Add("x-dead-letter-exchange", properties.DlqDeadLetterExchange); - } - - if (properties.DlqDeadLetterRoutingKey != null) - { - args.Add("x-dead-letter-routing-key", properties.DlqDeadLetterRoutingKey); - } - } - - AddAdditionalArgs(args, properties, isDlq); - return args; - } - - private void AddAdditionalArgs(Dictionary args, RabbitCommonOptions properties, bool isDlq) - { - int? expires = isDlq ? properties.DlqExpires : properties.Expires; - int? maxLength = isDlq ? properties.DlqMaxLength : properties.MaxLength; - int? maxLengthBytes = isDlq ? properties.DlqMaxLengthBytes : properties.MaxLengthBytes; - int? maxPriority = isDlq ? properties.DlqMaxPriority : properties.MaxPriority; - int? ttl = isDlq ? properties.DlqTtl : properties.Ttl; - bool? lazy = isDlq ? properties.DlqLazy : properties.Lazy; - string overflow = isDlq ? properties.DlqOverflowBehavior : properties.OverflowBehavior; - RabbitCommonOptions.QuorumConfig quorum = isDlq ? properties.DlqQuorum : properties.Quorum; - bool? singleActive = isDlq ? properties.DlqSingleActiveConsumer : properties.SingleActiveConsumer; - - if (expires != null) - { - args.Add("x-expires", expires.Value); - } - - if (maxLength != null) - { - args.Add("x-max-length", maxLength.Value); - } - - if (maxLengthBytes != null) - { - args.Add("x-max-length-bytes", maxLengthBytes.Value); - } - - if (maxPriority != null) - { - args.Add("x-max-priority", maxPriority.Value); - } - - if (ttl != null) - { - args.Add("x-message-ttl", ttl.Value); - } - - if (lazy.GetValueOrDefault()) - { - args.Add("x-queue-mode", "lazy"); - } - - if (!string.IsNullOrEmpty(overflow)) - { - args.Add("x-overflow", overflow); - } - - if (quorum != null && quorum.Enabled.Value) - { - args.Add("x-queue-type", "quorum"); - - if (quorum.DeliveryLimit != null) - { - args.Add("x-delivery-limit", quorum.DeliveryLimit.Value); - } - - if (quorum.InitialQuorumSize != null) - { - args.Add("x-quorum-initial-group-size", quorum.InitialQuorumSize.Value); - } - } - - if (singleActive.GetValueOrDefault()) - { - args.Add("x-single-active-consumer", true); - } - } - - private IExchange BuildExchange(RabbitCommonOptions properties, string exchangeName) - { - try - { - var builder = new ExchangeBuilder(exchangeName, properties.ExchangeType); - builder.Durable(properties.ExchangeDurable.GetValueOrDefault()); - - if (properties.ExchangeAutoDelete.GetValueOrDefault()) - { - builder.AutoDelete(); - } - - if (properties.DelayedExchange.GetValueOrDefault()) - { - builder.Delayed(); - } - - return builder.Build(); - } - catch (Exception e) - { - throw new ProvisioningException("Failed to create exchange object", e); - } - } - - private void DeclareExchange(string rootName, IExchange exchangeArg) - { - IExchange exchange = exchangeArg; - - foreach (IDeclarableCustomizer customizer in Customizers) - { - exchange = (IExchange)customizer.Apply(exchange); - } - - try - { - Admin.DeclareExchange(exchange); - } - catch (RabbitConnectException e) - { - _logger.LogDebug(e, "Declaration of exchange: {exchangeName} deferred - connection not available", exchange.ExchangeName); - } - catch (Exception e) - { - if (_notOurAdminException) - { - _notOurAdminException = false; - throw; - } - - _logger.LogDebug(e, "Declaration of exchange: {exchangeName} deferred", exchange.ExchangeName); - } - - AddToAutoDeclareContext($"{rootName}.exchange", exchange); - } - - private void AddToAutoDeclareContext(string name, object bean) - { - lock (_autoDeclareContext) - { - if (!_autoDeclareContext.ContainsService(name, bean.GetType())) - { - _autoDeclareContext.Register(name, bean); - } - } - } - - private void DeclareBinding(string rootName, Steeltoe.Messaging.RabbitMQ.Configuration.IBinding bindingArg) - { - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = bindingArg; - - foreach (IDeclarableCustomizer customizer in Customizers) - { - binding = (Steeltoe.Messaging.RabbitMQ.Configuration.IBinding)customizer.Apply(binding); - } - - try - { - Admin.DeclareBinding(binding); - } - catch (RabbitConnectException e) - { - _logger.LogDebug(e, "Declaration of binding: {name}.binding deferred - connection not available", rootName); - } - catch (Exception e) - { - if (_notOurAdminException) - { - _notOurAdminException = false; - throw; - } - - _logger.LogDebug(e, "Declaration of binding: {name}.binding deferred", rootName); - } - - AddToAutoDeclareContext($"{rootName}.binding", binding); - } - - private void RemoveSingleton(string name) - { - if (_autoDeclareContext.ContainsService(name)) - { - _autoDeclareContext.Deregister(name); - } - } - - private sealed class GivenNamingStrategy : INamingStrategy - { - private readonly Func _strategy; - - public GivenNamingStrategy(Func strategy) - { - _strategy = strategy; - } - - public string GenerateName() - { - return _strategy(); - } - } - - private sealed class RabbitProducerDestination : IProducerDestination - { - public IExchange Exchange { get; } - - public string Name => Exchange.ExchangeName; - - public Steeltoe.Messaging.RabbitMQ.Configuration.IBinding Binding { get; } - - public RabbitProducerDestination(IExchange exchange, Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding) - { - ArgumentGuard.NotNull(exchange); - - Exchange = exchange; - Binding = binding; - } - - public string GetNameForPartition(int partition) - { - return Exchange.ExchangeName; - } - - public override string ToString() - { - return $"RabbitProducerDestination{{exchange={Exchange}, binding={Binding}}}"; - } - } - - private sealed class RabbitConsumerDestination : IConsumerDestination - { - public string Name { get; } - - public Steeltoe.Messaging.RabbitMQ.Configuration.IBinding Binding { get; } - - public RabbitConsumerDestination(string queueName, Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding) - { - ArgumentGuard.NotNull(queueName); - - Name = queueName; - Binding = binding; - } - - public override string ToString() - { - return $"RabbitConsumerDestination{{queue={Name}, binding={Binding}}}"; - } - } -} diff --git a/src/Stream/src/BinderRabbitMQ/RabbitExpressionEvaluatingInterceptor.cs b/src/Stream/src/BinderRabbitMQ/RabbitExpressionEvaluatingInterceptor.cs deleted file mode 100644 index 9fa5420bed..0000000000 --- a/src/Stream/src/BinderRabbitMQ/RabbitExpressionEvaluatingInterceptor.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Stream.Binder.RabbitMQ; - -public class RabbitExpressionEvaluatingInterceptor : IChannelInterceptor -{ - public const string RoutingKeyHeader = "scst_routingKey"; - public const string DelayHeader = "scst_delay"; - - private IExpressionParser Parser { get; } = new SpelExpressionParser(); - - private IExpression RoutingKeyExpression { get; } - - private IExpression DelayExpression { get; } - - private IEvaluationContext EvaluationContext { get; } - - public int Order => 0; - - public RabbitExpressionEvaluatingInterceptor(IExpression routingKeyExpression, IExpression delayExpression, IEvaluationContext evaluationContext) - { - if (routingKeyExpression == null && delayExpression == null) - { - throw new ArgumentException("At least one expression is required."); - } - - ArgumentGuard.NotNull(evaluationContext); - - RoutingKeyExpression = routingKeyExpression; - DelayExpression = delayExpression; - EvaluationContext = evaluationContext; - } - - public void AfterReceiveCompletion(IMessage message, IMessageChannel channel, Exception exception) - { - // Do nothing - } - - public void AfterSendCompletion(IMessage message, IMessageChannel channel, bool sent, Exception exception) - { - // Do nothing - } - - public IMessage PostReceive(IMessage message, IMessageChannel channel) - { - return message; - } - - public void PostSend(IMessage message, IMessageChannel channel, bool sent) - { - // Do nothing - } - - public bool PreReceive(IMessageChannel channel) - { - return true; - } - - public IMessage PreSend(IMessage message, IMessageChannel channel) - { - IntegrationMessageBuilder builder = IntegrationMessageBuilder.FromMessage(message); - - if (RoutingKeyExpression != null) - { - builder.SetHeader(RoutingKeyHeader, RoutingKeyExpression.GetValue(EvaluationContext, message)); - } - - if (DelayExpression != null) - { - builder.SetHeader(DelayHeader, DelayExpression.GetValue(EvaluationContext, message)); - } - - return builder.Build(); - } -} diff --git a/src/Stream/src/BinderRabbitMQ/RabbitMessageChannelBinder.cs b/src/Stream/src/BinderRabbitMQ/RabbitMessageChannelBinder.cs deleted file mode 100644 index ca5c9d2e59..0000000000 --- a/src/Stream/src/BinderRabbitMQ/RabbitMessageChannelBinder.cs +++ /dev/null @@ -1,751 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using System.Text.RegularExpressions; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using RabbitMQ.Client; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Common.Util; -using Steeltoe.Integration; -using Steeltoe.Integration.Acks; -using Steeltoe.Integration.RabbitMQ.Inbound; -using Steeltoe.Integration.RabbitMQ.Outbound; -using Steeltoe.Integration.RabbitMQ.Support; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.RabbitMQ; -using Steeltoe.Messaging.RabbitMQ.Batch; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Listener.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Retry; -using Steeltoe.Messaging.RabbitMQ.Support; -using Steeltoe.Messaging.RabbitMQ.Support.Converter; -using Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Steeltoe.Stream.Binder.RabbitMQ.Provisioning; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Provisioning; -using IntegrationChannel = Steeltoe.Integration.Channel; -using MessagingSupport = Steeltoe.Messaging.Support; -using SteeltoeConnectionFactory = Steeltoe.Messaging.RabbitMQ.Connection.IConnectionFactory; - -namespace Steeltoe.Stream.Binder.RabbitMQ; - -public class RabbitMessageChannelBinder : AbstractPollableMessageSourceBinder -{ - private static readonly SimplePassThroughMessageConverter PassThoughConverter = new(); - private static readonly IMessageHeadersConverter InboundMessagePropertiesConverter = new DefaultBinderMessagePropertiesConverter(); - private static readonly RabbitMessageHeaderErrorMessageStrategy ErrorMessageStrategy = new(); - private static readonly Regex InterceptorNeededPattern = new("(Payload|#root|#this)"); - - protected ILogger logger; - - protected RabbitExchangeQueueProvisioner ProvisioningProvider => (RabbitExchangeQueueProvisioner)InnerProvisioningProvider; - - public SteeltoeConnectionFactory ConnectionFactory { get; } - - public IOptionsMonitor RabbitConnectionOptions { get; } - - public RabbitBinderOptions BinderOptions { get; } - - public RabbitBindingsOptions BindingsOptions { get; } - - public IMessagePostProcessor DecompressingPostProcessor { get; set; } = new DelegatingDecompressingPostProcessor(); - - public IMessagePostProcessor CompressingPostProcessor { get; set; } = new GZipPostProcessor(); - - public string[] AdminAddresses { get; set; } - - public string[] Nodes { get; set; } - - public bool Clustered => Nodes?.Length > 1; - - public override string ServiceName { get; set; } - - public RabbitMessageChannelBinder(IApplicationContext context, ILogger logger, SteeltoeConnectionFactory connectionFactory, - IOptionsMonitor rabbitOptions, IOptionsMonitor binderOptions, - IOptionsMonitor bindingsOptions, RabbitExchangeQueueProvisioner provisioningProvider) - : this(context, logger, connectionFactory, rabbitOptions, binderOptions, bindingsOptions, provisioningProvider, null, null) - { - } - - public RabbitMessageChannelBinder(IApplicationContext context, ILogger logger, SteeltoeConnectionFactory connectionFactory, - IOptionsMonitor rabbitOptions, IOptionsMonitor binderOptions, - IOptionsMonitor bindingsOptions, RabbitExchangeQueueProvisioner provisioningProvider, - IListenerContainerCustomizer containerCustomizer) - : this(context, logger, connectionFactory, rabbitOptions, binderOptions, bindingsOptions, provisioningProvider, containerCustomizer, null) - { - } - - public RabbitMessageChannelBinder(IApplicationContext context, ILogger logger, SteeltoeConnectionFactory connectionFactory, - IOptionsMonitor rabbitOptions, IOptionsMonitor binderOptions, - IOptionsMonitor bindingsOptions, RabbitExchangeQueueProvisioner provisioningProvider, - IListenerContainerCustomizer containerCustomizer, IMessageSourceCustomizer sourceCustomizer) - : base(context, Array.Empty(), provisioningProvider, containerCustomizer, sourceCustomizer, logger) - { - ArgumentGuard.NotNull(connectionFactory); - ArgumentGuard.NotNull(rabbitOptions); - - this.logger = logger; - ConnectionFactory = connectionFactory; - RabbitConnectionOptions = rabbitOptions; - BinderOptions = binderOptions?.CurrentValue; - BindingsOptions = bindingsOptions?.CurrentValue; - ServiceName = "rabbitBinder"; - } - - public RabbitConsumerOptions GetConsumerOptions(string channelName) - { - return BindingsOptions.GetRabbitConsumerOptions(channelName); - } - - public RabbitProducerOptions GetProducerOptions(string channelName) - { - return BindingsOptions.GetRabbitProducerOptions(channelName); - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - ConnectionFactory.Destroy(); - } - - base.Dispose(disposing); - } - - protected override IMessageHandler CreateProducerMessageHandler(IProducerDestination destination, IProducerOptions producerProperties, - IMessageChannel errorChannel) - { - if (producerProperties.HeaderMode == HeaderMode.EmbeddedHeaders) - { - throw new InvalidOperationException("The RabbitMQ binder does not support embedded headers since RabbitMQ supports headers natively"); - } - - RabbitProducerOptions extendedProperties = BindingsOptions.GetRabbitProducerOptions(producerProperties.BindingName); - string prefix = extendedProperties.Prefix; - string exchangeName = destination.Name; - string destinationName = string.IsNullOrEmpty(prefix) ? exchangeName : exchangeName[prefix.Length..]; - RabbitTemplate template = BuildRabbitTemplate(extendedProperties, errorChannel != null || extendedProperties.UseConfirmHeader.GetValueOrDefault()); - - var endpoint = new RabbitOutboundEndpoint(ApplicationContext, template, logger) - { - ExchangeName = exchangeName - }; - - bool expressionInterceptorNeeded = ExpressionInterceptorNeeded(extendedProperties); - string routingKeyExpression = extendedProperties.RoutingKeyExpression; - - if (!producerProperties.IsPartitioned) - { - UpdateRoutingKeyExpressionForNonPartitioned(endpoint, destinationName, expressionInterceptorNeeded, routingKeyExpression); - } - else - { - UpdateRoutingKeyExpressionForPartitioned(destinationName, endpoint, expressionInterceptorNeeded, routingKeyExpression); - } - - if (extendedProperties.DelayExpression != null) - { - if (expressionInterceptorNeeded) - { - endpoint.SetDelayExpressionString($"Headers['{RabbitExpressionEvaluatingInterceptor.DelayHeader}']"); - } - else - { - endpoint.DelayExpression = ExpressionParser.ParseExpression(extendedProperties.DelayExpression); - } - } - - DefaultRabbitHeaderMapper mapper = DefaultRabbitHeaderMapper.GetOutboundMapper(logger); - - var headerPatterns = new List(extendedProperties.HeaderPatterns.Count + 3) - { - $"!{BinderHeaders.PartitionHeader}", - $"!{IntegrationMessageHeaderAccessor.SourceData}", - $"!{IntegrationMessageHeaderAccessor.DeliveryAttempt}" - }; - - headerPatterns.AddRange(extendedProperties.HeaderPatterns); - - mapper.SetRequestHeaderNames(headerPatterns.ToArray()); - endpoint.HeaderMapper = mapper; - - endpoint.DefaultDeliveryMode = extendedProperties.DeliveryMode.Value; - - if (errorChannel != null) - { - CheckConnectionFactoryIsErrorCapable(); - endpoint.ReturnChannel = errorChannel; - - if (!extendedProperties.UseConfirmHeader.GetValueOrDefault()) - { - endpoint.ConfirmNackChannel = errorChannel; - - string ackChannelBeanName = !string.IsNullOrEmpty(extendedProperties.ConfirmAckChannel) - ? extendedProperties.ConfirmAckChannel - : IntegrationContextUtils.NullChannelBeanName; - - if (ackChannelBeanName != IntegrationContextUtils.NullChannelBeanName && - !ApplicationContext.ContainsService(ackChannelBeanName)) - { - var ackChannel = new IntegrationChannel.DirectChannel(ApplicationContext); - ApplicationContext.Register(ackChannelBeanName, ackChannel); - } - - endpoint.ConfirmAckChannelName = ackChannelBeanName; - endpoint.SetConfirmCorrelationExpressionString("#root"); - } - else - { - if (!string.IsNullOrEmpty(extendedProperties.ConfirmAckChannel)) - { - throw new InvalidOperationException("You cannot specify a 'confirmAckChannel' when 'useConfirmHeader' is true"); - } - } - - endpoint.ErrorMessageStrategy = new DefaultErrorMessageStrategy(); - } - - endpoint.HeadersMappedLast = true; - endpoint.Initialize(); - return endpoint; - } - - protected override void PostProcessOutputChannel(IMessageChannel outputChannel, IProducerOptions producerOptions) - { - RabbitProducerOptions rabbitProducerOptions = BindingsOptions.GetRabbitProducerOptions(producerOptions.BindingName); - - if (ExpressionInterceptorNeeded(rabbitProducerOptions)) - { - IExpression rkExpression = null; - IExpression delayExpression = null; - - if (rabbitProducerOptions.RoutingKeyExpression != null) - { - rkExpression = ExpressionParser.ParseExpression(rabbitProducerOptions.RoutingKeyExpression); - } - - if (rabbitProducerOptions.DelayExpression != null) - { - delayExpression = ExpressionParser.ParseExpression(rabbitProducerOptions.DelayExpression); - } - - ((IntegrationChannel.AbstractMessageChannel)outputChannel).AddInterceptor(0, - new RabbitExpressionEvaluatingInterceptor(rkExpression, delayExpression, EvaluationContext)); - } - } - - protected override IMessageProducer CreateConsumerEndpoint(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - if (consumerOptions.HeaderMode == HeaderMode.EmbeddedHeaders) - { - throw new InvalidOperationException("The RabbitMQ binder does not support embedded headers since RabbitMQ supports headers natively"); - } - - string destinationName = destination.Name; - - RabbitConsumerOptions properties = BindingsOptions.GetRabbitConsumerOptions(consumerOptions.BindingName); - - var listenerContainer = new DirectMessageListenerContainer(ApplicationContext, ConnectionFactory) - { - AcknowledgeMode = properties.AcknowledgeMode.GetValueOrDefault(AcknowledgeMode.Auto), - IsChannelTransacted = properties.Transacted.GetValueOrDefault(), - DefaultRequeueRejected = properties.RequeueRejected ?? true - }; - - int concurrency = consumerOptions.Concurrency; - concurrency = concurrency > 0 ? concurrency : 1; - listenerContainer.ConsumersPerQueue = concurrency; - listenerContainer.PrefetchCount = properties.Prefetch ?? listenerContainer.PrefetchCount; - listenerContainer.RecoveryInterval = properties.RecoveryInterval ?? listenerContainer.RecoveryInterval; - IEnumerable queueNames = destinationName.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()); - listenerContainer.SetQueueNames(queueNames.ToArray()); - listenerContainer.SetAfterReceivePostProcessors(DecompressingPostProcessor); - listenerContainer.MessageHeadersConverter = InboundMessagePropertiesConverter; - listenerContainer.Exclusive = properties.Exclusive ?? listenerContainer.Exclusive; - listenerContainer.MissingQueuesFatal = properties.MissingQueuesFatal ?? listenerContainer.MissingQueuesFatal; - - if (properties.FailedDeclarationRetryInterval != null) - { - listenerContainer.FailedDeclarationRetryInterval = properties.FailedDeclarationRetryInterval.Value; - } - - ListenerContainerCustomizer?.Configure(listenerContainer, destination.Name, group); - - if (!string.IsNullOrEmpty(properties.ConsumerTagPrefix)) - { - listenerContainer.ConsumerTagStrategy = new RabbitBinderConsumerTagStrategy(properties.ConsumerTagPrefix); - } - - listenerContainer.Initialize(); - - var adapter = new RabbitInboundChannelAdapter(ApplicationContext, listenerContainer, logger) - { - BindSourceMessage = true, - ServiceName = $"inbound.{destinationName}" - }; - - ErrorInfrastructure errorInfrastructure = RegisterErrorInfrastructure(destination, group, consumerOptions, logger); - - if (consumerOptions.MaxAttempts > 1) - { - adapter.RetryTemplate = BuildRetryTemplate(consumerOptions); - adapter.RecoveryCallback = errorInfrastructure.Recoverer; - } - else - { - adapter.ErrorMessageStrategy = ErrorMessageStrategy; - adapter.ErrorChannel = errorInfrastructure.ErrorChannel; - } - - adapter.MessageConverter = PassThoughConverter; - return adapter; - } - - protected override PolledConsumerResources CreatePolledConsumerResources(string name, string group, IConsumerDestination destination, - IConsumerOptions consumerOptions) - { - if (consumerOptions.Multiplex) - { - throw new InvalidOperationException("The Polled MessageSource does not currently support multiple queues"); - } - - var source = new RabbitMessageSource(ApplicationContext, ConnectionFactory, destination.Name) - { - RawMessageHeader = true - }; - - MessageSourceCustomizer?.Configure(source, destination.Name, group); - return new PolledConsumerResources(source, RegisterErrorInfrastructure(destination, group, consumerOptions, true, logger)); - } - - protected override void PostProcessPollableSource(DefaultPollableMessageSource bindingTarget) - { - bindingTarget.AttributeProvider = (accessor, message) => - { - var rawMessage = message.Headers.Get(RabbitMessageHeaderErrorMessageStrategy.AmqpRawMessage); - - if (rawMessage != null) - { - accessor.SetAttribute(RabbitMessageHeaderErrorMessageStrategy.AmqpRawMessage, rawMessage); - } - }; - } - - protected override IErrorMessageStrategy GetErrorMessageStrategy() - { - return ErrorMessageStrategy; - } - - protected override IMessageHandler GetErrorMessageHandler(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - RabbitConsumerOptions properties = BindingsOptions.GetRabbitConsumerOptions(consumerOptions.BindingName); - - if (properties.RepublishToDlq.Value) - { - return new RepublishToDlqErrorMessageHandler(this, properties, logger); - } - - if (consumerOptions.MaxAttempts > 1) - { - return new RejectingErrorMessageHandler(logger); - } - - return base.GetErrorMessageHandler(destination, group, consumerOptions); - } - - protected override IMessageHandler GetPolledConsumerErrorMessageHandler(IConsumerDestination destination, string group, IConsumerOptions consumerProperties) - { - IMessageHandler handler = GetErrorMessageHandler(destination, group, consumerProperties); - - if (handler != null) - { - return handler; - } - - IMessageHandler superHandler = base.GetErrorMessageHandler(destination, group, consumerProperties); - RabbitConsumerOptions properties = BindingsOptions.GetRabbitConsumerOptions(consumerProperties.BindingName); - return new DefaultPolledConsumerErrorMessageHandler(superHandler, properties, logger); - } - - protected override string GetErrorsBaseName(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - return $"{destination.Name}.errors"; - } - - protected override void AfterUnbindConsumer(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - ProvisioningProvider.CleanAutoDeclareContext(destination, consumerOptions); - } - - private static bool ExpressionInterceptorNeeded(RabbitProducerOptions extendedProperties) - { - string rkExpression = extendedProperties.RoutingKeyExpression; - string delayExpression = extendedProperties.DelayExpression; - - return (rkExpression != null && InterceptorNeededPattern.IsMatch(rkExpression)) || - (delayExpression != null && InterceptorNeededPattern.IsMatch(delayExpression)); - } - - private void UpdateRoutingKeyExpressionForPartitioned(string destinationName, RabbitOutboundEndpoint endpoint, bool expressionInterceptorNeeded, - string routingKeyExpression) - { - if (routingKeyExpression == null) - { - endpoint.RoutingKeyExpression = BuildPartitionRoutingExpression(destinationName, false); - } - else - { - endpoint.RoutingKeyExpression = expressionInterceptorNeeded - ? BuildPartitionRoutingExpression($"Headers['{RabbitExpressionEvaluatingInterceptor.RoutingKeyHeader}']", true) - : BuildPartitionRoutingExpression(routingKeyExpression, true); - } - } - - private void UpdateRoutingKeyExpressionForNonPartitioned(RabbitOutboundEndpoint endpoint, string destinationName, bool expressionInterceptorNeeded, - string routingKeyExpression) - { - if (routingKeyExpression == null) - { - endpoint.RoutingKey = destinationName; - } - else - { - if (expressionInterceptorNeeded) - { - endpoint.SetRoutingKeyExpressionString($"Headers['{RabbitExpressionEvaluatingInterceptor.RoutingKeyHeader}']"); - } - else - { - endpoint.RoutingKeyExpression = ExpressionParser.ParseExpression(routingKeyExpression); - } - } - } - - private void CheckConnectionFactoryIsErrorCapable() - { - if (ConnectionFactory is not CachingConnectionFactory factory) - { - logger.LogWarning("Unknown connection factory type, cannot determine error capabilities: {type}", ConnectionFactory.GetType()); - } - else - { - CachingConnectionFactory ccf = factory; - - if (!ccf.IsPublisherConfirms && !ccf.IsPublisherReturns) - { - logger.LogWarning("Producer error channel is enabled, but the connection factory is not configured for " + - "returns or confirms; the error channel will receive no messages"); - } - else if (!ccf.IsPublisherConfirms) - { - logger.LogInformation("Producer error channel is enabled, but the connection factory is only configured to " + - "handle returned messages; negative acks will not be reported"); - } - else if (!ccf.IsPublisherReturns) - { - logger.LogInformation("Producer error channel is enabled, but the connection factory is only configured to " + - "handle negatively acked messages; returned messages will not be reported"); - } - } - } - - private IExpression BuildPartitionRoutingExpression(string expressionRoot, bool rootIsExpression) - { - string partitionRoutingExpression = rootIsExpression - ? $"{expressionRoot} + '-' + Headers['{BinderHeaders.PartitionHeader}']" - : $"'{expressionRoot}-' + Headers['{BinderHeaders.PartitionHeader}']"; - - return ExpressionParser.ParseExpression(partitionRoutingExpression); - } - - private RabbitTemplate BuildRabbitTemplate(RabbitProducerOptions properties, bool mandatory) - { - RabbitTemplate rabbitTemplate; - - if (properties.BatchingEnabled.Value) - { - var batchingStrategy = new SimpleBatchingStrategy(properties.BatchSize.Value, properties.BatchBufferLimit.Value, properties.BatchTimeout.Value); - rabbitTemplate = new BatchingRabbitTemplate(RabbitConnectionOptions, null, batchingStrategy); - } - else - { - rabbitTemplate = new RabbitTemplate(); - } - - rabbitTemplate.MessageConverter = PassThoughConverter; - rabbitTemplate.IsChannelTransacted = properties.Transacted.Value; - rabbitTemplate.ConnectionFactory = ConnectionFactory; - rabbitTemplate.UsePublisherConnection = true; - - if (properties.Compress.Value) - { - rabbitTemplate.SetBeforePublishPostProcessors(CompressingPostProcessor); - } - - rabbitTemplate.Mandatory = mandatory; // returned messages - - if (RabbitConnectionOptions != null && RabbitConnectionOptions.CurrentValue.Template.Retry.Enabled) - { - RabbitOptions.RetryOptions retry = RabbitConnectionOptions.CurrentValue.Template.Retry; - - var retryTemplate = new PollyRetryTemplate(retry.MaxAttempts, (int)retry.InitialInterval.TotalMilliseconds, - (int)retry.MaxInterval.TotalMilliseconds, retry.Multiplier, logger); - - rabbitTemplate.RetryTemplate = retryTemplate; - } - - return rabbitTemplate; - } - - private sealed class DefaultPolledConsumerErrorMessageHandler : IMessageHandler - { - private readonly IMessageHandler _superHandler; - private readonly RabbitConsumerOptions _properties; - private readonly ILogger _logger; - - public string ServiceName { get; set; } - - public DefaultPolledConsumerErrorMessageHandler(IMessageHandler superHandler, RabbitConsumerOptions properties, ILogger logger) - { - _superHandler = superHandler; - _properties = properties; - _logger = logger; - ServiceName = $"{GetType()}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - var amqpMessage = message.Headers.Get(RabbitMessageHeaderErrorMessageStrategy.AmqpRawMessage); - - if (message is not MessagingSupport.ErrorMessage errorMessage) - { - _logger?.LogError("Expected an ErrorMessage, not a {type} for: {message}", message.GetType(), message); - } - else if (amqpMessage == null) - { - if (_superHandler != null) - { - _superHandler.HandleMessage(message); - } - } - else - { - if (errorMessage.Payload is MessagingException payload) - { - IAcknowledgmentCallback ack = StaticMessageHeaderAccessor.GetAcknowledgmentCallback(payload.FailedMessage); - - if (ack != null) - { - ack.Acknowledge(_properties.RequeueRejected.Value ? Status.Requeue : Status.Reject); - } - } - } - } - } - - private sealed class RejectingErrorMessageHandler : IMessageHandler - { - private readonly RejectAndDoNotRequeueRecoverer _recoverer = new(); - private readonly ILogger _logger; - - public string ServiceName { get; set; } - - public RejectingErrorMessageHandler(ILogger logger) - { - ServiceName = $"{GetType()}@{GetHashCode()}"; - _logger = logger; - } - - public void HandleMessage(IMessage message) - { - if (message is not MessagingSupport.ErrorMessage errorMessage) - { - _logger?.LogError("Expected an ErrorMessage, not a {type} for: {message}", message.GetType(), message); - throw new ListenerExecutionFailedException($"Unexpected error message {message}", new RabbitRejectAndDoNotRequeueException(string.Empty), null); - } - - _recoverer.Recover(errorMessage, errorMessage.Payload); - } - } - - private sealed class RepublishToDlqErrorMessageHandler : IMessageHandler - { - private readonly RabbitMessageChannelBinder _binder; - private readonly RabbitTemplate _template; - private readonly string _exchange; - private readonly string _routingKey; - private readonly int _frameMaxHeadroom; - private readonly RabbitConsumerOptions _properties; - private readonly ILogger _logger; - private int _maxStackTraceLength = -1; - - public string ServiceName { get; set; } - - public RepublishToDlqErrorMessageHandler(RabbitMessageChannelBinder binder, RabbitConsumerOptions properties, ILogger logger) - { - _binder = binder; - - _template = new RabbitTemplate(_binder.ConnectionFactory) - { - UsePublisherConnection = true - }; - - _exchange = GetDeadLetterExchangeName(properties); - _routingKey = properties.DeadLetterRoutingKey; - _frameMaxHeadroom = properties.FrameMaxHeadroom.Value; - _properties = properties; - _logger = logger; - ServiceName = $"{GetType()}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - if (message.Headers[IntegrationMessageHeaderAccessor.SourceData] is not IMessage errorMessage) - { - _logger?.LogError("Expected an ErrorMessage, not a {type} for: {message}", message.GetType(), message); - return; - } - - var cause = message.Payload as Exception; - - if (!ShouldRepublish(cause)) - { - _logger.LogDebug("Skipping republish of: {message}", message); - return; - } - - string stackTraceAsString = cause.StackTrace; - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(errorMessage); - - if (_maxStackTraceLength < 0) - { - int result = RabbitUtils.GetMaxFrame(_binder.ConnectionFactory); - - if (result > 0) - { - _maxStackTraceLength = result - _frameMaxHeadroom; - } - } - - if (_maxStackTraceLength > 0 && stackTraceAsString.Length > _maxStackTraceLength) - { - stackTraceAsString = stackTraceAsString.Substring(0, _maxStackTraceLength); - - _logger.LogWarning(cause, - "Stack trace in republished message header truncated due to frame_max limitations; consider increasing frame_max on the broker or reduce the stack trace depth"); - } - - accessor.SetHeader(RepublishMessageRecoverer.XExceptionStacktrace, stackTraceAsString); - accessor.SetHeader(RepublishMessageRecoverer.XExceptionMessage, cause.InnerException != null ? cause.InnerException.Message : cause.Message); - accessor.SetHeader(RepublishMessageRecoverer.XOriginalExchange, accessor.ReceivedExchange); - accessor.SetHeader(RepublishMessageRecoverer.XOriginalRoutingKey, accessor.ReceivedRoutingKey); - - if (_properties.RepublishDeliveryMode != null) - { - accessor.DeliveryMode = _properties.RepublishDeliveryMode.Value; - } - - _template.Send(_exchange, _routingKey ?? accessor.ConsumerQueue, errorMessage); - - if (_properties.AcknowledgeMode == AcknowledgeMode.Manual) - { - ulong deliveryTag = errorMessage.Headers.DeliveryTag().GetValueOrDefault(); - var channel = errorMessage.Headers[RabbitMessageHeaders.Channel] as IModel; - channel.BasicAck(deliveryTag, false); - } - } - - private static bool ShouldRepublish(Exception exception) - { - Exception cause = exception; - - while (cause != null && cause is not RabbitRejectAndDoNotRequeueException && cause is not ImmediateAcknowledgeException) - { - cause = cause.InnerException; - } - - return cause is not ImmediateAcknowledgeException; - } - - private static string GetDeadLetterExchangeName(RabbitCommonOptions properties) - { - if (properties.DeadLetterExchange == null) - { - return ApplyPrefix(properties.Prefix, RabbitCommonOptions.DeadLetterExchangeName); - } - - return properties.DeadLetterExchange; - } - } - - private sealed class RabbitBinderConsumerTagStrategy : IConsumerTagStrategy - { - private readonly string _prefix; - - private readonly AtomicInteger _index = new(); - - public string ServiceName { get; set; } = nameof(RabbitBinderConsumerTagStrategy); - - public RabbitBinderConsumerTagStrategy(string prefix) - { - _prefix = prefix; - } - - public string CreateConsumerTag(string queue) - { - return $"{_prefix}#{_index.GetAndIncrement()}"; - } - } - - private sealed class DefaultBinderMessagePropertiesConverter : DefaultMessageHeadersConverter - { - public override IMessageHeaders ToMessageHeaders(IBasicProperties source, Envelope envelope, Encoding charset) - { - IMessageHeaders result = base.ToMessageHeaders(source, envelope, charset); - RabbitHeaderAccessor accessor = RabbitHeaderAccessor.GetMutableAccessor(result); - accessor.DeliveryMode = null; - return accessor.MessageHeaders; - } - } - - private sealed class SimplePassThroughMessageConverter : AbstractMessageConverter - { - private readonly SimpleMessageConverter _converter = new(); - - public override string ServiceName - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - public override object FromMessage(IMessage message, Type targetType, object conversionHint) - { - return message.Payload; - } - - protected override IMessage CreateMessage(object payload, IMessageHeaders headers, object conversionHint) - { - if (payload is byte[] payloadBytes) - { - return Message.Create(payloadBytes, headers); - } - - // just for safety (backwards compatibility) - return _converter.ToMessage(payload, headers); - } - } -} diff --git a/src/Stream/src/BinderRabbitMQ/Startup.cs b/src/Stream/src/BinderRabbitMQ/Startup.cs deleted file mode 100644 index 9209687d57..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Startup.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Binder.RabbitMQ; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Steeltoe.Stream.Binder.RabbitMQ.Provisioning; - -[assembly: Binder("rabbit", typeof(Startup))] - -namespace Steeltoe.Stream.Binder.RabbitMQ; - -public class Startup -{ - public IConfiguration Configuration { get; } - - public bool ConfigureServicesInvoked { get; set; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.ConfigureRabbitOptions(Configuration); - IConfigurationSection c = Configuration.GetSection(RabbitBindingsOptions.Prefix); - services.Configure(c); - services.Configure(o => o.PostProcess()); - - services.Configure(Configuration.GetSection(RabbitBinderOptions.Prefix)); - - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - - services.AddSingleton(p => - { - return p.GetRequiredService(); - }); - } -} diff --git a/src/Stream/src/BinderRabbitMQ/Steeltoe.Stream.Binder.RabbitMQ.csproj b/src/Stream/src/BinderRabbitMQ/Steeltoe.Stream.Binder.RabbitMQ.csproj deleted file mode 100644 index 4a5a960e8b..0000000000 --- a/src/Stream/src/BinderRabbitMQ/Steeltoe.Stream.Binder.RabbitMQ.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - net8.0;net6.0 - Steeltoe Stream RabbitMQ Binder - Streams, ASPNET Core, Spring, Spring Cloud, RabbitMQ, Binder - true - - - - - - - - - - diff --git a/src/Stream/src/Stream/Attributes/BinderAttribute.cs b/src/Stream/src/Stream/Attributes/BinderAttribute.cs deleted file mode 100644 index 472bcf7bb4..0000000000 --- a/src/Stream/src/Stream/Attributes/BinderAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Attributes; - -[AttributeUsage(AttributeTargets.Assembly)] -public sealed class BinderAttribute : Attribute -{ - public string Name { get; set; } - - public string ConfigureType { get; set; } - - public BinderAttribute() - { - Name = string.Empty; - ConfigureType = string.Empty; - } - - public BinderAttribute(string name, Type configureType) - { - Name = name; - ConfigureType = configureType.AssemblyQualifiedName; - } -} diff --git a/src/Stream/src/Stream/Binder/AbstractBinder.cs b/src/Stream/src/Stream/Binder/AbstractBinder.cs deleted file mode 100644 index 41694c5f1c..0000000000 --- a/src/Stream/src/Stream/Binder/AbstractBinder.cs +++ /dev/null @@ -1,137 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Retry; -using Steeltoe.Common.RetryPolly; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binder; - -public abstract class AbstractBinder : IBinder -{ - private const string GroupIndexDelimiter = "."; - - private readonly IApplicationContext _context; - private readonly ILogger _logger; - - private IEvaluationContext _evaluationContext; - private IExpressionParser _expressionParser; - - protected virtual IEvaluationContext EvaluationContext - { - get - { - _evaluationContext ??= _context.GetService() ?? new StandardEvaluationContext(); - return _evaluationContext; - } - set => _evaluationContext = value; - } - - protected virtual IExpressionParser ExpressionParser - { - get - { - _expressionParser ??= new SpelExpressionParser(); - return _expressionParser; - } - set => _expressionParser = value; - } - - public virtual IApplicationContext ApplicationContext => _context; - - public abstract string ServiceName { get; set; } - - public abstract Type TargetType { get; } - - protected AbstractBinder(IApplicationContext context, ILogger logger) - { - _context = context; - _logger = logger; - } - - public static string ApplyPrefix(string prefix, string name) - { - return prefix + name; - } - - public static string ConstructDlqName(string name) - { - return $"{name}.dlq"; - } - - public virtual IBinding BindConsumer(string name, string group, T inboundTarget, IConsumerOptions consumerOptions) - { - if (string.IsNullOrEmpty(group) && consumerOptions.IsPartitioned) - { - throw new ArgumentException("A consumer group is required for a partitioned subscription.", nameof(group)); - } - - return DoBindConsumer(name, group, inboundTarget, consumerOptions); - } - - public virtual IBinding BindConsumer(string name, string group, object inboundTarget, IConsumerOptions consumerOptions) - { - return DoBindConsumer(name, group, (T)inboundTarget, consumerOptions); - } - - public virtual IBinding BindProducer(string name, T outboundTarget, IProducerOptions producerOptions) - { - return DoBindProducer(name, outboundTarget, producerOptions); - } - - public virtual IBinding BindProducer(string name, object outboundTarget, IProducerOptions producerOptions) - { - return DoBindProducer(name, (T)outboundTarget, producerOptions); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - } - - protected abstract IBinding DoBindProducer(string name, T outboundTarget, IProducerOptions producerOptions); - - protected abstract IBinding DoBindConsumer(string name, string group, T inputTarget, IConsumerOptions consumerOptions); - - protected virtual string GroupedName(string name, string group) - { - return name + GroupIndexDelimiter + (!string.IsNullOrEmpty(group) ? group : "default"); - } - - protected RetryTemplate BuildRetryTemplate(IConsumerOptions options) - { - return new PollyRetryTemplate(GetRetryableExceptions(options.RetryableExceptions), options.MaxAttempts, options.DefaultRetryable, - options.BackOffInitialInterval, options.BackOffMaxInterval, options.BackOffMultiplier, _logger); - } - - protected Dictionary GetRetryableExceptions(List exceptionList) - { - var dict = new Dictionary(); - - foreach (string exception in exceptionList) - { - if (exception[0] == '!') - { - var type = Type.GetType(exception[1..], true); - dict.Add(type, false); - } - else - { - dict.Add(Type.GetType(exception), true); - } - } - - return dict; - } -} diff --git a/src/Stream/src/Stream/Binder/AbstractBinding.cs b/src/Stream/src/Stream/Binder/AbstractBinding.cs deleted file mode 100644 index 26ed7bd607..0000000000 --- a/src/Stream/src/Stream/Binder/AbstractBinding.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -public abstract class AbstractBinding : IBinding -{ - public virtual IDictionary ExtendedInfo => new Dictionary(); - - public virtual string Name => null; - - public virtual string BindingName => null; - - public virtual bool IsInput => - throw new InvalidOperationException($"Binding implementation `{GetType().Name}` must implement this operation before it is called"); - - public virtual bool IsRunning => false; - - public virtual Task PauseAsync() - { - return StopAsync(); - } - - public virtual Task ResumeAsync() - { - return StartAsync(); - } - - public virtual Task StartAsync() - { - return Task.CompletedTask; - } - - public virtual Task StopAsync() - { - return Task.CompletedTask; - } - - public virtual Task UnbindAsync() - { - return Task.CompletedTask; - } -} diff --git a/src/Stream/src/Stream/Binder/AbstractMessageChannelBinder.cs b/src/Stream/src/Stream/Binder/AbstractMessageChannelBinder.cs deleted file mode 100644 index 9826af2dbe..0000000000 --- a/src/Stream/src/Stream/Binder/AbstractMessageChannelBinder.cs +++ /dev/null @@ -1,708 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text.Json; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Integration; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Provisioning; -using AbstractMessageChannel = Steeltoe.Integration.Channel.AbstractMessageChannel; -using AbstractSubscribableChannel = Steeltoe.Integration.Channel.AbstractSubscribableChannel; - -namespace Steeltoe.Stream.Binder; - -public abstract class AbstractMessageChannelBinder : AbstractBinder -{ - private readonly ILogger _logger; - protected readonly IProvisioningProvider InnerProvisioningProvider; - protected readonly EmbeddedHeadersChannelInterceptor CurrentEmbeddedHeadersChannelInterceptor = new(); - protected readonly string[] HeadersToEmbed; - protected bool producerBindingExist; - - protected virtual IListenerContainerCustomizer ListenerContainerCustomizer { get; } - - protected virtual IMessageSourceCustomizer MessageSourceCustomizer { get; } - - public override Type TargetType { get; } = typeof(IMessageChannel); - - protected AbstractMessageChannelBinder(IApplicationContext context, string[] headersToEmbed, IProvisioningProvider provisioningProvider, ILogger logger) - : this(context, headersToEmbed, provisioningProvider, null, null, logger) - { - _logger = logger; - } - - protected AbstractMessageChannelBinder(IApplicationContext context, string[] headersToEmbed, IProvisioningProvider provisioningProvider, - IListenerContainerCustomizer containerCustomizer, IMessageSourceCustomizer sourceCustomizer, ILogger logger) - : base(context, logger) - { - HeadersToEmbed = headersToEmbed ?? Array.Empty(); - InnerProvisioningProvider = provisioningProvider; - ListenerContainerCustomizer = containerCustomizer; - MessageSourceCustomizer = sourceCustomizer; - _logger = logger; - } - - protected override IBinding DoBindProducer(string name, IMessageChannel outboundTarget, IProducerOptions producerOptions) - { - if (outboundTarget is not ISubscribableChannel subscribableChannel) - { - throw new ArgumentException($"Binding is supported only for {nameof(ISubscribableChannel)} instances.", nameof(outboundTarget)); - } - - IMessageHandler producerMessageHandler; - IProducerDestination producerDestination; - - try - { - producerDestination = InnerProvisioningProvider.ProvisionProducerDestination(name, producerOptions); - ISubscribableChannel errorChannel = producerOptions.ErrorChannelEnabled ? RegisterErrorInfrastructure(producerDestination) : null; - producerMessageHandler = CreateProducerMessageHandler(producerDestination, producerOptions, subscribableChannel, errorChannel); - } - catch (Exception e) when (e is BinderException || e is ProvisioningException) - { - throw; - } - catch (Exception e) - { - throw new BinderException("Exception thrown while building outbound endpoint", e); - } - - if (producerOptions.AutoStartup && producerMessageHandler is ILifecycle producerMsgHandlerLifecycle) - { - producerMsgHandlerLifecycle.StartAsync().GetAwaiter().GetResult(); - } - - PostProcessOutputChannel(subscribableChannel, producerOptions); - - var sendingHandler = new SendingHandler(ApplicationContext, producerMessageHandler, producerOptions.HeaderMode == HeaderMode.EmbeddedHeaders, - HeadersToEmbed, UseNativeEncoding(producerOptions)); - - sendingHandler.Initialize(); - subscribableChannel.Subscribe(sendingHandler); - - IBinding binding = new DefaultProducingMessageChannelBinding(this, name, subscribableChannel, producerMessageHandler as ILifecycle, producerOptions, - producerDestination, _logger); - - producerBindingExist = true; - return binding; - } - - protected virtual bool UseNativeEncoding(IProducerOptions producerOptions) - { - return producerOptions.UseNativeEncoding; - } - - protected virtual void PostProcessOutputChannel(IMessageChannel outputChannel, IProducerOptions producerOptions) - { - // Intentionally left empty. - } - - protected virtual IMessageHandler CreateProducerMessageHandler(IProducerDestination destination, IProducerOptions producerProperties, - IMessageChannel channel, IMessageChannel errorChannel) - { - return CreateProducerMessageHandler(destination, producerProperties, errorChannel); - } - - protected abstract IMessageHandler CreateProducerMessageHandler(IProducerDestination destination, IProducerOptions producerProperties, - IMessageChannel errorChannel); - - protected virtual void AfterUnbindProducer(IProducerDestination destination, IProducerOptions producerOptions) - { - } - - protected override IBinding DoBindConsumer(string name, string group, IMessageChannel inputTarget, IConsumerOptions consumerOptions) - { - IMessageProducer consumerEndpoint = null; - - try - { - IConsumerDestination destination = InnerProvisioningProvider.ProvisionConsumerDestination(name, group, consumerOptions); - - if (consumerOptions.HeaderMode == HeaderMode.EmbeddedHeaders) - { - EnhanceMessageChannel(inputTarget); - } - - consumerEndpoint = CreateConsumerEndpoint(destination, group, consumerOptions); - consumerEndpoint.OutputChannel = inputTarget; - - if (consumerOptions.AutoStartup && consumerEndpoint is ILifecycle lifecycle) - { - lifecycle.StartAsync().GetAwaiter().GetResult(); - } - - IBinding binding = new DefaultConsumerMessageChannelBinding(this, name, group, inputTarget, consumerEndpoint as ILifecycle, consumerOptions, - destination, _logger); - - return binding; - } - catch (Exception e) - { - if (consumerEndpoint is ILifecycle lifecycle) - { - lifecycle.StopAsync().GetAwaiter().GetResult(); - } - - if (e is BinderException) - { - throw; - } - - if (e is ProvisioningException) - { - throw; - } - - throw new BinderException("Exception thrown while starting consumer: ", e); - } - } - - protected abstract IMessageProducer CreateConsumerEndpoint(IConsumerDestination destination, string group, IConsumerOptions consumerOptions); - - protected virtual void AfterUnbindConsumer(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - } - - protected virtual ErrorInfrastructure RegisterErrorInfrastructure(IConsumerDestination destination, string group, IConsumerOptions consumerOptions, - ILogger logger) - { - return RegisterErrorInfrastructure(destination, group, consumerOptions, false, logger); - } - - protected virtual ErrorInfrastructure RegisterErrorInfrastructure(IConsumerDestination destination, string group, IConsumerOptions consumerOptions, - bool polled, ILogger logger) - { - IErrorMessageStrategy errorMessageStrategy = GetErrorMessageStrategy(); - - string errorChannelName = GetErrorsBaseName(destination, group, consumerOptions); - - ISubscribableChannel errorChannel = GetErrorChannel(logger, errorChannelName); - - var recoverer = new ErrorMessageSendingRecoverer(ApplicationContext, errorChannel, errorMessageStrategy); - - string recovererBeanName = GetErrorRecovererName(destination, group, consumerOptions); - ApplicationContext.Register(recovererBeanName, recoverer); - - IMessageHandler handler = polled - ? GetPolledConsumerErrorMessageHandler(destination, group, consumerOptions) - : GetErrorMessageHandler(destination, group, consumerOptions); - - var defaultErrorChannel = ApplicationContext.GetService(IntegrationContextUtils.ErrorChannelBeanName); - - if (handler == null && errorChannel is ILastSubscriberAwareChannel channel) - { - handler = GetDefaultErrorMessageHandler(channel, defaultErrorChannel != null); - } - - string errorMessageHandlerName = GetErrorMessageHandlerName(destination, group, consumerOptions); - - if (handler != null) - { - handler.ServiceName = errorMessageHandlerName; - - if (IsSubscribable(errorChannel)) - { - IMessageHandler errorHandler = handler; - ApplicationContext.Register(errorMessageHandlerName, errorHandler); - errorChannel.Subscribe(handler); - } - else - { - _logger?.LogWarning( - "The provided errorChannel '{channel}' is an instance of DirectChannel, so no more subscribers could be added " + - "which may affect DLQ processing. Resolution: Configure your own errorChannel as an instance of PublishSubscribeChannel", errorChannelName); - } - } - - if (defaultErrorChannel != null) - { - if (IsSubscribable(errorChannel)) - { - var errorBridge = new BridgeHandler(ApplicationContext) - { - OutputChannel = defaultErrorChannel - }; - - errorChannel.Subscribe(errorBridge); - - string errorBridgeHandlerName = GetErrorBridgeName(destination, group, consumerOptions); - errorBridge.ServiceName = errorBridgeHandlerName; - ApplicationContext.Register(errorBridgeHandlerName, errorBridge); - } - else - { - _logger?.LogWarning( - "The provided errorChannel '{channel}' is an instance of DirectChannel, so no more subscribers could be added " + - "and no error messages will be sent to global error channel. Resolution: Configure your own errorChannel as an instance of PublishSubscribeChannel", - errorChannelName); - } - } - - return new ErrorInfrastructure(errorChannel, recoverer, handler); - } - - protected virtual IMessageHandler GetErrorMessageHandler(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - return null; - } - - protected virtual IMessageHandler GetPolledConsumerErrorMessageHandler(IConsumerDestination destination, string group, IConsumerOptions consumerProperties) - { - return null; - } - - protected virtual IMessageHandler GetDefaultErrorMessageHandler(ILastSubscriberAwareChannel errorChannel, bool defaultErrorChannelPresent) - { - return new FinalRethrowingErrorMessageHandler(errorChannel, defaultErrorChannelPresent); - } - - protected virtual IErrorMessageStrategy GetErrorMessageStrategy() - { - return null; - } - - protected virtual string GetErrorRecovererName(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - return $"{GetErrorsBaseName(destination, group, consumerOptions)}.recoverer"; - } - - protected virtual string GetErrorMessageHandlerName(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - return $"{GetErrorsBaseName(destination, group, consumerOptions)}.handler"; - } - - protected virtual string GetErrorsBaseName(IProducerDestination destination) - { - return $"{destination.Name}.errors"; - } - - protected virtual string GetErrorsBaseName(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - return $"{destination.Name}.{group}.errors"; - } - - protected virtual string GetErrorBridgeName(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - return $"{GetErrorsBaseName(destination, group, consumerOptions)}.bridge"; - } - - protected virtual string GetErrorBridgeName(IProducerDestination destination) - { - return $"{GetErrorsBaseName(destination)}.bridge"; - } - - private static bool IsSubscribable(ISubscribableChannel errorChannel) - { - return errorChannel is PublishSubscribeChannel || errorChannel is not AbstractSubscribableChannel || - (errorChannel is AbstractSubscribableChannel subscribableChannel && subscribableChannel.SubscriberCount == 0); - } - - private static Dictionary DoGetExtendedInfo(object destination, object properties) - { - var extendedInfo = new Dictionary - { - { "bindingDestination", destination.ToString() } - }; - - object value; - - if (properties is string stringValue) - { - value = JsonSerializer.Deserialize>(stringValue); - } - else - { - value = properties; - } - - extendedInfo.Add(properties.GetType().Name, value); - return extendedInfo; - } - - private ISubscribableChannel GetErrorChannel(ILogger logger, string errorChannelName) - { - ISubscribableChannel errorChannel; - var errorChannelObject = ApplicationContext.GetService(errorChannelName); - - if (errorChannelObject != null) - { - if (errorChannelObject is not ISubscribableChannel subscribableChannel) - { - throw new InvalidOperationException($"Error channel '{errorChannelName}' must be a {nameof(ISubscribableChannel)}."); - } - - errorChannel = subscribableChannel; - } - else - { - errorChannel = new BinderErrorChannel(ApplicationContext, errorChannelName, logger); - ApplicationContext.Register(errorChannelName, errorChannel); - } - - return errorChannel; - } - - private ISubscribableChannel RegisterErrorInfrastructure(IProducerDestination destination) - { - string errorChannelName = GetErrorsBaseName(destination); - ISubscribableChannel errorChannel; - var errorChannelObject = ApplicationContext.GetService(errorChannelName); - - if (errorChannelObject != null) - { - if (errorChannelObject is not ISubscribableChannel subscribableChannel) - { - throw new InvalidOperationException($"Error channel '{errorChannelName}' must be a ISubscribableChannel"); - } - - errorChannel = subscribableChannel; - } - else - { - errorChannel = new PublishSubscribeChannel(ApplicationContext); - ApplicationContext.Register(errorChannelName, errorChannel); - } - - var defaultErrorChannel = ApplicationContext.GetService(IntegrationContextUtils.ErrorChannelBeanName); - - if (defaultErrorChannel != null) - { - var errorBridge = new BridgeHandler(ApplicationContext) - { - OutputChannel = defaultErrorChannel - }; - - errorChannel.Subscribe(errorBridge); - string errorBridgeHandlerName = GetErrorBridgeName(destination); - ApplicationContext.Register(errorBridgeHandlerName, errorBridge); - } - - return errorChannel; - } - - private void DestroyErrorInfrastructure(IProducerDestination destination) - { - string errorChannelName = GetErrorsBaseName(destination); - string errorBridgeHandlerName = GetErrorBridgeName(destination); - - if (ApplicationContext.GetService(errorChannelName) is ISubscribableChannel channel) - { - var bridgeHandler = ApplicationContext.GetService(errorBridgeHandlerName); - - if (bridgeHandler != null) - { - channel.Unsubscribe(bridgeHandler); - ApplicationContext.Deregister(errorBridgeHandlerName); - } - - ApplicationContext.Deregister(errorChannelName); - } - } - - private void DestroyErrorInfrastructure(IConsumerDestination destination, string group, IConsumerOptions options) - { - try - { - string recoverer = GetErrorRecovererName(destination, group, options); - - DestroyBean(recoverer); - - string errorChannelName = GetErrorsBaseName(destination, group, options); - string errorMessageHandlerName = GetErrorMessageHandlerName(destination, group, options); - string errorBridgeHandlerName = GetErrorBridgeName(destination, group, options); - - if (ApplicationContext.GetService(errorChannelName) is ISubscribableChannel channel) - { - var bridgeHandler = ApplicationContext.GetService(errorBridgeHandlerName); - - if (bridgeHandler != null) - { - channel.Unsubscribe(bridgeHandler); - DestroyBean(errorBridgeHandlerName); - } - - var messageHandler = ApplicationContext.GetService(errorMessageHandlerName); - - if (messageHandler != null) - { - channel.Unsubscribe(messageHandler); - DestroyBean(errorMessageHandlerName); - } - - DestroyBean(errorChannelName); - } - } - catch (Exception ex) - { - _logger?.LogError(ex, ex.Message); - } - } - - private void DestroyBean(string beanName) - { - ApplicationContext.Deregister(beanName); - } - - private void EnhanceMessageChannel(IMessageChannel inputChannel) - { - ((AbstractMessageChannel)inputChannel).AddInterceptor(0, CurrentEmbeddedHeadersChannelInterceptor); - } - - public class ErrorInfrastructure - { - public ISubscribableChannel ErrorChannel { get; } - - public ErrorMessageSendingRecoverer Recoverer { get; } - - public IMessageHandler Handler { get; } - - public ErrorInfrastructure(ISubscribableChannel errorChannel, ErrorMessageSendingRecoverer recoverer, IMessageHandler handler) - { - ErrorChannel = errorChannel; - Recoverer = recoverer; - Handler = handler; - } - } - - protected class PolledConsumerResources - { - protected internal IMessageSource Source { get; } - - protected internal ErrorInfrastructure ErrorInfrastructure { get; } - - public PolledConsumerResources(IMessageSource source, ErrorInfrastructure errorInfrastructure) - { - Source = source; - ErrorInfrastructure = errorInfrastructure; - } - } - - protected class SendingHandler : AbstractMessageHandler, ILifecycle - { - private readonly bool _embedHeaders; - - private readonly string[] _embeddedHeaders; - - private readonly IMessageHandler _handler; - - private readonly bool _useNativeEncoding; - - public bool IsRunning => _handler is ILifecycle lifecycle && lifecycle.IsRunning; - - public SendingHandler(IApplicationContext context, IMessageHandler handler, bool embedHeaders, string[] headersToEmbed, bool useNativeEncoding) - : base(context) - { - _handler = handler; - _embedHeaders = embedHeaders; - _embeddedHeaders = headersToEmbed; - _useNativeEncoding = useNativeEncoding; - } - - public override void Initialize() - { - } - - public Task StartAsync() - { - if (_handler is ILifecycle lifecycle) - { - return lifecycle.StartAsync(); - } - - return Task.CompletedTask; - } - - public Task StopAsync() - { - if (_handler is ILifecycle lifecycle) - { - return lifecycle.StopAsync(); - } - - return Task.CompletedTask; - } - - protected override void HandleMessageInternal(IMessage message) - { - IMessage messageToSend = _useNativeEncoding ? message : SerializeAndEmbedHeadersIfApplicable(message); - _handler.HandleMessage(messageToSend); - } - - private IMessage SerializeAndEmbedHeadersIfApplicable(IMessage message) - { - var transformed = new MessageValues(message); - - object payload; - - if (_embedHeaders) - { - transformed.TryGetValue(MessageHeaders.ContentType, out object contentType); - - // transform content type headers to String, so that they can be properly - // embedded in JSON - if (contentType != null) - { - transformed[MessageHeaders.ContentType] = contentType.ToString(); - } - - payload = EmbeddedHeaderUtils.EmbedHeaders(transformed, _embeddedHeaders); - } - else - { - payload = transformed.Payload; - } - - return IntegrationServices.MessageBuilderFactory.WithPayload(payload).CopyHeaders(transformed.Headers).Build(); - } - } - - protected class DefaultProducingMessageChannelBinding : DefaultBinding - { - private readonly AbstractMessageChannelBinder _binder; - private readonly IProducerOptions _options; - private readonly IProducerDestination _producerDestination; - private readonly ILogger _logger; - - public override IDictionary ExtendedInfo => DoGetExtendedInfo(Name, _options); - - public override bool IsInput => false; - - public DefaultProducingMessageChannelBinding(AbstractMessageChannelBinder binder, string destination, IMessageChannel target, ILifecycle lifecycle, - IProducerOptions options, IProducerDestination producerDestination, ILogger logger = null) - : base(destination, target, lifecycle) - { - _binder = binder; - _options = options; - _producerDestination = producerDestination; - _logger = logger; - } - - protected override void AfterUnbind() - { - try - { - _binder.DestroyErrorInfrastructure(_producerDestination); - } - catch (Exception ex) - { - _logger?.LogError(ex, ex.Message); - } - - _binder.AfterUnbindProducer(_producerDestination, _options); - } - } - - protected class DefaultConsumerMessageChannelBinding : DefaultBinding - { - private readonly AbstractMessageChannelBinder _binder; - private readonly IConsumerOptions _options; - private readonly IConsumerDestination _destination; - private readonly ILogger _logger; - - public override IDictionary ExtendedInfo => DoGetExtendedInfo(_destination, _options); - - public override bool IsInput => true; - - public DefaultConsumerMessageChannelBinding(AbstractMessageChannelBinder binder, string name, string group, IMessageChannel inputChannel, - ILifecycle lifecycle, IConsumerOptions options, IConsumerDestination consumerDestination, ILogger logger = null) - : base(name, group, inputChannel, lifecycle) - { - _binder = binder; - _options = options; - _destination = consumerDestination; - _logger = logger; - } - - protected override void AfterUnbind() - { - try - { - if (Endpoint is IDisposable disposable) - { - disposable.Dispose(); - } - } - catch (Exception ex) - { - _logger?.LogError(ex, ex.Message); - } - - _binder.AfterUnbindConsumer(_destination, Group, _options); - _binder.DestroyErrorInfrastructure(_destination, Group, _options); - } - } - - protected class DefaultPollableChannelBinding : DefaultBinding> - { - private readonly AbstractMessageChannelBinder _binder; - private readonly IConsumerOptions _options; - private readonly IConsumerDestination _destination; - - public override IDictionary ExtendedInfo => DoGetExtendedInfo(_destination, _options); - - public override bool IsInput => true; - - public DefaultPollableChannelBinding(AbstractMessageChannelBinder binder, string name, string group, IPollableSource inboundBindTarget, - ILifecycle lifecycle, IConsumerOptions options, IConsumerDestination consumerDestination) - : base(name, group, inboundBindTarget, lifecycle) - { - _binder = binder; - _options = options; - _destination = consumerDestination; - } - - protected override void AfterUnbind() - { - _binder.AfterUnbindConsumer(_destination, Group, _options); - _binder.DestroyErrorInfrastructure(_destination, Group, _options); - } - } - - protected class EmbeddedHeadersChannelInterceptor : AbstractChannelInterceptor - { - protected readonly ILogger Logger; - - public EmbeddedHeadersChannelInterceptor(ILogger logger = null) - { - Logger = logger; - } - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - if (message.Payload is byte[] payloadBytes && !message.Headers.ContainsKey(BinderHeaders.NativeHeadersPresent) && - EmbeddedHeaderUtils.MayHaveEmbeddedHeaders(payloadBytes)) - { - MessageValues messageValues; - - try - { - messageValues = EmbeddedHeaderUtils.ExtractHeaders((IMessage)message, true); - } - catch (Exception e) - { - /* - * debug() rather than error() since we don't know for sure that it - * really is a message with embedded headers, it just meets the - * criteria in EmbeddedHeaderUtils.mayHaveEmbeddedHeaders(). - */ - - Logger?.LogDebug(e, e.Message); - messageValues = new MessageValues(message); - } - - return messageValues.ToMessage(); - } - - return message; - } - } -} diff --git a/src/Stream/src/Stream/Binder/AbstractPollableMessageSourceBinder.cs b/src/Stream/src/Stream/Binder/AbstractPollableMessageSourceBinder.cs deleted file mode 100644 index 25d819ace3..0000000000 --- a/src/Stream/src/Stream/Binder/AbstractPollableMessageSourceBinder.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Retry; -using Steeltoe.Integration; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Provisioning; - -namespace Steeltoe.Stream.Binder; - -public abstract class AbstractPollableMessageSourceBinder : AbstractMessageChannelBinder, IPollableConsumerBinder -{ - protected AbstractPollableMessageSourceBinder(IApplicationContext context, string[] headersToEmbed, IProvisioningProvider provisioningProvider, - ILogger logger) - : this(context, headersToEmbed, provisioningProvider, null, null, logger) - { - } - - protected AbstractPollableMessageSourceBinder(IApplicationContext context, string[] headersToEmbed, IProvisioningProvider provisioningProvider, - IListenerContainerCustomizer containerCustomizer, IMessageSourceCustomizer sourceCustomizer, ILogger logger) - : base(context, headersToEmbed, provisioningProvider, containerCustomizer, sourceCustomizer, logger) - { - } - - public override IBinding BindConsumer(string name, string group, object inboundTarget, IConsumerOptions consumerOptions) - { - return inboundTarget is IPollableSource source - ? BindConsumer(name, group, source, consumerOptions) - : base.BindConsumer(name, group, inboundTarget, consumerOptions); - } - - public virtual IBinding BindConsumer(string name, string group, IPollableSource inboundTarget, IConsumerOptions consumerOptions) - { - if (inboundTarget is not DefaultPollableMessageSource bindingTarget) - { - throw new InvalidOperationException(nameof(inboundTarget)); - } - - IConsumerDestination destination = InnerProvisioningProvider.ProvisionConsumerDestination(name, group, consumerOptions); - - if (consumerOptions.HeaderMode == HeaderMode.EmbeddedHeaders) - { - bindingTarget.AddInterceptor(0, CurrentEmbeddedHeadersChannelInterceptor); - } - - PolledConsumerResources resources = CreatePolledConsumerResources(name, group, destination, consumerOptions); - - IMessageSource messageSource = resources.Source; - - bindingTarget.Source = messageSource; - - if (resources.ErrorInfrastructure != null) - { - if (resources.ErrorInfrastructure.ErrorChannel != null) - { - bindingTarget.ErrorChannel = resources.ErrorInfrastructure.ErrorChannel; - } - - IErrorMessageStrategy ems = GetErrorMessageStrategy(); - - if (ems != null) - { - bindingTarget.ErrorMessageStrategy = ems; - } - } - - if (consumerOptions.MaxAttempts > 1) - { - bindingTarget.RetryTemplate = BuildRetryTemplate(consumerOptions); - bindingTarget.RecoveryCallback = GetPolledConsumerRecoveryCallback(resources.ErrorInfrastructure, consumerOptions); - } - - PostProcessPollableSource(bindingTarget); - var sourceAsLifecycle = resources.Source as ILifecycle; - - sourceAsLifecycle?.StartAsync().GetAwaiter().GetResult(); - - var binding = new DefaultPollableChannelBinding(this, name, group, inboundTarget, sourceAsLifecycle ?? null, consumerOptions, destination); - return binding; - } - - public virtual IBinding BindProducer(string name, IPollableSource outboundTarget, IProducerOptions producerOptions) - { - throw new NotImplementedException(); - } - - protected virtual void PostProcessPollableSource(DefaultPollableMessageSource bindingTarget) - { - } - - protected virtual IRecoveryCallback GetPolledConsumerRecoveryCallback(ErrorInfrastructure errorInfrastructure, IConsumerOptions options) - { - return errorInfrastructure.Recoverer; - } - - protected virtual PolledConsumerResources CreatePolledConsumerResources(string name, string group, IConsumerDestination destination, - IConsumerOptions consumerOptions) - { - throw new InvalidOperationException("This binder does not support pollable consumers"); - } -} diff --git a/src/Stream/src/Stream/Binder/AbstractPollableSource.cs b/src/Stream/src/Stream/Binder/AbstractPollableSource.cs deleted file mode 100644 index aa8fe42609..0000000000 --- a/src/Stream/src/Stream/Binder/AbstractPollableSource.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -public abstract class AbstractPollableSource : IPollableSource -{ - public abstract bool Poll(THandler handler); - - public virtual bool Poll(THandler handler, Type type) - { - return Poll(handler); - } - - public bool Poll(object handler) - { - return Poll((THandler)handler); - } - - public bool Poll(object handler, Type type) - { - return Poll((THandler)handler, type); - } -} diff --git a/src/Stream/src/Stream/Binder/BinderConfiguration.cs b/src/Stream/src/Stream/Binder/BinderConfiguration.cs deleted file mode 100644 index 090a9e0ec5..0000000000 --- a/src/Stream/src/Stream/Binder/BinderConfiguration.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binder; - -public class BinderConfiguration -{ - private readonly IBinderOptions _options; - - public string ConfigureClass { get; } - - public string ConfigureAssembly { get; } - - public string ResolvedAssembly { get; set; } - - public IDictionary Properties => _options.Environment; - - public bool IsInheritEnvironment => _options.InheritEnvironment; - - public bool IsDefaultCandidate => _options.DefaultCandidate; - - public BinderConfiguration(string binderType, string binderAssemblyPath, IBinderOptions options) - { - ArgumentGuard.NotNull(binderType); - ArgumentGuard.NotNull(options); - - ConfigureClass = binderType; - ConfigureAssembly = binderAssemblyPath; - _options = options; - } -} diff --git a/src/Stream/src/Stream/Binder/BinderErrorChannel.cs b/src/Stream/src/Stream/Binder/BinderErrorChannel.cs deleted file mode 100644 index d1fdb60cdc..0000000000 --- a/src/Stream/src/Stream/Binder/BinderErrorChannel.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -internal sealed class BinderErrorChannel : PublishSubscribeChannel, ILastSubscriberAwareChannel -{ - private int _subscribers; - - private volatile ILastSubscriberMessageHandler _finalHandler; - - public int Subscribers => _subscribers; - - public BinderErrorChannel(IApplicationContext context, string name, ILogger logger) - : base(context, name, logger) - { - } - - public override bool Subscribe(IMessageHandler handler) - { - Interlocked.Increment(ref _subscribers); - - if (handler is ILastSubscriberMessageHandler && _finalHandler != null) - { - throw new InvalidOperationException("Only one LastSubscriberMessageHandler is allowed"); - } - - if (_finalHandler != null) - { - base.Unsubscribe(_finalHandler); - } - - bool result = base.Subscribe(handler); - - if (_finalHandler != null) - { - base.Subscribe(_finalHandler); - } - - if (handler is ILastSubscriberMessageHandler lastSubHandler && _finalHandler == null) - { - _finalHandler = lastSubHandler; - } - - return result; - } - - public override bool Unsubscribe(IMessageHandler handler) - { - Interlocked.Decrement(ref _subscribers); - return base.Unsubscribe(handler); - } -} diff --git a/src/Stream/src/Stream/Binder/BinderException.cs b/src/Stream/src/Stream/Binder/BinderException.cs deleted file mode 100644 index 253864e7e2..0000000000 --- a/src/Stream/src/Stream/Binder/BinderException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -public class BinderException : Exception -{ - public BinderException(string message) - : base(message) - { - } - - public BinderException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Stream/src/Stream/Binder/BinderHeaders.cs b/src/Stream/src/Stream/Binder/BinderHeaders.cs deleted file mode 100644 index 9ef738aa19..0000000000 --- a/src/Stream/src/Stream/Binder/BinderHeaders.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Integration; -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -public static class BinderHeaders -{ - private const string Prefix = "scst_"; - public const string BinderOriginalContentType = "originalContentType"; - - public const string PartitionHeader = $"{Prefix}partition"; - - public const string PartitionOverride = $"{Prefix}partitionOverride"; - - public const string NativeHeadersPresent = $"{Prefix}nativeHeadersPresent"; - - public static readonly string[] StandardHeaders = - { - IntegrationMessageHeaderAccessor.CorrelationId, - IntegrationMessageHeaderAccessor.SequenceSize, - IntegrationMessageHeaderAccessor.SequenceNumber, - MessageHeaders.ContentType, - BinderOriginalContentType - }; -} diff --git a/src/Stream/src/Stream/Binder/BinderType.cs b/src/Stream/src/Stream/Binder/BinderType.cs deleted file mode 100644 index fabf21d100..0000000000 --- a/src/Stream/src/Stream/Binder/BinderType.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -public class BinderType : IBinderType -{ - public string Name { get; } - - public string ConfigureClass { get; } - - public string AssemblyPath { get; } - - public BinderType(string name, string configurationClass, string assemblyPath) - { - Name = name; - ConfigureClass = configurationClass; - AssemblyPath = assemblyPath; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is not BinderType other || GetType() != obj.GetType()) - { - return false; - } - - return Name == other.Name && ConfigureClass == other.ConfigureClass && AssemblyPath == other.AssemblyPath; - } - - public override int GetHashCode() - { - return HashCode.Combine(Name, ConfigureClass, AssemblyPath); - } -} diff --git a/src/Stream/src/Stream/Binder/DefaultBinderFactory.cs b/src/Stream/src/Stream/Binder/DefaultBinderFactory.cs deleted file mode 100644 index 89ff75a479..0000000000 --- a/src/Stream/src/Stream/Binder/DefaultBinderFactory.cs +++ /dev/null @@ -1,202 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Options; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Util; - -namespace Steeltoe.Stream.Binder; - -public class DefaultBinderFactory : IBinderFactory, IDisposable -{ - private readonly object _lock = new(); - private readonly IBinderConfigurations _binderConfigurations; - private readonly IApplicationContext _context; - private readonly IOptionsMonitor _optionsMonitor; - private Dictionary _binderInstanceCache; - - private BindingServiceOptions Options => _optionsMonitor.CurrentValue; - - private string DefaultBinder => Options.DefaultBinder; - - public DefaultBinderFactory(IApplicationContext context, IOptionsMonitor optionsMonitor, IBinderConfigurations binderConfigurations, - IEnumerable listeners = null) - { - ArgumentGuard.NotNull(binderConfigurations); - - _context = context; - _optionsMonitor = optionsMonitor; - _binderConfigurations = binderConfigurations; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - if (_binderInstanceCache != null) - { - foreach (KeyValuePair binder in _binderInstanceCache) - { - binder.Value.Dispose(); - } - - _binderInstanceCache = null; - } - - _context?.Dispose(); - } - } - - public IBinder GetBinder(string name) - { - return GetBinder(name, typeof(object)); - } - - public IBinder GetBinder(string name, Type bindableType) - { - string binderName = !string.IsNullOrEmpty(name) ? name : DefaultBinder; - IBinder result = null; - IEnumerable binders = _context.GetServices(); - - if (!string.IsNullOrEmpty(binderName)) - { - result = binders.SingleOrDefault(b => b.ServiceName == binderName); - } - else if (binders.Count() == 1) - { - result = binders.Single(); - } - - if (result == null && string.IsNullOrEmpty(binderName) && binders.Count() > 1) - { - throw new InvalidOperationException("Multiple binders are available, however neither default nor per-destination binder name is provided."); - } - - result ??= DoGetBinder(binderName, bindableType); - return result; - } - - private IBinder DoGetBinder(string name, Type bindableType) - { - string configurationName; - - if (string.IsNullOrEmpty(name)) - { - var defaultCandidateConfigurations = new HashSet(); - - if (string.IsNullOrEmpty(DefaultBinder)) - { - foreach (KeyValuePair configuration in _binderConfigurations.Configurations) - { - if (configuration.Value.IsDefaultCandidate) - { - defaultCandidateConfigurations.Add(configuration.Key); - } - } - - if (defaultCandidateConfigurations.Count == 1) - { - // Single default candidate - configurationName = defaultCandidateConfigurations.Single(); - } - else - { - // Multiple default candidates, find by target type match - var candidatesForBindableType = new List(); - - foreach (string defaultCandidateConfiguration in defaultCandidateConfigurations) - { - IBinder binder = GetBinderInstance(defaultCandidateConfiguration); - - if (VerifyBinderTypeMatchesTarget(binder, bindableType)) - { - candidatesForBindableType.Add(defaultCandidateConfiguration); - } - } - - if (candidatesForBindableType.Count == 1) - { - configurationName = candidatesForBindableType.Single(); - } - else - { - throw new InvalidOperationException("A default binder has been requested, but there are too many candidates or none available"); - } - } - } - else - { - configurationName = DefaultBinder; - } - } - else - { - configurationName = name; - } - - IBinder binderInstance = GetBinderInstance(configurationName); - - if (binderInstance == null) - { - throw new InvalidOperationException($"Unknown binder configuration: {configurationName}"); - } - - if (!VerifyBinderTypeMatchesTarget(binderInstance, bindableType)) - { - throw new InvalidOperationException($"The binder {configurationName} cannot bind a {bindableType.FullName}"); - } - - return binderInstance; - } - - private IBinder GetBinderInstance(string configurationName) - { - BuildBinderCache(); - _binderInstanceCache.TryGetValue(configurationName, out IBinder binder); - - return binder; - } - - private void BuildBinderCache() - { - if (_binderInstanceCache == null) - { - lock (_lock) - { - if (_binderInstanceCache == null) - { - _binderInstanceCache = new Dictionary(); - IEnumerable binders = _context.GetServices(); - - foreach (IBinder binder in binders) - { - string binderName = binder.ServiceName; - _binderInstanceCache.TryAdd(binderName, binder); - - List names = _binderConfigurations.FindMatchingConfigurationsIfAny(binder); - - foreach (string name in names) - { - _binderInstanceCache.TryAdd(name, binder); - } - } - } - } - } - } - - private bool VerifyBinderTypeMatchesTarget(IBinder binderInstance, Type bindingTargetType) - { - return (binderInstance is IPollableConsumerBinder && GenericsUtils.CheckCompatiblePollableBinder(binderInstance, bindingTargetType)) || - GenericsUtils.GetParameterType(binderInstance.GetType(), typeof(IBinder<>), 0).IsAssignableFrom(bindingTargetType); - } -} diff --git a/src/Stream/src/Stream/Binder/DefaultBinderTypeRegistry.cs b/src/Stream/src/Stream/Binder/DefaultBinderTypeRegistry.cs deleted file mode 100644 index 017c740984..0000000000 --- a/src/Stream/src/Stream/Binder/DefaultBinderTypeRegistry.cs +++ /dev/null @@ -1,198 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Runtime.InteropServices; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Binder; - -public class DefaultBinderTypeRegistry : IBinderTypeRegistry -{ - private static readonly string ThisAssemblyName = typeof(DefaultBinderTypeRegistry).Assembly.GetName().Name; - private readonly Dictionary _binderTypes; - - internal List SearchDirectories { get; } - - public DefaultBinderTypeRegistry() - { - var searchDirectories = new List(); - string executingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - searchDirectories.Add(executingDirectory); - - if (executingDirectory != Environment.CurrentDirectory) - { - searchDirectories.Add(Environment.CurrentDirectory); - } - - SearchDirectories = searchDirectories; - - _binderTypes = FindBinders(searchDirectories); - } - - public DefaultBinderTypeRegistry(List searchDirectories, bool checkLoadedAssemblies = true) - { - SearchDirectories = searchDirectories; - _binderTypes = FindBinders(searchDirectories, checkLoadedAssemblies); - } - - internal DefaultBinderTypeRegistry(Dictionary binderTypes) - { - _binderTypes = binderTypes; - } - - public IBinderType Get(string name) - { - _binderTypes.TryGetValue(name, out IBinderType result); - return result; - } - - public IDictionary GetAll() - { - return _binderTypes; - } - - internal static Dictionary FindBinders(List searchDirectories, bool checkLoadedAssemblies = true) - { - var binderTypes = new Dictionary(); - - ParseBinderConfigurations(searchDirectories, binderTypes, checkLoadedAssemblies); - - return binderTypes; - } - - internal static void ParseBinderConfigurations(List searchDirectories, Dictionary registrations, - bool checkLoadedAssemblies = true) - { - if (checkLoadedAssemblies) - { - AddBinderTypes(AppDomain.CurrentDomain.GetAssemblies(), registrations); - } - - foreach (string path in searchDirectories) - { - AddBinderTypes(path, registrations); - } - } - - internal static void AddBinderTypes(Assembly[] assemblies, Dictionary registrations) - { - foreach (Assembly assembly in assemblies) - { - IBinderType binderType = CheckAssembly(assembly); - - if (binderType != null) - { - registrations.TryAdd(binderType.Name, binderType); - } - } - } - - internal static void AddBinderTypes(string directory, Dictionary registrations) - { - var context = new MetadataLoadContext(GetAssemblyResolver(directory)); - var directoryInfo = new DirectoryInfo(directory); - - foreach (FileInfo file in directoryInfo.EnumerateFiles("*.dll")) - { - try - { - if (ShouldCheckFile(file)) - { - IBinderType reg = LoadAndCheckAssembly(context, file.FullName); - - if (reg != null) - { - registrations.TryAdd(reg.Name, reg); - } - } - } - catch (Exception) - { - // log - } - } - - context.Dispose(); - } - - internal static bool ShouldCheckFile(FileInfo file) - { - string fileName = Path.GetFileNameWithoutExtension(file.Name); - - if (fileName.Equals(ThisAssemblyName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - return true; - } - - internal static IBinderType LoadAndCheckAssembly(MetadataLoadContext context, string assemblyPath) - { - try - { - Assembly assembly = context.LoadFromAssemblyPath(assemblyPath); - return CheckAssembly(assembly); - } - catch - { - // most failures here are situations that aren't relevant, so just fail silently - } - - return null; - } - - internal static IBinderType CheckAssembly(Assembly assembly) - { - foreach (CustomAttributeData data in assembly.GetCustomAttributesData()) - { - if (data.AttributeType.FullName == typeof(BinderAttribute).FullName) - { - return new BinderType(GetName(data), GetConfigureClass(data), assembly.Location); - } - } - - return null; - } - - internal static PathAssemblyResolver GetAssemblyResolver(string directory) - { - var paths = new List(); - paths.AddRange(Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll")); - paths.AddRange(Directory.GetFiles(directory, "*.dll")); - return new PathAssemblyResolver(paths); - } - - internal static string GetName(CustomAttributeData data) - { - if (data.ConstructorArguments[0].Value is not string result) - { - result = GetNamedArgument(data.NamedArguments, "Name"); - } - - return result; - } - - internal static T GetNamedArgument(IList namedArguments, string name) - where T : class - { - foreach (CustomAttributeNamedArgument arg in namedArguments) - { - if (arg.MemberName == name) - { - return (T)arg.TypedValue.Value; - } - } - - return default; - } - - internal static string GetConfigureClass(CustomAttributeData data) - { - Type type = data.ConstructorArguments[1].Value as Type ?? GetNamedArgument(data.NamedArguments, "ConfigureType"); - - return type?.AssemblyQualifiedName; - } -} diff --git a/src/Stream/src/Stream/Binder/DefaultBinding.cs b/src/Stream/src/Stream/Binder/DefaultBinding.cs deleted file mode 100644 index f229eb47de..0000000000 --- a/src/Stream/src/Stream/Binder/DefaultBinding.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Lifecycle; - -namespace Steeltoe.Stream.Binder; - -public class DefaultBinding : AbstractBinding -{ - private readonly bool _restartable; - protected readonly T Target; - protected readonly ILifecycle Lifecycle; - - private bool _paused; - - private string RunningState => IsRunning ? "running" : "stopped"; - - protected internal virtual ILifecycle Endpoint => Lifecycle; - - public override string Name { get; } - - public virtual string Group { get; } - - public override string BindingName => Name; - - public virtual string State - { - get - { - string state = "N/A"; - - if (Lifecycle != null) - { - if (IsPausable) - { - state = _paused ? "paused" : RunningState; - } - else - { - state = RunningState; - } - } - - return state; - } - } - - public override bool IsRunning => Lifecycle != null && Lifecycle.IsRunning; - - public virtual bool IsPausable => Lifecycle is IPausable; - - public DefaultBinding(string name, string group, T target, ILifecycle lifecycle) - { - ArgumentGuard.NotNull(target); - - Name = name; - Group = group; - Target = target; - Lifecycle = lifecycle; - _restartable = !string.IsNullOrEmpty(group); - } - - public DefaultBinding(string name, T target, ILifecycle lifecycle) - : this(name, null, target, lifecycle) - { - _restartable = true; - } - - public override Task StartAsync() - { - if (!IsRunning && Lifecycle != null && _restartable) - { - return Lifecycle.StartAsync(); - } // else this.logger.warn("Can not re-bind an anonymous binding") - - return Task.CompletedTask; - } - - public override Task StopAsync() - { - if (IsRunning) - { - return Lifecycle.StopAsync(); - } - - return Task.CompletedTask; - } - - public override async Task PauseAsync() - { - if (Lifecycle is IPausable pausable) - { - await pausable.PauseAsync(); - _paused = true; - } - } - - public override async Task ResumeAsync() - { - if (Lifecycle is IPausable pausable) - { - await pausable.ResumeAsync(); - _paused = false; - } - } - - public override async Task UnbindAsync() - { - await StopAsync(); - AfterUnbind(); - } - - public override string ToString() - { - return $" Binding [name={Name}, target={Target}, lifecycle={Lifecycle}]"; - } - - protected virtual void AfterUnbind() - { - } -} diff --git a/src/Stream/src/Stream/Binder/DefaultPollableMessageSource.cs b/src/Stream/src/Stream/Binder/DefaultPollableMessageSource.cs deleted file mode 100644 index 071bfd9b61..0000000000 --- a/src/Stream/src/Stream/Binder/DefaultPollableMessageSource.cs +++ /dev/null @@ -1,306 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Retry; -using Steeltoe.Common.Util; -using Steeltoe.Integration; -using Steeltoe.Integration.Acks; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Stream.Binder; - -public class DefaultPollableMessageSource : AbstractPollableSource, IPollableMessageSource, ILifecycle, IRetryListener -{ - private static readonly AsyncLocal AttributesHolder = new(); - - private readonly DirectChannel _dummyChannel; - private readonly MessagingTemplate _messagingTemplate; - private readonly ISmartMessageConverter _messageConverter; - private readonly List _interceptors = new(); - private RetryTemplate _retryTemplate; - private IRecoveryCallback _recoveryCallback; - private int _running; - - public RetryTemplate RetryTemplate - { - get => _retryTemplate; - set - { - _retryTemplate = value; - _retryTemplate.RegisterListener(this); - } - } - - public IRecoveryCallback RecoveryCallback - { - get => _recoveryCallback; - set => _recoveryCallback = new RecoveryCallbackWrapper(value); - } - - public IMessageChannel ErrorChannel { get; set; } - - public IErrorMessageStrategy ErrorMessageStrategy { get; set; } = new DefaultErrorMessageStrategy(); - - public Action AttributeProvider { get; set; } - - public IMessageSource Source { get; set; } - - public bool IsRunning => _running != 0; - - public DefaultPollableMessageSource(IApplicationContext context, ISmartMessageConverter messageConverter) - { - _messageConverter = messageConverter; - _messagingTemplate = new MessagingTemplate(context); - _dummyChannel = new DirectChannel(context); - } - - public void AddInterceptor(IChannelInterceptor interceptor) - { - _interceptors.Add(interceptor); - } - - public void AddInterceptor(int index, IChannelInterceptor interceptor) - { - _interceptors.Insert(index, interceptor); - } - - public Task StartAsync() - { - if (Interlocked.CompareExchange(ref _running, 1, 0) == 0 && Source is ILifecycle asLifeCycle) - { - return asLifeCycle.StartAsync(); - } - - return Task.CompletedTask; - } - - public Task StopAsync() - { - if (Interlocked.CompareExchange(ref _running, 0, 1) == 1 && Source is ILifecycle asLifeCycle) - { - return asLifeCycle.StopAsync(); - } - - return Task.CompletedTask; - } - - public override bool Poll(IMessageHandler handler) - { - return Poll(handler, null); - } - - public override bool Poll(IMessageHandler handler, Type type) - { - IMessage message = Receive(type); - - if (message == null) - { - return false; - } - - IAcknowledgmentCallback ackCallback = StaticMessageHeaderAccessor.GetAcknowledgmentCallback(message); - - try - { - if (RetryTemplate == null) - { - Handle(message, handler); - } - else - { - RetryTemplate.Execute(_ => Handle(message, handler), _recoveryCallback); - } - - return true; - } - catch (MessagingException e) - { - if (RetryTemplate == null && !ShouldRequeue(e)) - { - _messagingTemplate.Send(ErrorChannel, ErrorMessageStrategy.BuildErrorMessage(e, AttributesHolder.Value)); - return true; - } - else if (!ackCallback.IsAcknowledged && ShouldRequeue(e)) - { - AckUtils.Requeue(ackCallback); - return true; - } - else - { - AckUtils.AutoNack(ackCallback); - } - - if (e.FailedMessage.Equals(message)) - { - throw; - } - - throw new MessageHandlingException(message, e); - } - catch (Exception e) - { - AckUtils.AutoNack(ackCallback); - - switch (e) - { - case MessageHandlingException exception when exception.FailedMessage.Equals(message): - throw; - default: - throw new MessageHandlingException(message, e); - } - } - finally - { - AckUtils.AutoAck(ackCallback); - } - } - - public bool Open(IRetryContext context) - { - if (_recoveryCallback != null) - { - AttributesHolder.Value = context; - } - - return true; - } - - public void Close(IRetryContext context, Exception exception) - { - AttributesHolder.Value = null; - } - - public void OnError(IRetryContext context, Exception exception) - { - // Ignore - } - - protected internal static bool ShouldRequeue(Exception e) - { - bool requeue = false; - Exception t = e.InnerException; - - while (t != null && !requeue) - { - requeue = t is RequeueCurrentMessageException; - t = t.InnerException; - } - - return requeue; - } - - private IMessage Receive(Type type) - { - IMessage result = Source.Receive(); - - if (result == null) - { - return null; - } - - IMessage message = ApplyInterceptors(result); - - if (message != null && type != null && _messageConverter != null) - { - Type targetType = type; - object payload = _messageConverter.FromMessage(message, targetType, type); - - if (payload == null) - { - throw new MessageConversionException(message, "No converter could convert Message"); - } - - message = MessageBuilder.WithPayload(payload).CopyHeaders(message.Headers).Build(); - } - - return message; - } - - private void DoHandleMessage(IMessageHandler handler, IMessage message) - { - try - { - handler.HandleMessage(message); - } - catch (Exception t) - { - throw new MessageHandlingException(message, t); - } - } - - private IMessage ApplyInterceptors(IMessage message) - { - IMessage received = message; - - foreach (IChannelInterceptor interceptor in _interceptors) - { - received = interceptor.PreSend(received, _dummyChannel); - - if (received == null) - { - return null; - } - } - - return received; - } - - private void SetAttributesIfNecessary(IMessage message) - { - bool needHolder = ErrorChannel != null && RetryTemplate == null; - bool needAttributes = needHolder || RetryTemplate != null; - - if (needHolder) - { - AttributesHolder.Value = ErrorMessageUtils.GetAttributeAccessor(null, null); - } - - if (needAttributes) - { - IAttributeAccessor attributes = AttributesHolder.Value; - - if (attributes != null) - { - attributes.SetAttribute(ErrorMessageUtils.InputMessageContextKey, message); - - if (AttributeProvider != null) - { - AttributeProvider.Invoke(attributes, message); - } - } - } - } - - private void Handle(IMessage message, IMessageHandler handler) - { - SetAttributesIfNecessary(message); - DoHandleMessage(handler, message); - } - - private sealed class RecoveryCallbackWrapper : IRecoveryCallback - { - private readonly IRecoveryCallback _recoveryCallback; - - public RecoveryCallbackWrapper(IRecoveryCallback recoveryCallback) - { - _recoveryCallback = recoveryCallback; - } - - public object Recover(IRetryContext context) - { - if (!ShouldRequeue((MessagingException)context.LastException)) - { - return _recoveryCallback.Recover(context); - } - - throw (MessagingException)context.LastException; - } - } -} diff --git a/src/Stream/src/Stream/Binder/DirectHandler.cs b/src/Stream/src/Stream/Binder/DirectHandler.cs deleted file mode 100644 index 91a662f5ad..0000000000 --- a/src/Stream/src/Stream/Binder/DirectHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -public class DirectHandler : IMessageHandler -{ - private readonly IMessageChannel _outputChannel; - - public virtual string ServiceName { get; set; } - - public DirectHandler(IMessageChannel outputChannel) - { - _outputChannel = outputChannel; - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - _outputChannel.Send(message); - } -} diff --git a/src/Stream/src/Stream/Binder/EmbeddedHeaderUtils.cs b/src/Stream/src/Stream/Binder/EmbeddedHeaderUtils.cs deleted file mode 100644 index 5bc23891ba..0000000000 --- a/src/Stream/src/Stream/Binder/EmbeddedHeaderUtils.cs +++ /dev/null @@ -1,170 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Newtonsoft.Json; -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -public static class EmbeddedHeaderUtils -{ - public static byte[] EmbedHeaders(MessageValues original, params string[] headers) - { - byte[][] headerValues = new byte[headers.Length][]; - int offset = 0; - int headerCount = 0; - int headersLength = 0; - - foreach (string header in headers) - { - original.TryGetValue(header, out object value); - - if (value != null) - { - string json = JsonConvert.SerializeObject(value); - headerValues[offset] = Encoding.UTF8.GetBytes(json); - headerCount++; - headersLength += header.Length + headerValues[offset].Length; - } - else - { - headerValues[offset] = null; - } - - offset++; - } - - // 0xff, n(1), [ [lenHdr(1), hdr, lenValue(4), value] ... ] - var byteBuffer = new MemoryStream(); - byteBuffer.WriteByte(0xff); // signal new format - byteBuffer.WriteByte((byte)headerCount); - - for (int i = 0; i < headers.Length; i++) - { - if (headerValues[i] != null) - { - byteBuffer.WriteByte((byte)headers[i].Length); - - byte[] buffer = Encoding.UTF8.GetBytes(headers[i]); - byteBuffer.Write(buffer, 0, buffer.Length); - - buffer = GetBigEndianBytes(headerValues[i].Length); - byteBuffer.Write(buffer, 0, buffer.Length); - - byteBuffer.Write(headerValues[i], 0, headerValues[i].Length); - } - } - - byte[] payloadBuffer = (byte[])original.Payload; - byteBuffer.Write(payloadBuffer, 0, payloadBuffer.Length); - - return byteBuffer.ToArray(); - } - - public static bool MayHaveEmbeddedHeaders(byte[] bytes) - { - return bytes.Length > 8 && (bytes[0] & 0xff) == 0xff; - } - - public static MessageValues ExtractHeaders(byte[] payload) - { - return ExtractHeaders(payload, false, null); - } - - public static MessageValues ExtractHeaders(IMessage message, bool copyRequestHeaders) - { - return ExtractHeaders(message.Payload, copyRequestHeaders, message.Headers); - } - - public static string[] HeadersToEmbed(string[] configuredHeaders) - { - if (configuredHeaders == null || configuredHeaders.Length == 0) - { - return BinderHeaders.StandardHeaders; - } - - var combinedHeadersToMap = new List(BinderHeaders.StandardHeaders); - combinedHeadersToMap.AddRange(configuredHeaders); - return combinedHeadersToMap.ToArray(); - } - - private static MessageValues ExtractHeaders(byte[] payload, bool copyRequestHeaders, IMessageHeaders requestHeaders) - { - var byteBuffer = new MemoryStream(payload); - int headerCount = byteBuffer.ReadByte() & 0xff; - - if (headerCount == 0xff) - { - headerCount = byteBuffer.ReadByte() & 0xff; - var headers = new Dictionary(); - - for (int i = 0; i < headerCount; i++) - { - int len = byteBuffer.ReadByte() & 0xff; - string headerName = Encoding.UTF8.GetString(payload, (int)byteBuffer.Position, len); - - byteBuffer.Position += len; - - byte[] intBytes = new byte[4]; - -#pragma warning disable S2674 // The length returned from a stream read should be checked - byteBuffer.Read(intBytes, 0, 4); -#pragma warning restore S2674 // The length returned from a stream read should be checked - - len = GetIntFromBigEndianBytes(intBytes); - string headerValue = Encoding.UTF8.GetString(payload, (int)byteBuffer.Position, len); - object headerContent = JsonConvert.DeserializeObject(headerValue); - - headers.Add(headerName, headerContent); - byteBuffer.Position += len; - } - - long remaining = byteBuffer.Length - byteBuffer.Position; - byte[] newPayload = new byte[remaining]; - -#pragma warning disable S2674 // The length returned from a stream read should be checked - byteBuffer.Read(newPayload, 0, (int)remaining); -#pragma warning restore S2674 // The length returned from a stream read should be checked - - return BuildMessageValues(newPayload, headers, copyRequestHeaders, requestHeaders); - } - - return BuildMessageValues(payload, new Dictionary(), copyRequestHeaders, requestHeaders); - } - - private static MessageValues BuildMessageValues(byte[] payload, Dictionary headers, bool copyRequestHeaders, IMessageHeaders requestHeaders) - { - var messageValues = new MessageValues(payload, headers); - - if (copyRequestHeaders && requestHeaders != null) - { - messageValues.CopyHeadersIfAbsent(requestHeaders); - } - - return messageValues; - } - - private static byte[] GetBigEndianBytes(int value) - { - byte[] bytes = BitConverter.GetBytes(value); - - if (BitConverter.IsLittleEndian) - { - Array.Reverse(bytes); - } - - return bytes; - } - - private static int GetIntFromBigEndianBytes(byte[] bytes) - { - if (BitConverter.IsLittleEndian) - { - Array.Reverse(bytes); - } - - return BitConverter.ToInt32(bytes, 0); - } -} diff --git a/src/Stream/src/Stream/Binder/FinalRethrowingErrorMessageHandler.cs b/src/Stream/src/Stream/Binder/FinalRethrowingErrorMessageHandler.cs deleted file mode 100644 index 53a98dbf2b..0000000000 --- a/src/Stream/src/Stream/Binder/FinalRethrowingErrorMessageHandler.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -internal sealed class FinalRethrowingErrorMessageHandler : ILastSubscriberMessageHandler -{ - private readonly ILastSubscriberAwareChannel _errorChannel; - - private readonly bool _defaultErrorChannelPresent; - - public string ServiceName { get; set; } - - public FinalRethrowingErrorMessageHandler(ILastSubscriberAwareChannel errorChannel, bool defaultErrorChannelPresent) - { - _errorChannel = errorChannel; - _defaultErrorChannelPresent = defaultErrorChannelPresent; - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - if (_errorChannel.Subscribers > (_defaultErrorChannelPresent ? 2 : 1)) - { - // user has subscribed; default is 2, this and the bridge to the - // errorChannel - return; - } - - if (message.Payload is MessagingException exception) - { - throw exception; - } - - throw new MessagingException((IMessage)null, (Exception)message.Payload); - } -} diff --git a/src/Stream/src/Stream/Binder/ILastSubscriberAwareChannel.cs b/src/Stream/src/Stream/Binder/ILastSubscriberAwareChannel.cs deleted file mode 100644 index ae8b06f282..0000000000 --- a/src/Stream/src/Stream/Binder/ILastSubscriberAwareChannel.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -public interface ILastSubscriberAwareChannel : ISubscribableChannel -{ - int Subscribers { get; } -} diff --git a/src/Stream/src/Stream/Binder/ILastSubscriberMessageHandler.cs b/src/Stream/src/Stream/Binder/ILastSubscriberMessageHandler.cs deleted file mode 100644 index 53f31b0dc0..0000000000 --- a/src/Stream/src/Stream/Binder/ILastSubscriberMessageHandler.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -public interface ILastSubscriberMessageHandler : IMessageHandler -{ -} diff --git a/src/Stream/src/Stream/Binder/MessageValues.cs b/src/Stream/src/Stream/Binder/MessageValues.cs deleted file mode 100644 index c0603b6206..0000000000 --- a/src/Stream/src/Stream/Binder/MessageValues.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binder; - -public class MessageValues : IDictionary -{ - public object Payload { get; set; } - - public Dictionary Headers { get; set; } - - public ICollection Keys => Headers.Keys; - - public ICollection Values => Headers.Values; - - public int Count => Headers.Count; - - public bool IsReadOnly => false; - - public object this[string key] - { - get - { - Headers.TryGetValue(key, out object result); - return result; - } - set => Headers[key] = value; - } - - public MessageValues(IMessage message) - { - Payload = message.Payload; - Headers = new Dictionary(); - - foreach (KeyValuePair header in (IDictionary)message.Headers) - { - Headers.Add(header.Key, header.Value); - } - } - - public MessageValues(object payload, IDictionary headers) - { - Payload = payload; - Headers = new Dictionary(headers); - } - - public IMessage ToMessage() - { - return IntegrationMessageBuilder.WithPayload(Payload).CopyHeaders(Headers).Build(); - } - - public void CopyHeadersIfAbsent(IDictionary headersToCopy) - { - foreach (KeyValuePair headersToCopyEntry in headersToCopy) - { - if (!ContainsKey(headersToCopyEntry.Key)) - { - Add(headersToCopyEntry.Key, headersToCopyEntry.Value); - } - } - } - - public void Add(string key, object value) - { - Headers.Add(key, value); - } - - public void Add(KeyValuePair item) - { - Headers.Add(item.Key, item.Value); - } - - public void Clear() - { - Headers.Clear(); - } - - public bool Contains(KeyValuePair item) - { - return Headers.ContainsKey(item.Key) && Headers.ContainsValue(item.Value); - } - - public bool ContainsKey(string key) - { - return Headers.ContainsKey(key); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - ((ICollection>)Headers).CopyTo(array, arrayIndex); - } - - public IEnumerator> GetEnumerator() - { - return Headers.GetEnumerator(); - } - - public bool Remove(string key) - { - return Headers.Remove(key); - } - - public bool Remove(KeyValuePair item) - { - return Headers.Remove(item.Key); - } - - public bool TryGetValue(string key, [MaybeNullWhen(false)] out object value) - { - return Headers.TryGetValue(key, out value); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return Headers.GetEnumerator(); - } -} diff --git a/src/Stream/src/Stream/Binder/PartitionHandler.cs b/src/Stream/src/Stream/Binder/PartitionHandler.cs deleted file mode 100644 index a0a101ef47..0000000000 --- a/src/Stream/src/Stream/Binder/PartitionHandler.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Messaging; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binder; - -public class PartitionHandler -{ - private readonly IProducerOptions _producerOptions; - - private readonly IExpressionParser _expressionParser; - private readonly IEvaluationContext _evaluationContext; - internal readonly IPartitionKeyExtractorStrategy PartitionKeyExtractorStrategy; - internal readonly IPartitionSelectorStrategy PartitionSelectorStrategy; - - public int PartitionCount { get; set; } - - public PartitionHandler(IExpressionParser expressionParser, IEvaluationContext evaluationContext, IProducerOptions options, - IPartitionKeyExtractorStrategy partitionKeyExtractorStrategy, IPartitionSelectorStrategy partitionSelectorStrategy) - { - _expressionParser = expressionParser; - _evaluationContext = evaluationContext ?? new StandardEvaluationContext(); - _producerOptions = options; - PartitionKeyExtractorStrategy = partitionKeyExtractorStrategy; - PartitionSelectorStrategy = partitionSelectorStrategy; - PartitionCount = _producerOptions.PartitionCount; - } - - public int DeterminePartition(IMessage message) - { - object key = ExtractKey(message); - - int partition; - - if (!string.IsNullOrEmpty(_producerOptions.PartitionSelectorExpression) && _expressionParser != null) - { - IExpression expr = _expressionParser.ParseExpression(_producerOptions.PartitionSelectorExpression); - partition = expr.GetValue(_evaluationContext, key); - } - else - { - partition = PartitionSelectorStrategy.SelectPartition(key, PartitionCount); - } - - //// protection in case a user selector returns a negative. - return Math.Abs(partition % PartitionCount); - } - - private object ExtractKey(IMessage message) - { - object key = InvokeKeyExtractor(message); - - if (key == null && !string.IsNullOrEmpty(_producerOptions.PartitionKeyExpression) && _expressionParser != null) - { - IExpression expr = _expressionParser.ParseExpression(_producerOptions.PartitionKeyExpression); - key = expr.GetValue(_evaluationContext ?? new StandardEvaluationContext(), message); - } - - if (key == null) - { - throw new InvalidOperationException("Partition key cannot be null."); - } - - return key; - } - - private object InvokeKeyExtractor(IMessage message) - { - if (PartitionKeyExtractorStrategy != null) - { - return PartitionKeyExtractorStrategy.ExtractKey(message); - } - - return null; - } -} diff --git a/src/Stream/src/Stream/Binder/RequeueCurrentMessageException.cs b/src/Stream/src/Stream/Binder/RequeueCurrentMessageException.cs deleted file mode 100644 index c874734011..0000000000 --- a/src/Stream/src/Stream/Binder/RequeueCurrentMessageException.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder; - -public class RequeueCurrentMessageException : Exception -{ - public RequeueCurrentMessageException() - { - } - - public RequeueCurrentMessageException(string message) - : base(message) - { - } - - public RequeueCurrentMessageException(Exception innerException) - : base(null, innerException) - { - } - - public RequeueCurrentMessageException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Stream/src/Stream/Binding/AbstractBindable.cs b/src/Stream/src/Stream/Binding/AbstractBindable.cs deleted file mode 100644 index 5c94144477..0000000000 --- a/src/Stream/src/Stream/Binding/AbstractBindable.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -public abstract class AbstractBindable : IBindable -{ - private static readonly ICollection Bindings = new List(); - private static readonly ICollection Empty = new List(); - - public virtual Type BindingType { get; } - - public virtual ICollection Inputs => Empty; - - public virtual ICollection Outputs => Empty; - - protected AbstractBindable() - { - } - - protected AbstractBindable(Type bindingType) - { - BindingType = bindingType; - } - - public virtual ICollection CreateAndBindInputs(IBindingService bindingService) - { - return Bindings; - } - - public virtual ICollection CreateAndBindOutputs(IBindingService bindingService) - { - return Bindings; - } - - public virtual object GetBoundInputTarget(string name) - { - return null; - } - - public virtual object GetBoundOutputTarget(string name) - { - return null; - } - - public virtual object GetBoundTarget(string name) - { - return null; - } - - public virtual void UnbindInputs(IBindingService bindingService) - { - } - - public virtual void UnbindOutputs(IBindingService bindingService) - { - } -} diff --git a/src/Stream/src/Stream/Binding/AbstractBindableProxyFactory.cs b/src/Stream/src/Stream/Binding/AbstractBindableProxyFactory.cs deleted file mode 100644 index 4784739128..0000000000 --- a/src/Stream/src/Stream/Binding/AbstractBindableProxyFactory.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -public class AbstractBindableProxyFactory : AbstractBindable -{ - protected IDictionary bindables; - protected IList bindingTargetFactories; - - protected Dictionary> boundInputTargets = new(); - protected Dictionary> boundOutputTargets = new(); - - public override ICollection Inputs => boundInputTargets.Keys; - - public override ICollection Outputs => boundOutputTargets.Keys; - - public AbstractBindableProxyFactory(Type bindingType, IEnumerable bindingTargetFactories) - : base(bindingType) - { - this.bindingTargetFactories = bindingTargetFactories.ToList(); - Initialize(); - } - - public override ICollection CreateAndBindInputs(IBindingService bindingService) - { - var bindings = new List(); - - foreach (KeyValuePair> boundTarget in boundInputTargets) - { - ICollection result = bindingService.BindConsumer(boundTarget.Value.Value, boundTarget.Key); - bindings.AddRange(result); - } - - return bindings; - } - - public override ICollection CreateAndBindOutputs(IBindingService bindingService) - { - var bindings = new List(); - - foreach (KeyValuePair> boundTarget in boundOutputTargets) - { - IBinding result = bindingService.BindProducer(boundTarget.Value.Value, boundTarget.Key); - bindings.Add(result); - } - - return bindings; - } - - public override void UnbindInputs(IBindingService bindingService) - { - foreach (KeyValuePair> boundTarget in boundInputTargets) - { - bindingService.UnbindConsumers(boundTarget.Key); - } - } - - public override void UnbindOutputs(IBindingService bindingService) - { - foreach (KeyValuePair> boundTarget in boundOutputTargets) - { - bindingService.UnbindProducers(boundTarget.Key); - } - } - - public override object GetBoundTarget(string name) - { - object result = GetBoundInputTarget(name) ?? GetBoundOutputTarget(name); - return result; - } - - public override object GetBoundInputTarget(string name) - { - boundInputTargets.TryGetValue(name, out Lazy result); - return result.Value; - } - - public override object GetBoundOutputTarget(string name) - { - boundOutputTargets.TryGetValue(name, out Lazy result); - return result.Value; - } - - protected void Initialize() - { - bindables = BindingHelpers.CollectBindables(BindingType); - - foreach (Bindable bindable in bindables.Values) - { - IBindingTargetFactory factory = GetBindingTargetFactory(bindable.BindingTargetType); - - if (bindable.IsInput) - { - var creator = new Lazy(() => factory.CreateInput(bindable.Name)); - boundInputTargets.Add(bindable.Name, creator); - } - else - { - var creator = new Lazy(() => factory.CreateOutput(bindable.Name)); - boundOutputTargets.Add(bindable.Name, creator); - } - } - } - - protected virtual IBindingTargetFactory GetBindingTargetFactory(Type bindingTargetType) - { - IBindingTargetFactory result = null; - - foreach (IBindingTargetFactory factory in bindingTargetFactories) - { - if (factory.CanCreate(bindingTargetType)) - { - if (result == null) - { - result = factory; - } - else - { - throw new InvalidOperationException($"Multiple factories found for binding target type: {bindingTargetType}"); - } - } - } - - if (result == null) - { - throw new InvalidOperationException($"No factory found for binding target type: {bindingTargetType}"); - } - - return result; - } -} diff --git a/src/Stream/src/Stream/Binding/AbstractBindingLifecycle.cs b/src/Stream/src/Stream/Binding/AbstractBindingLifecycle.cs deleted file mode 100644 index ca38c8a594..0000000000 --- a/src/Stream/src/Stream/Binding/AbstractBindingLifecycle.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Lifecycle; - -namespace Steeltoe.Stream.Binding; - -public abstract class AbstractBindingLifecycle : ISmartLifecycle -{ - private readonly List _bindables; - protected readonly IBindingService BindingService; - - private volatile bool _running; - - public virtual bool IsRunning => _running; - - public virtual bool IsAutoStartup => true; - - public virtual int Phase => int.MaxValue; - - protected AbstractBindingLifecycle(IBindingService bindingService, IEnumerable bindables) - { - BindingService = bindingService; - _bindables = bindables.ToList(); - } - - public virtual async Task StartAsync() - { - if (!_running) - { - foreach (IBindable bindable in _bindables) - { - await Task.Run(() => DoStartWithBindable(bindable)); - } - - _running = true; - } - } - - public virtual async Task StopAsync() - { - if (_running) - { - foreach (IBindable bindable in _bindables) - { - await Task.Run(() => DoStopWithBindable(bindable)); - } - - _running = false; - } - } - - public async Task StopAsync(Action callback) - { - await StopAsync(); - callback?.Invoke(); - } - - protected abstract void DoStartWithBindable(IBindable bindable); - - protected abstract void DoStopWithBindable(IBindable bindable); -} diff --git a/src/Stream/src/Stream/Binding/AbstractBindingTargetFactory.cs b/src/Stream/src/Stream/Binding/AbstractBindingTargetFactory.cs deleted file mode 100644 index f8c8f99928..0000000000 --- a/src/Stream/src/Stream/Binding/AbstractBindingTargetFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; - -namespace Steeltoe.Stream.Binding; - -public abstract class AbstractBindingTargetFactory : IBindingTargetFactory -{ - public Type BindingTargetType { get; } - - public IApplicationContext ApplicationContext { get; } - - protected AbstractBindingTargetFactory(IApplicationContext context) - { - BindingTargetType = typeof(T); - ApplicationContext = context; - } - - public virtual bool CanCreate(Type type) - { - return type.IsAssignableFrom(BindingTargetType); - } - - public abstract T CreateInput(string name); - - public abstract T CreateOutput(string name); - - object IBindingTargetFactory.CreateInput(string name) - { - return CreateInput(name); - } - - object IBindingTargetFactory.CreateOutput(string name) - { - return CreateOutput(name); - } -} diff --git a/src/Stream/src/Stream/Binding/AbstractContentTypeInterceptor.cs b/src/Stream/src/Stream/Binding/AbstractContentTypeInterceptor.cs deleted file mode 100644 index 2ff94bdcf8..0000000000 --- a/src/Stream/src/Stream/Binding/AbstractContentTypeInterceptor.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Stream.Binding; - -internal abstract class AbstractContentTypeInterceptor : AbstractChannelInterceptor -{ - protected readonly MimeType MimeType; - - protected AbstractContentTypeInterceptor(string contentType) - { - MimeType = MimeTypeUtils.ParseMimeType(contentType); - } - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - return message is ErrorMessage ? message : DoPreSend(message, channel); - } - - public abstract IMessage DoPreSend(IMessage message, IMessageChannel channel); -} diff --git a/src/Stream/src/Stream/Binding/AbstractStreamListenerSetupMethodOrchestrator.cs b/src/Stream/src/Stream/Binding/AbstractStreamListenerSetupMethodOrchestrator.cs deleted file mode 100644 index d0304583c6..0000000000 --- a/src/Stream/src/Stream/Binding/AbstractStreamListenerSetupMethodOrchestrator.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Binding; - -public abstract class AbstractStreamListenerSetupMethodOrchestrator : IStreamListenerSetupMethodOrchestrator -{ - protected readonly IApplicationContext Context; - - protected AbstractStreamListenerSetupMethodOrchestrator(IApplicationContext context) - { - Context = context; - } - - public object[] AdaptAndRetrieveInboundArguments(MethodInfo method, string inboundName, - params IStreamListenerParameterAdapter[] streamListenerParameterAdapters) - { - object[] arguments = new object[method.GetParameters().Length]; - - for (int parameterIndex = 0; parameterIndex < arguments.Length; parameterIndex++) - { - ParameterInfo methodParameter = method.GetParameters()[parameterIndex]; - Type parameterType = methodParameter.ParameterType; - string targetReferenceValue = null; - - if (methodParameter.GetCustomAttribute() != null) - { - var attr = methodParameter.GetCustomAttribute(); - targetReferenceValue = attr.Name; - } - else if (methodParameter.GetCustomAttribute() != null) - { - var attr = methodParameter.GetCustomAttribute(); - targetReferenceValue = attr.Name; - } - else if (arguments.Length == 1 && !string.IsNullOrEmpty(inboundName)) - { - targetReferenceValue = inboundName; - } - - if (targetReferenceValue != null) - { - object targetBean = BindingHelpers.GetBindableTarget(Context, targetReferenceValue); - - // Iterate existing parameter adapters first - foreach (IStreamListenerParameterAdapter streamListenerParameterAdapter in streamListenerParameterAdapters) - { - if (streamListenerParameterAdapter.Supports(targetBean.GetType(), methodParameter)) - { - arguments[parameterIndex] = streamListenerParameterAdapter.Adapt(targetBean, methodParameter); - break; - } - } - - if (arguments[parameterIndex] == null && parameterType.IsInstanceOfType(targetBean)) - { - arguments[parameterIndex] = targetBean; - } - - if (arguments[parameterIndex] == null) - { - throw new InvalidOperationException($"Cannot convert argument {parameterIndex} of {method} from {targetBean.GetType()} to {parameterType}"); - } - } - else - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidDeclarativeMethodParameters); - } - } - - return arguments; - } - - public abstract void OrchestrateStreamListener(StreamListenerAttribute streamListener, MethodInfo method, Type implementationType); - - public abstract bool Supports(MethodInfo method); -} diff --git a/src/Stream/src/Stream/Binding/Bindable.cs b/src/Stream/src/Stream/Binding/Bindable.cs deleted file mode 100644 index 396725bc78..0000000000 --- a/src/Stream/src/Stream/Binding/Bindable.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Stream.Binding; - -public record struct Bindable -{ - public bool IsInput { get; set; } - - public string Name { get; set; } - - public Type BindingTargetType { get; set; } - - public MethodInfo FactoryMethod { get; set; } -} diff --git a/src/Stream/src/Stream/Binding/BindableProxyFactory.cs b/src/Stream/src/Stream/Binding/BindableProxyFactory.cs deleted file mode 100644 index 8a74ccbd7d..0000000000 --- a/src/Stream/src/Stream/Binding/BindableProxyFactory.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Reflection; -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binding; - -public class BindableProxyFactory : AbstractBindableProxyFactory, IBindableProxyFactory -{ - private readonly ConcurrentDictionary _targetCache = new(); - - public BindableProxyFactory(Type bindingType, IEnumerable bindingTargetFactories) - : base(bindingType, bindingTargetFactories) - { - } - - public virtual object Invoke(MethodInfo info) - { - if (_targetCache.TryGetValue(info, out object boundTarget)) - { - return boundTarget; - } - - Bindable bindable = bindables.Values.SingleOrDefault(c => c.FactoryMethod == info); - - // Doesn't exist, return null - if (bindable.Name == null) - { - return null; - } - - // Otherwise, its valid and must be an Input or Output - if (bindable.IsInput) - { - return _targetCache.GetOrAdd(info, boundInputTargets[bindable.Name].Value); - } - - return _targetCache.GetOrAdd(info, boundOutputTargets[bindable.Name].Value); - } - - public virtual void ReplaceInputChannel(string originalChannelName, string newChannelName, ISubscribableChannel messageChannel) - { - boundInputTargets.Remove(originalChannelName); - var creator = new Lazy(messageChannel); - boundInputTargets.Add(newChannelName, creator); - } - - public virtual void ReplaceOutputChannel(string originalChannelName, string newChannelName, IMessageChannel messageChannel) - { - boundOutputTargets.Remove(originalChannelName); - var creator = new Lazy(messageChannel); - boundOutputTargets.Add(newChannelName, creator); - } -} diff --git a/src/Stream/src/Stream/Binding/BinderAwareChannelResolver.cs b/src/Stream/src/Stream/Binding/BinderAwareChannelResolver.cs deleted file mode 100644 index f30e10e302..0000000000 --- a/src/Stream/src/Stream/Binding/BinderAwareChannelResolver.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Options; -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binding; - -public class BinderAwareChannelResolver : DefaultMessageChannelDestinationResolver -{ - private readonly IBindingService _bindingService; - private readonly SubscribableChannelBindingTargetFactory _bindingTargetFactory; - private readonly DynamicDestinationsBindable _dynamicDestinationsBindable; - private readonly INewDestinationBindingCallback _newBindingCallback; - private readonly IOptionsMonitor _optionsMonitor; - - public BindingServiceOptions Options => _optionsMonitor.CurrentValue; - - public BinderAwareChannelResolver(IApplicationContext context, IOptionsMonitor optionsMonitor, IBindingService bindingService, - SubscribableChannelBindingTargetFactory bindingTargetFactory, DynamicDestinationsBindable dynamicDestinationsBindable) - : this(context, optionsMonitor, bindingService, bindingTargetFactory, dynamicDestinationsBindable, null) - { - } - - public BinderAwareChannelResolver(IApplicationContext context, IOptionsMonitor optionsMonitor, IBindingService bindingService, - SubscribableChannelBindingTargetFactory bindingTargetFactory, DynamicDestinationsBindable dynamicDestinationsBindable, - INewDestinationBindingCallback callback) - : base(context) - { - ArgumentGuard.NotNull(bindingService); - ArgumentGuard.NotNull(bindingTargetFactory); - - _dynamicDestinationsBindable = dynamicDestinationsBindable; - _optionsMonitor = optionsMonitor; - _bindingService = bindingService; - _bindingTargetFactory = bindingTargetFactory; - _newBindingCallback = callback; - } - - public override IMessageChannel ResolveDestination(string name) - { - BindingServiceOptions options = Options; - List dynamicDestinations = options.DynamicDestinations; - - IMessageChannel channel; - bool dynamicAllowed = dynamicDestinations.Count == 0 || dynamicDestinations.Contains(name); - - try - { - channel = base.ResolveDestination(name); - - if (channel == null && dynamicAllowed) - { - channel = CreateDynamic(name, options); - } - } - catch (DestinationResolutionException) - { - if (!dynamicAllowed) - { - throw; - } - - channel = CreateDynamic(name, options); - } - - return channel; - } - - private IMessageChannel CreateDynamic(string name, BindingServiceOptions options) - { - ISubscribableChannel channel = _bindingTargetFactory.CreateOutput(name); - - if (_newBindingCallback != null) - { - ProducerOptions producerOptions = options.GetProducerOptions(name); - - _newBindingCallback.Configure(name, channel, producerOptions, null); - options.UpdateProducerOptions(name, producerOptions); - } - - IBinding binding = _bindingService.BindProducer(channel, name); - _dynamicDestinationsBindable.AddOutputBinding(name, binding); - return channel; - } - - public interface INewDestinationBindingCallback - { - void Configure(string channelName, IMessageChannel channel, ProducerOptions producerOptions, object extendedProducerOptions); - } -} diff --git a/src/Stream/src/Stream/Binding/BindingHelpers.cs b/src/Stream/src/Stream/Binding/BindingHelpers.cs deleted file mode 100644 index 3badb93147..0000000000 --- a/src/Stream/src/Stream/Binding/BindingHelpers.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Binding; - -public static class BindingHelpers -{ - public static T GetBindable(IApplicationContext context, string name) - { - return context.GetService(name); - } - - public static object GetBindableTarget(IApplicationContext context, string name) - { - return context.GetService(name); - } - - public static IDictionary CollectBindables(Type bindingType) - { - IDictionary bindables = new Dictionary(); - CollectFromProperties(bindingType, bindables); - CollectFromMethods(bindingType, bindables); - return bindables; - } - - internal static void CollectFromProperties(Type bindingType, IDictionary targets) - { - PropertyInfo[] infos = bindingType.GetProperties(BindingFlags.Instance | BindingFlags.Public); - - foreach (PropertyInfo info in infos) - { - MethodInfo getMethod = info.GetGetMethod(); - - if (getMethod != null) - { - if (info.GetCustomAttribute(typeof(InputAttribute)) is InputAttribute attribute) - { - var target = new Bindable - { - IsInput = true, - Name = attribute.Name ?? info.Name, - BindingTargetType = getMethod.ReturnType, - FactoryMethod = getMethod - }; - - AddBindableComponent(target, targets); - } - - if (info.GetCustomAttribute(typeof(OutputAttribute)) is OutputAttribute attribute2) - { - var target = new Bindable - { - IsInput = false, - Name = attribute2.Name ?? info.Name, - BindingTargetType = getMethod.ReturnType, - FactoryMethod = getMethod - }; - - AddBindableComponent(target, targets); - } - } - } - - foreach (Type @interface in bindingType.GetInterfaces()) - { - CollectFromProperties(@interface, targets); - } - } - - internal static void CollectFromMethods(Type bindingType, IDictionary components) - { - MethodInfo[] methods = bindingType.GetMethods(BindingFlags.Instance | BindingFlags.Public); - - foreach (MethodInfo method in methods) - { - if (method.GetCustomAttribute(typeof(InputAttribute)) is InputAttribute attribute) - { - var target = new Bindable - { - IsInput = true, - Name = attribute.Name ?? method.Name, - BindingTargetType = method.ReturnType, - FactoryMethod = method - }; - - AddBindableComponent(target, components); - } - - if (method.GetCustomAttribute(typeof(OutputAttribute)) is OutputAttribute attribute2) - { - var target = new Bindable - { - IsInput = false, - Name = attribute2.Name ?? method.Name, - BindingTargetType = method.ReturnType, - FactoryMethod = method - }; - - AddBindableComponent(target, components); - } - } - - foreach (Type @interface in bindingType.GetInterfaces()) - { - CollectFromMethods(@interface, components); - } - } - - internal static void AddBindableComponent(Bindable component, IDictionary components) - { - if (components.ContainsKey(component.Name)) - { - throw new InvalidOperationException($"Duplicate bindable target with name: {component.Name}"); - } - - components.Add(component.Name, component); - } -} diff --git a/src/Stream/src/Stream/Binding/BindingProxyGenerator.cs b/src/Stream/src/Stream/Binding/BindingProxyGenerator.cs deleted file mode 100644 index 286a842f18..0000000000 --- a/src/Stream/src/Stream/Binding/BindingProxyGenerator.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Castle.DynamicProxy; -using Steeltoe.Common; - -namespace Steeltoe.Stream.Binding; - -public class BindingProxyGenerator -{ - public static object CreateProxy(IBindableProxyFactory factory) - { - return GenerateProxy(factory); - } - - internal static object GenerateProxy(IBindableProxyFactory factory) - { - ArgumentGuard.NotNull(factory); - - var generator = new ProxyGenerator(); - Func del = factory.Invoke; - object proxy = generator.CreateInterfaceProxyWithoutTarget(factory.BindingType, new BindingInterceptor(del)); - return proxy; - } - - private sealed class BindingInterceptor : IInterceptor - { - private readonly Delegate _impl; - - public BindingInterceptor(Delegate impl) - { - _impl = impl; - } - - public void Intercept(IInvocation invocation) - { - object result = _impl.DynamicInvoke(invocation.Method); - invocation.ReturnValue = result; - } - } -} diff --git a/src/Stream/src/Stream/Binding/BindingService.cs b/src/Stream/src/Stream/Binding/BindingService.cs deleted file mode 100644 index 57c5852fd1..0000000000 --- a/src/Stream/src/Stream/Binding/BindingService.cs +++ /dev/null @@ -1,220 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binding; - -public class BindingService : IBindingService -{ - private readonly IBinderFactory _binderFactory; - private readonly BindingServiceOptions _bindingServiceOptions; - private readonly IOptionsMonitor _optionsMonitor; - private readonly ILogger _logger; - internal IDictionary ProducerBindings = new Dictionary(); - internal IDictionary> ConsumerBindings = new Dictionary>(); - - public BindingServiceOptions Options - { - get - { - if (_optionsMonitor != null) - { - return _optionsMonitor.CurrentValue; - } - - return _bindingServiceOptions; - } - } - - public BindingService(IOptionsMonitor optionsMonitor, IBinderFactory binderFactory, ILogger logger = null) - { - _optionsMonitor = optionsMonitor; - _binderFactory = binderFactory; - _logger = logger; - } - - internal BindingService(BindingServiceOptions bindingServiceOptions, IBinderFactory binderFactory, ILogger logger = null) - { - _bindingServiceOptions = bindingServiceOptions; - _binderFactory = binderFactory; - _logger = logger; - } - - public ICollection BindConsumer(T inputChannel, string name) - { - var bindings = new List(); - IBinder binder = GetBinder(name); - IConsumerOptions consumerOptions = Options.GetConsumerOptions(name); - - ValidateOptions(consumerOptions); - - string bindingTarget = Options.GetBindingDestination(name); - - if (consumerOptions.Multiplex) - { - bindings.Add(DoBindConsumer(inputChannel, name, binder, consumerOptions, bindingTarget)); - } - else - { - string[] bindingTargets = bindingTarget == null - ? Array.Empty() - : bindingTarget.Split(new[] - { - ',' - }, StringSplitOptions.RemoveEmptyEntries); - - foreach (string target in bindingTargets) - { - IBinding binding = DoBindConsumer(inputChannel, name, binder, consumerOptions, target); - bindings.Add(binding); - } - } - - ConsumerBindings[name] = new List(bindings); - return bindings; - } - - public IBinding BindProducer(T outputChannel, string name) - { - string bindingTarget = Options.GetBindingDestination(name); - IBinder binder = GetBinder(name); - ProducerOptions producerOptions = Options.GetProducerOptions(name); - ValidateOptions(producerOptions); - IBinding binding = DoBindProducer(outputChannel, bindingTarget, binder, producerOptions); - ProducerBindings[name] = binding; - return binding; - } - - public IBinding DoBindConsumer(T inputTarget, string name, IBinder binder, IConsumerOptions consumerOptions, string bindingTarget) - { - if (Options.BindingRetryInterval <= 0) - { - return binder.BindConsumer(bindingTarget, Options.GetGroup(name), inputTarget, consumerOptions); - } - - return DoBindConsumerWithRetry(inputTarget, name, binder, consumerOptions, bindingTarget); - } - - public IBinding DoBindConsumerWithRetry(T inputChan, string name, IBinder binder, IConsumerOptions consumerOptions, string bindingTarget) - { - do - { - try - { - return binder.BindConsumer(bindingTarget, Options.GetGroup(name), inputChan, consumerOptions); - } - catch (Exception ex) - { - _logger?.LogDebug(ex, ex.Message); - Thread.Sleep(Options.BindingRetryInterval * 1000); - } - } - while (true); - } - - public IBinding DoBindProducer(T outputChan, string bindingTarget, IBinder binder, IProducerOptions producerOptions) - { - if (Options.BindingRetryInterval <= 0) - { - return binder.BindProducer(bindingTarget, outputChan, producerOptions); - } - - return DoBindProducerWithRetry(outputChan, bindingTarget, binder, producerOptions); - } - - public IBinding DoBindProducerWithRetry(T outputChan, string bindingTarget, IBinder binder, IProducerOptions producerOptions) - { - do - { - try - { - return binder.BindProducer(bindingTarget, outputChan, producerOptions); - } - catch (Exception) - { - // log - Thread.Sleep(Options.BindingRetryInterval * 1000); - } - } - while (true); - } - - public void UnbindProducers(string outputName) - { - if (ProducerBindings.TryGetValue(outputName, out IBinding binding)) - { - ProducerBindings.Remove(outputName); - binding.UnbindAsync().GetAwaiter().GetResult(); - } - } - - public void UnbindConsumers(string inputName) - { - if (ConsumerBindings.TryGetValue(inputName, out List bindings)) - { - ConsumerBindings.Remove(inputName); - - foreach (IBinding binding in bindings) - { - binding.UnbindAsync().GetAwaiter().GetResult(); - } - } - } - - protected IBinder GetBinder(string channelName) - { - string configName = Options.GetBinder(channelName); - return _binderFactory.GetBinder(configName, typeof(T)); - } - - private static void ValidateOptions(IProducerOptions producerOptions) - { - if (producerOptions.PartitionCount <= 0) - { - throw new InvalidOperationException("Partition count should be greater than zero."); - } - } - - private static void ValidateOptions(IConsumerOptions consumerOptions) - { - if (consumerOptions.Concurrency <= 0) - { - throw new InvalidOperationException("Concurrency should be greater than zero."); - } - - if (consumerOptions.InstanceCount <= -1) - { - throw new InvalidOperationException("Instance count should be greater than or equal to -1."); - } - - if (consumerOptions.InstanceIndex <= -1) - { - throw new InvalidOperationException("Instance index should be greater than or equal to -1."); - } - - if (consumerOptions.MaxAttempts <= 0) - { - throw new InvalidOperationException("Max attempts should be greater than zero."); - } - - if (consumerOptions.BackOffInitialInterval <= 0) - { - throw new InvalidOperationException("Backoff initial interval should be greater than zero."); - } - - if (consumerOptions.BackOffMaxInterval <= 0) - { - throw new InvalidOperationException("Backoff max interval should be greater than zero."); - } - - if (consumerOptions.BackOffMultiplier <= 0) - { - throw new InvalidOperationException("Backoff multiplier should be greater than zero."); - } - } -} diff --git a/src/Stream/src/Stream/Binding/CompositeMessageChannelConfigurer.cs b/src/Stream/src/Stream/Binding/CompositeMessageChannelConfigurer.cs deleted file mode 100644 index ba09f8871c..0000000000 --- a/src/Stream/src/Stream/Binding/CompositeMessageChannelConfigurer.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -public class CompositeMessageChannelConfigurer : IMessageChannelAndSourceConfigurer -{ - private readonly List _messageChannelConfigurers; - - public CompositeMessageChannelConfigurer(IEnumerable messageChannelConfigurers) - { - _messageChannelConfigurers = messageChannelConfigurers.ToList(); - } - - public void ConfigureInputChannel(IMessageChannel messageChannel, string channelName) - { - foreach (IMessageChannelConfigurer messageChannelConfigurer in _messageChannelConfigurers) - { - messageChannelConfigurer.ConfigureInputChannel(messageChannel, channelName); - } - } - - public void ConfigureOutputChannel(IMessageChannel messageChannel, string channelName) - { - foreach (IMessageChannelConfigurer messageChannelConfigurer in _messageChannelConfigurers) - { - messageChannelConfigurer.ConfigureOutputChannel(messageChannel, channelName); - } - } - - public void ConfigurePolledMessageSource(IPollableMessageSource binding, string name) - { - foreach (IMessageChannelConfigurer channelConfigurer in _messageChannelConfigurers) - { - if (channelConfigurer is IMessageChannelAndSourceConfigurer configurer) - { - configurer.ConfigurePolledMessageSource(binding, name); - } - } - } -} diff --git a/src/Stream/src/Stream/Binding/DefaultPartitionSelector.cs b/src/Stream/src/Stream/Binding/DefaultPartitionSelector.cs deleted file mode 100644 index 59e538cc38..0000000000 --- a/src/Stream/src/Stream/Binding/DefaultPartitionSelector.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -internal sealed class DefaultPartitionSelector : IPartitionSelectorStrategy -{ - public string ServiceName { get; set; } = "DefaultPartitionSelector"; - - public int SelectPartition(object key, int partitionCount) - { - int hashcode = key.GetHashCode(); - - if (hashcode == int.MinValue) - { - hashcode = 0; - } - - return Math.Abs(hashcode); - } -} diff --git a/src/Stream/src/Stream/Binding/DefaultStreamListenerSetupMethodOrchestrator.cs b/src/Stream/src/Stream/Binding/DefaultStreamListenerSetupMethodOrchestrator.cs deleted file mode 100644 index 10fc9f75e2..0000000000 --- a/src/Stream/src/Stream/Binding/DefaultStreamListenerSetupMethodOrchestrator.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Binding; - -public class DefaultStreamListenerSetupMethodOrchestrator : AbstractStreamListenerSetupMethodOrchestrator -{ - private readonly List _streamListenerParameterAdapters; - private readonly List _streamListenerResultAdapters; - private readonly StreamListenerAttributeProcessor _processor; - - public DefaultStreamListenerSetupMethodOrchestrator(StreamListenerAttributeProcessor processor, IApplicationContext context, - IEnumerable streamListenerParameterAdapters, IEnumerable streamListenerResultAdapters) - : base(context) - { - _streamListenerParameterAdapters = streamListenerParameterAdapters.ToList(); - _streamListenerResultAdapters = streamListenerResultAdapters.ToList(); - _processor = processor; - } - - public override void OrchestrateStreamListener(StreamListenerAttribute streamListener, MethodInfo method, Type implementationType) - { - string methodAnnotatedInboundName = streamListener.Target; - - var streamListenerMethod = new StreamListenerMethodValidator(method, Context, _streamListenerParameterAdapters); - streamListenerMethod.Validate(methodAnnotatedInboundName, streamListener.Condition); - - bool isDeclarative = streamListenerMethod.CheckDeclarativeMethod(methodAnnotatedInboundName); - - if (isDeclarative) - { - string methodAnnotatedOutboundName = streamListenerMethod.GetOutboundBindingTargetName(); - object[] adaptedInboundArguments = AdaptAndRetrieveInboundArguments(method, methodAnnotatedInboundName, _streamListenerParameterAdapters.ToArray()); - InvokeStreamListenerResultAdapter(method, implementationType, methodAnnotatedOutboundName, adaptedInboundArguments); - } - else - { - RegisterHandlerMethodOnListenedChannel(streamListenerMethod, streamListener, implementationType); - } - } - - public override bool Supports(MethodInfo method) - { - return true; - } - - private void InvokeStreamListenerResultAdapter(MethodInfo method, Type implementationType, string outboundName, params object[] arguments) - { - object bean = ActivatorUtilities.CreateInstance(Context.ServiceProvider, implementationType); - - if (method.ReturnType == typeof(void)) - { - method.Invoke(bean, arguments); - } - else - { - object result = method.Invoke(bean, arguments); - - if (string.IsNullOrEmpty(outboundName)) - { - ParameterInfo[] parameters = method.GetParameters(); - - foreach (ParameterInfo methodParameter in parameters) - { - var attr = methodParameter.GetCustomAttribute(); - - if (attr != null) - { - outboundName = attr.Name; - } - } - } - - object targetBean = BindingHelpers.GetBindableTarget(Context, outboundName); - - foreach (IStreamListenerResultAdapter streamListenerResultAdapter in _streamListenerResultAdapters) - { - if (streamListenerResultAdapter.Supports(result.GetType(), targetBean.GetType())) - { - streamListenerResultAdapter.Adapt(result, targetBean); - break; - } - } - } - } - - private void RegisterHandlerMethodOnListenedChannel(StreamListenerMethodValidator streamListenerMethod, StreamListenerAttribute streamListener, - Type implementationType) - { - if (string.IsNullOrEmpty(streamListener.Target)) - { - throw new ArgumentException($"{nameof(streamListener.Target)} in {nameof(streamListener)} must not be null or empty.", nameof(streamListener)); - } - - MethodInfo method = streamListenerMethod.Method; - - string defaultOutputChannel = streamListenerMethod.GetOutboundBindingTargetName(); - - if (method.ReturnType == typeof(void)) - { - if (!string.IsNullOrEmpty(defaultOutputChannel)) - { - throw new InvalidOperationException("An output channel cannot be specified for a method that does not return a value."); - } - } - else - { - if (string.IsNullOrEmpty(defaultOutputChannel)) - { - throw new InvalidOperationException("An output channel must be specified for a method that can return a value."); - } - } - - streamListenerMethod.ValidateStreamListenerMessageHandler(); - - _processor.AddMappedListenerMethod(streamListener.Target, - new StreamListenerHandlerMethodMapping(implementationType, method, streamListener.Condition, defaultOutputChannel, streamListener.CopyHeaders)); - } -} diff --git a/src/Stream/src/Stream/Binding/DispatchingStreamListenerMessageHandler.cs b/src/Stream/src/Stream/Binding/DispatchingStreamListenerMessageHandler.cs deleted file mode 100644 index 7fd05b9f10..0000000000 --- a/src/Stream/src/Stream/Binding/DispatchingStreamListenerMessageHandler.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Integration.Handler; -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binding; - -public class DispatchingStreamListenerMessageHandler : AbstractReplyProducingMessageHandler -{ - private readonly List _handlerMethods; - private readonly bool _evaluateExpressions; - private readonly IEvaluationContext _evaluationContext; - - protected override bool ShouldCopyRequestHeaders => false; - - internal DispatchingStreamListenerMessageHandler(IApplicationContext context, ICollection handlerMethods, - IEvaluationContext evaluationContext = null) - : base(context) - { - ArgumentGuard.NotNullOrEmpty(handlerMethods); - - _handlerMethods = new List(handlerMethods); - bool evaluateExpressions = false; - - foreach (ConditionalStreamListenerMessageHandlerWrapper handlerMethod in handlerMethods) - { - if (handlerMethod.Condition != null) - { - evaluateExpressions = true; - break; - } - } - - _evaluateExpressions = evaluateExpressions; - - if (evaluateExpressions && evaluationContext == null) - { - throw new InvalidOperationException("Evaluation context must be provided when evaluating expressions."); - } - - _evaluationContext = evaluationContext; - } - - public override void Initialize() - { - // Intentionally left empty. - } - - protected override object HandleRequestMessage(IMessage requestMessage) - { - List matchingHandlers = _evaluateExpressions ? FindMatchingHandlers(requestMessage) : _handlerMethods; - - if (matchingHandlers.Count == 0) - { - return null; - } - - if (matchingHandlers.Count > 1) - { - foreach (ConditionalStreamListenerMessageHandlerWrapper matchingMethod in matchingHandlers) - { - matchingMethod.StreamListenerMessageHandler.HandleMessage(requestMessage); - } - - return null; - } - - ConditionalStreamListenerMessageHandlerWrapper singleMatchingHandler = matchingHandlers[0]; - singleMatchingHandler.StreamListenerMessageHandler.HandleMessage(requestMessage); - return null; - } - - private List FindMatchingHandlers(IMessage message) - { - var matchingMethods = new List(); - - foreach (ConditionalStreamListenerMessageHandlerWrapper wrapper in _handlerMethods) - { - if (wrapper.Condition == null) - { - matchingMethods.Add(wrapper); - } - else - { - bool conditionMetOnMessage = wrapper.Condition.GetValue(_evaluationContext, message); - - if (conditionMetOnMessage) - { - matchingMethods.Add(wrapper); - } - } - } - - return matchingMethods; - } - - internal sealed class ConditionalStreamListenerMessageHandlerWrapper - { - public IExpression Condition { get; } - - public bool IsVoid => StreamListenerMessageHandler.IsVoid; - - public StreamListenerMessageHandler StreamListenerMessageHandler { get; } - - internal ConditionalStreamListenerMessageHandlerWrapper(IExpression condition, StreamListenerMessageHandler streamListenerMessageHandler) - { - ArgumentGuard.NotNull(streamListenerMessageHandler); - - if (!(condition == null || streamListenerMessageHandler.IsVoid)) - { - throw new ArgumentException("Cannot specify a condition and a return value at the same time."); - } - - Condition = condition; - StreamListenerMessageHandler = streamListenerMessageHandler; - } - } -} diff --git a/src/Stream/src/Stream/Binding/DynamicDestinationsBindable.cs b/src/Stream/src/Stream/Binding/DynamicDestinationsBindable.cs deleted file mode 100644 index cd0301a994..0000000000 --- a/src/Stream/src/Stream/Binding/DynamicDestinationsBindable.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -public class DynamicDestinationsBindable : AbstractBindable -{ - private readonly ConcurrentDictionary _outputBindings = new(); - - public override Type BindingType => typeof(DynamicDestinationsBindable); - - public override ICollection Outputs => _outputBindings.Keys; - - public void AddOutputBinding(string name, IBinding binding) - { - _outputBindings.TryAdd(name, binding); - } - - public override void UnbindOutputs(IBindingService bindingService) - { - foreach (KeyValuePair entry in _outputBindings) - { - _outputBindings.TryRemove(entry.Key, out IBinding binding); - - binding?.UnbindAsync().GetAwaiter().GetResult(); - } - } -} diff --git a/src/Stream/src/Stream/Binding/IBindableProxyFactory.cs b/src/Stream/src/Stream/Binding/IBindableProxyFactory.cs deleted file mode 100644 index dade3ac7e6..0000000000 --- a/src/Stream/src/Stream/Binding/IBindableProxyFactory.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Stream.Binding; - -public interface IBindableProxyFactory -{ - Type BindingType { get; } - - object Invoke(MethodInfo info); -} diff --git a/src/Stream/src/Stream/Binding/IMessageChannelAndSourceConfigurer.cs b/src/Stream/src/Stream/Binding/IMessageChannelAndSourceConfigurer.cs deleted file mode 100644 index 8dc88af74a..0000000000 --- a/src/Stream/src/Stream/Binding/IMessageChannelAndSourceConfigurer.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -public interface IMessageChannelAndSourceConfigurer : IMessageChannelConfigurer -{ - void ConfigurePolledMessageSource(IPollableMessageSource binding, string name); -} diff --git a/src/Stream/src/Stream/Binding/IMessageChannelConfigurer.cs b/src/Stream/src/Stream/Binding/IMessageChannelConfigurer.cs deleted file mode 100644 index 07c440f1fe..0000000000 --- a/src/Stream/src/Stream/Binding/IMessageChannelConfigurer.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binding; - -public interface IMessageChannelConfigurer -{ - void ConfigureInputChannel(IMessageChannel messageChannel, string channelName); - - void ConfigureOutputChannel(IMessageChannel messageChannel, string channelName); -} diff --git a/src/Stream/src/Stream/Binding/IStreamListenerParameterAdapter.cs b/src/Stream/src/Stream/Binding/IStreamListenerParameterAdapter.cs deleted file mode 100644 index c8396a9954..0000000000 --- a/src/Stream/src/Stream/Binding/IStreamListenerParameterAdapter.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Stream.Binding; - -public interface IStreamListenerParameterAdapter -{ - bool Supports(Type bindingTargetType, ParameterInfo methodParameter); - - object Adapt(object bindingTarget, ParameterInfo parameter); -} - -public interface IStreamListenerParameterAdapter : IStreamListenerParameterAdapter -{ - TResult Adapt(TTarget bindingTarget, ParameterInfo parameter); -} diff --git a/src/Stream/src/Stream/Binding/IStreamListenerResultAdapter.cs b/src/Stream/src/Stream/Binding/IStreamListenerResultAdapter.cs deleted file mode 100644 index 71e1023794..0000000000 --- a/src/Stream/src/Stream/Binding/IStreamListenerResultAdapter.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binding; - -public interface IStreamListenerResultAdapter -{ - bool Supports(Type resultType, Type bindingTarget); - - IDisposable Adapt(object streamListenerResult, object bindingTarget); -} - -public interface IStreamListenerResultAdapter : IStreamListenerResultAdapter -{ - IDisposable Adapt(TResult streamListenerResult, TTarget bindingTarget); -} diff --git a/src/Stream/src/Stream/Binding/IStreamListenerSetupMethodOrchestrator.cs b/src/Stream/src/Stream/Binding/IStreamListenerSetupMethodOrchestrator.cs deleted file mode 100644 index e7c29f45ea..0000000000 --- a/src/Stream/src/Stream/Binding/IStreamListenerSetupMethodOrchestrator.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Binding; - -public interface IStreamListenerSetupMethodOrchestrator -{ - bool Supports(MethodInfo method); - - void OrchestrateStreamListener(StreamListenerAttribute streamListener, MethodInfo method, Type implementationType); - - object[] AdaptAndRetrieveInboundArguments(MethodInfo method, string inboundName, params IStreamListenerParameterAdapter[] streamListenerParameterAdapters); -} diff --git a/src/Stream/src/Stream/Binding/InboundContentTypeEnhancingInterceptor.cs b/src/Stream/src/Stream/Binding/InboundContentTypeEnhancingInterceptor.cs deleted file mode 100644 index 9dece5e0af..0000000000 --- a/src/Stream/src/Stream/Binding/InboundContentTypeEnhancingInterceptor.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -internal sealed class InboundContentTypeEnhancingInterceptor : AbstractContentTypeInterceptor -{ - public InboundContentTypeEnhancingInterceptor(string contentType) - : base(contentType) - { - } - - public override IMessage DoPreSend(IMessage message, IMessageChannel channel) - { - var messageHeaders = message.Headers as MessageHeaders; - MimeType contentType = MimeType; - - /* - * NOTE: The below code for BINDER_ORIGINAL_CONTENT_TYPE is to support legacy - * message format established in 1.x version of the Java Streams framework and should/will - * no longer be supported in 3.x of Java Streams - */ - - if (message.Headers.ContainsKey(BinderHeaders.BinderOriginalContentType)) - { - object ct = message.Headers.Get(BinderHeaders.BinderOriginalContentType); - - switch (ct) - { - case string stringValue: - contentType = MimeType.ToMimeType(stringValue); - break; - case MimeType mimeType: - contentType = mimeType; - break; - } - - messageHeaders.RawHeaders.Remove(BinderHeaders.BinderOriginalContentType); - - if (messageHeaders.RawHeaders.ContainsKey(MessageHeaders.ContentType)) - { - messageHeaders.RawHeaders.Remove(MessageHeaders.ContentType); - } - } - - if (!message.Headers.ContainsKey(MessageHeaders.ContentType)) - { - messageHeaders.RawHeaders.Add(MessageHeaders.ContentType, contentType); - } - else if (message.Headers.TryGetValue(MessageHeaders.ContentType, out object header) && header is string stringHeader) - { - messageHeaders.RawHeaders[MessageHeaders.ContentType] = MimeType.ToMimeType(stringHeader); - } - - return message; - } -} diff --git a/src/Stream/src/Stream/Binding/InputBindingLifecycle.cs b/src/Stream/src/Stream/Binding/InputBindingLifecycle.cs deleted file mode 100644 index 57dafea9ae..0000000000 --- a/src/Stream/src/Stream/Binding/InputBindingLifecycle.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -public class InputBindingLifecycle : AbstractBindingLifecycle -{ - internal List InputBindings = new(); - - public override int Phase { get; } = int.MaxValue - 1000; - - public InputBindingLifecycle(IBindingService bindingService, IEnumerable bindables) - : base(bindingService, bindables) - { - } - - protected override void DoStartWithBindable(IBindable bindable) - { - ICollection bindableBindings = bindable.CreateAndBindInputs(BindingService); - - if (bindableBindings != null) - { - InputBindings.AddRange(bindableBindings); - } - } - - protected override void DoStopWithBindable(IBindable bindable) - { - bindable.UnbindInputs(BindingService); - } -} diff --git a/src/Stream/src/Stream/Binding/MessageChannelStreamListenerResultAdapter.cs b/src/Stream/src/Stream/Binding/MessageChannelStreamListenerResultAdapter.cs deleted file mode 100644 index 9706ee24eb..0000000000 --- a/src/Stream/src/Stream/Binding/MessageChannelStreamListenerResultAdapter.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Handler; -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Binding; - -public class MessageChannelStreamListenerResultAdapter : IStreamListenerResultAdapter -{ - private readonly IApplicationContext _context; - - public MessageChannelStreamListenerResultAdapter(IApplicationContext context) - { - _context = context; - } - - public bool Supports(Type resultType, Type bindingTarget) - { - return typeof(IMessageChannel).IsAssignableFrom(resultType) && typeof(IMessageChannel).IsAssignableFrom(bindingTarget); - } - - public IDisposable Adapt(IMessageChannel streamListenerResult, IMessageChannel bindingTarget) - { - var handler = new BridgeHandler(_context) - { - OutputChannel = bindingTarget - }; - - ((ISubscribableChannel)streamListenerResult).Subscribe(handler); - - return new NoOpDisposable(); - } - - public IDisposable Adapt(object streamListenerResult, object bindingTarget) - { - if (streamListenerResult is not IMessageChannel listenerResult) - { - throw new ArgumentException($"Value must be of type {nameof(IMessageChannel)}.", nameof(streamListenerResult)); - } - - if (bindingTarget is not IMessageChannel target) - { - throw new ArgumentException($"Value must be of type {nameof(IMessageChannel)}.", nameof(bindingTarget)); - } - - return Adapt(listenerResult, target); - } - - public sealed class NoOpDisposable : IDisposable - { - public void Dispose() - { - } - } -} diff --git a/src/Stream/src/Stream/Binding/MessageConverterConfigurer.cs b/src/Stream/src/Stream/Binding/MessageConverterConfigurer.cs deleted file mode 100644 index 4db8f2c426..0000000000 --- a/src/Stream/src/Stream/Binding/MessageConverterConfigurer.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Options; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binding; - -public class MessageConverterConfigurer : IMessageChannelAndSourceConfigurer -{ - private readonly IOptionsMonitor _optionsMonitor; - private readonly IMessageConverterFactory _messageConverterFactory; - private readonly IEnumerable _extractors; - private readonly IEnumerable _selectors; - - private BindingServiceOptions Options => _optionsMonitor.CurrentValue; - - public MessageConverterConfigurer(IApplicationContext applicationContext, IOptionsMonitor optionsMonitor, - IMessageConverterFactory messageConverterFactory, IEnumerable extractors, - IEnumerable selectors) - { - _optionsMonitor = optionsMonitor; - _messageConverterFactory = messageConverterFactory; - _extractors = extractors; - _selectors = selectors; - } - - public void ConfigureInputChannel(IMessageChannel messageChannel, string channelName) - { - ConfigureMessageChannel(messageChannel, channelName, true); - } - - public void ConfigureOutputChannel(IMessageChannel messageChannel, string channelName) - { - ConfigureMessageChannel(messageChannel, channelName, false); - } - - public void ConfigurePolledMessageSource(IPollableMessageSource binding, string name) - { - IBindingOptions bindingOptions = Options.GetBindingOptions(name); - string contentType = bindingOptions.ContentType; - IConsumerOptions consumerOptions = bindingOptions.Consumer; - - if ((consumerOptions == null || !consumerOptions.UseNativeDecoding) && binding is DefaultPollableMessageSource source) - { - source.AddInterceptor(new InboundContentTypeEnhancingInterceptor(contentType)); - } - } - - private static bool IsNativeEncodingNotSet(IProducerOptions producerOptions, IConsumerOptions consumerOptions, bool input) - { - return input ? consumerOptions == null || !consumerOptions.UseNativeDecoding : producerOptions == null || !producerOptions.UseNativeEncoding; - } - - private void ConfigureMessageChannel(IMessageChannel channel, string channelName, bool inbound) - { - if (channel is not AbstractMessageChannel messageChannel) - { - throw new ArgumentException($"{nameof(channel)} must be of type {nameof(AbstractMessageChannel)}.", nameof(channel)); - } - - IBindingOptions bindingOptions = Options.GetBindingOptions(channelName); - string contentType = bindingOptions.ContentType; - IProducerOptions producerOptions = bindingOptions.Producer; - - if (!inbound && producerOptions != null && producerOptions.IsPartitioned) - { - messageChannel.AddInterceptor(new PartitioningInterceptor(new SpelExpressionParser(), null, bindingOptions, - GetPartitionKeyExtractorStrategy(producerOptions), GetPartitionSelectorStrategy(producerOptions))); - } - - IConsumerOptions consumerOptions = bindingOptions.Consumer; - - if (IsNativeEncodingNotSet(producerOptions, consumerOptions, inbound)) - { - if (inbound) - { - messageChannel.AddInterceptor(new InboundContentTypeEnhancingInterceptor(contentType)); - } - else - { - messageChannel.AddInterceptor( - new OutboundContentTypeConvertingInterceptor(contentType, _messageConverterFactory.MessageConverterForAllRegistered)); - } - } - } - - private IPartitionKeyExtractorStrategy GetPartitionKeyExtractorStrategy(IProducerOptions options) - { - IPartitionKeyExtractorStrategy strategy = null; - - if (!string.IsNullOrEmpty(options.PartitionKeyExtractorName)) - { - strategy = _extractors?.FirstOrDefault(s => s.ServiceName == options.PartitionKeyExtractorName); - - if (strategy == null) - { - throw new InvalidOperationException( - $"PartitionKeyExtractorStrategy bean with the name '{options.PartitionKeyExtractorName}' can not be found."); - } - } - else - { - if (_extractors?.Count() > 1) - { - throw new InvalidOperationException("Multiple `IPartitionKeyExtractorStrategy` found from service container."); - } - - if (_extractors?.Count() == 1) - { - strategy = _extractors.Single(); - } - } - - return strategy; - } - - private IPartitionSelectorStrategy GetPartitionSelectorStrategy(IProducerOptions options) - { - IPartitionSelectorStrategy strategy = null; - - if (!string.IsNullOrEmpty(options.PartitionSelectorName)) - { - strategy = _selectors.FirstOrDefault(s => s.ServiceName == options.PartitionSelectorName); - - if (strategy == null) - { - throw new InvalidOperationException($"IPartitionSelectorStrategy bean with the name '{options.PartitionSelectorName}' can not be found."); - } - } - else - { - if (_selectors.Count() > 1) - { - throw new InvalidOperationException("Multiple `IPartitionSelectorStrategy` found from service container."); - } - - strategy = _selectors.Count() == 1 ? _selectors.Single() : new DefaultPartitionSelector(); - } - - return strategy; - } -} diff --git a/src/Stream/src/Stream/Binding/MessageSourceBindingTargetFactory.cs b/src/Stream/src/Stream/Binding/MessageSourceBindingTargetFactory.cs deleted file mode 100644 index b50ac8eb50..0000000000 --- a/src/Stream/src/Stream/Binding/MessageSourceBindingTargetFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging.Converter; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -public class MessageSourceBindingTargetFactory : AbstractBindingTargetFactory -{ - private readonly IMessageChannelAndSourceConfigurer _messageConfigurer; - private readonly ISmartMessageConverter _messageConverter; - - public MessageSourceBindingTargetFactory(IApplicationContext context, ISmartMessageConverter messageConverter, - CompositeMessageChannelConfigurer messageConfigurer) - : base(context) - { - _messageConfigurer = messageConfigurer; - _messageConverter = messageConverter; - } - - public override IPollableMessageSource CreateInput(string name) - { - var chan = new DefaultPollableMessageSource(ApplicationContext, _messageConverter); - _messageConfigurer.ConfigurePolledMessageSource(chan, name); - return chan; - } - - public override IPollableMessageSource CreateOutput(string name) - { - throw new InvalidOperationException(); - } -} diff --git a/src/Stream/src/Stream/Binding/OutboundContentTypeConvertingInterceptor.cs b/src/Stream/src/Stream/Binding/OutboundContentTypeConvertingInterceptor.cs deleted file mode 100644 index 433a0f4c04..0000000000 --- a/src/Stream/src/Stream/Binding/OutboundContentTypeConvertingInterceptor.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -internal sealed class OutboundContentTypeConvertingInterceptor : AbstractContentTypeInterceptor -{ - private readonly IMessageConverter _messageConverter; - - public OutboundContentTypeConvertingInterceptor(string contentType, IMessageConverter messageConverter) - : base(contentType) - { - _messageConverter = messageConverter; - } - - public override IMessage DoPreSend(IMessage message, IMessageChannel channel) - { - // If handler is a function, FunctionInvoker will already perform message - // conversion. - // In fact in the future we should consider propagating knowledge of the - // default content type - // to MessageConverters instead of interceptors - if (message.Payload is byte[] && message.Headers.ContainsKey(MessageHeaders.ContentType)) - { - return message; - } - - // ===== 1.3 backward compatibility code part-1 === - string oct = null; - - if (message.Headers.ContainsKey(MessageHeaders.ContentType)) - { - oct = message.Headers.Get(MessageHeaders.ContentType).ToString(); - } - - string ct = oct; - - if (message.Payload is string) - { - ct = oct == MimeTypeUtils.ApplicationJsonValue ? MimeTypeUtils.ApplicationJsonValue : MimeTypeUtils.TextPlainValue; - } - - // ===== END 1.3 backward compatibility code part-1 === - if (!message.Headers.ContainsKey(MessageHeaders.ContentType)) - { - var messageHeaders = message.Headers as MessageHeaders; - messageHeaders.RawHeaders[MessageHeaders.ContentType] = MimeType; - } - - IMessage result = message.Payload is byte[] ? message : _messageConverter.ToMessage(message.Payload, message.Headers); - - if (result == null) - { - throw new InvalidOperationException($"Failed to convert message: '{message}' to outbound message."); - } - - // ===== 1.3 backward compatibility code part-2 === - if (ct != null && ct != oct && oct != null) - { - var messageHeaders = result.Headers as MessageHeaders; - messageHeaders.RawHeaders[MessageHeaders.ContentType] = MimeType.ToMimeType(ct); - messageHeaders.RawHeaders[BinderHeaders.BinderOriginalContentType] = MimeType.ToMimeType(oct); - } - - // ===== END 1.3 backward compatibility code part-2 === - return result; - } -} diff --git a/src/Stream/src/Stream/Binding/OutputBindingLifecycle.cs b/src/Stream/src/Stream/Binding/OutputBindingLifecycle.cs deleted file mode 100644 index 185b768f8a..0000000000 --- a/src/Stream/src/Stream/Binding/OutputBindingLifecycle.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Binding; - -public class OutputBindingLifecycle : AbstractBindingLifecycle -{ - internal List OutputBindings = new(); - - public override int Phase { get; } = int.MinValue + 1000; - - public OutputBindingLifecycle(IBindingService bindingService, IEnumerable bindables) - : base(bindingService, bindables) - { - } - - protected override void DoStartWithBindable(IBindable bindable) - { - ICollection bindableBindings = bindable.CreateAndBindOutputs(BindingService); - OutputBindings.AddRange(bindableBindings); - } - - protected override void DoStopWithBindable(IBindable bindable) - { - bindable.UnbindOutputs(BindingService); - } -} diff --git a/src/Stream/src/Stream/Binding/PartitioningInterceptor.cs b/src/Stream/src/Stream/Binding/PartitioningInterceptor.cs deleted file mode 100644 index 6e71bd712e..0000000000 --- a/src/Stream/src/Stream/Binding/PartitioningInterceptor.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binding; - -internal sealed class PartitioningInterceptor : AbstractChannelInterceptor -{ - internal readonly IBindingOptions BindingOptions; - - internal readonly PartitionHandler PartitionHandler; - internal readonly IMessageBuilderFactory MessageBuilderFactory = new MutableIntegrationMessageBuilderFactory(); - - public int PartitionCount - { - get => PartitionHandler.PartitionCount; - set => PartitionHandler.PartitionCount = value; - } - - public PartitioningInterceptor(IExpressionParser expressionParser, IEvaluationContext evaluationContext, IBindingOptions bindingOptions, - IPartitionKeyExtractorStrategy partitionKeyExtractorStrategy, IPartitionSelectorStrategy partitionSelectorStrategy) - { - BindingOptions = bindingOptions; - - PartitionHandler = new PartitionHandler(expressionParser, evaluationContext, BindingOptions.Producer, partitionKeyExtractorStrategy, - partitionSelectorStrategy); - } - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - IMessage - objMessage = message as IMessage ?? - Message.Create(message.Payload, message.Headers); // Primitives are not covariant with out T, so box the primitive ... - - if (!message.Headers.ContainsKey(BinderHeaders.PartitionOverride)) - { - int partition = PartitionHandler.DeterminePartition(message); - return MessageBuilderFactory.FromMessage(objMessage).SetHeader(BinderHeaders.PartitionHeader, partition).Build(); - } - - return MessageBuilderFactory.FromMessage(objMessage).SetHeader(BinderHeaders.PartitionHeader, message.Headers[BinderHeaders.PartitionOverride]) - .RemoveHeader(BinderHeaders.PartitionOverride).Build(); - } -} diff --git a/src/Stream/src/Stream/Binding/StreamListenerAttributeProcessor.cs b/src/Stream/src/Stream/Binding/StreamListenerAttributeProcessor.cs deleted file mode 100644 index 913eedda38..0000000000 --- a/src/Stream/src/Stream/Binding/StreamListenerAttributeProcessor.cs +++ /dev/null @@ -1,194 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Integration.Handler; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binding; - -public class StreamListenerAttributeProcessor -{ - private readonly IApplicationContext _context; - private readonly IOptionsMonitor _springIntegrationOptionsMonitor; - private readonly IDestinationResolver _binderAwareChannelResolver; - private readonly IMessageHandlerMethodFactory _messageHandlerMethodFactory; - private readonly List _methodOrchestrators; - private readonly List _streamListenerMethods; - internal readonly Dictionary> MappedListenerMethods = new(); - - private SpringIntegrationOptions IntegrationOptions => _springIntegrationOptionsMonitor.CurrentValue; - -#pragma warning disable S107 // Methods should not have too many parameters - public StreamListenerAttributeProcessor(IApplicationContext context, IOptionsMonitor springIntegrationOptionsMonitor, - IEnumerable streamListenerParameterAdapters, IEnumerable streamListenerResultAdapters, - IDestinationResolver binderAwareChannelResolver, IMessageHandlerMethodFactory messageHandlerMethodFactory, - IEnumerable methodOrchestrators, IEnumerable methods) -#pragma warning restore S107 // Methods should not have too many parameters - { - _context = context; - _springIntegrationOptionsMonitor = springIntegrationOptionsMonitor; - _binderAwareChannelResolver = binderAwareChannelResolver; - _messageHandlerMethodFactory = messageHandlerMethodFactory; - _streamListenerMethods = methods.ToList(); - _methodOrchestrators = methodOrchestrators.ToList(); - - _methodOrchestrators.Add( - new DefaultStreamListenerSetupMethodOrchestrator(this, context, streamListenerParameterAdapters, streamListenerResultAdapters)); - } - - public void Initialize() - { - if (_streamListenerMethods.Count <= 0) - { - return; - } - - foreach (IStreamListenerMethod method in _streamListenerMethods) - { - DoPostProcess(method); - } - - foreach (KeyValuePair> mappedBindingEntry in MappedListenerMethods) - { - var handlers = new List(); - - foreach (StreamListenerHandlerMethodMapping mapping in mappedBindingEntry.Value) - { - object targetBean = CreateTargetBean(mapping.ImplementationType); - - IInvocableHandlerMethod invocableHandlerMethod = _messageHandlerMethodFactory.CreateInvocableHandlerMethod(targetBean, mapping.Method); - - var streamListenerMessageHandler = new StreamListenerMessageHandler(_context, invocableHandlerMethod, mapping.CopyHeaders, - IntegrationOptions.MessageHandlerNotPropagatedHeaders); - - if (!string.IsNullOrEmpty(mapping.DefaultOutputChannel)) - { - streamListenerMessageHandler.OutputChannelName = mapping.DefaultOutputChannel; - } - - if (!string.IsNullOrEmpty(mapping.Condition)) - { - var parser = _context.GetService(); - - if (parser == null) - { - throw new InvalidOperationException("StreamListener attribute contains a 'Condition', but no Expression parser is configured"); - } - - string conditionAsString = ResolveExpressionAsString(mapping.Condition); - IExpression condition = parser.ParseExpression(conditionAsString); - - handlers.Add(new DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper(condition, - streamListenerMessageHandler)); - } - else - { - handlers.Add(new DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper(null, - streamListenerMessageHandler)); - } - } - - if (handlers.Count > 1) - { - foreach (DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper h in handlers) - { - if (!h.IsVoid) - { - throw new InvalidOperationException(StreamListenerErrorMessages.MultipleValueReturningMethods); - } - } - } - - AbstractReplyProducingMessageHandler handler; - - if (handlers.Count > 1 || handlers[0].Condition != null) - { - var evaluationContext = _context.GetService(); - handler = new DispatchingStreamListenerMessageHandler(_context, handlers, evaluationContext); - } - else - { - handler = handlers[0].StreamListenerMessageHandler; - } - - handler.IntegrationServices.ChannelResolver = _binderAwareChannelResolver; - - if (BindingHelpers.GetBindable(_context, mappedBindingEntry.Key) is not ISubscribableChannel channel) - { - throw new InvalidOperationException($"Unable to locate ISubscribableChannel with ServiceName: {mappedBindingEntry.Key}"); - } - - channel.Subscribe(handler); - } - - MappedListenerMethods.Clear(); - } - - internal void AddMappedListenerMethod(string key, StreamListenerHandlerMethodMapping mappedMethod) - { - if (!MappedListenerMethods.TryGetValue(key, out List mappings)) - { - mappings = new List(); - MappedListenerMethods.Add(key, mappings); - } - - mappings.Add(mappedMethod); - } - - protected virtual StreamListenerAttribute PostProcessAttribute(StreamListenerAttribute attribute) - { - return attribute; - } - - private static string ResolveExpressionAsString(string value) - { - string resolvedValue = value; - - if (value.StartsWith("#{", StringComparison.Ordinal) && value.EndsWith('}')) - { - resolvedValue = value.Substring(2, value.Length - 2 - 1); - } - - return resolvedValue; - } - - private object CreateTargetBean(Type implementationType) - { - try - { - return _context.GetService(implementationType); - } - catch (Exception e) - { - // Log - throw new InvalidOperationException($"Unable to CreateInstance of type containing StreamListener method, Type: {implementationType}", e); - } - } - - private void DoPostProcess(IStreamListenerMethod listenerMethod) - { - MethodInfo method = listenerMethod.Method; - StreamListenerAttribute streamListener = PostProcessAttribute(listenerMethod.Attribute); - Type beanType = listenerMethod.ImplementationType; - - IEnumerable orchestrators = _methodOrchestrators.Select(o => o.Supports(method) ? o : null); - - if (orchestrators.Count() != 1) - { - throw new InvalidOperationException("Unable to determine IStreamListenerSetupMethodOrchestrator to utilize"); - } - - IStreamListenerSetupMethodOrchestrator orchestrator = orchestrators.Single(); - orchestrator.OrchestrateStreamListener(streamListener, method, beanType); - } -} diff --git a/src/Stream/src/Stream/Binding/StreamListenerErrorMessages.cs b/src/Stream/src/Stream/Binding/StreamListenerErrorMessages.cs deleted file mode 100644 index 91c93f1420..0000000000 --- a/src/Stream/src/Stream/Binding/StreamListenerErrorMessages.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binding; - -public static class StreamListenerErrorMessages -{ - private const string Prefix = "A method attributed with StreamListener "; - - public const string InvalidInboundName = "The Input attribute must have the name of an input as value"; - - public const string InvalidOutboundName = "The Output attribute must have the name of an output as value"; - - public const string AtLeastOneOutput = "At least one output must be specified"; - - public const string SendToMultipleDestinations = "Multiple destinations cannot be specified"; - - public const string SendToEmptyDestination = "An empty destination cannot be specified"; - - public const string InvalidInputOutputMethodParameters = "Input or Output attribute are not permitted on " + - "method parameters while using the StreamListener value and a method-level output specification"; - - public const string NoInputDestination = "No input destination is configured. Use either the StreamListener value or Input"; - - public const string AmbiguousMessageHandlerMethodArguments = "Ambiguous method arguments for the StreamListener method"; - - public const string InvalidInputValues = "Cannot set both StreamListener value and Input attribute as method parameter"; - - public const string InvalidInputValueWithOutputMethodParam = "Setting the StreamListener value when using Output attribute " + - "as method parameter is not permitted. Use Input method parameter attribute to specify inbound value instead"; - - public const string InvalidOutputValues = "Cannot set both output (Output/SendTo) method attribute value and Output attribute as a method parameter"; - - public const string ConditionOnDeclarativeMethod = "Cannot set a condition when using StreamListener in declarative mode"; - - public const string ConditionOnMethodReturningValue = "Cannot set a condition for methods that return a value"; - - public const string MultipleValueReturningMethods = - "If multiple StreamListener methods are listening to the same binding target, none of them may return a value"; - - public const string InputAtStreamListener = - $"{Prefix}may never be annotated with Input. If it should listen to a specific input, use the value of StreamListener instead"; - - public const string ReturnTypeNoOutboundSpecified = $"{Prefix}having a return type should also have an outbound target specified"; - - public const string ReturnTypeMultipleOutboundSpecified = $"{Prefix}having a return type should have only one outbound target specified"; - - public const string InvalidDeclarativeMethodParameters = - $"{Prefix}may use Input or Output attributes only in declarative mode and for parameters that are binding targets or convertible from binding targets."; -} diff --git a/src/Stream/src/Stream/Binding/StreamListenerHandlerMethodMapping.cs b/src/Stream/src/Stream/Binding/StreamListenerHandlerMethodMapping.cs deleted file mode 100644 index 84099b0530..0000000000 --- a/src/Stream/src/Stream/Binding/StreamListenerHandlerMethodMapping.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; - -namespace Steeltoe.Stream.Binding; - -internal sealed class StreamListenerHandlerMethodMapping -{ - public Type ImplementationType { get; } - - public MethodInfo Method { get; } - - public string Condition { get; } - - public string DefaultOutputChannel { get; } - - public bool CopyHeaders { get; } - - public StreamListenerHandlerMethodMapping(Type implementationType, MethodInfo method, string condition, string defaultOutputChannel, bool copyHeaders) - { - ImplementationType = implementationType; - Method = method; - Condition = condition; - DefaultOutputChannel = defaultOutputChannel; - CopyHeaders = copyHeaders; - } -} diff --git a/src/Stream/src/Stream/Binding/StreamListenerMessageHandler.cs b/src/Stream/src/Stream/Binding/StreamListenerMessageHandler.cs deleted file mode 100644 index 11165df580..0000000000 --- a/src/Stream/src/Stream/Binding/StreamListenerMessageHandler.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Handler; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Stream.Binding; - -public class StreamListenerMessageHandler : AbstractReplyProducingMessageHandler -{ - private readonly IInvocableHandlerMethod _invocableHandlerMethod; - - protected override bool ShouldCopyRequestHeaders { get; } - - public bool IsVoid => _invocableHandlerMethod.IsVoid; - - public StreamListenerMessageHandler(IApplicationContext context, IInvocableHandlerMethod invocableHandlerMethod, bool copyHeaders, - IList notPropagatedHeaders) - : base(context) - { - _invocableHandlerMethod = invocableHandlerMethod; - ShouldCopyRequestHeaders = copyHeaders; - NotPropagatedHeaders = notPropagatedHeaders; - } - - public override void Initialize() - { - // Intentionally left empty. - } - - protected override object HandleRequestMessage(IMessage requestMessage) - { - try - { - object result = _invocableHandlerMethod.Invoke(requestMessage); - return result; - } - catch (Exception e) - { - if (e is MessagingException) - { - throw; - } - - throw new MessagingException(requestMessage, $"Exception thrown while invoking {_invocableHandlerMethod.ShortLogMessage}", e); - } - } -} diff --git a/src/Stream/src/Stream/Binding/StreamListenerMethodValidator.cs b/src/Stream/src/Stream/Binding/StreamListenerMethodValidator.cs deleted file mode 100644 index acc9fc2b97..0000000000 --- a/src/Stream/src/Stream/Binding/StreamListenerMethodValidator.cs +++ /dev/null @@ -1,335 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Binding; - -public class StreamListenerMethodValidator -{ - private readonly IApplicationContext _context; - private readonly List _streamListenerParameterAdapters; - - public MethodInfo Method { get; } - - public StreamListenerMethodValidator(MethodInfo method, IApplicationContext context = null, - List streamListenerParameterAdapters = null) - { - Method = method; - _context = context; - _streamListenerParameterAdapters = streamListenerParameterAdapters ?? new List(); - } - - public int GetInputAnnotationCount() - { - int count = 0; - ParameterInfo[] parameters = Method.GetParameters(); - - foreach (ParameterInfo methodParameter in parameters) - { - if (methodParameter.GetCustomAttribute() != null) - { - count++; - } - } - - return count; - } - - public int GetOutputAnnotationCount() - { - int count = 0; - ParameterInfo[] parameters = Method.GetParameters(); - - foreach (ParameterInfo methodParameter in parameters) - { - if (methodParameter.GetCustomAttribute() != null) - { - count++; - } - } - - return count; - } - - public void Validate(string methodAnnotatedInboundName, string condition) - { - string methodAnnotatedOutboundName = GetOutboundBindingTargetName(); - int inputAnnotationCount = GetInputAnnotationCount(); - int outputAnnotationCount = GetOutputAnnotationCount(); - bool isDeclarative = CheckDeclarativeMethod(methodAnnotatedInboundName); - - ParameterInfo[] parameters = Method.GetParameters(); - int methodArgumentsLength = parameters.Length; - Type returnType = Method.ReturnType; - - if (!isDeclarative && !(inputAnnotationCount == 0 && outputAnnotationCount == 0)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidDeclarativeMethodParameters); - } - - if (!string.IsNullOrEmpty(methodAnnotatedInboundName) && !string.IsNullOrEmpty(methodAnnotatedOutboundName) && - !(inputAnnotationCount == 0 && outputAnnotationCount == 0)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidInputOutputMethodParameters); - } - - if (!string.IsNullOrEmpty(methodAnnotatedInboundName)) - { - if (inputAnnotationCount != 0) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidInputValues); - } - - if (outputAnnotationCount != 0) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidInputValueWithOutputMethodParam); - } - } - else - { - if (inputAnnotationCount < 1) - { - throw new InvalidOperationException(StreamListenerErrorMessages.NoInputDestination); - } - } - - if (!string.IsNullOrEmpty(methodAnnotatedOutboundName) && outputAnnotationCount != 0) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidOutputValues); - } - - if (returnType != typeof(void) && !string.IsNullOrEmpty(condition)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.ConditionOnMethodReturningValue); - } - - if (isDeclarative) - { - if (!string.IsNullOrEmpty(condition)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.ConditionOnDeclarativeMethod); - } - - for (int parameterIndex = 0; parameterIndex < methodArgumentsLength; parameterIndex++) - { - ParameterInfo methodParameter = parameters[parameterIndex]; - - if (methodParameter.GetCustomAttribute() != null) - { - var attribute = methodParameter.GetCustomAttribute(); - string inboundName = attribute.Name; - - if (string.IsNullOrEmpty(inboundName)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidInboundName); - } - } - - if (methodParameter.GetCustomAttribute() != null) - { - var attribute = methodParameter.GetCustomAttribute(); - string outboundName = attribute.Name; - - if (string.IsNullOrEmpty(outboundName)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidOutboundName); - } - } - } - - if (methodArgumentsLength > 1 && inputAnnotationCount + outputAnnotationCount != methodArgumentsLength) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidDeclarativeMethodParameters); - } - } - - if (typeof(void) != returnType && string.IsNullOrEmpty(methodAnnotatedOutboundName)) - { - if (outputAnnotationCount == 0) - { - throw new InvalidOperationException(StreamListenerErrorMessages.ReturnTypeNoOutboundSpecified); - } - - if (outputAnnotationCount != 1) - { - throw new InvalidOperationException(StreamListenerErrorMessages.ReturnTypeMultipleOutboundSpecified); - } - } - } - - public void ValidateStreamListenerMessageHandler() - { - ParameterInfo[] parameters = Method.GetParameters(); - int methodArgumentsLength = parameters.Length; - - if (methodArgumentsLength > 1) - { - int numAnnotatedMethodParameters = 0; - int numPayloadAnnotations = 0; - - for (int parameterIndex = 0; parameterIndex < methodArgumentsLength; parameterIndex++) - { - ParameterInfo methodParameter = parameters[parameterIndex]; - IEnumerable attributes = methodParameter.GetCustomAttributes(); - - if (attributes.Any()) - { - numAnnotatedMethodParameters++; - } - - if (methodParameter.GetCustomAttribute() != null) - { - numPayloadAnnotations++; - } - } - - if (numPayloadAnnotations > 0 && !(methodArgumentsLength == numAnnotatedMethodParameters && numPayloadAnnotations <= 1)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.AmbiguousMessageHandlerMethodArguments); - } - } - } - - public string GetOutboundBindingTargetName() - { - var sendTo = Method.GetCustomAttribute(); - - if (sendTo != null) - { - if (sendTo.Destinations == null || !sendTo.Destinations.Any()) - { - throw new InvalidOperationException(StreamListenerErrorMessages.AtLeastOneOutput); - } - - if (sendTo.Destinations.Length != 1) - { - throw new InvalidOperationException(StreamListenerErrorMessages.SendToMultipleDestinations); - } - - if (string.IsNullOrEmpty(sendTo.Destinations[0])) - { - throw new InvalidOperationException(StreamListenerErrorMessages.SendToEmptyDestination); - } - - return sendTo.Destinations[0]; - } - - var output = Method.GetCustomAttribute(); - - if (output != null) - { - if (string.IsNullOrEmpty(output.Name)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.AtLeastOneOutput); - } - - return output.Name; - } - - return null; - } - - public bool CheckDeclarativeMethod(string methodAnnotatedInboundName) - { - ParameterInfo[] parameters = Method.GetParameters(); - int methodArgumentsLength = parameters.Length; - string methodAnnotatedOutboundName = GetOutboundBindingTargetName(); - - for (int parameterIndex = 0; parameterIndex < methodArgumentsLength; parameterIndex++) - { - ParameterInfo methodParameter = parameters[parameterIndex]; - - if (methodParameter.GetCustomAttribute() != null) - { - var attribute = methodParameter.GetCustomAttribute(); - string inboundName = attribute.Name; - - if (string.IsNullOrEmpty(inboundName)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidInboundName); - } - - if (IsDeclarativeMethodParameter(inboundName, methodParameter)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidDeclarativeMethodParameters); - } - - return true; - } - - if (methodParameter.GetCustomAttribute() != null) - { - var attribute = methodParameter.GetCustomAttribute(); - string outboundName = attribute.Name; - - if (string.IsNullOrEmpty(outboundName)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidOutboundName); - } - - if (IsDeclarativeMethodParameter(outboundName, methodParameter)) - { - throw new InvalidOperationException(StreamListenerErrorMessages.InvalidDeclarativeMethodParameters); - } - - return true; - } - - if (!string.IsNullOrEmpty(methodAnnotatedOutboundName)) - { - return IsDeclarativeMethodParameter(methodAnnotatedOutboundName, methodParameter); - } - - if (!string.IsNullOrEmpty(methodAnnotatedInboundName)) - { - return IsDeclarativeMethodParameter(methodAnnotatedInboundName, methodParameter); - } - } - - return false; - } - - private bool IsDeclarativeMethodParameter(string target, ParameterInfo methodParameter) - { - bool declarative = false; - - if (_context != null) - { - object targetBindable = BindingHelpers.GetBindableTarget(_context, target); - - if (!methodParameter.ParameterType.IsAssignableFrom(typeof(object)) && targetBindable != null) - { - declarative = typeof(IMessageChannel).IsAssignableFrom(methodParameter.ParameterType); - - if (!declarative) - { - Type targetBeanClass = targetBindable.GetType(); - - foreach (IStreamListenerParameterAdapter adapter in _streamListenerParameterAdapters) - { - if (adapter.Supports(targetBeanClass, methodParameter)) - { - declarative = true; - break; - } - } - } - } - } - else - { - if (!methodParameter.ParameterType.IsAssignableFrom(typeof(object))) - { - return typeof(IMessageChannel).IsAssignableFrom(methodParameter.ParameterType); - } - } - - return declarative; - } -} diff --git a/src/Stream/src/Stream/Binding/SubscribableChannelBindingTargetFactory.cs b/src/Stream/src/Stream/Binding/SubscribableChannelBindingTargetFactory.cs deleted file mode 100644 index 1720940a3a..0000000000 --- a/src/Stream/src/Stream/Binding/SubscribableChannelBindingTargetFactory.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Binding; - -public class SubscribableChannelBindingTargetFactory : AbstractBindingTargetFactory -{ - private readonly IMessageChannelConfigurer _messageChannelConfigurer; - - public SubscribableChannelBindingTargetFactory(IApplicationContext context, CompositeMessageChannelConfigurer messageChannelConfigurer) - : base(context) - { - _messageChannelConfigurer = messageChannelConfigurer; - } - - public override ISubscribableChannel CreateInput(string name) - { - var chan = new DirectWithAttributesChannel(ApplicationContext) - { - ServiceName = name - }; - - chan.SetAttribute("type", "input"); - _messageChannelConfigurer.ConfigureInputChannel(chan, name); - - AddChannelInterceptors(chan); - - ApplicationContext.Register(name, chan); - - return chan; - } - - public override ISubscribableChannel CreateOutput(string name) - { - var chan = new DirectWithAttributesChannel(ApplicationContext) - { - ServiceName = name - }; - - chan.SetAttribute("type", "output"); - _messageChannelConfigurer.ConfigureOutputChannel(chan, name); - - AddChannelInterceptors(chan); - - ApplicationContext.Register(name, chan); - - return chan; - } - - private void AddChannelInterceptors(IMessageChannel chan) - { - if (chan is IChannelInterceptorAware aware) - { - IEnumerable interceptors = ApplicationContext.GetServices(); - - foreach (IChannelInterceptor interceptor in interceptors) - { - aware.AddInterceptor(interceptor); - } - } - } -} diff --git a/src/Stream/src/Stream/Configuration/BinderConfigurations.cs b/src/Stream/src/Stream/Configuration/BinderConfigurations.cs deleted file mode 100644 index c8967fe7fc..0000000000 --- a/src/Stream/src/Stream/Configuration/BinderConfigurations.cs +++ /dev/null @@ -1,132 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Configuration; - -public class BinderConfigurations : IBinderConfigurations -{ - private Dictionary _configurations; - - private IBinderTypeRegistry Registry { get; } - - private BindingServiceOptions Options { get; } - - public Dictionary Configurations - { - get - { - _configurations ??= GetBinderConfigurations(); - return _configurations; - } - } - - public BinderConfigurations(IBinderTypeRegistry binderTypeRegistry, IConfiguration configuration) - { - Registry = binderTypeRegistry; - Options = new BindingServiceOptions(configuration); - } - - public BinderConfigurations(IBinderTypeRegistry binderTypeRegistry, BindingServiceOptions options) - { - Registry = binderTypeRegistry; - Options = options; - } - - public List FindMatchingConfigurationsIfAny(IBinder binder) - { - var results = new List(); - IBinderType registryEntry = Registry.Get(binder.ServiceName); - - if (registryEntry != null) - { - foreach (KeyValuePair configuration in Configurations) - { - if (registryEntry.AssemblyPath == configuration.Value.ResolvedAssembly) - { - results.Add(configuration.Key); - } - } - } - - return results; - } - - internal Dictionary GetBinderConfigurations() - { - var binderConfigurations = new Dictionary(); - Dictionary declaredBinders = Options.Binders; - bool defaultCandidatesExist = false; - - // Check to see if configuration has any declared default binders - foreach (KeyValuePair binder in declaredBinders) - { - if (binder.Value.DefaultCandidate.Value) - { - defaultCandidatesExist = true; - break; - } - } - - // Go thru and match up configured binders with what was discovered in "classpath" - var existingBinderConfigurations = new List(); - - foreach (KeyValuePair declBinder in declaredBinders) - { - BinderOptions binderOptions = declBinder.Value; - - if (Registry.Get(declBinder.Key) != null) - { - // Declared binder also found on "classpath" - IBinderType binder = Registry.Get(declBinder.Key); - binderConfigurations.Add(binder.Name, new BinderConfiguration(binder.ConfigureClass, binder.AssemblyPath, binderOptions)); - } - else - { - // Declared binder, but not found on 'classpath' - - // Must have a Type configured for the configured binder - if (string.IsNullOrEmpty(binderOptions.ConfigureClass)) - { - throw new InvalidOperationException($"No 'Type' setting present for custom binder: {declBinder.Key}"); - } - - // Log, configured binder not found on "classpath" - binderConfigurations.Add(declBinder.Key, new BinderConfiguration(binderOptions.ConfigureClass, binderOptions.ConfigureAssembly, binderOptions)); - } - - existingBinderConfigurations.Add(declBinder.Key); - } - - // Check for a default binder in the matched up configuration - foreach (KeyValuePair configurationEntry in binderConfigurations) - { - if (configurationEntry.Value.IsDefaultCandidate) - { - defaultCandidatesExist = true; - } - } - - // No Default candidate, make them all default - if (!defaultCandidatesExist) - { - foreach (KeyValuePair binderEntry in Registry.GetAll()) - { - if (!existingBinderConfigurations.Contains(binderEntry.Key)) - { - binderConfigurations.Add(binderEntry.Key, new BinderConfiguration(binderEntry.Value.ConfigureClass, binderEntry.Value.AssemblyPath, - new BinderOptions - { - DefaultCandidate = true, - InheritEnvironment = true - })); - } - } - } - - return binderConfigurations; - } -} diff --git a/src/Stream/src/Stream/Configuration/BinderOptions.cs b/src/Stream/src/Stream/Configuration/BinderOptions.cs deleted file mode 100644 index eaf753ab30..0000000000 --- a/src/Stream/src/Stream/Configuration/BinderOptions.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Configuration; - -public class BinderOptions : IBinderOptions -{ - private const bool InheritEnvironmentDefault = true; - private const bool DefaultCandidateDefault = true; - - bool IBinderOptions.InheritEnvironment => InheritEnvironment.Value; - - bool IBinderOptions.DefaultCandidate => DefaultCandidate.Value; - - public string ConfigureClass { get; set; } - - public string ConfigureAssembly { get; set; } - - public Dictionary Environment { get; set; } - - public bool? InheritEnvironment { get; set; } - - public bool? DefaultCandidate { get; set; } - - internal void PostProcess() - { - Environment ??= new Dictionary(); - InheritEnvironment ??= InheritEnvironmentDefault; - DefaultCandidate ??= DefaultCandidateDefault; - } -} diff --git a/src/Stream/src/Stream/Configuration/BindingOptions.cs b/src/Stream/src/Stream/Configuration/BindingOptions.cs deleted file mode 100644 index ae22858522..0000000000 --- a/src/Stream/src/Stream/Configuration/BindingOptions.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; - -namespace Steeltoe.Stream.Configuration; - -public class BindingOptions : IBindingOptions -{ - public static readonly MimeType DefaultContentType = MimeTypeUtils.ApplicationJson; - - IConsumerOptions IBindingOptions.Consumer => Consumer; - - IProducerOptions IBindingOptions.Producer => Producer; - - public string Destination { get; set; } - - public string Group { get; set; } - - public string ContentType { get; set; } - - public string Binder { get; set; } - - public ConsumerOptions Consumer { get; set; } - - public ProducerOptions Producer { get; set; } - - internal void PostProcess(string name, BindingOptions @default) - { - Destination ??= @default?.Destination; - Group ??= @default?.Group; - ContentType ??= @default != null ? @default.ContentType : DefaultContentType.ToString(); - Binder ??= @default?.Binder; - - Consumer?.PostProcess(name, @default?.Consumer); - Producer?.PostProcess(name, @default?.Producer); - } - - internal BindingOptions Clone(bool deep = false) - { - var clone = (BindingOptions)MemberwiseClone(); - - if (deep) - { - if (Producer != null) - { - Producer = (ProducerOptions)Producer.Clone(); - } - - if (Consumer != null) - { - Consumer = (ConsumerOptions)Consumer.Clone(); - } - } - - return clone; - } -} diff --git a/src/Stream/src/Stream/Configuration/BindingServiceOptions.cs b/src/Stream/src/Stream/Configuration/BindingServiceOptions.cs deleted file mode 100644 index 894009121a..0000000000 --- a/src/Stream/src/Stream/Configuration/BindingServiceOptions.cs +++ /dev/null @@ -1,202 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Steeltoe.Common; - -namespace Steeltoe.Stream.Configuration; - -public class BindingServiceOptions // : IBindingServiceOptions -{ - private const int InstanceIndexDefault = 0; - private const int InstanceCountDefault = 1; - private const int BindingRetryIntervalDefault = 30; - private const bool OverrideCloudConnectorsDefault = false; - public const string Prefix = "spring:cloud:stream"; - - public int InstanceIndex { get; set; } = int.MinValue; - - public int InstanceCount { get; set; } = int.MinValue; - - public string DefaultBinder { get; set; } - - public int BindingRetryInterval { get; set; } = int.MinValue; - - public bool? OverrideCloudConnectors { get; set; } - - public List DynamicDestinations { get; set; } - - public Dictionary Binders { get; set; } - - public Dictionary Bindings { get; set; } - - public BindingOptions Default { get; set; } - - public BindingServiceOptions() - { - PostProcess(); - } - - internal BindingServiceOptions(IConfiguration configuration) - { - ArgumentGuard.NotNull(configuration); - - configuration.Bind(this); - PostProcess(); - } - - internal void PostProcess() - { - if (InstanceIndex == int.MinValue) - { - InstanceIndex = InstanceIndexDefault; - } - - if (InstanceCount == int.MinValue) - { - InstanceCount = InstanceCountDefault; - } - - if (BindingRetryInterval == int.MinValue) - { - BindingRetryInterval = BindingRetryIntervalDefault; - } - - OverrideCloudConnectors ??= OverrideCloudConnectorsDefault; - DynamicDestinations ??= new List(); - Binders ??= new Dictionary(); - Bindings ??= new Dictionary(); - Default ??= new BindingOptions(); - - if (Default != null) - { - Default.PostProcess("default", null); - } - - foreach (KeyValuePair binding in Bindings) - { - binding.Value.PostProcess(binding.Key, Default); - } - - foreach (KeyValuePair binder in Binders) - { - binder.Value.PostProcess(); - } - } - - public string GetBinder(string name) - { - return GetBindingOptions(name).Binder; - } - - public IDictionary AsDictionary() - { - IDictionary options = new Dictionary - { - ["instanceIndex"] = InstanceIndex, - ["instanceCount"] = InstanceCount, - ["defaultBinder"] = DefaultBinder, - - ["dynamicDestinations"] = DynamicDestinations - }; - - foreach (KeyValuePair entry in Bindings) - { - options.Add(entry.Key, entry.Value); - } - - foreach (KeyValuePair entry in Binders) - { - options.Add(entry.Key, entry.Value); - } - - return options; - } - - public BindingOptions GetBindingOptions(string name) - { - MakeBindingIfNecessary(name); - BindingOptions options = Bindings[name]; - options.Destination ??= name; - - return options; - } - - public ConsumerOptions GetConsumerOptions(string inputBindingName) - { - ArgumentGuard.NotNull(inputBindingName); - - BindingOptions bindingOptions = GetBindingOptions(inputBindingName); - ConsumerOptions consumerOptions = bindingOptions.Consumer; - - if (consumerOptions == null) - { - consumerOptions = new ConsumerOptions(); - consumerOptions.PostProcess(inputBindingName, Default.Consumer); - bindingOptions.Consumer = consumerOptions; - } - - // propagate instance count and instance index if not already set - if (consumerOptions.InstanceCount < 0) - { - consumerOptions.InstanceCount = InstanceCount; - } - - if (consumerOptions.InstanceIndex < 0) - { - consumerOptions.InstanceIndex = InstanceIndex; - } - - return consumerOptions; - } - - public ProducerOptions GetProducerOptions(string outputBindingName) - { - ArgumentGuard.NotNull(outputBindingName); - - BindingOptions bindingOptions = GetBindingOptions(outputBindingName); - ProducerOptions producerOptions = bindingOptions.Producer; - - if (producerOptions == null) - { - producerOptions = new ProducerOptions(); - producerOptions.PostProcess(outputBindingName, Default.Producer); - bindingOptions.Producer = producerOptions; - } - - return producerOptions; - } - - public string GetGroup(string bindingName) - { - return GetBindingOptions(bindingName).Group; - } - - public string GetBindingDestination(string bindingName) - { - return GetBindingOptions(bindingName).Destination; - } - - public void UpdateProducerOptions(string bindingName, ProducerOptions producerOptions) - { - if (Bindings.ContainsKey(bindingName)) - { - Bindings[bindingName].Producer = producerOptions; - } - } - - internal void MakeBindingIfNecessary(string bindingName) - { - if (!Bindings.ContainsKey(bindingName)) - { - BindToDefaults(bindingName); - } - } - - internal void BindToDefaults(string binding) - { - BindingOptions options = Default.Clone(true); - Bindings[binding] = options; - } -} diff --git a/src/Stream/src/Stream/Configuration/ConsumerOptions.cs b/src/Stream/src/Stream/Configuration/ConsumerOptions.cs deleted file mode 100644 index a7a55f32f4..0000000000 --- a/src/Stream/src/Stream/Configuration/ConsumerOptions.cs +++ /dev/null @@ -1,136 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; - -namespace Steeltoe.Stream.Configuration; - -public sealed class ConsumerOptions : IConsumerOptions -{ - private const bool AutoStartupDefault = true; - private const int ConcurrencyDefault = 1; - private const bool IsPartitionedDefault = false; - private const int InstanceCountDefault = -1; - private const int InstanceIndexDefault = -1; - private const int MaxAttemptsDefault = 3; - private const int BackOffInitialIntervalDefault = 1000; - private const int BackOffMaxIntervalDefault = 10000; - private const double BackOffMultiplierDefault = 2.0; - private const bool DefaultRetryableDefault = true; - private const bool UseNativeDecodingDefault = false; - private const bool MultiplexDefault = false; - - bool IConsumerOptions.AutoStartup => AutoStartup.Value; - - bool IConsumerOptions.IsPartitioned => Partitioned.Value; - - bool IConsumerOptions.DefaultRetryable => DefaultRetryable.Value; - - HeaderMode IConsumerOptions.HeaderMode => HeaderMode.Value; - - bool IConsumerOptions.UseNativeDecoding => UseNativeDecoding.GetValueOrDefault(); - - bool IConsumerOptions.Multiplex => Multiplex.GetValueOrDefault(); - - public string BindingName { get; set; } - - public bool? AutoStartup { get; set; } - - public int Concurrency { get; set; } = int.MinValue; - - public bool? Partitioned { get; set; } - - public int InstanceCount { get; set; } = int.MinValue; - - public int InstanceIndex { get; set; } = int.MinValue; - - public List InstanceIndexList { get; set; } - - public int MaxAttempts { get; set; } = int.MinValue; - - public int BackOffInitialInterval { get; set; } = int.MinValue; - - public int BackOffMaxInterval { get; set; } = int.MinValue; - - public double BackOffMultiplier { get; set; } = double.NaN; - - public bool? DefaultRetryable { get; set; } - - public List RetryableExceptions { get; set; } - - public HeaderMode? HeaderMode { get; set; } - - public bool? UseNativeDecoding { get; set; } - - public bool? Multiplex { get; set; } - - public ConsumerOptions() - { - } - - public ConsumerOptions(string bindingName) - { - ArgumentGuard.NotNull(bindingName); - - BindingName = bindingName; - } - - public IConsumerOptions Clone() - { - var clone = (ConsumerOptions)MemberwiseClone(); - clone.RetryableExceptions = new List(RetryableExceptions); - clone.InstanceIndexList = new List(InstanceIndexList); - return clone; - } - - internal void PostProcess(string name, ConsumerOptions @default = null) - { - BindingName = name; - Multiplex ??= @default != null ? @default.Multiplex : MultiplexDefault; - UseNativeDecoding ??= @default != null ? @default.UseNativeDecoding : UseNativeDecodingDefault; - HeaderMode ??= @default != null ? @default.HeaderMode : Configuration.HeaderMode.None; - RetryableExceptions ??= @default != null ? @default.RetryableExceptions : new List(); - DefaultRetryable ??= @default != null ? @default.DefaultRetryable : DefaultRetryableDefault; - - if (double.IsNaN(BackOffMultiplier)) - { - BackOffMultiplier = @default?.BackOffMultiplier ?? BackOffMultiplierDefault; - } - - if (BackOffMaxInterval == int.MinValue) - { - BackOffMaxInterval = @default?.BackOffMaxInterval ?? BackOffMaxIntervalDefault; - } - - if (BackOffInitialInterval == int.MinValue) - { - BackOffInitialInterval = @default?.BackOffInitialInterval ?? BackOffInitialIntervalDefault; - } - - if (MaxAttempts == int.MinValue) - { - MaxAttempts = @default?.MaxAttempts ?? MaxAttemptsDefault; - } - - if (InstanceIndex == int.MinValue) - { - InstanceIndex = @default?.InstanceIndex ?? InstanceIndexDefault; - } - - if (InstanceCount == int.MinValue) - { - InstanceCount = @default?.InstanceCount ?? InstanceCountDefault; - } - - InstanceIndexList ??= @default != null ? @default.InstanceIndexList : new List(); - Partitioned ??= @default != null ? @default.Partitioned : IsPartitionedDefault; - - if (Concurrency == int.MinValue) - { - Concurrency = @default?.Concurrency ?? ConcurrencyDefault; - } - - AutoStartup ??= @default != null ? @default.AutoStartup : AutoStartupDefault; - } -} diff --git a/src/Stream/src/Stream/Configuration/IBinderConfigurations.cs b/src/Stream/src/Stream/Configuration/IBinderConfigurations.cs deleted file mode 100644 index 85083fab40..0000000000 --- a/src/Stream/src/Stream/Configuration/IBinderConfigurations.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Configuration; - -public interface IBinderConfigurations -{ - Dictionary Configurations { get; } - - List FindMatchingConfigurationsIfAny(IBinder binder); -} diff --git a/src/Stream/src/Stream/Configuration/IStreamListenerMethod.cs b/src/Stream/src/Stream/Configuration/IStreamListenerMethod.cs deleted file mode 100644 index b972a6187d..0000000000 --- a/src/Stream/src/Stream/Configuration/IStreamListenerMethod.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Configuration; - -public interface IStreamListenerMethod -{ - public MethodInfo Method { get; } - - public StreamListenerAttribute Attribute { get; } - - public Type ImplementationType { get; } -} diff --git a/src/Stream/src/Stream/Configuration/ProducerOptions.cs b/src/Stream/src/Stream/Configuration/ProducerOptions.cs deleted file mode 100644 index 6dd6717b98..0000000000 --- a/src/Stream/src/Stream/Configuration/ProducerOptions.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common; - -namespace Steeltoe.Stream.Configuration; - -public class ProducerOptions : IProducerOptions -{ - private const bool AutoStartupDefault = true; - private const int PartitionCountDefault = 1; - private const bool UseNativeEncodingDefault = false; - private const bool IsErrorChannelEnabledDefault = false; - - bool IProducerOptions.AutoStartup => AutoStartup.Value; - - HeaderMode IProducerOptions.HeaderMode => HeaderMode.Value; - - bool IProducerOptions.UseNativeEncoding => UseNativeEncoding.Value; - - bool IProducerOptions.ErrorChannelEnabled => ErrorChannelEnabled.Value; - - public string BindingName { get; set; } - - public bool? AutoStartup { get; set; } - - public string PartitionKeyExpression { get; set; } - - public string PartitionKeyExtractorName { get; set; } - - public string PartitionSelectorName { get; set; } - - public string PartitionSelectorExpression { get; set; } - - public int PartitionCount { get; set; } = int.MinValue; - - public List RequiredGroups { get; set; } - - public HeaderMode? HeaderMode { get; set; } - - public bool? UseNativeEncoding { get; set; } - - public bool? ErrorChannelEnabled { get; set; } - - public bool IsPartitioned => PartitionKeyExpression != null || PartitionCount > 1 || PartitionKeyExtractorName != null; - - public ProducerOptions() - { - } - - public ProducerOptions(string bindingName) - { - ArgumentGuard.NotNull(bindingName); - - BindingName = bindingName; - } - - public IProducerOptions Clone() - { - var clone = (ProducerOptions)MemberwiseClone(); - clone.RequiredGroups = new List(RequiredGroups); - return clone; - } - - internal void PostProcess(string name, ProducerOptions @default = null) - { - BindingName = name; - ErrorChannelEnabled ??= @default != null ? @default.ErrorChannelEnabled : IsErrorChannelEnabledDefault; - UseNativeEncoding ??= @default != null ? @default.UseNativeEncoding : UseNativeEncodingDefault; - HeaderMode ??= @default != null ? @default.HeaderMode : Configuration.HeaderMode.None; - RequiredGroups ??= @default != null ? @default.RequiredGroups : new List(); - - if (PartitionCount == int.MinValue) - { - PartitionCount = @default?.PartitionCount ?? PartitionCountDefault; - } - - PartitionSelectorExpression ??= @default?.PartitionSelectorExpression; - PartitionSelectorName ??= @default?.PartitionSelectorName; - PartitionKeyExtractorName ??= @default?.PartitionKeyExtractorName; - PartitionKeyExpression ??= @default?.PartitionKeyExpression; - AutoStartup ??= @default != null ? @default.AutoStartup : AutoStartupDefault; - } -} diff --git a/src/Stream/src/Stream/Configuration/SmartMessageMethodArgumentResolver.cs b/src/Stream/src/Stream/Configuration/SmartMessageMethodArgumentResolver.cs deleted file mode 100644 index fc90a84e9c..0000000000 --- a/src/Stream/src/Stream/Configuration/SmartMessageMethodArgumentResolver.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Stream.Configuration; - -public class SmartMessageMethodArgumentResolver : MessageMethodArgumentResolver -{ - public SmartMessageMethodArgumentResolver() - : this(null) - { - } - - public SmartMessageMethodArgumentResolver(IMessageConverter converter) - : base(converter) - { - } - - public override object ResolveArgument(ParameterInfo parameter, IMessage message) - { - Type targetPayloadType = GetPayloadType(parameter, message); - - Type payloadClass = message.Payload.GetType(); - - if (message is ErrorMessage || ConversionNotRequired(payloadClass, targetPayloadType)) - { - return message; - } - - object payload = message.Payload; - - if (IsEmptyPayload(payload)) - { - throw new MessageConversionException(message, - $"Cannot convert from actual payload type '{payload.GetType()}' to expected payload type '{targetPayloadType}' when payload is empty"); - } - - payload = ConvertPayload(message, parameter, targetPayloadType); - return MessageBuilder.CreateMessage(payload, message.Headers, targetPayloadType); - } - - protected override bool IsEmptyPayload(object payload) - { - return payload switch - { - null => true, - byte[] v => v.Length == 0, - string sPayload => string.IsNullOrEmpty(sPayload), - _ => false - }; - } - - private bool ConversionNotRequired(Type leftType, Type rightType) - { - return rightType == typeof(object) ? ClassUtils.IsAssignable(leftType, rightType) : ClassUtils.IsAssignable(rightType, leftType); - } - - private object ConvertPayload(IMessage message, ParameterInfo parameter, Type targetPayloadType) - { - object result = null; - - if (Converter is ISmartMessageConverter smartConverter) - { - result = smartConverter.FromMessage(message, targetPayloadType, parameter); - } - else if (Converter != null) - { - result = Converter.FromMessage(message, targetPayloadType); - } - - if (result == null) - { - throw new MessageConversionException(message, - $"No converter found from actual payload type '{message.Payload.GetType()}' to expected payload type '{targetPayloadType}'"); - } - - return result; - } -} diff --git a/src/Stream/src/Stream/Configuration/SmartPayloadArgumentResolver.cs b/src/Stream/src/Stream/Configuration/SmartPayloadArgumentResolver.cs deleted file mode 100644 index b698dc83f3..0000000000 --- a/src/Stream/src/Stream/Configuration/SmartPayloadArgumentResolver.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Handler.Attributes.Support; - -namespace Steeltoe.Stream.Configuration; - -public class SmartPayloadArgumentResolver : PayloadMethodArgumentResolver -{ - public SmartPayloadArgumentResolver(IMessageConverter messageConverter) - : base(messageConverter) - { - } - - public SmartPayloadArgumentResolver(IMessageConverter messageConverter, bool useDefaultResolution) - : base(messageConverter, useDefaultResolution) - { - } - - public override bool SupportsParameter(ParameterInfo parameter) - { - return !typeof(IMessage).IsAssignableFrom(parameter.ParameterType) && !typeof(MessageHeaders).IsAssignableFrom(parameter.ParameterType) && - parameter.GetCustomAttribute() == null && parameter.GetCustomAttribute() == null; - } - - public override object ResolveArgument(ParameterInfo parameter, IMessage message) - { - var ann = parameter.GetCustomAttribute(); - - if (ann != null && !string.IsNullOrEmpty(ann.Expression)) - { - throw new InvalidOperationException("PayloadAttribute SpEL expressions not supported by this resolver"); - } - - object payload = message.Payload; - - if (IsEmptyPayload(payload)) - { - if (ann == null || ann.Required) - { - throw new MethodArgumentNotValidException(message, parameter, "Payload value must not be empty"); - } - - return null; - } - - Type targetClass = parameter.ParameterType; - Type payloadClass = payload.GetType(); - - if (ConversionNotRequired(payloadClass, targetClass)) - { - return payload; - } - - if (Converter is ISmartMessageConverter smartConverter) - { - payload = smartConverter.FromMessage(message, targetClass, parameter); - } - else - { - payload = Converter.FromMessage(message, targetClass); - } - - if (payload == null) - { - throw new MessageConversionException(message, $"Cannot convert from [{payloadClass.Name}] to [{targetClass.Name}] for {message}"); - } - - return payload; - } - - private bool ConversionNotRequired(Type leftType, Type rightType) - { - return rightType == typeof(object) ? ClassUtils.IsAssignable(leftType, rightType) : ClassUtils.IsAssignable(rightType, leftType); - } -} diff --git a/src/Stream/src/Stream/Configuration/SpringIntegrationOptions.cs b/src/Stream/src/Stream/Configuration/SpringIntegrationOptions.cs deleted file mode 100644 index ef4e81ed26..0000000000 --- a/src/Stream/src/Stream/Configuration/SpringIntegrationOptions.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.Configuration; - -public class SpringIntegrationOptions -{ - public const string Prefix = "spring:cloud:stream:integration"; - - private static readonly string[] DefaultMessageHandlerNotPropagatedHeaders = - { - MessageHeaders.ContentType - }; - - public string[] MessageHandlerNotPropagatedHeaders { get; set; } = DefaultMessageHandlerNotPropagatedHeaders; -} diff --git a/src/Stream/src/Stream/Configuration/StreamListenerMethod.cs b/src/Stream/src/Stream/Configuration/StreamListenerMethod.cs deleted file mode 100644 index 63ca56591e..0000000000 --- a/src/Stream/src/Stream/Configuration/StreamListenerMethod.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Configuration; - -public class StreamListenerMethod : IStreamListenerMethod -{ - public MethodInfo Method { get; } - - public StreamListenerAttribute Attribute { get; } - - public Type ImplementationType => Method.DeclaringType; - - public StreamListenerMethod(MethodInfo method, StreamListenerAttribute attribute) - { - Method = method; - Attribute = attribute; - } -} diff --git a/src/Stream/src/Stream/Configuration/StreamMessageHandlerMethodFactory.cs b/src/Stream/src/Stream/Configuration/StreamMessageHandlerMethodFactory.cs deleted file mode 100644 index d6c5cffb9f..0000000000 --- a/src/Stream/src/Stream/Configuration/StreamMessageHandlerMethodFactory.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Integration.Handler.Support; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; - -namespace Steeltoe.Stream.Configuration; - -public class StreamMessageHandlerMethodFactory : DefaultMessageHandlerMethodFactory -{ - public StreamMessageHandlerMethodFactory(IApplicationContext applicationContext, ISmartMessageConverter compositeMessageConverter, - IConversionService conversionService) - : base(conversionService, compositeMessageConverter) - { - MessageConverter = compositeMessageConverter; - - var resolvers = new List - { - new SmartPayloadArgumentResolver(compositeMessageConverter), - new SmartMessageMethodArgumentResolver(compositeMessageConverter), - new HeaderMethodArgumentResolver(conversionService), - new HeadersMethodArgumentResolver(), - - new NullAwarePayloadArgumentResolver(compositeMessageConverter), - new PayloadExpressionArgumentResolver(applicationContext), - - new PayloadsArgumentResolver(applicationContext), - - new DictionaryArgumentResolver(applicationContext) - }; - - SetArgumentResolvers(resolvers); - } -} diff --git a/src/Stream/src/Stream/Converter/ApplicationJsonMessageMarshallingConverter.cs b/src/Stream/src/Stream/Converter/ApplicationJsonMessageMarshallingConverter.cs deleted file mode 100644 index 3342cc7da0..0000000000 --- a/src/Stream/src/Stream/Converter/ApplicationJsonMessageMarshallingConverter.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Stream.Converter; - -public class ApplicationJsonMessageMarshallingConverter : NewtonJsonMessageConverter -{ - internal ApplicationJsonMessageMarshallingConverter() - { - } - - protected override object ConvertToInternal(object payload, IMessageHeaders headers, object conversionHint) - { - return payload switch - { - byte[] => payload, - string sPayload => EncodingUtils.Utf8.GetBytes(sPayload), - _ => base.ConvertToInternal(payload, headers, conversionHint) - }; - } - - protected override object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - if (conversionHint is ParameterInfo info) - { - Type conversionHintType = info.ParameterType; - - if (IsIMessageGenericType(conversionHintType)) - { - /* - * Ensures that super won't attempt to create Message as a result of - * conversion and stays at payload conversion only. The Message will - * eventually be created in - * MessageMethodArgumentResolver.resolveArgument(..) - */ - conversionHint = null; - } - } - - object result; - - if (message.Payload is byte[] v && targetClass.IsAssignableFrom(typeof(string))) - { - result = EncodingUtils.Utf8.GetString(v); - } - else - { - result = base.ConvertFromInternal(message, targetClass, conversionHint); - } - - return result; - } -} diff --git a/src/Stream/src/Stream/Converter/CompositeMessageConverterFactory.cs b/src/Stream/src/Stream/Converter/CompositeMessageConverterFactory.cs deleted file mode 100644 index 0ad11c22c8..0000000000 --- a/src/Stream/src/Stream/Converter/CompositeMessageConverterFactory.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging.Converter; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Converter; - -public class CompositeMessageConverterFactory : IMessageConverterFactory -{ - public ISmartMessageConverter MessageConverterForAllRegistered => new CompositeMessageConverter(new List(AllRegistered)); - - public IList AllRegistered { get; } - - public CompositeMessageConverterFactory() - : this(null) - { - } - - public CompositeMessageConverterFactory(IEnumerable converters) - { - AllRegistered = converters == null ? new List() : new List(converters); - - InitDefaultConverters(); - - var resolver = new DefaultContentTypeResolver - { - DefaultMimeType = BindingOptions.DefaultContentType - }; - - foreach (IMessageConverter mc in AllRegistered) - { - if (mc is AbstractMessageConverter converter) - { - converter.ContentTypeResolver = resolver; - } - } - } - - public IMessageConverter GetMessageConverterForType(MimeType mimeType) - { - var converters = new List(); - - foreach (IMessageConverter converter in AllRegistered) - { - if (converter is AbstractMessageConverter abstractMessageConverter) - { - foreach (MimeType type in abstractMessageConverter.SupportedMimeTypes) - { - if (type.Includes(mimeType)) - { - converters.Add(converter); - } - } - } - } - - return converters.Count switch - { - 0 => throw new ConversionException($"No message converter is registered for {mimeType}"), - > 1 => new CompositeMessageConverter(converters), - _ => converters[0] - }; - } - - private void InitDefaultConverters() - { - var applicationJsonConverter = new ApplicationJsonMessageMarshallingConverter(); - - AllRegistered.Add(applicationJsonConverter); - - AllRegistered.Add(new ObjectSupportingByteArrayMessageConverter()); - AllRegistered.Add(new ObjectStringMessageConverter()); - } -} diff --git a/src/Stream/src/Stream/Converter/ConversionException.cs b/src/Stream/src/Stream/Converter/ConversionException.cs deleted file mode 100644 index 5f7fa981eb..0000000000 --- a/src/Stream/src/Stream/Converter/ConversionException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Converter; - -public class ConversionException : Exception -{ - public ConversionException(string message) - : base(message) - { - } - - public ConversionException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Stream/src/Stream/Converter/ObjectStringMessageConverter.cs b/src/Stream/src/Stream/Converter/ObjectStringMessageConverter.cs deleted file mode 100644 index 792e68592e..0000000000 --- a/src/Stream/src/Stream/Converter/ObjectStringMessageConverter.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Stream.Converter; - -public class ObjectStringMessageConverter : AbstractMessageConverter -{ - public const string DefaultServiceName = nameof(ObjectStringMessageConverter); - - public override string ServiceName { get; set; } = DefaultServiceName; - - public ObjectStringMessageConverter() - : base(new MimeType("text", "*", EncodingUtils.Utf8)) - { - StrictContentTypeMatch = true; - } - - // only supports the conversion to String - public override bool CanConvertFrom(IMessage message, Type targetClass) - { - return SupportsMimeType(message.Headers); - } - - protected override bool Supports(Type type) - { - return true; - } - - protected override bool SupportsMimeType(IMessageHeaders headers) - { - MimeType mimeType = GetMimeType(headers); - - if (mimeType != null) - { - foreach (MimeType current in SupportedMimeTypes) - { - if (current.Type == mimeType.Type) - { - return true; - } - } - } - - return base.SupportsMimeType(headers); - } - - protected override object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - if (message.Payload != null) - { - return message.Payload switch - { - byte[] v => typeof(byte[]).IsAssignableFrom(targetClass) ? message.Payload : EncodingUtils.Utf8.GetString(v), - _ => typeof(byte[]).IsAssignableFrom(targetClass) ? EncodingUtils.Utf8.GetBytes(message.Payload.ToString()) : message.Payload - }; - } - - return null; - } - - protected override object ConvertToInternal(object payload, IMessageHeaders headers, object conversionHint) - { - if (payload != null) - { - return payload switch - { - byte[] => payload, - _ => EncodingUtils.Utf8.GetBytes(payload.ToString()) - }; - } - - return null; - } -} diff --git a/src/Stream/src/Stream/Converter/ObjectSupportingByteArrayMessageConverter.cs b/src/Stream/src/Stream/Converter/ObjectSupportingByteArrayMessageConverter.cs deleted file mode 100644 index 6f462bd505..0000000000 --- a/src/Stream/src/Stream/Converter/ObjectSupportingByteArrayMessageConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Stream.Converter; - -public class ObjectSupportingByteArrayMessageConverter : ByteArrayMessageConverter -{ - protected override bool Supports(Type type) - { - if (!base.Supports(type)) - { - return typeof(object) == type; - } - - return true; - } -} diff --git a/src/Stream/src/Stream/Extensions/BinderServicesExtensions.cs b/src/Stream/src/Stream/Extensions/BinderServicesExtensions.cs deleted file mode 100644 index 1addef6239..0000000000 --- a/src/Stream/src/Stream/Extensions/BinderServicesExtensions.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Extensions; - -public static class BinderServicesExtensions -{ - public static IServiceCollection AddBinderServices(this IServiceCollection services, IConfiguration configuration) - { - var registry = new DefaultBinderTypeRegistry(); - services.TryAddSingleton(registry); - services.AddBinderServices(registry, configuration); - - return services; - } - - internal static void AddBinderServices(this IServiceCollection services, IBinderTypeRegistry registry, IConfiguration configuration) - { - var binderConfigurations = new BinderConfigurations(registry, configuration.GetSection("spring:cloud:stream")); - services.TryAddSingleton(binderConfigurations); - services.AddBinderServices(binderConfigurations, configuration); - } - - internal static void AddBinderServices(this IServiceCollection services, IBinderConfigurations binderConfigurations, IConfiguration configuration) - { - services.TryAddSingleton(); - services.ConfigureBinderServices(binderConfigurations, configuration); - } - - internal static void ConfigureBinderServices(this IServiceCollection services, IBinderConfigurations binderConfigurations, IConfiguration configuration) - { - foreach (KeyValuePair binderConfiguration in binderConfigurations.Configurations) - { - Type type = FindConfigureType(binderConfiguration.Value); - - if (type != null) - { - ConstructorInfo constructor = FindConstructor(type); - MethodInfo method = FindConfigureServicesMethod(type); - - if (constructor != null && method != null) - { - try - { - object instance = constructor.Invoke(new object[] - { - configuration - }); - - method.Invoke(instance, new object[] - { - services - }); - - binderConfiguration.Value.ResolvedAssembly = type.Assembly.Location; - } - catch (Exception) - { - // Log - } - } - } - } - } - - internal static MethodInfo FindConfigureServicesMethod(Type type) - { - return type.GetMethod("ConfigureServices", new[] - { - typeof(IServiceCollection) - }); - } - - internal static Type FindConfigureType(BinderConfiguration binderConfiguration) - { - if (string.IsNullOrEmpty(binderConfiguration.ConfigureAssembly)) - { - return Type.GetType(binderConfiguration.ConfigureClass, false); - } -#pragma warning disable S3885 // "Assembly.Load" should be used - Assembly assembly = Assembly.LoadFrom(binderConfiguration.ConfigureAssembly); -#pragma warning restore S3885 // "Assembly.Load" should be used - return assembly.GetType(binderConfiguration.ConfigureClass.Split(',')[0], false); - } - - internal static ConstructorInfo FindConstructor(Type type) - { - ConstructorInfo constructor = type.GetConstructor(new[] - { - typeof(IConfiguration) - }); - - if (constructor == null) - { - constructor = type.GetConstructor(Array.Empty()); - } - - return constructor; - } -} diff --git a/src/Stream/src/Stream/Extensions/CoreServicesExtensions.cs b/src/Stream/src/Stream/Extensions/CoreServicesExtensions.cs deleted file mode 100644 index e68dc6c64c..0000000000 --- a/src/Stream/src/Stream/Extensions/CoreServicesExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Lifecycle; - -namespace Steeltoe.Stream.Extensions; - -public static class CoreServicesExtensions -{ - public static IServiceCollection AddCoreServices(this IServiceCollection services) - { - services.TryAddSingleton(); - services.TryAddSingleton(DefaultConversionService.Singleton); - services.TryAddSingleton(); - - return services; - } -} diff --git a/src/Stream/src/Stream/Extensions/EnableBindingsExtensions.cs b/src/Stream/src/Stream/Extensions/EnableBindingsExtensions.cs deleted file mode 100644 index d6f5b3fbfa..0000000000 --- a/src/Stream/src/Stream/Extensions/EnableBindingsExtensions.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Steeltoe.Common; -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Extensions; - -public static class EnableBindingsExtensions -{ - public static IServiceCollection AddEnableBinding(this IServiceCollection services) - { - ArgumentGuard.NotNull(services); - - Type type = typeof(T); - object attr = type.GetCustomAttributes(true).SingleOrDefault(attr => attr is EnableBindingAttribute); - - if (attr != null) - { - var enableBindingAttribute = (EnableBindingAttribute)attr; - - Type[] filtered = enableBindingAttribute.BindingTypes - .Where(b => b.Name != nameof(ISource) && b.Name != nameof(ISink) && b.Name != nameof(IProcessor)).ToArray(); // These are added by default - - if (filtered.Length > 0) - { - services.AddStreamBindings(filtered); - } - - services.AddStreamListeners(type); - services.TryAddSingleton(type); - } - - return services; - } - - public static IServiceCollection AddProcessorStreamBinding(this IServiceCollection services) - { - ArgumentGuard.NotNull(services); - - return services.AddStreamBinding(); - } - - public static IServiceCollection AddSinkStreamBinding(this IServiceCollection services) - { - ArgumentGuard.NotNull(services); - - return services.AddStreamBinding(); - } - - public static IServiceCollection AddSourceStreamBinding(this IServiceCollection services) - { - ArgumentGuard.NotNull(services); - - return services.AddStreamBinding(); - } - - public static IServiceCollection AddDefaultBindings(this IServiceCollection services) - { - services.AddProcessorStreamBinding(); - return services; - } - - public static IServiceCollection AddStreamBinding(this IServiceCollection services) - { - ArgumentGuard.NotNull(services); - - return services.AddStreamBindings(typeof(TBinding)); - } - - public static IServiceCollection AddStreamBindings(this IServiceCollection services) - { - ArgumentGuard.NotNull(services); - - return services.AddStreamBindings(typeof(TBinding1), typeof(TBinding2)); - } - - public static IServiceCollection AddStreamBindings(this IServiceCollection services) - { - ArgumentGuard.NotNull(services); - - return services.AddStreamBindings(typeof(TBinding1), typeof(TBinding2), typeof(TBinding3)); - } - - public static IServiceCollection AddStreamBindings(this IServiceCollection services, params Type[] bindingTypes) - { - ArgumentGuard.NotNullOrEmpty(services); - - // Add all the bindings to container - services.AddBindings(bindingTypes); - - return services; - } - - internal static void AddBindings(this IServiceCollection services, Type[] bindingTypes) - { - foreach (Type bindingType in bindingTypes) - { - // Validate binding interface - if (!bindingType.IsInterface || !bindingType.IsPublic || bindingType.IsGenericType) - { - throw new ArgumentException($"Binding {bindingType} is incorrectly defined.", nameof(bindingTypes)); - } - - // Add the binding to container - services.AddBinding(bindingType); - } - } - - internal static void AddBinding(this IServiceCollection services, Type bindingType) - { - AddBindableTargets(services, bindingType); - - // Add the IBindable for this binding (i.e. BindableProxyFactory) - services.AddSingleton(p => - { - IEnumerable bindingTargetFactories = p.GetServices(); - return new BindableProxyFactory(bindingType, bindingTargetFactories); - }); - - // Add Binding (ISink, IProcessor, IFooBar) - services.AddSingleton(bindingType, p => - { - // Find the bindabe for this binding - IEnumerable bindables = p.GetServices(); - IBindable bindable = bindables.SingleOrDefault(b => b.BindingType == bindingType); - - if (bindable == null) - { - throw new InvalidOperationException("Unable to find bindable for binding"); - } - - return BindingProxyGenerator.CreateProxy((BindableProxyFactory)bindable); - }); - - List derivedInterfaces = bindingType.FindInterfaces((_, _) => true, null).ToList(); - - foreach (Type derived in derivedInterfaces) - { - services.AddSingleton(derived, p => p.GetService(bindingType)); - } - } - - internal static void AddBindableTargets(this IServiceCollection services, Type bindingType) - { - IDictionary bindables = BindingHelpers.CollectBindables(bindingType); - - foreach (Bindable bindable in bindables.Values) - { - // Add bindable defined in a binding - Type bindableTargetType = bindable.BindingTargetType; - - services.AddSingleton(bindableTargetType, p => - { - object impl = p.GetRequiredService(bindingType); - object result = bindable.FactoryMethod.Invoke(impl, Array.Empty()); - return result; - }); - - // Also register an IMessageChannel if bindableTargetType is a IMessageChannel - if (bindableTargetType != typeof(IMessageChannel) && typeof(IMessageChannel).IsAssignableFrom(bindableTargetType)) - { - services.AddSingleton(typeof(IMessageChannel), p => - { - object impl = p.GetRequiredService(bindingType); - object result = bindable.FactoryMethod.Invoke(impl, Array.Empty()); - return result; - }); - } - } - } -} diff --git a/src/Stream/src/Stream/Extensions/HostBuilderExtensions.cs b/src/Stream/src/Stream/Extensions/HostBuilderExtensions.cs deleted file mode 100644 index ace1a3bc20..0000000000 --- a/src/Stream/src/Stream/Extensions/HostBuilderExtensions.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Steeltoe.Configuration.SpringBoot; -using Steeltoe.Stream.StreamHost; - -namespace Steeltoe.Stream.Extensions; - -public static class HostBuilderExtensions -{ - public static IHostBuilder AddStreamServices(this IHostBuilder builder) - { - return builder.ConfigureAppConfiguration((context, configurationBuilder) => - { - configurationBuilder.AddSpringBootFromEnvironmentVariable(); - configurationBuilder.AddSpringBootFromCommandLine(context.Configuration); - }).ConfigureServices((context, services) => - { - services.AddStreamServices(context.Configuration); - services.AddHostedService(); - }); - } - - public static IWebHostBuilder AddStreamServices(this IWebHostBuilder builder) - { - return builder.ConfigureAppConfiguration((context, configurationBuilder) => - { - configurationBuilder.AddSpringBootFromEnvironmentVariable(); - configurationBuilder.AddSpringBootFromCommandLine(context.Configuration); - }).ConfigureServices((context, services) => - { - services.AddStreamServices(context.Configuration); - services.AddHostedService(); - }); - } - - public static WebApplicationBuilder AddStreamServices(this WebApplicationBuilder builder) - { - builder.Configuration.AddSpringBootFromEnvironmentVariable(); - builder.Configuration.AddSpringBootFromCommandLine(builder.Configuration); - builder.Services.AddStreamServices(builder.Configuration); - builder.Services.AddHostedService(); - return builder; - } -} diff --git a/src/Stream/src/Stream/Extensions/StreamListenerExtensions.cs b/src/Stream/src/Stream/Extensions/StreamListenerExtensions.cs deleted file mode 100644 index 47b48d93d4..0000000000 --- a/src/Stream/src/Stream/Extensions/StreamListenerExtensions.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Steeltoe.Common; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Extensions; - -public static class StreamListenerExtensions -{ - public static IServiceCollection AddStreamListeners(this IServiceCollection services) - where T : class - { - return services.AddStreamListeners(typeof(T)); - } - - public static IServiceCollection AddStreamListeners(this IServiceCollection services, Type type) - { - ArgumentGuard.NotNull(services); - - MethodInfo[] targetMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - - bool listenersAdded = false; - - foreach (MethodInfo method in targetMethods) - { - var attr = method.GetCustomAttribute(); - - if (attr != null) - { - services.AddStreamListener(method, attr); - listenersAdded = true; - } - } - - if (listenersAdded) - { - services.TryAddSingleton(type); - } - - return services; - } - - public static IServiceCollection AddStreamListener(this IServiceCollection services, MethodInfo method, StreamListenerAttribute attribute) - { - var streamListenerMethod = new StreamListenerMethodValidator(method); - streamListenerMethod.Validate(attribute.Target, attribute.Condition); - - services.AddSingleton(new StreamListenerMethod(method, attribute)); - return services; - } - - public static IServiceCollection AddStreamListener(this IServiceCollection services, MethodInfo method, string target, string condition = null, - bool copyHeaders = true) - { - ArgumentGuard.NotNull(services); - - var attribute = new StreamListenerAttribute(target, condition, copyHeaders); - services.AddStreamListener(method, attribute); - return services; - } -} diff --git a/src/Stream/src/Stream/Extensions/StreamServicesExtensions.cs b/src/Stream/src/Stream/Extensions/StreamServicesExtensions.cs deleted file mode 100644 index 4fa17f656b..0000000000 --- a/src/Stream/src/Stream/Extensions/StreamServicesExtensions.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Expression.Internal.Spring.Support; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Connectors.RabbitMQ; -using Steeltoe.Integration.Extensions; -using Steeltoe.Integration.Support.Converter; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Converter; - -namespace Steeltoe.Stream.Extensions; - -public static class StreamServicesExtensions -{ - public static void AddStreamConfiguration(this IServiceCollection services, IConfiguration configuration) - { - services.Configure(configuration.GetSection(SpringIntegrationOptions.Prefix)); - services.Configure(configuration.GetSection(BindingServiceOptions.Prefix)); - services.PostConfigure(o => o.PostProcess()); - } - - public static void AddStreamCoreServices(this IServiceCollection services, IConfiguration configuration) - { - // ContentTypeConfiguration - services.TryAddSingleton(); - services.TryAddSingleton(); - services.AddSingleton(p => p.GetRequiredService()); - - // messageHandlerFactoryMethod - services.TryAddSingleton(); - - // messageConverterConfigurer - services.TryAddSingleton(); - services.TryAddSingleton(p => p.GetRequiredService()); - - // compositeMessageChannelConfigurer - services.TryAddSingleton(); - - // channelFactory - services.TryAddSingleton(); - services.AddSingleton(p => p.GetRequiredService()); - - // messageSourceFactory - services.TryAddSingleton(); - services.AddSingleton(p => p.GetRequiredService()); - - // binderAwareChannelResolver - services.TryAddSingleton(); - services.TryAddSingleton>(p => p.GetRequiredService()); - - // bindingService - services.TryAddSingleton(); - services.TryAddSingleton(p => p.GetRequiredService()); - - // dynamicDestinationsBindable - services.TryAddSingleton(); - services.AddSingleton(p => p.GetRequiredService()); - - // messageChannelStreamListenerResultAdapter - services.TryAddSingleton(); - services.AddSingleton(p => p.GetRequiredService()); - - // outputBindingLifecycle - services.TryAddSingleton(); - services.AddSingleton(p => p.GetRequiredService()); - - // inputBindingLifecycle - services.TryAddSingleton(); - services.AddSingleton(p => p.GetRequiredService()); - - // streamListenerAnnotationBeanPostProcessor - services.TryAddSingleton(); - } - - public static void AddStreamServices(this IServiceCollection services, IConfiguration configuration) - { - services.AddStreamConfiguration(configuration); - - services.AddCoreServices(); - - services.AddIntegrationServices(); - - services.AddBinderServices(configuration); - - services.AddStreamCoreServices(configuration); - } - - public static void AddStreamServices(this IServiceCollection services, IConfiguration configuration) - { - services.AddOptions(); - services.AddRabbitMQ(configuration); - services.AddRabbitConnectionFactory(); - services.ConfigureRabbitOptions(configuration); - services.AddSingleton(); - - services.AddStreamServices(configuration); - services.AddSourceStreamBinding(); - services.AddSinkStreamBinding(); - - services.AddSingleton(); - services.AddSingleton(); - - services.AddEnableBinding(); - } -} diff --git a/src/Stream/src/Stream/Messaging/DirectWithAttributesChannel.cs b/src/Stream/src/Stream/Messaging/DirectWithAttributesChannel.cs deleted file mode 100644 index 1ef626732d..0000000000 --- a/src/Stream/src/Stream/Messaging/DirectWithAttributesChannel.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; - -namespace Steeltoe.Stream.Messaging; - -public class DirectWithAttributesChannel : DirectChannel -{ - private readonly IDictionary _attributes = new Dictionary(); - - public DirectWithAttributesChannel(IApplicationContext context) - : base(context) - { - } - - public void SetAttribute(string key, object value) - { - _attributes[key] = value; - } - - public object GetAttribute(string key) - { - return _attributes[key]; - } -} diff --git a/src/Stream/src/Stream/Properties/AssemblyInfo.cs b/src/Stream/src/Stream/Properties/AssemblyInfo.cs deleted file mode 100644 index e191e84434..0000000000 --- a/src/Stream/src/Stream/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Steeltoe.Stream.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Stream.Binder.RabbitMQ.Test")] -[assembly: InternalsVisibleTo("Steeltoe.Stream.Binder.Test")] diff --git a/src/Stream/src/Stream/Provisioning/ProvisioningException.cs b/src/Stream/src/Stream/Provisioning/ProvisioningException.cs deleted file mode 100644 index cc88adc16b..0000000000 --- a/src/Stream/src/Stream/Provisioning/ProvisioningException.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Provisioning; - -public class ProvisioningException : Exception -{ - public ProvisioningException(string message) - : base(message) - { - } - - public ProvisioningException(string message, Exception innerException) - : base(message, innerException) - { - } -} diff --git a/src/Stream/src/Stream/Steeltoe.Stream.csproj b/src/Stream/src/Stream/Steeltoe.Stream.csproj deleted file mode 100644 index 08f2e7ae7e..0000000000 --- a/src/Stream/src/Stream/Steeltoe.Stream.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - net8.0;net6.0 - Steeltoe Stream Base - Streams, NET Core, Spring, Spring Cloud - true - - - - - - - - - - - - - - - - diff --git a/src/Stream/src/Stream/StreamHost/StreamHost.cs b/src/Stream/src/Stream/StreamHost/StreamHost.cs deleted file mode 100644 index cfeb86bd9f..0000000000 --- a/src/Stream/src/Stream/StreamHost/StreamHost.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binding; - -namespace Steeltoe.Stream.StreamHost; - -public sealed class StreamHost : IHost -{ - private readonly IHost _host; - - public IServiceProvider Services => _host.Services; - - public StreamHost(IHost host) - { - _host = host; - } - - public static IHostBuilder CreateDefaultBuilder() - { - return new StreamsHostBuilder(Host.CreateDefaultBuilder()); - } - - public static IHostBuilder CreateDefaultBuilder(string[] args) - { - return new StreamsHostBuilder(Host.CreateDefaultBuilder(args)); - } - - public void Dispose() - { - _host?.Dispose(); - } - - public Task StartAsync(CancellationToken cancellationToken = default) - { - var lifecycleProcessor = _host.Services.GetRequiredService(); - lifecycleProcessor.OnRefreshAsync(); - var processor = _host.Services.GetRequiredService(); - processor.Initialize(); - return _host.StartAsync(cancellationToken); - } - - public Task StopAsync(CancellationToken cancellationToken = default) - { - var lifecycleProcessor = _host.Services.GetRequiredService(); - lifecycleProcessor.StopAsync(); - - // Stop that thing - return _host.StopAsync(cancellationToken); - } -} diff --git a/src/Stream/src/Stream/StreamHost/StreamLifeCycleService.cs b/src/Stream/src/Stream/StreamHost/StreamLifeCycleService.cs deleted file mode 100644 index 0c80b5d596..0000000000 --- a/src/Stream/src/Stream/StreamHost/StreamLifeCycleService.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Hosting; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binding; - -namespace Steeltoe.Stream.StreamHost; - -/// -/// Implements a hosted service to be used in a Generic Host (as opposed to streams host). -/// -public class StreamLifeCycleService : IHostedService -{ - private readonly IApplicationContext _applicationContext; - - public StreamLifeCycleService(IApplicationContext applicationContext) - { - _applicationContext = applicationContext; - } - - public async Task StartAsync(CancellationToken cancellationToken) - { - var lifecycleProcessor = _applicationContext.GetService(); - - await lifecycleProcessor.OnRefreshAsync(); - var attributeProcessor = _applicationContext.GetService(); - - attributeProcessor.Initialize(); - } - - public Task StopAsync(CancellationToken cancellationToken) - { - var lifecycleProcessor = _applicationContext.GetService(); - return lifecycleProcessor.StopAsync(); - } -} diff --git a/src/Stream/src/Stream/StreamHost/StreamsHostBuilder.cs b/src/Stream/src/Stream/StreamHost/StreamsHostBuilder.cs deleted file mode 100644 index 0c8d9039e3..0000000000 --- a/src/Stream/src/Stream/StreamHost/StreamsHostBuilder.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Steeltoe.Configuration.SpringBoot; -using Steeltoe.Stream.Extensions; - -namespace Steeltoe.Stream.StreamHost; - -public class StreamsHostBuilder : IHostBuilder -{ - private readonly IHostBuilder _hostBuilder; - - public IDictionary Properties => _hostBuilder.Properties; - - public StreamsHostBuilder(IHostBuilder hostBuilder) - { - _hostBuilder = hostBuilder.ConfigureAppConfiguration((context, configurationBuilder) => - { - configurationBuilder.AddSpringBootFromEnvironmentVariable(); - configurationBuilder.AddSpringBootFromCommandLine(context.Configuration); - }).ConfigureServices((context, services) => services.AddStreamServices(context.Configuration)); - } - - public IHost Build() - { - IHost host = _hostBuilder.Build(); - return new StreamHost(host); - } - - public IHostBuilder ConfigureAppConfiguration(Action configureDelegate) - { - _hostBuilder.ConfigureAppConfiguration(configureDelegate); - return this; - } - - public IHostBuilder ConfigureContainer(Action configureDelegate) - { - _hostBuilder.ConfigureContainer(configureDelegate); - return this; - } - - public IHostBuilder ConfigureHostConfiguration(Action configureDelegate) - { - _hostBuilder.ConfigureHostConfiguration(configureDelegate); - return this; - } - - public IHostBuilder ConfigureServices(Action configureDelegate) - { - _hostBuilder.ConfigureServices(configureDelegate); - return this; - } - - public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) - { - _hostBuilder.UseServiceProviderFactory(factory); - return this; - } - - public IHostBuilder UseServiceProviderFactory(Func> factory) - { - _hostBuilder.UseServiceProviderFactory(factory); - return this; - } -} diff --git a/src/Stream/src/Stream/Util/GenericsUtils.cs b/src/Stream/src/Stream/Util/GenericsUtils.cs deleted file mode 100644 index 8fb9a3f57b..0000000000 --- a/src/Stream/src/Stream/Util/GenericsUtils.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Util; - -internal static class GenericsUtils -{ - internal static Type GetParameterType(Type evaluatedClass, Type interfaceType, int position) - { - Type bindableType = null; - - if (!interfaceType.IsInterface) - { - throw new ArgumentException($"{nameof(interfaceType)} must be an interface.", nameof(interfaceType)); - } - - Type currentType = evaluatedClass; - - while (currentType != typeof(object) && bindableType == null) - { - Type[] interfaces = currentType.GetInterfaces(); - Type resolvableType = null; - - foreach (Type @interface in interfaces) - { - Type typeToCheck = @interface; - - if (@interface.IsGenericType) - { - typeToCheck = @interface.GetGenericTypeDefinition(); - } - - if (interfaceType == typeToCheck) - { - resolvableType = @interface; - break; - } - } - - if (resolvableType == null) - { - currentType = currentType.BaseType; - } - else - { - if (resolvableType.IsGenericType) - { - Type[] genArgs = resolvableType.GetGenericArguments(); - bindableType = genArgs[position]; - } - else - { - bindableType = typeof(object); - } - } - } - - if (bindableType == null) - { - throw new InvalidOperationException($"Cannot find parameter of {evaluatedClass.Name} for {interfaceType} at position {position}"); - } - - return bindableType; - } - - internal static bool CheckCompatiblePollableBinder(IBinder binderInstance, Type bindingTargetType) - { - Type binderInstanceType = binderInstance.GetType(); - Type[] binderInterfaces = binderInstanceType.GetInterfaces(); - - foreach (Type binderInterface in binderInterfaces) - { - if (typeof(IPollableConsumerBinder).IsAssignableFrom(binderInterface)) - { - Type[] targetInterfaces = bindingTargetType.GetInterfaces(); - Type psType = FindPollableSourceType(targetInterfaces); - - if (psType != null) - { - return GetParameterType(binderInstance.GetType(), binderInterface, 0).IsAssignableFrom(psType); - } - } - } - - return false; - } - - internal static Type FindPollableSourceType(Type[] targetInterfaces) - { - foreach (Type targetInterface in targetInterfaces) - { - if (typeof(IPollableSource).IsAssignableFrom(targetInterface)) - { - Type[] interfaces = targetInterface.GetInterfaces(); - - foreach (Type type in interfaces) - { - if (type.IsGenericType) - { - Type resolvableType = type.GetGenericTypeDefinition(); - - if (resolvableType == typeof(IPollableSource<>)) - { - return type.GetGenericArguments()[0]; - } - } - } - } - } - - return null; - } -} diff --git a/src/Stream/test/Binder.Test/AbstractBinderTests.cs b/src/Stream/test/Binder.Test/AbstractBinderTests.cs deleted file mode 100644 index 230e1e7b38..0000000000 --- a/src/Stream/test/Binder.Test/AbstractBinderTests.cs +++ /dev/null @@ -1,627 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Messaging.Handler.Invocation; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Converter; -using Xunit; -using Xunit.Abstractions; - -namespace Steeltoe.Stream.Binder.Test; - -public abstract class AbstractBinderTests - where TTestBinder : AbstractTestBinder - where TBinder : AbstractBinder -{ - protected virtual ISmartMessageConverter MessageConverter { get; set; } - - protected virtual double TimeoutMultiplier { get; set; } = 1.0D; - - protected virtual ITestOutputHelper Output { get; set; } - - protected virtual ServiceCollection Services { get; } - - protected virtual ConfigurationBuilder ConfigurationBuilder { get; set; } - - protected CachingConnectionFactory CachingConnectionFactory { get; set; } - - public ILoggerFactory LoggerFactory { get; } - - protected AbstractBinderTests(ITestOutputHelper output, ILoggerFactory loggerFactory) - { - MessageConverter = new CompositeMessageConverterFactory().MessageConverterForAllRegistered; - LoggerFactory = loggerFactory; - Output = output; - Services = new ServiceCollection(); - ConfigurationBuilder = new ConfigurationBuilder(); - } - - [Fact] - public async Task TestClean() - { - var bindingsOptions = new RabbitBindingsOptions(); - TTestBinder binder = GetBinder(); - string delimiter = GetDestinationNameDelimiter(); - - IBinding foo0ProducerBinding = binder.BindProducer($"foo{delimiter}0", CreateBindableChannel("output", GetDefaultBindingOptions()), - GetProducerOptions("output", bindingsOptions)); - - IBinding foo0ConsumerBinding = binder.BindConsumer($"foo{delimiter}0", "testClean", CreateBindableChannel("input", GetDefaultBindingOptions()), - GetConsumerOptions("input", bindingsOptions)); - - IBinding foo1ProducerBinding = binder.BindProducer($"foo{delimiter}1", CreateBindableChannel("output", GetDefaultBindingOptions()), - GetProducerOptions("output1", bindingsOptions)); - - IBinding foo1ConsumerBinding = binder.BindConsumer($"foo{delimiter}1", "testClean", CreateBindableChannel("input", GetDefaultBindingOptions()), - GetConsumerOptions("input1", bindingsOptions)); - - IBinding foo2ProducerBinding = binder.BindProducer($"foo{delimiter}2", CreateBindableChannel("output", GetDefaultBindingOptions()), - GetProducerOptions("output2", bindingsOptions)); - - await foo0ProducerBinding.UnbindAsync(); - Assert.False(foo0ProducerBinding.IsRunning); - - await foo0ConsumerBinding.UnbindAsync(); - await foo1ProducerBinding.UnbindAsync(); - - Assert.False(foo0ConsumerBinding.IsRunning); - Assert.False(foo1ProducerBinding.IsRunning); - - await foo1ConsumerBinding.UnbindAsync(); - await foo2ProducerBinding.UnbindAsync(); - - Assert.False(foo1ConsumerBinding.IsRunning); - Assert.False(foo2ProducerBinding.IsRunning); - } - - [Fact] - public async Task TestSendAndReceive() - { - var bindingsOptions = new RabbitBindingsOptions(); - TTestBinder binder = GetBinder(bindingsOptions); - string delimiter = GetDestinationNameDelimiter(); - - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - ConsumerOptions consumerProperties = GetConsumerOptions("output", bindingsOptions); - DirectChannel moduleOutputChannel = CreateBindableChannel("output", GetDefaultBindingOptions()); - - DirectChannel moduleInputChannel = CreateBindableChannel("input", GetDefaultBindingOptions()); - - IBinding producerBinding = binder.BindProducer($"foo{delimiter}0", moduleOutputChannel, producerOptions); - IBinding consumerBinding = binder.BindConsumer($"foo{delimiter}0", "testSendAndReceive", moduleInputChannel, consumerProperties); - IMessage message = MessageBuilder.WithPayload("foo").SetHeader(MessageHeaders.ContentType, MimeType.ToMimeType("text/plain")).Build(); - - var latch = new CountdownEvent(1); - var inboundMessageRef = new AtomicReference(); - - moduleInputChannel.Subscribe(new TestMessageHandler - { - OnHandleMessage = message => - { - inboundMessageRef.GetAndSet(message); - latch.Signal(); - } - }); - - moduleOutputChannel.Send(message); - - Assert.True(latch.Wait(TimeSpan.FromSeconds(5)), "Failed to receive message"); - - IMessage actual = inboundMessageRef.Value; - Assert.Equal("foo".GetBytes(), actual.Payload); - Assert.Equal("text/plain", actual.Headers.ContentType()); - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestSendAndReceiveMultipleTopics() - { - TTestBinder binder = GetBinder(); - - string delimiter = GetDestinationNameDelimiter(); - var bindingsOptions = new RabbitBindingsOptions(); - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - BindingOptions producerBindingProperties = CreateProducerBindingOptions(producerOptions); - - ConsumerOptions consumerProperties = GetConsumerOptions("output", bindingsOptions); - DirectChannel moduleOutputChannel1 = CreateBindableChannel("output1", producerBindingProperties); - - DirectChannel moduleOutputChannel2 = CreateBindableChannel("output2", producerBindingProperties); - var moduleInputChannel = new QueueChannel(); - IBinding producerBinding1 = binder.BindProducer($"foo{delimiter}xy", moduleOutputChannel1, producerBindingProperties.Producer); - IBinding producerBinding2 = binder.BindProducer($"foo{delimiter}yz", moduleOutputChannel2, producerBindingProperties.Producer); - IBinding consumerBinding1 = binder.BindConsumer($"foo{delimiter}xy", "testSendAndReceiveMultipleTopics", moduleInputChannel, consumerProperties); - IBinding consumerBinding2 = binder.BindConsumer($"foo{delimiter}yz", "testSendAndReceiveMultipleTopics", moduleInputChannel, consumerProperties); - - string testPayload1 = $"foo{Guid.NewGuid()}"; - IMessage message1 = MessageBuilder.WithPayload(testPayload1.GetBytes()).SetHeader("contentType", MimeTypeUtils.ApplicationOctetStream).Build(); - string testPayload2 = $"foo{Guid.NewGuid()}"; - IMessage message2 = MessageBuilder.WithPayload(testPayload2.GetBytes()).SetHeader("contentType", MimeTypeUtils.ApplicationOctetStream).Build(); - - BinderBindUnbindLatency(); - moduleOutputChannel1.Send(message1); - moduleOutputChannel2.Send(message2); - - IMessage[] messages = - { - Receive(moduleInputChannel), - Receive(moduleInputChannel) - }; - - Assert.NotNull(messages[0]); - Assert.NotNull(messages[1]); - - Assert.Contains(messages, m => ((byte[])m.Payload).GetString() == testPayload1); - Assert.Contains(messages, m => ((byte[])m.Payload).GetString() == testPayload2); - - await producerBinding1.UnbindAsync(); - await producerBinding2.UnbindAsync(); - await consumerBinding1.UnbindAsync(); - await consumerBinding2.UnbindAsync(); - } - - [Fact] - public async Task TestSendAndReceiveNoOriginalContentType() - { - TTestBinder binder = GetBinder(); - - string delimiter = GetDestinationNameDelimiter(); - var bindingsOptions = new RabbitBindingsOptions(); - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - BindingOptions producerBindingProperties = CreateProducerBindingOptions(producerOptions); - - DirectChannel moduleOutputChannel = CreateBindableChannel("output", producerBindingProperties); - - ConsumerOptions consumerProperties = GetConsumerOptions("output", bindingsOptions); - BindingOptions inputBindingProperties = CreateConsumerBindingOptions(consumerProperties); - DirectChannel moduleInputChannel = CreateBindableChannel("input", inputBindingProperties); - - IBinding producerBinding = binder.BindProducer($"bar{delimiter}0", moduleOutputChannel, producerBindingProperties.Producer); - IBinding consumerBinding = binder.BindConsumer($"bar{delimiter}0", "testSendAndReceiveNoOriginalContentType", moduleInputChannel, consumerProperties); - BinderBindUnbindLatency(); - IMessage message = MessageBuilder.WithPayload("foo").SetHeader("contentType", MimeTypeUtils.TextPlain).Build(); - - moduleOutputChannel.Send(message); - var latch = new CountdownEvent(1); - var inboundMessageRef = new AtomicReference(); - - moduleInputChannel.Subscribe(new TestMessageHandler - { - OnHandleMessage = message => - { - inboundMessageRef.GetAndSet(message); - latch.Signal(); - } - }); - - moduleOutputChannel.Send(message); - - Assert.True(latch.Wait(TimeSpan.FromSeconds(5)), "Failed to receive message"); - Assert.NotNull(inboundMessageRef.Value); - Assert.Equal("foo", ((byte[])inboundMessageRef.Value.Payload).GetString()); - Assert.Equal("text/plain", inboundMessageRef.Value.Headers.ContentType()); - - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestSendPojoReceivePojoWithStreamListenerDefaultContentType() - { - StreamListenerMessageHandler handler = BuildStreamListener(GetType(), "EchoStation", typeof(Station)); - TTestBinder binder = GetBinder(); - - string delimiter = GetDestinationNameDelimiter(); - var bindingsOptions = new RabbitBindingsOptions(); - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - BindingOptions producerBindingProperties = CreateProducerBindingOptions(producerOptions); - - DirectChannel moduleOutputChannel = CreateBindableChannel("output", producerBindingProperties); - ConsumerOptions consumerProperties = GetConsumerOptions("output", bindingsOptions); - BindingOptions consumerBindingProperties = CreateConsumerBindingOptions(consumerProperties); - - DirectChannel moduleInputChannel = CreateBindableChannel("input", consumerBindingProperties); - IBinding producerBinding = binder.BindProducer($"bar{delimiter}0a", moduleOutputChannel, producerBindingProperties.Producer); - IBinding consumerBinding = binder.BindConsumer($"bar{delimiter}0a", "test-1", moduleInputChannel, consumerBindingProperties.Consumer); - - var station = new Station(); - IMessage message = MessageBuilder.WithPayload(station).Build(); - moduleInputChannel.Subscribe(handler); - moduleOutputChannel.Send(message); - var replyChannel = (QueueChannel)handler.OutputChannel; - IMessage replyMessage = replyChannel.Receive(5000); - - Assert.IsType(replyMessage.Payload); - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestSendJsonReceivePojoWithStreamListener() - { - StreamListenerMessageHandler handler = BuildStreamListener(GetType(), "EchoStation", typeof(Station)); - TTestBinder binder = GetBinder(); - - string delimiter = GetDestinationNameDelimiter(); - var bindingsOptions = new RabbitBindingsOptions(); - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - BindingOptions producerBindingProperties = CreateProducerBindingOptions(producerOptions); - - DirectChannel moduleOutputChannel = CreateBindableChannel("output", producerBindingProperties); - ConsumerOptions consumerProperties = GetConsumerOptions("output", bindingsOptions); - BindingOptions consumerBindingProperties = CreateConsumerBindingOptions(consumerProperties); - - DirectChannel moduleInputChannel = CreateBindableChannel("input", consumerBindingProperties); - IBinding producerBinding = binder.BindProducer($"bad{delimiter}0d", moduleOutputChannel, producerBindingProperties.Producer); - IBinding consumerBinding = binder.BindConsumer($"bad{delimiter}0d", "test-4", moduleInputChannel, consumerBindingProperties.Consumer); - - const string value = - "{\"readings\":[{\"stationid\":\"fgh\",\"customerid\":\"12345\",\"timestamp\":null},{\"stationid\":\"hjk\",\"customerid\":\"222\",\"timestamp\":null}]}"; - - IMessage message = MessageBuilder.WithPayload(value).SetHeader("contentType", MimeTypeUtils.ApplicationJson).Build(); - moduleInputChannel.Subscribe(handler); - moduleOutputChannel.Send(message); - var channel = (QueueChannel)handler.OutputChannel; - IMessage reply = channel.Receive(5000); - - Assert.NotNull(reply); - Assert.IsType(reply.Payload); - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestSendJsonReceiveJsonWithStreamListener() - { - StreamListenerMessageHandler handler = BuildStreamListener(GetType(), "EchoStationString", typeof(string)); - TTestBinder binder = GetBinder(); - - string delimiter = GetDestinationNameDelimiter(); - var bindingsOptions = new RabbitBindingsOptions(); - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - BindingOptions producerBindingProperties = CreateProducerBindingOptions(producerOptions); - - DirectChannel moduleOutputChannel = CreateBindableChannel("output", producerBindingProperties); - ConsumerOptions consumerProperties = GetConsumerOptions("output", bindingsOptions); - BindingOptions consumerBindingProperties = CreateConsumerBindingOptions(consumerProperties); - - DirectChannel moduleInputChannel = CreateBindableChannel("input", consumerBindingProperties); - IBinding producerBinding = binder.BindProducer($"bad{delimiter}0e", moduleOutputChannel, producerBindingProperties.Producer); - IBinding consumerBinding = binder.BindConsumer($"bad{delimiter}0e", "test-5", moduleInputChannel, consumerBindingProperties.Consumer); - - const string value = - "{\"readings\":[{\"stationid\":\"fgh\",\"customerid\":\"12345\",\"timestamp\":null},{\"stationid\":\"hjk\",\"customerid\":\"222\",\"timestamp\":null}]}"; - - IMessage message = MessageBuilder.WithPayload(value).SetHeader("contentType", MimeTypeUtils.ApplicationJson).Build(); - - moduleInputChannel.Subscribe(handler); - moduleOutputChannel.Send(message); - var channel = (QueueChannel)handler.OutputChannel; - IMessage reply = channel.Receive(5000); - Assert.NotNull(reply); - Assert.IsType(reply.Payload); - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestSendPojoReceivePojoWithStreamListener() - { - StreamListenerMessageHandler handler = BuildStreamListener(GetType(), "EchoStation", typeof(Station)); - TTestBinder binder = GetBinder(); - - string delimiter = GetDestinationNameDelimiter(); - var bindingsOptions = new RabbitBindingsOptions(); - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - BindingOptions producerBindingProperties = CreateProducerBindingOptions(producerOptions); - - DirectChannel moduleOutputChannel = CreateBindableChannel("output", producerBindingProperties); - ConsumerOptions consumerProperties = GetConsumerOptions("output", bindingsOptions); - BindingOptions consumerBindingProperties = CreateConsumerBindingOptions(consumerProperties); - - DirectChannel moduleInputChannel = CreateBindableChannel("input", consumerBindingProperties); - IBinding producerBinding = binder.BindProducer($"bad{delimiter}0f", moduleOutputChannel, producerBindingProperties.Producer); - IBinding consumerBinding = binder.BindConsumer($"bad{delimiter}0f", "test-6", moduleInputChannel, consumerBindingProperties.Consumer); - - var r1 = new Station.Readings - { - CustomerId = "123", - StationId = "XYZ" - }; - - var r2 = new Station.Readings - { - CustomerId = "546", - StationId = "ABC" - }; - - var station = new Station - { - ReadingsList = - { - r1, - r2 - } - }; - - IMessage message = MessageBuilder.WithPayload(station).SetHeader("contentType", MimeTypeUtils.ApplicationJson).Build(); - moduleInputChannel.Subscribe(handler); - moduleOutputChannel.Send(message); - var channel = (QueueChannel)handler.OutputChannel; - IMessage reply = channel.Receive(5000); - Assert.NotNull(reply); - Assert.IsType(reply.Payload); - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - } - - protected CachingConnectionFactory GetResource() - { - if (CachingConnectionFactory == null) - { - CachingConnectionFactory = new CachingConnectionFactory("localhost"); - CachingConnectionFactory.CreateConnection().Close(); - } - - return CachingConnectionFactory; - } - - protected BindingOptions CreateConsumerBindingOptions(ConsumerOptions consumerOptions) - { - return new BindingOptions - { - ContentType = BindingOptions.DefaultContentType.ToString(), - Consumer = consumerOptions - }; - } - - protected BindingOptions CreateProducerBindingOptions(ProducerOptions producerOptions) - { - return new BindingOptions - { - ContentType = BindingOptions.DefaultContentType.ToString(), - Producer = producerOptions - }; - } - - protected IMessage Receive(IPollableChannel channel) - { - return Receive(channel, 1); - } - - protected IMessage Receive(IPollableChannel channel, int additionalMultiplier) - { - return channel.Receive((int)(1000 * TimeoutMultiplier * additionalMultiplier)); - } - - protected DirectChannel CreateBindableChannel(string channelName, BindingOptions bindingProperties) - { - // The 'channelName.contains("input")' is strictly for convenience to avoid - // modifications in multiple tests - return CreateBindableChannel(channelName, bindingProperties, channelName.Contains("input", StringComparison.Ordinal)); - } - - protected DirectChannel CreateBindableChannel(string channelName, BindingOptions bindingProperties, bool inputChannel) - { - MessageConverterConfigurer messageConverterConfigurer = CreateConverterConfigurer(channelName, bindingProperties); - - var channel = new DirectChannel(LoggerFactory.CreateLogger()) - { - ServiceName = channelName - }; - - if (inputChannel) - { - messageConverterConfigurer.ConfigureInputChannel(channel, channelName); - } - else - { - messageConverterConfigurer.ConfigureOutputChannel(channel, channelName); - } - - return channel; - } - - protected string GetDestinationNameDelimiter() - { - return "."; - } - - protected TTestBinder GetBinder() - { - return GetBinder(null); - } - - protected abstract TTestBinder GetBinder(RabbitBindingsOptions bindingsOptions); - - protected void BinderBindUnbindLatency() - { - } - - protected ConsumerOptions GetConsumerOptions(string bindingName, RabbitBindingsOptions bindingsOptions) - { - return GetConsumerOptions(bindingName, bindingsOptions, null, null); - } - - protected ConsumerOptions GetConsumerOptions(string bindingName, RabbitBindingsOptions bindingsOptions, RabbitConsumerOptions rabbitConsumerOptions, - RabbitBindingOptions bindingOptions) - { - rabbitConsumerOptions ??= new RabbitConsumerOptions(); - rabbitConsumerOptions.PostProcess(); - - bindingOptions ??= new RabbitBindingOptions(); - bindingOptions.Consumer = rabbitConsumerOptions; - bindingsOptions.Bindings.Add(bindingName, bindingOptions); - - var consumerOptions = new ConsumerOptions - { - BindingName = bindingName - }; - - consumerOptions.PostProcess(bindingName); - return consumerOptions; - } - - protected ProducerOptions GetProducerOptions(string bindingName, RabbitBindingsOptions bindingsOptions) - { - return GetProducerOptions(bindingName, bindingsOptions, null); - } - - protected ProducerOptions GetProducerOptions(string bindingName, RabbitBindingsOptions bindingsOptions, RabbitBindingOptions bindingOptions) - { - var rabbitProducerOptions = new RabbitProducerOptions(); - rabbitProducerOptions.PostProcess(); - - bindingOptions ??= new RabbitBindingOptions(); - - bindingOptions.Producer = rabbitProducerOptions; - - bindingsOptions.Bindings.Add(bindingName, bindingOptions); - - var producerOptions = new ProducerOptions - { - BindingName = bindingName - }; - - producerOptions.PostProcess(bindingName); - - return producerOptions; - } - - protected BindingOptions GetDefaultBindingOptions() - { - return new BindingOptions - { - ContentType = BindingOptions.DefaultContentType.ToString() - }; - } - - public Station EchoStation(Station station) - { - return station; - } - - public string EchoStationString(string station) - { - return station; - } - - private StreamListenerMessageHandler BuildStreamListener(Type handlerType, string handlerMethodName, params Type[] parameters) - { - string channelName = $"reply_{default(DateTime).Ticks}"; - TTestBinder binder = GetBinder(); - - binder.ApplicationContext.Register(channelName, new QueueChannel()); - - MethodInfo methodInfo = handlerType.GetMethod(handlerMethodName, parameters); - var method = new InvocableHandlerMethod(this, methodInfo); - var resolver = new HandlerMethodArgumentResolverComposite(); - var factory = new CompositeMessageConverterFactory(); - - resolver.AddResolver(new PayloadArgumentResolver(factory.MessageConverterForAllRegistered)); - method.MessageMethodArgumentResolvers = resolver; - - ConstructorInfo constructor = typeof(StreamListenerMessageHandler).GetConstructor(new[] - { - typeof(IApplicationContext), - typeof(InvocableHandlerMethod), - typeof(bool), - typeof(string[]) - }); - - var handler = (StreamListenerMessageHandler)constructor.Invoke(new object[] - { - binder.ApplicationContext, - method, - false, - Array.Empty() - }); - - handler.OutputChannelName = channelName; - return handler; - } - - private MessageConverterConfigurer CreateConverterConfigurer(string channelName, BindingOptions bindingProperties) - { - var bindingServiceProperties = new BindingServiceOptions(); - bindingServiceProperties.Bindings.Add(channelName, bindingProperties); - IApplicationContext applicationContext = GetBinder().ApplicationContext; - - IEnumerable extractors = applicationContext.GetServices(); - IEnumerable selectors = applicationContext.GetServices(); - var bindingServiceOptionsMonitor = new BindingServiceOptionsMonitor(bindingServiceProperties); - - var messageConverterConfigurer = new MessageConverterConfigurer(applicationContext, bindingServiceOptionsMonitor, - new CompositeMessageConverterFactory(), extractors, selectors); - - return messageConverterConfigurer; - } - - protected sealed class TestMessageHandler : IMessageHandler - { - public Action OnHandleMessage { get; set; } - - public string ServiceName - { - get => "TestMessageHandler"; - set => throw new NotImplementedException(); - } - - public void HandleMessage(IMessage message) - { - OnHandleMessage.Invoke(message); - } - } - - public sealed class Station - { - public List ReadingsList { get; } = new(); - - public sealed class Readings - { - public string StationId { get; set; } - - public string CustomerId { get; set; } - - public string TimeStamp { get; set; } - } - } - - private sealed class BindingServiceOptionsMonitor : IOptionsMonitor - { - public BindingServiceOptions CurrentValue { get; } - - public BindingServiceOptionsMonitor(BindingServiceOptions options) - { - CurrentValue = options; - } - - public BindingServiceOptions Get(string name) - { - return CurrentValue; - } - - public IDisposable OnChange(Action listener) - { - return null; - } - } -} diff --git a/src/Stream/test/Binder.Test/AbstractPollableConsumerTestBinder.cs b/src/Stream/test/Binder.Test/AbstractPollableConsumerTestBinder.cs deleted file mode 100644 index 1633094702..0000000000 --- a/src/Stream/test/Binder.Test/AbstractPollableConsumerTestBinder.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binder.Test; - -public class AbstractPollableConsumerTestBinder : AbstractTestBinder, IPollableConsumerBinder - where TBinder : AbstractBinder -{ - private IPollableConsumerBinder _binder; - - public IPollableConsumerBinder PollableConsumerBinder - { - get => _binder; - set - { - Binder = (TBinder)value; - _binder = value; - } - } - - public IBinding BindConsumer(string name, string group, IPollableSource inboundTarget, IConsumerOptions consumerOptions) - { - return _binder.BindConsumer(name, group, inboundTarget, consumerOptions); - } - - public IBinding BindProducer(string name, IPollableSource outboundTarget, IProducerOptions producerOptions) - { - throw new NotImplementedException(); - } - - public override void Cleanup() - { - throw new NotImplementedException(); - } -} diff --git a/src/Stream/test/Binder.Test/AbstractTestBinder.cs b/src/Stream/test/Binder.Test/AbstractTestBinder.cs deleted file mode 100644 index b199cb584d..0000000000 --- a/src/Stream/test/Binder.Test/AbstractTestBinder.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binder.Test; - -public abstract class AbstractTestBinder : IBinder - where TBinder : AbstractBinder -{ - protected HashSet Queues { get; } = new(); - - protected HashSet Exchanges { get; } = new(); - - public Type TargetType => typeof(IMessageChannel); - - public TBinder CoreBinder { get; private set; } - - public TBinder Binder - { - get => CoreBinder; - set => CoreBinder = value; - } - - public IApplicationContext ApplicationContext => Binder?.ApplicationContext; - - public string ServiceName - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - public virtual IBinding BindConsumer(string name, string group, IMessageChannel inboundTarget, IConsumerOptions consumerOptions) - { - CheckChannelIsConfigured(inboundTarget, consumerOptions); - return BindConsumer(name, group, (object)inboundTarget, consumerOptions); - } - - public virtual IBinding BindConsumer(string name, string group, object inboundTarget, IConsumerOptions consumerOptions) - { - Queues.Add(name); - return CoreBinder.BindConsumer(name, group, inboundTarget, consumerOptions); - } - - public virtual IBinding BindProducer(string name, IMessageChannel outboundTarget, IProducerOptions producerOptions) - { - return BindProducer(name, (object)outboundTarget, producerOptions); - } - - public IBinding BindProducer(string name, object outboundTarget, IProducerOptions producerOptions) - { - Queues.Add(name); - return CoreBinder.BindProducer(name, outboundTarget, producerOptions); - } - - public abstract void Cleanup(); - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - } - - private void CheckChannelIsConfigured(IMessageChannel messageChannel, IConsumerOptions options) - { - if (messageChannel is AbstractSubscribableChannel subChan && !options.UseNativeDecoding && subChan.ChannelInterceptors.Count == 0) - { - throw new InvalidOperationException( - "'messageChannel' appears to be misconfigured. Consider creating channel via AbstractBinderTest.createBindableChannel(..)"); - } - } -} diff --git a/src/Stream/test/Binder.Test/PartitionCapableBinderTests.cs b/src/Stream/test/Binder.Test/PartitionCapableBinderTests.cs deleted file mode 100644 index bb2a56f1db..0000000000 --- a/src/Stream/test/Binder.Test/PartitionCapableBinderTests.cs +++ /dev/null @@ -1,311 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Util; -using Steeltoe.Integration; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Steeltoe.Stream.Configuration; -using Xunit; -using Xunit.Abstractions; - -namespace Steeltoe.Stream.Binder.Test; - -public abstract class PartitionCapableBinderTests : AbstractBinderTests - where TTestBinder : AbstractTestBinder - where TBinder : AbstractBinder -{ - private readonly ILogger> _logger; - - protected PartitionCapableBinderTests(ITestOutputHelper output, ILoggerFactory loggerFactory) - : base(output, loggerFactory) - { - _logger = loggerFactory?.CreateLogger>(); - } - - [Fact] - public async Task TestAnonymousGroup() - { - TTestBinder binder = GetBinder(); - var bindingsOptions = new RabbitBindingsOptions(); - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - BindingOptions producerBindingOptions = CreateProducerBindingOptions(producerOptions); - DirectChannel output = CreateBindableChannel("output", producerBindingOptions); - - ConsumerOptions consumerOptions = GetConsumerOptions("output", bindingsOptions); - string delimiter = GetDestinationNameDelimiter(); - IBinding producerBinding = binder.BindProducer($"defaultGroup{delimiter}0", output, producerBindingOptions.Producer); - - var input1 = new QueueChannel(); - IBinding binding1 = binder.BindConsumer($"defaultGroup{delimiter}0", null, input1, consumerOptions); - - var input2 = new QueueChannel(); - IBinding binding2 = binder.BindConsumer($"defaultGroup{delimiter}0", null, input2, consumerOptions); - - string testPayload1 = $"foo-{Guid.NewGuid()}"; - output.Send(MessageBuilder.WithPayload(testPayload1).SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build()); - - var receivedMessage1 = (Message)Receive(input1); - Assert.NotNull(receivedMessage1); - Assert.Equal(testPayload1, Encoding.UTF8.GetString(receivedMessage1.Payload)); - - var receivedMessage2 = (Message)Receive(input2); - Assert.NotNull(receivedMessage2); - Assert.Equal(testPayload1, Encoding.UTF8.GetString(receivedMessage2.Payload)); - - await binding2.UnbindAsync(); - - string testPayload2 = $"foo-{Guid.NewGuid()}"; - - output.Send(MessageBuilder.WithPayload(testPayload2).SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build()); - - binding2 = binder.BindConsumer($"defaultGroup{delimiter}0", null, input2, consumerOptions); - string testPayload3 = $"foo-{Guid.NewGuid()}"; - output.Send(MessageBuilder.WithPayload(testPayload3).SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build()); - - receivedMessage1 = (Message)Receive(input1); - Assert.NotNull(receivedMessage1); - Assert.Equal(testPayload2, Encoding.UTF8.GetString(receivedMessage1.Payload)); - receivedMessage1 = (Message)Receive(input1); - Assert.NotNull(receivedMessage1); - Assert.NotNull(receivedMessage1.Payload); - - receivedMessage2 = (Message)Receive(input2); - Assert.NotNull(receivedMessage2); - Assert.Equal(testPayload3, Encoding.UTF8.GetString(receivedMessage2.Payload)); - - await producerBinding.UnbindAsync(); - await binding1.UnbindAsync(); - await binding2.UnbindAsync(); - } - - [Fact] - public async Task TestOneRequiredGroup() - { - TTestBinder binder = GetBinder(); - var bindingsOptions = new RabbitBindingsOptions(); - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - BindingOptions producerBindingOptions = CreateProducerBindingOptions(producerOptions); - DirectChannel output = CreateBindableChannel("output", producerBindingOptions); - - ConsumerOptions consumerOptions = GetConsumerOptions("output", bindingsOptions); - - string testDestination = $"testDestination{Guid.NewGuid().ToString().Replace("-", string.Empty, StringComparison.Ordinal)}"; - - producerOptions.RequiredGroups = new List - { - "test1" - }; - - IBinding producerBinding = binder.BindProducer(testDestination, output, producerOptions); - string testPayload = $"foo-{Guid.NewGuid()}"; - - output.Send(MessageBuilder.WithPayload(testPayload).SetHeader("contentType", MimeTypeUtils.TextPlain).Build()); - var inbound1 = new QueueChannel(); - IBinding consumerBinding = binder.BindConsumer(testDestination, "test1", inbound1, consumerOptions); - var receivedMessage1 = (Message)Receive(inbound1); - - Assert.NotNull(receivedMessage1); - Assert.Equal(testPayload, Encoding.UTF8.GetString(receivedMessage1.Payload)); - - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestTwoRequiredGroups() - { - TTestBinder binder = GetBinder(); - var bindingsOptions = new RabbitBindingsOptions(); - ProducerOptions producerOptions = GetProducerOptions("input", bindingsOptions); - BindingOptions producerBindingOptions = CreateProducerBindingOptions(producerOptions); - DirectChannel output = CreateBindableChannel("output", producerBindingOptions); - - string testDestination = $"testDestination{Guid.NewGuid().ToString().Replace("-", string.Empty, StringComparison.Ordinal)}"; - - producerOptions.RequiredGroups = new List - { - "test1", - "test2" - }; - - IBinding producerBinding = binder.BindProducer(testDestination, output, producerOptions); - - string testPayload = $"foo-{Guid.NewGuid()}"; - - output.Send(MessageBuilder.WithPayload(testPayload).SetHeader("contentType", MimeTypeUtils.TextPlain).Build()); - var inbound1 = new QueueChannel(); - - ConsumerOptions consumerOptions = GetConsumerOptions("output", bindingsOptions); - IBinding consumerBinding1 = binder.BindConsumer(testDestination, "test1", inbound1, consumerOptions); - - var inbound2 = new QueueChannel(); - IBinding consumerBinding2 = binder.BindConsumer(testDestination, "test2", inbound2, consumerOptions); - - var receivedMessage1 = (Message)Receive(inbound1); - Assert.NotNull(receivedMessage1); - Assert.Equal(testPayload, Encoding.UTF8.GetString(receivedMessage1.Payload)); - - var receivedMessage2 = (Message)Receive(inbound2); - - Assert.NotNull(receivedMessage2); - Assert.Equal(testPayload, Encoding.UTF8.GetString(receivedMessage2.Payload)); - - await consumerBinding1.UnbindAsync(); - await consumerBinding2.UnbindAsync(); - await producerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestPartitionedModuleSpel() - { - var bindingsOptions = new RabbitBindingsOptions(); - TTestBinder binder = GetBinder(bindingsOptions); - - ConsumerOptions consumerProperties = GetConsumerOptions("input", bindingsOptions); - consumerProperties.Concurrency = 2; - consumerProperties.InstanceIndex = 0; - consumerProperties.InstanceCount = 3; - consumerProperties.Partitioned = true; - - string delimiter = GetDestinationNameDelimiter(); - - var input0 = new QueueChannel - { - ComponentName = "test.input0S" - }; - - IBinding input0Binding = binder.BindConsumer($"part{delimiter}0", "testPartitionedModuleSpEL", input0, consumerProperties); - - consumerProperties.InstanceIndex = 1; - - var input1 = new QueueChannel - { - ComponentName = "test.input1S" - }; - - IBinding input1Binding = binder.BindConsumer($"part{delimiter}0", "testPartitionedModuleSpEL", input1, consumerProperties); - - consumerProperties.InstanceIndex = 2; - - var input2 = new QueueChannel - { - ComponentName = "test.input2S" - }; - - IBinding input2Binding = binder.BindConsumer($"part{delimiter}0", "testPartitionedModuleSpEL", input2, consumerProperties); - - ProducerOptions producerProperties = GetProducerOptions("output", bindingsOptions); - RabbitProducerOptions rabbitProducerOptions = bindingsOptions.GetRabbitProducerOptions("output"); - rabbitProducerOptions.RoutingKeyExpression = "'part.0'"; - producerProperties.PartitionKeyExpression = "Payload"; - - producerProperties.PartitionSelectorExpression = - "ToString()"; // For strings, Java hash is not equivalent to GetHashCode, but for 0,1,2 ToString() is equivalent to hash. - - producerProperties.PartitionCount = 3; - DirectChannel output = CreateBindableChannel("output", CreateProducerBindingOptions(producerProperties)); - output.ComponentName = "test.output"; - - IBinding outputBinding = binder.BindProducer($"part{delimiter}0", output, producerProperties); - - try - { - ILifecycle endpoint = ExtractEndpoint(outputBinding); - CheckRkExpressionForPartitionedModuleSpel(endpoint); - } - catch (Exception ex) - { - _logger?.LogError(ex, ex.Message); - } - - IMessage message2 = MessageBuilder.WithPayload("2").SetHeader("correlationId", "foo").SetHeader("contentType", MimeTypeUtils.TextPlain) - .SetHeader("sequenceNumber", 42).SetHeader("sequenceSize", 43).Build(); - - output.Send(message2); - output.Send(MessageBuilder.WithPayload("1").SetHeader("contentType", MimeTypeUtils.TextPlain).Build()); - output.Send(MessageBuilder.WithPayload("0").SetHeader("contentType", MimeTypeUtils.TextPlain).Build()); - - IMessage receive0 = Receive(input0); - Assert.NotNull(receive0); - - IMessage receive1 = Receive(input1); - Assert.NotNull(receive1); - - IMessage receive2 = Receive(input2); - Assert.NotNull(receive2); - - Func correlationHeadersForPayload2 = m => - { - var accessor = new IntegrationMessageHeaderAccessor(m); - return "foo".Equals(accessor.GetCorrelationId()) && accessor.GetSequenceNumber() == 42 && accessor.GetSequenceSize() == 43; - }; - - if (UsesExplicitRouting()) - { - Assert.Equal("0", ((byte[])receive0.Payload).GetString()); - Assert.Equal("1", ((byte[])receive1.Payload).GetString()); - Assert.Equal("2", ((byte[])receive2.Payload).GetString()); - Assert.True(correlationHeadersForPayload2(receive2)); - } - else - { - var receivedMessages = new List - { - receive0, - receive1, - receive2 - }; - - Assert.Contains(receivedMessages, m => ((byte[])m.Payload).ToString() == "0"); - Assert.Contains(receivedMessages, m => ((byte[])m.Payload).ToString() == "1"); - Assert.Contains(receivedMessages, m => ((byte[])m.Payload).ToString() == "2"); - - Func payloadIs2 = m => m.Payload.Equals("2".GetBytes()); - Assert.Single(receivedMessages.Where(payloadIs2).Where(correlationHeadersForPayload2)); - } - - await input0Binding.UnbindAsync(); - await input1Binding.UnbindAsync(); - await input2Binding.UnbindAsync(); - await outputBinding.UnbindAsync(); - } - - protected abstract string GetEndpointRouting(object endpoint); - - protected abstract string GetExpectedRoutingBaseDestination(string name, string group); - - protected abstract bool UsesExplicitRouting(); - - protected ILifecycle ExtractEndpoint(IBinding binding) - { - return GetFieldValue(binding, "Lifecycle"); - } - - protected TValue GetFieldValue(object current, string name) - { - FieldInfo fi = current.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - return (TValue)fi.GetValue(current); - } - - protected TValue GetPropertyValue(object current, string name) - { - PropertyInfo pi = current.GetType().GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - return (TValue)pi.GetValue(current); - } - - protected virtual void CheckRkExpressionForPartitionedModuleSpel(object endpoint) - { - string routingExpression = GetEndpointRouting(endpoint); - string delimiter = GetDestinationNameDelimiter(); - string dest = $"{GetExpectedRoutingBaseDestination($"part{delimiter}0", "test")}-' + Headers['partition']"; - Assert.Contains(dest, routingExpression, StringComparison.Ordinal); - } -} diff --git a/src/Stream/test/Binder.Test/Properties/AssemblyInfo.cs b/src/Stream/test/Binder.Test/Properties/AssemblyInfo.cs deleted file mode 100644 index ca490cd846..0000000000 --- a/src/Stream/test/Binder.Test/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Steeltoe.Stream.Binder.RabbitMQ.Test")] diff --git a/src/Stream/test/Binder.Test/Spy.cs b/src/Stream/test/Binder.Test/Spy.cs deleted file mode 100644 index ef74b2dfa4..0000000000 --- a/src/Stream/test/Binder.Test/Spy.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder.Test; - -/// -/// Represents an out-of-band connection to the underlying middleware, so that tests can check that some messages actually do (or do not) transit through -/// it. -/// -public sealed class Spy -{ - public Func Receive { get; set; } -} diff --git a/src/Stream/test/Binder.Test/Steeltoe.Stream.Binder.Test.csproj b/src/Stream/test/Binder.Test/Steeltoe.Stream.Binder.Test.csproj deleted file mode 100644 index 6ba6d7111e..0000000000 --- a/src/Stream/test/Binder.Test/Steeltoe.Stream.Binder.Test.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - net8.0;net6.0 - - - - - - - - diff --git a/src/Stream/test/Binder.Test/TestExtensions.cs b/src/Stream/test/Binder.Test/TestExtensions.cs deleted file mode 100644 index 1a5fca3db0..0000000000 --- a/src/Stream/test/Binder.Test/TestExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; - -namespace Steeltoe.Stream.Binder.Test; - -public static class TestExtensions -{ - public static byte[] GetBytes(this string input) - { - return Encoding.UTF8.GetBytes(input); - } - - public static string GetString(this byte[] input) - { - return Encoding.UTF8.GetString(input); - } -} diff --git a/src/Stream/test/Binder.Test/xunit.runner.json b/src/Stream/test/Binder.Test/xunit.runner.json deleted file mode 100644 index fdeefaa456..0000000000 --- a/src/Stream/test/Binder.Test/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "maxParallelThreads": 1, - "parallelizeTestCollections": false -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/Configuration/RabbitBinderOptionsTest.cs b/src/Stream/test/BinderRabbitMQ.Test/Configuration/RabbitBinderOptionsTest.cs deleted file mode 100644 index b4b1b880fd..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/Configuration/RabbitBinderOptionsTest.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.IO.Compression; -using Microsoft.Extensions.Configuration; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Xunit; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Test.Configuration; - -public sealed class RabbitBinderOptionsTest -{ - [Fact] - public void InitializeAll_FromConfigValues() - { - var builder = new ConfigurationBuilder(); - - builder.AddInMemoryCollection(new Dictionary - { - { "spring:cloud:stream:rabbit:binder:adminAddresses:0", "adminAddresses0" }, - { "spring:cloud:stream:rabbit:binder:adminAddresses:1", "adminAddresses1" }, - { "spring:cloud:stream:rabbit:binder:nodes:0", "nodes0" }, - { "spring:cloud:stream:rabbit:binder:nodes:1", "nodes1" }, - { "spring:cloud:stream:rabbit:binder:compressionLevel", "NoCompression" }, - { "spring:cloud:stream:rabbit:binder:connectionNamePrefix", "connectionNamePrefix" } - }); - - IConfigurationSection configuration = builder.Build().GetSection("spring:cloud:stream:rabbit:binder"); - var options = new RabbitBinderOptions(configuration); - - Assert.Equal("adminAddresses0", options.AdminAddresses[0]); - Assert.Equal("adminAddresses1", options.AdminAddresses[1]); - Assert.Equal("nodes0", options.Nodes[0]); - Assert.Equal("nodes1", options.Nodes[1]); - Assert.Equal(CompressionLevel.NoCompression, options.CompressionLevel); - Assert.Equal("connectionNamePrefix", options.ConnectionNamePrefix); - } -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/Configuration/RabbitBindingsOptionsTest.cs b/src/Stream/test/BinderRabbitMQ.Test/Configuration/RabbitBindingsOptionsTest.cs deleted file mode 100644 index 3889cf9525..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/Configuration/RabbitBindingsOptionsTest.cs +++ /dev/null @@ -1,273 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.ComponentModel; -using System.Globalization; -using System.Reflection; -using FluentAssertions; -using Microsoft.Extensions.Configuration; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Xunit; -using Xunit.Abstractions; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Test.Configuration; - -public sealed class RabbitBindingsOptionsTest -{ - private readonly ITestOutputHelper _output; - - public RabbitBindingsOptionsTest(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - public void InitializeAll_FromDefaultValues() - { - var builder = new ConfigurationBuilder(); - IConfigurationSection configuration = builder.Build().GetSection("spring:cloud:stream:rabbit"); - var options = new RabbitBindingsOptions(configuration); - options.PostProcess(); - Assert.NotNull(options.Default); - Assert.NotNull(options.Default.Consumer); - Assert.NotNull(options.Default.Producer); - - Assert.Equal(1, options.Default.Consumer.Prefetch); - Assert.Equal(100, options.Default.Producer.BatchSize); - } - - [Fact] - public void InitializeAll_FromConfigValues() - { - var builder = new ConfigurationBuilder(); - - var dict = new Dictionary - { - { "spring:cloud:stream:rabbit:default:consumer:autoBindDlq", "true" }, - { "spring:cloud:stream:rabbit:default:consumer:dlqMaxLength", "10000" }, - { "spring:cloud:stream:rabbit:default:producer:autoBindDlq", "true" }, - { "spring:cloud:stream:rabbit:default:producer:dlqMaxLength", "10000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:autoBindDlq", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:bindingRoutingKey", "bindingRoutingKey" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:bindingRoutingKeyDelimiter", "bindingRoutingKeyDelimiter" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:bindQueue", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:consumerTagPrefix", "consumerTagPrefix" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:deadLetterQueueName", "deadLetterQueueName" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:deadLetterExchange", "deadLetterExchange" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:deadLetterExchangeType", "deadLetterExchangeType" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:deadLetterRoutingKey", "deadLetterRoutingKey" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:declareDlx", "false" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:declareExchange", "false" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:delayedExchange", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqBindingArguments:foo", "bar" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqBindingArguments:bar", "foo" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqDeadLetterExchange", "dlqDeadLetterExchange" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqDeadLetterRoutingKey", "dlqDeadLetterRoutingKey" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqExpires", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqLazy", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqMaxLength", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqMaxLengthBytes", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqMaxPriority", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqOverflowBehavior", "dlqOverflowBehavior" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqSingleActiveConsumer", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqTtl", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:durableSubscription", "false" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:exchangeAutoDelete", "false" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:exchangeDurable", "false" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:exchangeType", "exchangeType" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:exclusive", "false" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:expires", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:failedDeclarationRetryInterval", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:frameMaxHeadroom", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:headerPatterns:0", "headerPatterns" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:lazy", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:maxConcurrency", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:maxLength", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:maxLengthBytes", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:maxPriority", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:missingQueuesFatal", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:overflowBehavior", "overflowBehavior" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:prefetch", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:prefix", "prefix" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:queueBindingArguments:foo", "bar" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:queueBindingArguments:bar", "foo" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:queueDeclarationRetries", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:queueNameGroupOnly", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:recoveryInterval", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:requeueRejected", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:republishDeliveryMode", "NON_PERSISTENT" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:republishToDlq", "false" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:singleActiveConsumer", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:transacted", "true" }, - - // { "spring:cloud:stream:rabbit:bindings:input:consumer:txSize", "1000" }, // Not supported in Steeltoe: Not supported when the containerType is direct. - { "spring:cloud:stream:rabbit:bindings:input:consumer:ttl", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:quorum:deliveryLimit", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:quorum:enabled", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:quorum:initialQuorumSize", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqQuorum:deliveryLimit", "1000" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqQuorum:enabled", "true" }, - { "spring:cloud:stream:rabbit:bindings:input:consumer:dlqQuorum:initialQuorumSize", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:autoBindDlq", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:batchingEnabled", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:batchSize", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:batchBufferLimit", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:batchTimeout", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:bindingRoutingKey", "bindingRoutingKey" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:bindingRoutingKeyDelimiter", "bindingRoutingKeyDelimiter" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:bindQueue", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:compress", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:confirmAckChannel", "confirmAckChannel" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:deadLetterQueueName", "deadLetterQueueName" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:deadLetterExchange", "deadLetterExchange" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:deadLetterExchangeType", "deadLetterExchangeType" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:deadLetterRoutingKey", "deadLetterRoutingKey" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:declareDlx", "false" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:declareExchange", "false" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:delayExpression", "delayExpression" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:delayedExchange", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:deliveryMode", "NON_PERSISTENT" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqBindingArguments:foo", "bar" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqBindingArguments:bar", "foo" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqDeadLetterExchange", "dlqDeadLetterExchange" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqDeadLetterRoutingKey", "dlqDeadLetterRoutingKey" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqExpires", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqLazy", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqMaxLength", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqMaxLengthBytes", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqMaxPriority", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqSingleActiveConsumer", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqTtl", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:durableSubscription", "false" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:exchangeAutoDelete", "false" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:exchangeDurable", "false" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:exchangeType", "exchangeType" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:exclusive", "false" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:expires", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:failedDeclarationRetryInterval", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:headerPatterns:0", "headerPatterns0" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:headerPatterns:1", "headerPatterns1" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:lazy", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:maxLength", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:maxLengthBytes", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:maxPriority", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:overflowBehavior", "overflowBehavior" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:prefix", "prefix" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:queueBindingArguments:foo", "bar" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:queueBindingArguments:bar", "foo" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:queueNameGroupOnly", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:routingKeyExpression", "routingKeyExpression" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:singleActiveConsumer", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:transacted", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:ttl", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:quorum:deliveryLimit", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:quorum:enabled", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:quorum:initialQuorumSize", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqQuorum:deliveryLimit", "1000" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqQuorum:enabled", "true" }, - { "spring:cloud:stream:rabbit:bindings:output:producer:dlqQuorum:initialQuorumSize", "1000" }, - { "spring:cloud:stream:rabbit:binder:adminAddresses:0", "adminAddresses0" }, - { "spring:cloud:stream:rabbit:binder:adminAddresses:1", "adminAddresses1" }, - { "spring:cloud:stream:rabbit:binder:nodes:0", "nodes0" }, - { "spring:cloud:stream:rabbit:binder:nodes:1", "nodes1" }, - { "spring:cloud:stream:rabbit:binder:compressionLevel", "NoCompression" }, - { "spring:cloud:stream:rabbit:binder:connectionNamePrefix", "connectionNamePrefix" } - }; - - builder.AddInMemoryCollection(dict); - - IConfigurationSection configuration = builder.Build().GetSection(RabbitBindingsOptions.Prefix); - var options = new RabbitBindingsOptions(configuration); - options.PostProcess(); - Assert.NotNull(options.Default); - Assert.NotNull(options.Default.Consumer); - Assert.NotNull(options.Default.Producer); - - Assert.True(options.Default.Consumer.AutoBindDlq); - Assert.Equal(10000, options.Default.Consumer.DlqMaxLength); - Assert.True(options.Default.Producer.AutoBindDlq); - Assert.Equal(10000, options.Default.Producer.DlqMaxLength); - - RabbitConsumerOptions inputBinding = options.GetRabbitConsumerOptions("input"); - Assert.NotNull(inputBinding); - Assert.NotSame(options.Default.Consumer, inputBinding); - RabbitProducerOptions outputBinding = options.GetRabbitProducerOptions("output"); - Assert.NotNull(outputBinding); - Assert.NotSame(options.Default.Producer, outputBinding); - - const string inputBindingsKey = "bindings:input:consumer"; - - foreach (Tuple tuple in GetOptionsConfigPairs(configuration, inputBinding, inputBindingsKey)) - { - tuple.Item2.Should().Be(tuple.Item3, $"{inputBindingsKey}:{tuple.Item1}"); - } - - const string outputBindingsKey = "bindings:output:producer"; - - foreach (Tuple tuple in GetOptionsConfigPairs(configuration, outputBinding, outputBindingsKey)) - { - tuple.Item2.Should().Be(tuple.Item3, $"{outputBindingsKey}:{tuple.Item1}"); - } - } - - private IEnumerable> GetOptionsConfigPairs(IConfigurationSection section, object optionsObject, string inputBindingsKey) - { - IConfigurationSection inputBindingSection = section.GetSection(inputBindingsKey); - IEnumerable children = inputBindingSection.GetChildren(); - - foreach (IConfigurationSection child in children) - { - _output.WriteLine(child.Key + ":" + child.Value); - PropertyInfo pi = optionsObject.GetType().GetProperty(child.Key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); - - object value = null; - - if (pi == null) - { - // Check for dictionary Type - if (optionsObject is IDictionary) - { - var dict = optionsObject as Dictionary; - value = dict?[child.Key]; - } - - // Check for list Type - else if (optionsObject is List) - { - var list = optionsObject as IList; - value = list?[int.Parse(child.Key, CultureInfo.InvariantCulture)]; - } - else - { - throw new Exception($"Type {optionsObject.GetType()} not supported at ${inputBindingsKey}"); - } - } - else - { - value = pi.GetValue(optionsObject); - } - - if (child.Value == null) - { - foreach (Tuple kvp in GetOptionsConfigPairs(inputBindingSection, value, child.Key)) - { - yield return kvp; - } - } - else - { - string childValue = child.Value; - - if (pi != null) - { - TypeConverter converter = TypeDescriptor.GetConverter(pi.PropertyType); - object childObject = converter.ConvertFromString(childValue); - childValue = childObject?.ToString(); - } - - yield return new Tuple($"{inputBindingsKey}:{child.Key}", value?.ToString(), childValue); - } - } - } -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/ManagementClientFactory.cs b/src/Stream/test/BinderRabbitMQ.Test/ManagementClientFactory.cs deleted file mode 100644 index 2a986e60d9..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/ManagementClientFactory.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using EasyNetQ.Management.Client; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Test; - -internal static class ManagementClientFactory -{ - public static ManagementClient CreateDefault(string hostUrl = "http://localhost:15672", string username = "guest", string password = "guest") - { - return new ManagementClient(new Uri(hostUrl), username, password); - } -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/Poco.cs b/src/Stream/test/BinderRabbitMQ.Test/Poco.cs deleted file mode 100644 index 90ba5c96fc..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/Poco.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Binder.RabbitMQ.Test; - -public sealed class Poco -{ - // ReSharper disable once InconsistentNaming -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter -#pragma warning disable SA1401 // Fields should be private - public string field; -#pragma warning restore SA1401 // Fields should be private -#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter - - public Poco() - { - } - - public Poco(string field) - { - this.field = field; - } -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/RabbitBinderTestBase.cs b/src/Stream/test/BinderRabbitMQ.Test/RabbitBinderTestBase.cs deleted file mode 100644 index 3acca42ea9..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/RabbitBinderTestBase.cs +++ /dev/null @@ -1,211 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Runtime.ExceptionServices; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Retry; -using Steeltoe.Common.TestResources; -using Steeltoe.Integration.RabbitMQ.Inbound; -using Steeltoe.Integration.RabbitMQ.Outbound; -using Steeltoe.Messaging; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Steeltoe.Stream.Binder.Test; -using Xunit; -using Xunit.Abstractions; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Test; - -public abstract class RabbitBinderTestBase : PartitionCapableBinderTests, IDisposable -{ - protected const string TestPrefix = "bindertest."; - private static readonly string BigExceptionMessage = new('x', 10_000); - private bool _isDisposed; - - protected RabbitTestBinder TestBinder { get; set; } - - protected int MaxStackTraceSize { get; set; } - - protected RabbitBinderTestBase(ITestOutputHelper output) - : base(output, new XunitLoggerFactory(output)) - { - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public Spy SpyOn(string queue) - { - var template = new RabbitTemplate(GetResource()); - template.SetAfterReceivePostProcessors(new DelegatingDecompressingPostProcessor()); - - return new Spy - { - Receive = expectNull => - { - if (expectNull) - { - Thread.Sleep(50); - return template.ReceiveAndConvert(new RabbitConsumerOptions().Prefix + queue); - } - - object bar = null; - int n = 0; - - while (n++ < 100 && bar == null) - { - bar = template.ReceiveAndConvert(new RabbitConsumerOptions().Prefix + queue); - Thread.Sleep(100); - } - - Assert.True(n < 100, "Message did not arrive in RabbitMQ"); - return bar; - } - }; - } - - protected virtual void Dispose(bool disposing) - { - if (disposing && !_isDisposed) - { - Cleanup(); - _isDisposed = true; - } - } - - protected override RabbitTestBinder GetBinder(RabbitBindingsOptions bindingsOptions) - { - if (TestBinder == null) - { - var options = new RabbitOptions - { - PublisherReturns = true - }; - - CachingConnectionFactory ccf = GetResource(); - var rabbitOptions = new TestOptionsMonitor(options); - var binderOptions = new TestOptionsMonitor(null); - var rabbitBindingsOptions = new TestOptionsMonitor(bindingsOptions ?? new RabbitBindingsOptions()); - - TestBinder = new RabbitTestBinder(ccf, rabbitOptions, binderOptions, rabbitBindingsOptions, LoggerFactory); - } - - return TestBinder; - } - - protected DirectMessageListenerContainer VerifyContainer(RabbitInboundChannelAdapter endpoint) - { - DirectMessageListenerContainer container; - RetryTemplate retry; - container = GetPropertyValue(endpoint, "MessageListenerContainer"); - - Assert.Equal(AcknowledgeMode.None, container.AcknowledgeMode); - Assert.StartsWith("foo.props.0", container.GetQueueNames()[0], StringComparison.Ordinal); - Assert.False(container.IsChannelTransacted); - Assert.Equal(2, container.ConsumersPerQueue); - Assert.False(container.DefaultRequeueRejected); - Assert.Equal(20, container.PrefetchCount); - - retry = endpoint.RetryTemplate; - Assert.Equal(23, GetFieldValue(retry, "_maxAttempts")); - Assert.Equal(2000, GetFieldValue(retry, "_backOffInitialInterval")); - Assert.Equal(5.0, GetFieldValue(retry, "_backOffMultiplier")); - - return container; - } - - protected Exception BigCause(Exception innerException) - { - return BigCause(innerException, 0); - } - - private Exception BigCause(Exception innerException, int recursionDepth) - { - if (recursionDepth > 1000) - { - throw new InvalidOperationException("Internal error: Infinite recursion detected."); - } - - try - { - Exception capturedException = innerException ?? new Exception(BigExceptionMessage); - ExceptionDispatchInfo.Capture(capturedException).Throw(); - } - catch (Exception ex) - { - if (ex.StackTrace != null && ex.StackTrace.Length > MaxStackTraceSize) - { - return ex; - } - - innerException = ex; - } - - return BigCause(innerException, recursionDepth + 1); - } - - protected void Cleanup() - { - if (TestBinder != null) - { - Cleanup(TestBinder); - } - - if (CachingConnectionFactory != null) - { - CachingConnectionFactory.ResetConnection(); - CachingConnectionFactory.Destroy(); - CachingConnectionFactory = null; - } - - TestBinder = null; - } - - private void Cleanup(RabbitTestBinder binder) - { - binder.Cleanup(); - binder.CoreBinder.ConnectionFactory.Destroy(); - CachingConnectionFactory = null; - } - - public sealed class WrapperAccessor : RabbitOutboundEndpoint - { - public WrapperAccessor(IApplicationContext context, RabbitTemplate template) - : base(context, template, null) - { - } - - public CorrelationData GetWrapper(IMessage message) - { - return Activator.CreateInstance(typeof(CorrelationDataWrapper), null, message, message) as CorrelationData; - } - } - - public sealed class TestPartitionSupport : IPartitionKeyExtractorStrategy, IPartitionSelectorStrategy - { - public string ServiceName { get; set; } - - public TestPartitionSupport(string serviceName) - { - ServiceName = serviceName; - } - - public object ExtractKey(IMessage message) - { - return message.Payload; - } - - public int SelectPartition(object key, int partitionCount) - { - return (int)key; - } - } -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/RabbitBinderTests.cs b/src/Stream/test/BinderRabbitMQ.Test/RabbitBinderTests.cs deleted file mode 100644 index cb4dd0470c..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/RabbitBinderTests.cs +++ /dev/null @@ -1,2169 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Dynamic; -using System.IO.Compression; -using System.Net; -using System.Net.Http.Headers; -using System.Runtime.ExceptionServices; -using EasyNetQ.Management.Client; -using EasyNetQ.Management.Client.Model; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Expression.Internal.Spring.Standard; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Retry; -using Steeltoe.Common.TestResources; -using Steeltoe.Common.Util; -using Steeltoe.Integration; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.RabbitMQ.Inbound; -using Steeltoe.Integration.RabbitMQ.Outbound; -using Steeltoe.Integration.RabbitMQ.Support; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.RabbitMQ; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Messaging.RabbitMQ.Exceptions; -using Steeltoe.Messaging.RabbitMQ.Extensions; -using Steeltoe.Messaging.RabbitMQ.Listener; -using Steeltoe.Messaging.RabbitMQ.Retry; -using Steeltoe.Messaging.RabbitMQ.Support.PostProcessor; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Steeltoe.Stream.Binder.RabbitMQ.Provisioning; -using Steeltoe.Stream.Binder.Test; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Converter; -using Steeltoe.Stream.Provisioning; -using Xunit; -using Xunit.Abstractions; -using static Steeltoe.Messaging.RabbitMQ.Connection.CachingConnectionFactory; -using Message = Steeltoe.Messaging.Message; -using Queue = Steeltoe.Messaging.RabbitMQ.Configuration.Queue; -using RabbitBinding = Steeltoe.Messaging.RabbitMQ.Configuration.Binding; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Test; - -[Trait("Category", "Integration")] -public sealed class RabbitBinderTests : RabbitBinderTestBase -{ - public RabbitBinderTests(ITestOutputHelper output) - : base(output) - { - } - - [Fact] - public async Task TestSendAndReceiveBad() - { - var bindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(bindingsOptions); - DirectChannel moduleOutputChannel = CreateBindableChannel("output", GetDefaultBindingOptions()); - DirectChannel moduleInputChannel = CreateBindableChannel("input", GetDefaultBindingOptions()); - IBinding producerBinding = binder.BindProducer("bad.0", moduleOutputChannel, GetProducerOptions("output", bindingsOptions)); - - var endpoint = GetFieldValue(producerBinding, "Lifecycle"); - - Assert.True(endpoint.HeadersMappedLast); - Assert.Contains("PassThrough", endpoint.Template.MessageConverter.GetType().Name, StringComparison.Ordinal); - - ConsumerOptions consumerProps = GetConsumerOptions("input", bindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = bindingsOptions.GetRabbitConsumerOptions("input"); - - rabbitConsumerOptions.ContainerType = ContainerType.Direct; - - IBinding consumerBinding = binder.BindConsumer("bad.0", "test", moduleInputChannel, consumerProps); - - var inbound = GetFieldValue(consumerBinding, "Lifecycle"); - Assert.Contains("PassThrough", inbound.MessageConverter.GetType().Name, StringComparison.Ordinal); - var container = GetPropertyValue(inbound, "MessageListenerContainer"); - Assert.NotNull(container); - - IMessage message = MessageBuilder.WithPayload("bad".GetBytes()).SetHeader(MessageHeaders.ContentType, "foo/bar").Build(); - - var latch = new CountdownEvent(3); - - moduleInputChannel.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => - { - latch.Signal(); - throw new Exception(); - } - }); - - moduleOutputChannel.Send(message); - - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestProducerErrorChannel() - { - CachingConnectionFactory ccf = GetResource(); - ccf.IsPublisherConfirms = true; - ccf.PublisherConfirmType = ConfirmType.Correlated; - ccf.ResetConnection(); - var bindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(bindingsOptions); - - RegisterGlobalErrorChannel(binder); - - DirectChannel moduleOutputChannel = CreateBindableChannel("output", GetDefaultBindingOptions()); - ProducerOptions producerOptions = GetProducerOptions("output", bindingsOptions); - producerOptions.ErrorChannelEnabled = true; - - IBinding producerBinding = binder.BindProducer("ec.0", moduleOutputChannel, producerOptions); - - IMessage message = MessageBuilder.WithPayload("bad".GetBytes()).SetHeader(MessageHeaders.ContentType, "foo/bar").Build(); - - var ec = binder.ApplicationContext.GetService("ec.0.errors"); - Assert.NotNull(ec); - var errorMessage = new AtomicReference(); - - var latch = new CountdownEvent(2); - - ec.Subscribe(new TestMessageHandler - { - OnHandleMessage = innerMessage => - { - errorMessage.GetAndSet(innerMessage); - latch.Signal(); - } - }); - - var globalEc = binder.ApplicationContext.GetService(IntegrationContextUtils.ErrorChannelBeanName); - - globalEc.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => - { - latch.Signal(); - } - }); - - moduleOutputChannel.Send(message); - Assert.True(latch.Wait(TimeSpan.FromSeconds(10))); - Assert.IsAssignableFrom(errorMessage.Value); - Assert.IsAssignableFrom(errorMessage.Value.Payload); - - var exception = (ReturnedRabbitMessageException)errorMessage.Value.Payload; - Assert.Equal(312, exception.ReplyCode); - Assert.Equal("NO_ROUTE", exception.ReplyText); - - var endpoint = ExtractEndpoint(producerBinding) as RabbitOutboundEndpoint; - Assert.NotNull(endpoint); - var expression = GetPropertyValue(endpoint, "ConfirmCorrelationExpression"); - Assert.NotNull(expression); - Assert.Equal("#root", GetPropertyValue(expression, "ExpressionString")); - var template = new RabbitTemplate(); - var accessor = new WrapperAccessor(null, template); - CorrelationData correlationData = accessor.GetWrapper(message); - - latch.Reset(2); - endpoint.Confirm(correlationData, false, "Mock Nack"); - - Assert.IsAssignableFrom(errorMessage.Value); - - Assert.IsAssignableFrom(errorMessage.Value.Payload); - var nack = errorMessage.Value.Payload as NackedRabbitMessageException; - - Assert.NotNull(nack); - Assert.Equal("Mock Nack", nack.NackReason); - Assert.Equal(message, nack.CorrelationData); - Assert.Equal(message, nack.FailedMessage); - await producerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestProducerAckChannel() - { - var bindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(bindingsOptions); - CachingConnectionFactory ccf = GetResource(); - ccf.IsPublisherReturns = true; - ccf.PublisherConfirmType = ConfirmType.Correlated; - ccf.ResetConnection(); - - DirectChannel moduleOutputChannel = CreateBindableChannel("output", GetDefaultBindingOptions()); - ProducerOptions producerProps = GetProducerOptions("output", bindingsOptions); - producerProps.ErrorChannelEnabled = true; - - RabbitProducerOptions rabbitProducerOptions = bindingsOptions.GetRabbitProducerOptions("output"); - rabbitProducerOptions.ConfirmAckChannel = "acksChannel"; - - IBinding producerBinding = binder.BindProducer("acks.0", moduleOutputChannel, producerProps); - byte[] messageBytes = "acksMessage".GetBytes(); - IMessage message = MessageBuilder.WithPayload(messageBytes).Build(); - - var confirm = new AtomicReference(); - var confirmLatch = new CountdownEvent(1); - - binder.ApplicationContext.GetService("acksChannel").Subscribe(new TestMessageHandler - { - OnHandleMessage = m => - { - confirm.GetAndSet(m); - confirmLatch.Signal(); - } - }); - - moduleOutputChannel.Send(message); - Assert.True(confirmLatch.Wait(TimeSpan.FromSeconds(10000))); - Assert.Equal(messageBytes, confirm.Value.Payload); - await producerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestProducerConfirmHeader() - { - RabbitTestBinder binder = GetBinder(); - - CachingConnectionFactory ccf = GetResource(); - ccf.IsPublisherReturns = true; - ccf.PublisherConfirmType = ConfirmType.Correlated; - ccf.ResetConnection(); - - DirectChannel moduleOutputChannel = CreateBindableChannel("output", GetDefaultBindingOptions()); - var rabbitBindingsOptions = new RabbitBindingsOptions(); - var rabbitBindingOptions = new RabbitBindingOptions(); - - ProducerOptions producerProps = GetProducerOptions("output", rabbitBindingsOptions, rabbitBindingOptions); - rabbitBindingOptions.Producer.UseConfirmHeader = true; - IBinding producerBinding = binder.BindProducer("confirms.0", moduleOutputChannel, producerProps); - - var correlation = new CorrelationData("testConfirm"); - - IMessage message = MessageBuilder.WithPayload("confirmsMessage".GetBytes()).SetHeader(RabbitMessageHeaders.PublishConfirmCorrelation, correlation) - .Build(); - - moduleOutputChannel.Send(message); - CorrelationData.Confirm confirm = await correlation.Future; - Assert.True(confirm.Ack); - - await producerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestConsumerProperties() - { - var rabbitConsumerOptions = new RabbitConsumerOptions - { - RequeueRejected = true, - Transacted = true, - Exclusive = true, - MissingQueuesFatal = true, - FailedDeclarationRetryInterval = 1500L, - QueueDeclarationRetries = 23 - }; - - var bindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(bindingsOptions); - - ConsumerOptions properties = GetConsumerOptions("input", bindingsOptions, rabbitConsumerOptions, null); - - IBinding consumerBinding = binder.BindConsumer("props.0", null, CreateBindableChannel("input", GetDefaultBindingOptions()), properties); - - var endpoint = ExtractEndpoint(consumerBinding) as RabbitInboundChannelAdapter; - Assert.NotNull(endpoint); - var container = GetPropertyValue(endpoint, "MessageListenerContainer"); - Assert.NotNull(container); - Assert.Equal(AcknowledgeMode.Auto, container.AcknowledgeMode); - Assert.StartsWith(rabbitConsumerOptions.Prefix, container.GetQueueNames()[0], StringComparison.Ordinal); - Assert.True(container.Exclusive); - Assert.True(container.IsChannelTransacted); - Assert.True(container.Exclusive); - Assert.True(container.DefaultRequeueRejected); - Assert.Equal(1, container.PrefetchCount); - Assert.True(container.MissingQueuesFatal); - Assert.Equal(1500L, container.FailedDeclarationRetryInterval); - - RetryTemplate retry = endpoint.RetryTemplate; - Assert.NotNull(retry); - Assert.Equal(3, GetFieldValue(retry, "_maxAttempts")); - Assert.Equal(1000, GetFieldValue(retry, "_backOffInitialInterval")); - Assert.Equal(2.0, GetFieldValue(retry, "_backOffMultiplier")); - await consumerBinding.UnbindAsync(); - Assert.False(endpoint.IsRunning); - - bindingsOptions.Bindings.Remove("input"); - - properties = GetConsumerOptions("input", bindingsOptions); - rabbitConsumerOptions = bindingsOptions.GetRabbitConsumerOptions("input"); - rabbitConsumerOptions.AcknowledgeMode = AcknowledgeMode.None; - properties.BackOffInitialInterval = 2000; - properties.BackOffMaxInterval = 20000; - properties.BackOffMultiplier = 5.0; - properties.Concurrency = 2; - properties.MaxAttempts = 23; - - rabbitConsumerOptions.MaxConcurrency = 3; - rabbitConsumerOptions.Prefix = "foo."; - rabbitConsumerOptions.Prefetch = 20; - - rabbitConsumerOptions.HeaderPatterns = new[] - { - "foo" - }.ToList(); - - rabbitConsumerOptions.BatchSize = 10; - RabbitCommonOptions.QuorumConfig quorum = rabbitConsumerOptions.Quorum; - quorum.Enabled = true; - quorum.DeliveryLimit = 10; - quorum.InitialQuorumSize = 1; - properties.InstanceIndex = 0; - consumerBinding = binder.BindConsumer("props.0", "test", CreateBindableChannel("input", GetDefaultBindingOptions()), properties); - - endpoint = ExtractEndpoint(consumerBinding) as RabbitInboundChannelAdapter; - container = VerifyContainer(endpoint); - - Assert.Equal("foo.props.0.test", container.GetQueueNames()[0]); - - await consumerBinding.UnbindAsync(); - Assert.NotNull(endpoint); - Assert.False(endpoint.IsRunning); - } - - [Fact] - public void TestMultiplexOnPartitionedConsumer() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - ConsumerOptions consumerProperties = GetConsumerOptions(string.Empty, rabbitBindingsOptions); - RabbitProxy proxy = null; - - try - { - proxy = new RabbitProxy(LoggerFactory.CreateLogger()); - using var ccf = new CachingConnectionFactory("localhost", proxy.Port); - - var bindingsOptionsMonitor = new TestOptionsMonitor(rabbitBindingsOptions); - - var rabbitExchangeQueueProvisioner = new RabbitExchangeQueueProvisioner(ccf, bindingsOptionsMonitor, - GetBinder(rabbitBindingsOptions).ApplicationContext, LoggerFactory.CreateLogger()); - - consumerProperties.Multiplex = true; - consumerProperties.Partitioned = true; - - consumerProperties.InstanceIndexList = new[] - { - 1, - 2, - 3 - }.ToList(); - - IConsumerDestination consumerDestination = rabbitExchangeQueueProvisioner.ProvisionConsumerDestination("foo", "boo", consumerProperties); - Assert.Equal("foo.boo-1,foo.boo-2,foo.boo-3", consumerDestination.Name); - } - finally - { - proxy?.Stop(); - } - } - - [Fact] - public void TestMultiplexOnPartitionedConsumerWithMultipleDestinations() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - ConsumerOptions consumerProperties = GetConsumerOptions(string.Empty, rabbitBindingsOptions); - RabbitProxy proxy = null; - - try - { - proxy = new RabbitProxy(LoggerFactory.CreateLogger()); - using var ccf = new CachingConnectionFactory("localhost", proxy.Port); - var bindingsOptionsMonitor = new TestOptionsMonitor(rabbitBindingsOptions); - - var rabbitExchangeQueueProvisioner = new RabbitExchangeQueueProvisioner(ccf, bindingsOptionsMonitor, - GetBinder(rabbitBindingsOptions).ApplicationContext, LoggerFactory.CreateLogger()); - - consumerProperties.Multiplex = true; - consumerProperties.Partitioned = true; - - consumerProperties.InstanceIndexList = new[] - { - 1, - 2, - 3 - }.ToList(); - - IConsumerDestination consumerDestination = rabbitExchangeQueueProvisioner.ProvisionConsumerDestination("foo,qaa", "boo", consumerProperties); - - Assert.Equal("foo.boo-1,foo.boo-2,foo.boo-3,qaa.boo-1,qaa.boo-2,qaa.boo-3", consumerDestination.Name); - } - finally - { - proxy?.Stop(); - } - } - - [Fact] - public async Task TestConsumerPropertiesWithUserInfrastructureNoBind() - { - ILogger logger = LoggerFactory.CreateLogger(); - var admin = new RabbitAdmin(RabbitTestBinder.GetApplicationContext(), GetResource(), logger); - var queue = new Queue("propsUser1.infra"); - admin.DeclareQueue(queue); - - var exchange = new DirectExchange("propsUser1"); - admin.DeclareExchange(exchange); - admin.DeclareBinding(BindingBuilder.Bind(queue).To(exchange).With("foo")); - - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - rabbitConsumerOptions.DeclareExchange = false; - rabbitConsumerOptions.BindQueue = false; - - IBinding consumerBinding = binder.BindConsumer("propsUser1", "infra", CreateBindableChannel("input", GetDefaultBindingOptions()), properties); - - ILifecycle endpoint = ExtractEndpoint(consumerBinding); - var container = GetPropertyValue(endpoint, "MessageListenerContainer"); - - Assert.False(container.MissingQueuesFatal); - Assert.True(container.IsRunning); - - await consumerBinding.UnbindAsync(); - - Assert.False(container.IsRunning); - - var client = new HttpClient(); - byte[] byteArray = "guest:guest".GetBytes(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); - - var requestUri = new Uri($"http://guest:guest@localhost:15672/api/exchanges/%2F/{exchange.ExchangeName}/bindings/source"); - HttpResponseMessage response = await client.GetAsync(requestUri); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - string jsonResult = await response.Content.ReadAsStringAsync(); - var foo = JsonConvert.DeserializeObject>(jsonResult, new ExpandoObjectConverter()); - - Assert.Single(foo); - } - - [Fact] - public async Task TestAnonWithBuiltInExchange() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - rabbitConsumerOptions.DeclareExchange = false; - rabbitConsumerOptions.QueueNameGroupOnly = true; - - IBinding consumerBinding = binder.BindConsumer("amq.topic", null, CreateBindableChannel("input", GetDefaultBindingOptions()), properties); - ILifecycle endpoint = ExtractEndpoint(consumerBinding); - var container = GetPropertyValue(endpoint, "MessageListenerContainer"); - - string queueName = container.GetQueueNames()[0]; - - Assert.StartsWith("anonymous.", queueName, StringComparison.Ordinal); - Assert.True(container.IsRunning); - - await consumerBinding.UnbindAsync(); - Assert.False(container.IsRunning); - } - - [Fact] - public async Task TestAnonWithBuiltInExchangeCustomPrefix() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - rabbitConsumerOptions.DeclareExchange = false; - rabbitConsumerOptions.QueueNameGroupOnly = true; - rabbitConsumerOptions.AnonymousGroupPrefix = "customPrefix."; - - IBinding consumerBinding = binder.BindConsumer("amq.topic", null, CreateBindableChannel("input", GetDefaultBindingOptions()), properties); - ILifecycle endpoint = ExtractEndpoint(consumerBinding); - var container = GetPropertyValue(endpoint, "MessageListenerContainer"); - - string queueName = container.GetQueueNames()[0]; - Assert.StartsWith("customPrefix.", queueName, StringComparison.Ordinal); - Assert.True(container.IsRunning); - - await consumerBinding.UnbindAsync(); - Assert.False(container.IsRunning); - } - - [Fact] - public async Task TestConsumerPropertiesWithUserInfrastructureCustomExchangeAndRk() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - - rabbitConsumerOptions.ExchangeType = ExchangeType.Direct; - rabbitConsumerOptions.BindingRoutingKey = "foo,bar"; - rabbitConsumerOptions.BindingRoutingKeyDelimiter = ","; - rabbitConsumerOptions.QueueNameGroupOnly = true; - - // properties.Extension.DelayedExchange = true; // requires delayed message - - // exchange plugin; tested locally - const string group = "infra"; - IBinding consumerBinding = binder.BindConsumer("propsUser2", group, CreateBindableChannel("input", GetDefaultBindingOptions()), properties); - ILifecycle endpoint = ExtractEndpoint(consumerBinding); - var container = GetPropertyValue(endpoint, "MessageListenerContainer"); - - Assert.True(container.IsRunning); - await consumerBinding.UnbindAsync(); - - Assert.False(container.IsRunning); - Assert.Equal(group, container.GetQueueNames()[0]); - - ManagementClient client = ManagementClientFactory.CreateDefault(); - IEnumerable bindings = await client.GetBindingsWithSourceAsync("/", "propsUser2"); - int n = 0; - - while (n++ < 100 && !bindings.Any()) - { - await Task.Delay(100); - bindings = await client.GetBindingsWithSourceAsync("/", "propsUser2"); - } - - Assert.Equal(2, bindings.Count()); - - Assert.Equal("propsUser2", bindings.ElementAt(0).Source); - Assert.Equal(group, bindings.ElementAt(0).Destination); - - Assert.Contains(bindings.ElementAt(0).RoutingKey, new List - { - "foo", - "bar" - }); - - Assert.Equal("propsUser2", bindings.ElementAt(1).Source); - Assert.Equal(group, bindings.ElementAt(1).Destination); - - Assert.Contains(bindings.ElementAt(1).RoutingKey, new List - { - "foo", - "bar" - }); - - Assert.NotEqual(bindings.ElementAt(1).RoutingKey, bindings.ElementAt(0).RoutingKey); - - Exchange exchange = await client.GetExchangeAsync("/", "propsUser2"); - - Assert.Equal("direct", exchange.Type); - Assert.True(exchange.Durable); - Assert.False(exchange.AutoDelete); - } - - [Fact] - public async Task TestConsumerPropertiesWithUserInfrastructureCustomQueueArgs() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions extProps = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - - extProps.ExchangeType = ExchangeType.Direct; - extProps.ExchangeDurable = false; - extProps.ExchangeAutoDelete = true; - extProps.BindingRoutingKey = "foo"; - extProps.Expires = 30_000; - extProps.Lazy = true; - extProps.MaxLength = 10_000; - extProps.MaxLengthBytes = 100_000; - extProps.MaxPriority = 10; - extProps.OverflowBehavior = "drop-head"; - extProps.Ttl = 2_000; - extProps.AutoBindDlq = true; - extProps.DeadLetterQueueName = "customDLQ"; - extProps.DeadLetterExchange = "customDLX"; - extProps.DeadLetterExchangeType = ExchangeType.Topic; - extProps.DeadLetterRoutingKey = "customDLRK"; - extProps.DlqDeadLetterExchange = "propsUser3"; - - // GH-259 - if the next line was commented, the test failed. - extProps.DlqDeadLetterRoutingKey = "propsUser3"; - extProps.DlqExpires = 60_000; - extProps.DlqLazy = true; - extProps.DlqMaxLength = 20_000; - extProps.DlqMaxLengthBytes = 40_000; - extProps.DlqOverflowBehavior = "reject-publish"; - extProps.DlqMaxPriority = 8; - extProps.DlqTtl = 1_000; - extProps.ConsumerTagPrefix = "testConsumerTag"; - extProps.Exclusive = true; - - IBinding consumerBinding = binder.BindConsumer("propsUser3", "infra", CreateBindableChannel("input", GetDefaultBindingOptions()), properties); - ILifecycle endpoint = ExtractEndpoint(consumerBinding); - var container = GetPropertyValue(endpoint, "MessageListenerContainer"); - - Assert.True(container.IsRunning); - - ManagementClient client = ManagementClientFactory.CreateDefault(); - IEnumerable bindings = await client.GetBindingsWithSourceAsync("/", "propsUser3"); - - int n = 0; - - while (n++ < 100 && !bindings.Any()) - { - await Task.Delay(100); - bindings = await client.GetBindingsWithSourceAsync("/", "propsUser3"); - } - - Assert.Single(bindings); - Assert.Equal("propsUser3", bindings.ElementAt(0).Source); - Assert.Equal("propsUser3.infra", bindings.ElementAt(0).Destination); - Assert.Equal("foo", bindings.ElementAt(0).RoutingKey); - - bindings = await client.GetBindingsWithSourceAsync("/", "customDLX"); - n = 0; - - while (n++ < 100 && !bindings.Any()) - { - await Task.Delay(100); - bindings = await client.GetBindingsWithSourceAsync("/", "customDLX"); - } - - Assert.Equal("customDLX", bindings.ElementAt(0).Source); - Assert.Equal("customDLQ", bindings.ElementAt(0).Destination); - Assert.Equal("customDLRK", bindings.ElementAt(0).RoutingKey); - - Exchange exchange = await client.GetExchangeAsync("/", "propsUser3"); - - Assert.Equal("direct", exchange.Type); - Assert.False(exchange.Durable); - Assert.True(exchange.AutoDelete); - - exchange = await client.GetExchangeAsync("/", "customDLX"); - - Assert.Equal("topic", exchange.Type); - Assert.True(exchange.Durable); - Assert.False(exchange.AutoDelete); - - EasyNetQ.Management.Client.Model.Queue queue = await client.GetQueueAsync("/", "propsUser3.infra"); - n = 0; - - while (n++ < 100 && queue.Consumers == 0) - { - await Task.Delay(100); - queue = await client.GetQueueAsync("/", "propsUser3.infra"); - } - - Assert.NotNull(queue); - - Assert.Equal(30000d, queue.Arguments["x-expires"]); - Assert.Equal(10000d, queue.Arguments["x-max-length"]); - Assert.Equal(100_000d, queue.Arguments["x-max-length-bytes"]); - Assert.Equal("drop-head", queue.Arguments["x-overflow"]); - Assert.Equal(10d, queue.Arguments["x-max-priority"]); - - Assert.Equal(2000d, queue.Arguments["x-message-ttl"]); - Assert.Equal("customDLX", queue.Arguments["x-dead-letter-exchange"]); - Assert.Equal("customDLRK", queue.Arguments["x-dead-letter-routing-key"]); - - Assert.Equal("lazy", queue.Arguments["x-queue-mode"]); - Assert.Equal("testConsumerTag#0", queue.ExclusiveConsumerTag); - - queue = await client.GetQueueAsync("/", "customDLQ"); - Assert.NotNull(queue); - - Assert.Equal(60000d, queue.Arguments["x-expires"]); - Assert.Equal(20000d, queue.Arguments["x-max-length"]); - Assert.Equal(40000d, queue.Arguments["x-max-length-bytes"]); - Assert.Equal("reject-publish", queue.Arguments["x-overflow"]); - Assert.Equal(8d, queue.Arguments["x-max-priority"]); - - Assert.Equal(1000d, queue.Arguments["x-message-ttl"]); - Assert.Equal("propsUser3", queue.Arguments["x-dead-letter-exchange"]); - Assert.Equal("propsUser3", queue.Arguments["x-dead-letter-routing-key"]); - - Assert.Equal("lazy", queue.Arguments["x-queue-mode"]); - - await consumerBinding.UnbindAsync(); - Assert.False(container.IsRunning); - } - - [Fact] - public async Task TestConsumerPropertiesWithHeaderExchanges() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - rabbitConsumerOptions.ExchangeType = ExchangeType.Headers; - rabbitConsumerOptions.AutoBindDlq = true; - rabbitConsumerOptions.DeadLetterExchange = ExchangeType.Headers; - rabbitConsumerOptions.DeadLetterExchange = "propsHeader.dlx"; - - var queueBindingArguments = new Dictionary - { - { "x-match", "any" }, - { "foo", "bar" } - }; - - rabbitConsumerOptions.QueueBindingArguments = queueBindingArguments; - rabbitConsumerOptions.DlqBindingArguments = queueBindingArguments; - - const string group = "bindingArgs"; - IBinding consumerBinding = binder.BindConsumer("propsHeader", group, CreateBindableChannel("input", GetDefaultBindingOptions()), properties); - ILifecycle endpoint = ExtractEndpoint(consumerBinding); - var container = GetPropertyValue(endpoint, "MessageListenerContainer"); - - Assert.True(container.IsRunning); - await consumerBinding.UnbindAsync(); - - Assert.False(container.IsRunning); - Assert.Equal($"propsHeader.{group}", container.GetQueueNames()[0]); - - ManagementClient client = ManagementClientFactory.CreateDefault(); - IEnumerable bindings = await client.GetBindingsWithSourceAsync("/", "propsHeader"); - - int n = 0; - - while (n++ < 100 && !bindings.Any()) - { - await Task.Delay(100); - bindings = await client.GetBindingsWithSourceAsync("/", "propsHeader"); - } - - Assert.Single(bindings); - EasyNetQ.Management.Client.Model.Binding binding = bindings.First(); - Assert.Equal("propsHeader", binding.Source); - Assert.Equal($"propsHeader.{group}", binding.Destination); - Assert.NotNull(binding.Arguments); - Assert.Contains(binding.Arguments, arg => arg is { Key: "x-match", Value: "any" }); - Assert.Contains(binding.Arguments, arg => arg is { Key: "foo", Value: "bar" }); - - bindings = await client.GetBindingsWithSourceAsync("/", "propsHeader.dlx"); - n = 0; - - while (n++ < 100 && !bindings.Any()) - { - await Task.Delay(100); - bindings = await client.GetBindingsWithSourceAsync("/", "propsHeader.dlx"); - } - - Assert.Single(bindings); - binding = bindings.First(); - Assert.Equal("propsHeader.dlx", binding.Source); - Assert.Equal($"propsHeader.{group}.dlq", binding.Destination); - Assert.NotNull(binding.Arguments); - Assert.Contains(binding.Arguments, arg => arg is { Key: "x-match", Value: "any" }); - Assert.Contains(binding.Arguments, arg => arg is { Key: "foo", Value: "bar" }); - } - - [Fact] - public async Task TestProducerProperties() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - BindingOptions bindingOptions = GetDefaultBindingOptions(); - - ProducerOptions producerOptions = GetProducerOptions("input", rabbitBindingsOptions); - IBinding producerBinding = binder.BindProducer("props.0", CreateBindableChannel("input", bindingOptions), producerOptions); - - var endpoint = ExtractEndpoint(producerBinding) as RabbitOutboundEndpoint; - Assert.NotNull(endpoint); - Assert.Equal(MessageDeliveryMode.Persistent, endpoint.DefaultDeliveryMode); - - var mapper = GetPropertyValue(endpoint, "HeaderMapper"); - Assert.NotNull(mapper); - Assert.NotNull(mapper.RequestHeaderMatcher); - - var matchers = - GetPropertyValue.IHeaderMatcher>>(mapper.RequestHeaderMatcher, "Matchers"); - - Assert.NotNull(matchers); - Assert.Equal(4, matchers.Count); - - await producerBinding.UnbindAsync(); - Assert.False(endpoint.IsRunning); - - Assert.False(endpoint.Template.IsChannelTransacted); - - rabbitBindingsOptions.Bindings.Remove("input"); - ProducerOptions producerProperties = GetProducerOptions("input", rabbitBindingsOptions); - RabbitProducerOptions producerRabbitOptions = rabbitBindingsOptions.GetRabbitProducerOptions("input"); - binder.ApplicationContext.Register("pkExtractor", new TestPartitionSupport("pkExtractor")); - binder.ApplicationContext.Register("pkSelector", new TestPartitionSupport("pkSelector")); - - producerProperties.PartitionKeyExtractorName = "pkExtractor"; - producerProperties.PartitionSelectorName = "pkSelector"; - producerRabbitOptions.Prefix = "foo."; - producerRabbitOptions.DeliveryMode = MessageDeliveryMode.NonPersistent; - - producerRabbitOptions.HeaderPatterns = new[] - { - "foo" - }.ToList(); - - producerProperties.PartitionKeyExpression = "'foo'"; - producerProperties.PartitionSelectorExpression = "0"; - producerProperties.PartitionCount = 1; - producerRabbitOptions.Transacted = true; - producerRabbitOptions.DelayExpression = "42"; - - producerProperties.RequiredGroups = new[] - { - "prodPropsRequired" - }.ToList(); - - BindingOptions producerBindingProperties = CreateProducerBindingOptions(producerProperties); - DirectChannel channel = CreateBindableChannel("output", producerBindingProperties); - - producerBinding = binder.BindProducer("props.0", channel, producerProperties); - - endpoint = ExtractEndpoint(producerBinding) as RabbitOutboundEndpoint; - Assert.NotNull(endpoint); - Assert.Same(GetResource(), endpoint.Template.ConnectionFactory); - - Assert.Equal($"'props.0-' + Headers['{BinderHeaders.PartitionHeader}']", endpoint.RoutingKeyExpression.ExpressionString); - Assert.Equal("42", endpoint.DelayExpression.ExpressionString); - Assert.Equal(MessageDeliveryMode.NonPersistent, endpoint.DefaultDeliveryMode); - Assert.True(endpoint.Template.IsChannelTransacted); - - mapper = GetPropertyValue(endpoint, "HeaderMapper"); - Assert.NotNull(mapper); - Assert.NotNull(mapper.RequestHeaderMatcher); - matchers = GetPropertyValue.IHeaderMatcher>>(mapper.RequestHeaderMatcher, "Matchers"); - Assert.NotNull(matchers); - Assert.Equal(4, matchers.Count); - Assert.Equal("foo", GetPropertyValue(matchers[3], "Pattern")); - - IMessage message = MessageBuilder.WithPayload("foo").Build(); - channel.Send(message); - IMessage received = new RabbitTemplate(GetResource()).Receive("foo.props.0.prodPropsRequired-0", 10_000); - Assert.NotNull(received); - - Assert.Equal(42, received.Headers[RabbitMessageHeaders.ReceivedDelay]); - await producerBinding.UnbindAsync(); - Assert.False(endpoint.IsRunning); - } - - [Fact] - public async Task TestDurablePubSubWithAutoBindDlq() - { - ILogger logger = LoggerFactory.CreateLogger(); - var admin = new RabbitAdmin(GetResource(), logger); - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - - rabbitConsumerOptions.Prefix = TestPrefix; - rabbitConsumerOptions.AutoBindDlq = true; - rabbitConsumerOptions.DurableSubscription = true; - consumerProperties.MaxAttempts = 1; // disable retry - DirectChannel moduleInputChannel = CreateBindableChannel("input", CreateConsumerBindingOptions(consumerProperties)); - moduleInputChannel.ComponentName = "durableTest"; - - moduleInputChannel.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => throw new Exception("foo") - }); - - IBinding consumerBinding = binder.BindConsumer("durabletest.0", "tgroup", moduleInputChannel, consumerProperties); - - var template = new RabbitTemplate(GetResource()); - template.ConvertAndSend($"{TestPrefix}durabletest.0", string.Empty, "foo"); - - int n = 0; - - while (n++ < 100) - { - string deadLetter = template.ReceiveAndConvert($"{TestPrefix}durabletest.0.tgroup.dlq"); - - if (deadLetter != null) - { - Assert.Equal("foo", deadLetter); - break; - } - - await Task.Delay(100); - } - - Assert.InRange(n, 0, 150); - - await consumerBinding.UnbindAsync(); - Assert.NotNull(admin.GetQueueProperties($"{TestPrefix}durabletest.0.tgroup.dlq")); - } - - [Fact] - public async Task TestNonDurablePubSubWithAutoBindDlq() - { - ILogger logger = LoggerFactory.CreateLogger(); - var admin = new RabbitAdmin(GetResource(), logger); - - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - - rabbitConsumerOptions.Prefix = TestPrefix; - rabbitConsumerOptions.AutoBindDlq = true; - rabbitConsumerOptions.DurableSubscription = false; - consumerProperties.MaxAttempts = 1; // disable retry - BindingOptions bindingProperties = CreateConsumerBindingOptions(consumerProperties); - DirectChannel moduleInputChannel = CreateBindableChannel("input", bindingProperties); - moduleInputChannel.ComponentName = "nondurabletest"; - - moduleInputChannel.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => throw new Exception("foo") - }); - - IBinding consumerBinding = binder.BindConsumer("nondurabletest.0", "tgroup", moduleInputChannel, consumerProperties); - - await consumerBinding.UnbindAsync(); - Assert.Null(admin.GetQueueProperties($"{TestPrefix}nondurabletest.0.dlq")); - } - - [Fact] - public async Task TestAutoBindDlq() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - - rabbitConsumerOptions.Prefix = TestPrefix; - rabbitConsumerOptions.AutoBindDlq = true; - consumerProperties.MaxAttempts = 1; // disable retry - rabbitConsumerOptions.DurableSubscription = true; - BindingOptions bindingProperties = CreateConsumerBindingOptions(consumerProperties); - DirectChannel moduleInputChannel = CreateBindableChannel("input", bindingProperties); - moduleInputChannel.ComponentName = "dlqTest"; - - moduleInputChannel.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => throw new Exception("foo") - }); - - consumerProperties.Multiplex = true; - IBinding consumerBinding = binder.BindConsumer("dlqtest,dlqtest2", "default", moduleInputChannel, consumerProperties); - - ILifecycle endpoint = ExtractEndpoint(consumerBinding); - var container = GetPropertyValue(endpoint, "MessageListenerContainer"); - Assert.Equal(2, container.GetQueueNames().Length); - - var template = new RabbitTemplate(GetResource()); - template.ConvertAndSend(string.Empty, $"{TestPrefix}dlqtest.default", "foo"); - - int n = 0; - - while (n++ < 100) - { - string deadLetter = template.ReceiveAndConvert($"{TestPrefix}dlqtest.default.dlq"); - - if (deadLetter != null) - { - Assert.Equal("foo", deadLetter); - break; - } - - await Task.Delay(100); - } - - Assert.InRange(n, 0, 99); - - template.ConvertAndSend(string.Empty, $"{TestPrefix}dlqtest2.default", "bar"); - - n = 0; - - while (n++ < 100) - { - string deadLetter = template.ReceiveAndConvert($"{TestPrefix}dlqtest2.default.dlq"); - - if (deadLetter != null) - { - Assert.Equal("bar", deadLetter); - break; - } - - await Task.Delay(100); - } - - Assert.InRange(n, 0, 99); - await consumerBinding.UnbindAsync(); - - var provider = GetPropertyValue(binder.Binder, "ProvisioningProvider"); - var context = GetFieldValue(provider, "_autoDeclareContext"); - - Assert.False(context.ContainsService($"{TestPrefix}dlqtest.default.binding")); - Assert.False(context.ContainsService($"{TestPrefix}dlqtest.default")); - Assert.False(context.ContainsService($"{TestPrefix}dlqtest.default.dlq.binding")); - Assert.False(context.ContainsService($"{TestPrefix}dlqtest.default.dlq")); - } - - [Fact] - public async Task TestAutoBindDlqManualAcks() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - rabbitConsumerOptions.Prefix = TestPrefix; - rabbitConsumerOptions.AutoBindDlq = true; - consumerProperties.MaxAttempts = 2; - rabbitConsumerOptions.DurableSubscription = true; - rabbitConsumerOptions.AcknowledgeMode = AcknowledgeMode.Manual; - BindingOptions bindingProperties = CreateConsumerBindingOptions(consumerProperties); - - DirectChannel moduleInputChannel = CreateBindableChannel("input", bindingProperties); - moduleInputChannel.ComponentName = "dlqTestManual"; - - ManagementClient client = ManagementClientFactory.CreateDefault(); - Vhost vhost = client.GetVhost("/"); - - moduleInputChannel.Subscribe(new TestMessageHandler - { - // Wait until unacked state is reflected in the admin - OnHandleMessage = _ => - { - EasyNetQ.Management.Client.Model.Queue info = client.GetQueue(vhost, $"{TestPrefix}dlqTestManual.default"); - int n = 0; - - while (n++ < 100 && info.MessagesUnacknowledged < 1L) - { -#pragma warning disable xUnit1031 // Do not use blocking task operations in test method - Task.Delay(100).GetAwaiter().GetResult(); -#pragma warning restore xUnit1031 // Do not use blocking task operations in test method - info = client.GetQueue(vhost, $"{TestPrefix}dlqTestManual.default"); - } - - throw new Exception("foo"); - } - }); - - IBinding consumerBinding = binder.BindConsumer("dlqTestManual", "default", moduleInputChannel, consumerProperties); - - var template = new RabbitTemplate(GetResource()); - template.ConvertAndSend(string.Empty, $"{TestPrefix}dlqTestManual.default", "foo"); - - int n = 0; - - while (n++ < 100) - { - string deadLetter = template.ReceiveAndConvert($"{TestPrefix}dlqTestManual.default.dlq"); - - if (deadLetter != null) - { - Assert.Equal("foo", deadLetter); - break; - } - - await Task.Delay(200); - } - - Assert.InRange(n, 1, 100); - - n = 0; - EasyNetQ.Management.Client.Model.Queue info = client.GetQueue(vhost, $"{TestPrefix}dlqTestManual.default"); - - while (n++ < 100 && info.MessagesUnacknowledged > 0L) - { - await Task.Delay(200); - info = client.GetQueue(vhost, $"{TestPrefix}dlqTestManual.default"); - } - - Assert.Equal(0, info.MessagesUnacknowledged); - - await consumerBinding.UnbindAsync(); - - var provider = GetPropertyValue(binder.Binder, "ProvisioningProvider"); - var context = GetFieldValue(provider, "_autoDeclareContext"); - - Assert.False(context.ContainsService($"{TestPrefix}dlqTestManual.default.binding")); - Assert.False(context.ContainsService($"{TestPrefix}dlqTestManual.default")); - Assert.False(context.ContainsService($"{TestPrefix}dlqTestManual.default.dlq.binding")); - Assert.False(context.ContainsService($"{TestPrefix}dlqTestManual.default.dlq")); - } - - [Fact] - public void TestOptions() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - - RabbitProducerOptions producerOptions = rabbitBindingsOptions.GetRabbitProducerOptions("test"); - producerOptions.Prefix = "rets"; - RabbitProducerOptions producerOptions2 = rabbitBindingsOptions.GetRabbitProducerOptions("test"); - - Assert.Equal(producerOptions.Prefix, producerOptions2.Prefix); - } - - [Fact] - public async Task TestAutoBindDlqPartionedConsumerFirst() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - rabbitConsumerOptions.Prefix = "bindertest."; - rabbitConsumerOptions.AutoBindDlq = true; - consumerProperties.MaxAttempts = 1; // disable retry - consumerProperties.Partitioned = true; - consumerProperties.InstanceIndex = 0; - - DirectChannel input0 = CreateBindableChannel("input", CreateConsumerBindingOptions(consumerProperties)); - input0.ComponentName = "test.input0DLQ"; - IBinding input0Binding = binder.BindConsumer("partDLQ.0", "dlqPartGrp", input0, consumerProperties); - IBinding defaultConsumerBinding1 = binder.BindConsumer("partDLQ.0", "default", new QueueChannel(), consumerProperties); - consumerProperties.InstanceIndex = 1; - - DirectChannel input1 = CreateBindableChannel("input1", CreateConsumerBindingOptions(consumerProperties)); - input1.ComponentName = "test.input1DLQ"; - IBinding input1Binding = binder.BindConsumer("partDLQ.0", "dlqPartGrp", input1, consumerProperties); - - IBinding defaultConsumerBinding2 = binder.BindConsumer("partDLQ.0", "default", new QueueChannel(), consumerProperties); - - ProducerOptions producerProperties = GetProducerOptions("output", rabbitBindingsOptions); - RabbitProducerOptions rabbitProducerOptions = rabbitBindingsOptions.GetRabbitProducerOptions("output"); - rabbitProducerOptions.Prefix = "bindertest."; - - binder.ApplicationContext.Register("pkExtractor", new TestPartitionSupport("pkExtractor")); - binder.ApplicationContext.Register("pkSelector", new TestPartitionSupport("pkSelector")); - - rabbitProducerOptions.AutoBindDlq = true; - producerProperties.PartitionKeyExtractorName = "pkExtractor"; - producerProperties.PartitionSelectorName = "pkSelector"; - producerProperties.PartitionCount = 2; - - BindingOptions bindingProperties = CreateProducerBindingOptions(producerProperties); - - DirectChannel output = CreateBindableChannel("output", bindingProperties); - output.ComponentName = "test.output"; - IBinding outputBinding = binder.BindProducer("partDLQ.0", output, producerProperties); - - var latch0 = new CountdownEvent(1); - - input0.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => - { - if (latch0.CurrentCount <= 0) - { - throw new Exception("dlq"); - } - - latch0.Signal(); - } - }); - - var latch1 = new CountdownEvent(1); - - input1.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => - { - if (latch1.CurrentCount <= 0) - { - throw new Exception("dlq"); - } - - latch1.Signal(); - } - }); - - IMessage message = MessageBuilder.WithPayload(1).Build(); - output.Send(message); - Assert.True(latch1.Wait(TimeSpan.FromSeconds(10))); - - output.Send(Message.Create(0)); - Assert.True(latch0.Wait(TimeSpan.FromSeconds(10))); - - output.Send(Message.Create(1)); - - var template = new RabbitTemplate(GetResource()); - template.ReceiveTimeout = 10000; - - const string streamDlqName = "bindertest.partDLQ.0.dlqPartGrp.dlq"; - - IMessage received = template.Receive(streamDlqName); - Assert.NotNull(received); - - Assert.Equal("bindertest.partDLQ.0.dlqPartGrp-1", received.Headers.ReceivedRoutingKey()); - Assert.DoesNotContain(BinderHeaders.PartitionHeader, received.Headers.Select(h => h.Key)); - - output.Send(Message.Create(0)); - received = template.Receive(streamDlqName); - Assert.NotNull(received); - Assert.Equal("bindertest.partDLQ.0.dlqPartGrp-0", received.Headers.ReceivedRoutingKey()); - Assert.DoesNotContain(BinderHeaders.PartitionHeader, received.Headers.Select(h => h.Key)); - - await input0Binding.UnbindAsync(); - await input1Binding.UnbindAsync(); - await defaultConsumerBinding1.UnbindAsync(); - await defaultConsumerBinding2.UnbindAsync(); - await outputBinding.UnbindAsync(); - } - - [Fact] - public async Task TestAutoBindDlqPartitionedConsumerFirstWithRepublishNoRetry() - { - await TestAutoBindDlqPartionedConsumerFirstWithRepublishGutsAsync(false); - } - - [Fact] - public async Task TestAutoBindDlqPartitionedConsumerFirstWithRepublishWithRetry() - { - await TestAutoBindDlqPartionedConsumerFirstWithRepublishGutsAsync(true); - } - - [Fact] - public async Task TestAutoBindDlqPartitionedProducerFirst() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - ProducerOptions properties = GetProducerOptions("output", rabbitBindingsOptions); - RabbitProducerOptions rabbitProducerOptions = rabbitBindingsOptions.GetRabbitProducerOptions("output"); - rabbitProducerOptions.Prefix = "bindertest."; - rabbitProducerOptions.AutoBindDlq = true; - - properties.RequiredGroups = new[] - { - "dlqPartGrp" - }.ToList(); - - binder.ApplicationContext.Register("pkExtractor", new TestPartitionSupport("pkExtractor")); - properties.PartitionKeyExtractorName = "pkExtractor"; - properties.PartitionSelectorName = "pkExtractor"; - properties.PartitionCount = 2; - DirectChannel output = CreateBindableChannel("output", CreateProducerBindingOptions(properties)); - output.ComponentName = "test.output"; - IBinding outputBinding = binder.BindProducer("partDLQ.1", output, properties); - - rabbitConsumerOptions.Prefix = "bindertest."; - rabbitConsumerOptions.AutoBindDlq = true; - consumerProperties.MaxAttempts = 1; // disable retry - consumerProperties.Partitioned = true; - consumerProperties.InstanceIndex = 0; - DirectChannel input0 = CreateBindableChannel("input", CreateConsumerBindingOptions(consumerProperties)); - input0.ComponentName = "test.input0DLQ"; - IBinding input0Binding = binder.BindConsumer("partDLQ.1", "dlqPartGrp", input0, consumerProperties); - IBinding defaultConsumerBinding1 = binder.BindConsumer("partDLQ.1", "defaultConsumer", new QueueChannel(), consumerProperties); - consumerProperties.InstanceIndex = 1; - DirectChannel input1 = CreateBindableChannel("input1", CreateConsumerBindingOptions(consumerProperties)); - input1.ComponentName = "test.input1DLQ"; - IBinding input1Binding = binder.BindConsumer("partDLQ.1", "dlqPartGrp", input1, consumerProperties); - IBinding defaultConsumerBinding2 = binder.BindConsumer("partDLQ.1", "defaultConsumer", new QueueChannel(), consumerProperties); - - var latch0 = new CountdownEvent(1); - - input0.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => - { - if (latch0.CurrentCount <= 0) - { - throw new Exception("dlq"); - } - - latch0.Signal(); - } - }); - - var latch1 = new CountdownEvent(1); - - input1.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => - { - if (latch1.CurrentCount <= 0) - { - throw new Exception("dlq"); - } - - latch1.Signal(); - } - }); - - output.Send(Message.Create(1)); - Assert.True(latch1.Wait(TimeSpan.FromSeconds(10))); - - output.Send(Message.Create(0)); - Assert.True(latch0.Wait(TimeSpan.FromSeconds(10))); - - output.Send(Message.Create(1)); - - var template = new RabbitTemplate(GetResource()); - template.ReceiveTimeout = 10000; - - const string streamDlqName = "bindertest.partDLQ.1.dlqPartGrp.dlq"; - - IMessage received = template.Receive(streamDlqName); - Assert.NotNull(received); - Assert.Equal("bindertest.partDLQ.1.dlqPartGrp-1", received.Headers.ReceivedRoutingKey()); - Assert.Equal(MessageDeliveryMode.Persistent, received.Headers.ReceivedDeliveryMode()); - Assert.DoesNotContain(BinderHeaders.PartitionHeader, received.Headers); - - output.Send(Message.Create(0)); - received = template.Receive(streamDlqName); - Assert.NotNull(received); - - Assert.Equal("bindertest.partDLQ.1.dlqPartGrp-0", received.Headers.ReceivedRoutingKey()); - - Assert.DoesNotContain(BinderHeaders.PartitionHeader, received.Headers); - - await input0Binding.UnbindAsync(); - await input1Binding.UnbindAsync(); - await defaultConsumerBinding1.UnbindAsync(); - await defaultConsumerBinding2.UnbindAsync(); - await outputBinding.UnbindAsync(); - } - - [Fact] - public async Task TestAutoBindDlQWithRepublish() - { - MaxStackTraceSize = RabbitUtils.GetMaxFrame(GetResource()) - 20_000; - Assert.True(MaxStackTraceSize > 0); - - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - - rabbitConsumerOptions.Prefix = TestPrefix; - rabbitConsumerOptions.AutoBindDlq = true; - rabbitConsumerOptions.RepublishToDlq = true; - consumerProperties.MaxAttempts = 1; // disable retry - rabbitConsumerOptions.DurableSubscription = true; - DirectChannel moduleInputChannel = CreateBindableChannel("input", CreateConsumerBindingOptions(consumerProperties)); - moduleInputChannel.ComponentName = "dlqPubTest"; - Exception exception = BigCause(null); - - Assert.NotNull(exception.StackTrace); - Assert.True(exception.StackTrace.Length > MaxStackTraceSize); - var noNotRepublish = new AtomicBoolean(); - - moduleInputChannel.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => - { - if (noNotRepublish.Value) - { - throw new ImmediateAcknowledgeException("testDoNotRepublish"); - } - - ExceptionDispatchInfo.Capture(exception).Throw(); - } - }); - - consumerProperties.Multiplex = true; - IBinding consumerBinding = binder.BindConsumer("foo.dlqpubtest,foo.dlqpubtest2", "foo", moduleInputChannel, consumerProperties); - - var template = new RabbitTemplate(GetResource()); - template.ConvertAndSend(string.Empty, $"{TestPrefix}foo.dlqpubtest.foo", "foo"); - - template.ReceiveTimeout = 10_000; - - IMessage deadLetter = template.Receive($"{TestPrefix}foo.dlqpubtest.foo.dlq"); - Assert.NotNull(deadLetter); - Assert.Equal("foo", ((byte[])deadLetter.Payload).GetString()); - Assert.Contains(RepublishMessageRecoverer.XExceptionStacktrace, deadLetter.Headers); - - template.ConvertAndSend(string.Empty, $"{TestPrefix}foo.dlqpubtest2.foo", "bar"); - - deadLetter = template.Receive($"{TestPrefix}foo.dlqpubtest2.foo.dlq"); - Assert.NotNull(deadLetter); - - Assert.Equal("bar", ((byte[])deadLetter.Payload).GetString()); - Assert.Contains(RepublishMessageRecoverer.XExceptionStacktrace, deadLetter.Headers); - - noNotRepublish.GetAndSet(true); - template.ConvertAndSend(string.Empty, $"{TestPrefix}foo.dlqpubtest2.foo", "baz"); - template.ReceiveTimeout = 500; - Assert.Null(template.Receive($"{TestPrefix}foo.dlqpubtest2.foo.dlq")); - - await consumerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestBatchingAndCompression() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ProducerOptions producerProperties = GetProducerOptions("output", rabbitBindingsOptions); - RabbitProducerOptions rabbitProducerOptions = rabbitBindingsOptions.GetRabbitProducerOptions("output"); - - rabbitProducerOptions.DeliveryMode = MessageDeliveryMode.NonPersistent; - rabbitProducerOptions.BatchingEnabled = true; - rabbitProducerOptions.BatchSize = 2; - rabbitProducerOptions.BatchBufferLimit = 100_000; - rabbitProducerOptions.BatchTimeout = 30000; - rabbitProducerOptions.Compress = true; - - producerProperties.RequiredGroups = new[] - { - "default" - }.ToList(); - - DirectChannel output = CreateBindableChannel("output", CreateProducerBindingOptions(producerProperties)); - output.ComponentName = "batchingProducer"; - IBinding producerBinding = binder.BindProducer("batching.0", output, producerProperties); - - var postProcessor = binder.Binder.CompressingPostProcessor as GZipPostProcessor; - Assert.NotNull(postProcessor); - Assert.Equal(CompressionLevel.Fastest, postProcessor.Level); - - IMessage fooMessage = Message.Create("foo".GetBytes()); - IMessage barMessage = Message.Create("bar".GetBytes()); - - output.Send(fooMessage); - output.Send(barMessage); - - object obj = SpyOn("batching.0.default").Receive(false); - Assert.IsType(obj); - Assert.Equal("\u0000\u0000\u0000\u0003foo\u0000\u0000\u0000\u0003bar", ((byte[])obj).GetString()); - - var input = new QueueChannel - { - ComponentName = "batchingConsumer" - }; - - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - IBinding consumerBinding = binder.BindConsumer("batching.0", "test", input, consumerProperties); - - output.Send(fooMessage); - output.Send(barMessage); - - var inMessage = (Message)input.Receive(10000); - Assert.NotNull(inMessage); - Assert.Equal("foo", inMessage.Payload.GetString()); - inMessage = (Message)input.Receive(10000); - - Assert.NotNull(inMessage); - Assert.Equal("bar", inMessage.Payload.GetString()); - Assert.Null(inMessage.Headers[RabbitMessageHeaders.DeliveryMode]); - - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - } - - // TestProducerBatching, TestConsumerBatching only works with SMLC - not implemented in steeltoe - [Fact] - public async Task TestInternalHeadersNotPropagated() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ProducerOptions producerProperties = GetProducerOptions("output", rabbitBindingsOptions); - RabbitProducerOptions rabbitProducerOptions = rabbitBindingsOptions.GetRabbitProducerOptions("output"); - - rabbitProducerOptions.DeliveryMode = MessageDeliveryMode.NonPersistent; - - DirectChannel output = CreateBindableChannel("output", CreateProducerBindingOptions(producerProperties)); - output.ComponentName = "propagate.out"; - IBinding producerBinding = binder.BindProducer("propagate.1", output, producerProperties); - - var input = new QueueChannel - { - ComponentName = "propagate.in" - }; - - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - IBinding consumerBinding = binder.BindConsumer("propagate.0", "propagate", input, consumerProperties); - - ILogger logger = LoggerFactory.CreateLogger(); - var admin = new RabbitAdmin(GetResource(), logger); - - admin.DeclareQueue(new Queue("propagate")); - admin.DeclareBinding(new RabbitBinding("propagate_binding", "propagate", RabbitBinding.DestinationType.Queue, "propagate.1", "#", null)); - var template = new RabbitTemplate(GetResource()); - - template.ConvertAndSend("propagate.0.propagate", "foo"); - IMessage message = input.Receive(10_000); - Assert.NotNull(message); - output.Send(message); - IMessage received = template.Receive("propagate", 10_000); - Assert.NotNull(received); - - Assert.Equal("foo".GetBytes(), received.Payload); - Assert.Null(received.Headers[IntegrationMessageHeaderAccessor.SourceData]); - Assert.Null(received.Headers[IntegrationMessageHeaderAccessor.DeliveryAttempt]); - - await producerBinding.UnbindAsync(); - await consumerBinding.UnbindAsync(); - admin.DeleteQueue("propagate"); - } - - /* - * Test late binding due to broker down; queues with and without DLQs, and partitioned - * queues. - */ - [Fact] - public async Task TestLateBinding() - { - RabbitProxy proxy = null; - CachingConnectionFactory cf = null; - - try - { - proxy = new RabbitProxy(LoggerFactory.CreateLogger()); - cf = new CachingConnectionFactory("127.0.0.1", proxy.Port, LoggerFactory); - IApplicationContext context = RabbitTestBinder.GetApplicationContext(); - - var rabbitBindingsOptions = new TestOptionsMonitor(new RabbitBindingsOptions()); - RabbitBindingsOptions currentRabbitBindings = rabbitBindingsOptions.CurrentValue; - var rabbitOptions = new TestOptionsMonitor(new RabbitOptions()); - - var provisioner = - new RabbitExchangeQueueProvisioner(cf, rabbitBindingsOptions, context, LoggerFactory.CreateLogger()); - - var binderOptions = new TestOptionsMonitor(new RabbitBinderOptions()); - - var rabbitBinder = new RabbitMessageChannelBinder(context, LoggerFactory.CreateLogger(), cf, rabbitOptions, - binderOptions, rabbitBindingsOptions, provisioner); - - var binder = new RabbitTestBinder(cf, rabbitBinder, LoggerFactory.CreateLogger()); - TestBinder = binder; - - ProducerOptions producerProperties = GetProducerOptions("output", currentRabbitBindings); - RabbitProducerOptions rabbitProducerOptions = currentRabbitBindings.GetRabbitProducerOptions("output"); - rabbitProducerOptions.Prefix = "latebinder."; - rabbitProducerOptions.AutoBindDlq = true; - rabbitProducerOptions.Transacted = true; - - DirectChannel moduleOutputChannel = CreateBindableChannel("output", CreateProducerBindingOptions(producerProperties)); - IBinding late0ProducerBinding = binder.BindProducer("late.0", moduleOutputChannel, producerProperties); - - var moduleInputChannel = new QueueChannel(); - ConsumerOptions consumerOptions = GetConsumerOptions("input", currentRabbitBindings); - RabbitConsumerOptions rabbitConsumerOptions = currentRabbitBindings.GetRabbitConsumerOptions("input"); - rabbitConsumerOptions.Prefix = "latebinder."; - IBinding late0ConsumerBinding = binder.BindConsumer("late.0", "test", moduleInputChannel, consumerOptions); - producerProperties.PartitionKeyExpression = "Payload.Equals('0') ? 0 : 1"; - producerProperties.PartitionSelectorExpression = "GetHashCode()"; - producerProperties.PartitionCount = 2; - - DirectChannel partOutputChannel = CreateBindableChannel("output", CreateProducerBindingOptions(producerProperties)); - IBinding partlate0ProducerBinding = binder.BindProducer("partlate.0", partOutputChannel, producerProperties); - - var partInputChannel0 = new QueueChannel(); - var partInputChannel1 = new QueueChannel(); - - ConsumerOptions partLateConsumerProperties = GetConsumerOptions("partLate", currentRabbitBindings); - RabbitConsumerOptions partLateRabbitConsumerOptions = currentRabbitBindings.GetRabbitConsumerOptions("partLate"); - partLateRabbitConsumerOptions.Prefix = "latebinder."; - partLateConsumerProperties.Partitioned = true; - partLateConsumerProperties.InstanceIndex = 0; - - IBinding partlate0Consumer0Binding = binder.BindConsumer("partlate.0", "test", partInputChannel0, partLateConsumerProperties); - partLateConsumerProperties.InstanceIndex = 1; - IBinding partlate0Consumer1Binding = binder.BindConsumer("partlate.0", "test", partInputChannel1, partLateConsumerProperties); - - ProducerOptions noDlqProducerProperties = GetProducerOptions("noDlq", currentRabbitBindings); - RabbitProducerOptions noDlqRabbitProducerOptions = currentRabbitBindings.GetRabbitProducerOptions("noDlq"); - noDlqRabbitProducerOptions.Prefix = "latebinder."; - DirectChannel noDlqOutputChannel = CreateBindableChannel("output", CreateProducerBindingOptions(noDlqProducerProperties)); - IBinding noDlqProducerBinding = binder.BindProducer("lateNoDLQ.0", noDlqOutputChannel, noDlqProducerProperties); - - var noDlqInputChannel = new QueueChannel(); - ConsumerOptions noDlqConsumerProperties = GetConsumerOptions("noDlqConsumer", currentRabbitBindings); - RabbitConsumerOptions noDlqRabbitConsumerOptions = currentRabbitBindings.GetRabbitConsumerOptions("noDlqConsumer"); - noDlqRabbitConsumerOptions.Prefix = "latebinder."; - IBinding noDlqConsumerBinding = binder.BindConsumer("lateNoDLQ.0", "test", noDlqInputChannel, noDlqConsumerProperties); - - DirectChannel outputChannel = CreateBindableChannel("output", CreateProducerBindingOptions(noDlqProducerProperties)); - IBinding pubSubProducerBinding = binder.BindProducer("latePubSub", outputChannel, noDlqProducerProperties); - var pubSubInputChannel = new QueueChannel(); - noDlqRabbitConsumerOptions.DurableSubscription = false; - IBinding nonDurableConsumerBinding = binder.BindConsumer("latePubSub", "lategroup", pubSubInputChannel, noDlqConsumerProperties); - - var durablePubSubInputChannel = new QueueChannel(); - noDlqRabbitConsumerOptions.DurableSubscription = true; - IBinding durableConsumerBinding = binder.BindConsumer("latePubSub", "lateDurableGroup", durablePubSubInputChannel, noDlqConsumerProperties); - - proxy.Start(); - - await Task.Delay(5000); - - moduleOutputChannel.Send(MessageBuilder.WithPayload("foo").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build()); - - IMessage message = moduleInputChannel.Receive(20000); - Assert.NotNull(message); - Assert.NotNull(message.Payload); - - noDlqOutputChannel.Send(MessageBuilder.WithPayload("bar").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build()); - - message = noDlqInputChannel.Receive(10000); - Assert.NotNull(message); - Assert.Equal("bar".GetBytes(), message.Payload); - - outputChannel.Send(MessageBuilder.WithPayload("baz").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build()); - message = pubSubInputChannel.Receive(10000); - Assert.NotNull(message); - Assert.Equal("baz".GetBytes(), message.Payload); - message = durablePubSubInputChannel.Receive(10000); - Assert.NotNull(message); - Assert.Equal("baz".GetBytes(), message.Payload); - - partOutputChannel.Send(MessageBuilder.WithPayload("0").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build()); - partOutputChannel.Send(MessageBuilder.WithPayload("1").SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build()); - - message = partInputChannel0.Receive(10000); - Assert.NotNull(message); - - Assert.Equal("0".GetBytes(), message.Payload); - message = partInputChannel1.Receive(10000); - Assert.NotNull(message); - Assert.Equal("1".GetBytes(), message.Payload); - - await late0ProducerBinding.UnbindAsync(); - await late0ConsumerBinding.UnbindAsync(); - await partlate0ProducerBinding.UnbindAsync(); - await partlate0Consumer0Binding.UnbindAsync(); - await partlate0Consumer1Binding.UnbindAsync(); - await noDlqProducerBinding.UnbindAsync(); - await noDlqConsumerBinding.UnbindAsync(); - await pubSubProducerBinding.UnbindAsync(); - await nonDurableConsumerBinding.UnbindAsync(); - await durableConsumerBinding.UnbindAsync(); - - // Reset timeouts so cleanup happens - TestBinder.ResetConnectionFactoryTimeout(); - - Cleanup(); - } - finally - { - proxy?.Stop(); - cf?.Dispose(); - - GetResource().Dispose(); - } - } - - [Fact] - public async Task TestBadUserDeclarationsFatal() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - IApplicationContext context = binder.ApplicationContext; - context.Register("testBadUserDeclarationsFatal", new Queue("testBadUserDeclarationsFatal", false)); - context.Register("binder", binder); - - RabbitMessageChannelBinder channelBinder = binder.Binder; - var provisioner = GetPropertyValue(channelBinder, "ProvisioningProvider"); - - context.Register("provisioner", provisioner); - - var admin = new RabbitAdmin(GetResource(), LoggerFactory.CreateLogger()); - admin.DeclareQueue(new Queue("testBadUserDeclarationsFatal")); - - // reset the connection and configure the "user" admin to auto declare queues... - GetResource().ResetConnection(); - - context.Register("rabbitAdmin", admin); - - // the mis-configured queue should be fatal - IBinding binding = null; - - try - { - await Assert.ThrowsAsync(() => - { - ConsumerOptions consumerOptions = GetConsumerOptions("input", rabbitBindingsOptions); - binding = binder.BindConsumer("input", "baddecls", CreateBindableChannel("input", GetDefaultBindingOptions()), consumerOptions); - throw new Exception("Expected exception"); - }); - } - finally - { - admin.DeleteQueue("testBadUserDeclarationsFatal"); - - if (binding != null) - { - await binding.UnbindAsync(); - } - } - } - - [Fact] - public async Task TestRoutingKeyExpression() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ProducerOptions producerProperties = GetProducerOptions("output", rabbitBindingsOptions); - RabbitProducerOptions rabbitProducerOptions = rabbitBindingsOptions.GetRabbitProducerOptions("output"); - rabbitProducerOptions.RoutingKeyExpression = "Payload.field"; - - DirectChannel output = CreateBindableChannel("output", CreateProducerBindingOptions(producerProperties)); - output.ComponentName = "rkeProducer"; - IBinding producerBinding = binder.BindProducer("rke", output, producerProperties); - - var admin = new RabbitAdmin(GetResource()); - Queue queue = new AnonymousQueue(); - var exchange = new TopicExchange("rke"); - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = BindingBuilder.Bind(queue).To(exchange).With("rkeTest"); - admin.DeclareQueue(queue); - admin.DeclareBinding(binding); - - output.AddInterceptor(new TestChannelInterceptor - { - PreSendHandler = (message, _) => - { - Assert.Equal("rkeTest", message.Headers[RabbitExpressionEvaluatingInterceptor.RoutingKeyHeader]); - return message; - } - }); - - output.Send(Message.Create(new Poco("rkeTest"))); - - object bytes = SpyOn(queue.QueueName).Receive(false); - - Assert.IsType(bytes); - - Assert.Equal("{\"field\":\"rkeTest\"}", ((byte[])bytes).GetString()); - - await producerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestRoutingKeyExpressionPartitionedAndDelay() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ProducerOptions producerProperties = GetProducerOptions("output", rabbitBindingsOptions); - RabbitProducerOptions rabbitProducerOptions = rabbitBindingsOptions.GetRabbitProducerOptions("output"); - rabbitProducerOptions.RoutingKeyExpression = "#root.get_Payload().field"; - - // requires delayed message exchange plugin; tested locally - rabbitProducerOptions.DelayExpression = "1000"; - producerProperties.PartitionKeyExpression = "0"; - - DirectChannel output = CreateBindableChannel("output", CreateProducerBindingOptions(producerProperties)); - output.ComponentName = "rkeProducer"; - IBinding producerBinding = binder.BindProducer("rkep", output, producerProperties); - - var admin = new RabbitAdmin(GetResource()); - Queue queue = new AnonymousQueue(); - var exchange = new TopicExchange("rkep"); - Steeltoe.Messaging.RabbitMQ.Configuration.IBinding binding = BindingBuilder.Bind(queue).To(exchange).With("rkepTest-0"); - admin.DeclareQueue(queue); - admin.DeclareBinding(binding); - - output.AddInterceptor(new TestChannelInterceptor - { - PreSendHandler = (message, _) => - { - Assert.Equal("rkepTest", message.Headers[RabbitExpressionEvaluatingInterceptor.RoutingKeyHeader]); - - Assert.Equal(1000, message.Headers[RabbitExpressionEvaluatingInterceptor.DelayHeader]); - return message; - } - }); - - output.Send(Message.Create(new Poco("rkepTest"))); - - object bytes = SpyOn(queue.QueueName).Receive(false); - - Assert.IsType(bytes); - - Assert.Equal("{\"field\":\"rkepTest\"}", ((byte[])bytes).GetString()); - await producerBinding.UnbindAsync(); - } - - [Fact] - public async Task TestPolledConsumer() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ISmartMessageConverter messageConverter = new CompositeMessageConverterFactory().MessageConverterForAllRegistered; - var inboundBindTarget = new DefaultPollableMessageSource(binder.ApplicationContext, messageConverter); - - ConsumerOptions consumerOptions = GetConsumerOptions("input", rabbitBindingsOptions); - IBinding binding = binder.BindPollableConsumer("pollable", "group", inboundBindTarget, consumerOptions); - var template = new RabbitTemplate(GetResource()); - template.ConvertAndSend("pollable.group", "testPollable"); - - bool polled = inboundBindTarget.Poll(new TestMessageHandler - { - OnHandleMessage = m => - { - Assert.Equal("testPollable", m.Payload); - } - }); - - int n = 0; - - while (n++ < 100 && !polled) - { - polled = inboundBindTarget.Poll(new TestMessageHandler - { - OnHandleMessage = m => - { - Assert.Equal("testPollable", m.Payload); - } - }); - } - - Assert.True(polled); - await binding.UnbindAsync(); - } - - [Fact] - public async Task TestPolledConsumer_AbstractBinder() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ISmartMessageConverter messageConverter = new CompositeMessageConverterFactory().MessageConverterForAllRegistered; - var inboundBindTarget = new DefaultPollableMessageSource(binder.ApplicationContext, messageConverter); - - ConsumerOptions consumerOptions = GetConsumerOptions("input", rabbitBindingsOptions); - IBinding binding = binder.BindConsumer("pollable", "group", (object)inboundBindTarget, consumerOptions); - Assert.True(binding is DefaultBinding>); - await binding.UnbindAsync(); - } - - [Fact] - public async Task TestPolledConsumerRequeue() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ISmartMessageConverter messageConverter = new CompositeMessageConverterFactory().MessageConverterForAllRegistered; - var inboundBindTarget = new DefaultPollableMessageSource(binder.ApplicationContext, messageConverter); - - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - - IBinding binding = binder.BindPollableConsumer("pollableRequeue", "group", inboundBindTarget, properties); - var template = new RabbitTemplate(GetResource()); - template.ConvertAndSend("pollableRequeue.group", "testPollable"); - - try - { - bool polled = false; - int n = 0; - - while (n++ < 100 && !polled) - { - polled = inboundBindTarget.Poll(new TestMessageHandler - { - OnHandleMessage = m => - { - Assert.Equal("testPollable", m.Payload); - throw new RequeueCurrentMessageException(); - } - }); - } - } - catch (MessageHandlingException e) - { - Assert.IsAssignableFrom(e.InnerException); - } - - bool isPolled = inboundBindTarget.Poll(new TestMessageHandler - { - OnHandleMessage = m => - { - Assert.Equal("testPollable", m.Payload); - } - }); - - Assert.True(isPolled); - await binding.UnbindAsync(); - } - - [Fact] - public async Task TestPolledConsumerWithDlq() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ISmartMessageConverter messageConverter = new CompositeMessageConverterFactory().MessageConverterForAllRegistered; - var inboundBindTarget = new DefaultPollableMessageSource(binder.ApplicationContext, messageConverter); - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerProperties = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - properties.MaxAttempts = 2; - properties.BackOffInitialInterval = 0; - rabbitConsumerProperties.AutoBindDlq = true; - IBinding binding = binder.BindPollableConsumer("pollableDlq", "group", inboundBindTarget, properties); - var template = new RabbitTemplate(GetResource()); - template.ConvertAndSend("pollableDlq.group", "testPollable"); - - try - { - int n = 0; - - while (n++ < 100) - { - inboundBindTarget.Poll(new TestMessageHandler - { - OnHandleMessage = _ => throw new Exception("test DLQ") - }); - - await Task.Delay(100); - } - } - catch (MessageHandlingException e) - { - Assert.NotNull(e.InnerException); - Assert.Equal("test DLQ", e.InnerException.Message); - } - - IMessage deadLetter = template.Receive("pollableDlq.group.dlq", 10_000); - Assert.NotNull(deadLetter); - await binding.UnbindAsync(); - } - - [Fact] - public async Task TestPolledConsumerWithDlqNoRetry() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ISmartMessageConverter messageConverter = new CompositeMessageConverterFactory().MessageConverterForAllRegistered; - var inboundBindTarget = new DefaultPollableMessageSource(binder.ApplicationContext, messageConverter); - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - properties.MaxAttempts = 1; - rabbitConsumerOptions.AutoBindDlq = true; - IBinding binding = binder.BindPollableConsumer("pollableDlqNoRetry", "group", inboundBindTarget, properties); - var template = new RabbitTemplate(GetResource()); - - template.ConvertAndSend("pollableDlqNoRetry.group", "testPollable"); - - try - { - int n = 0; - - while (n++ < 100) - { - inboundBindTarget.Poll(new TestMessageHandler - { - OnHandleMessage = _ => throw new Exception("test DLQ") - }); - - await Task.Delay(100); - } - } - catch (MessageHandlingException e) - { - Assert.Equal("test DLQ", e.Message); - } - - IMessage deadLetter = template.Receive("pollableDlqNoRetry.group.dlq", 10_000); - Assert.NotNull(deadLetter); - await binding.UnbindAsync(); - } - - [Fact] - public async Task TestPolledConsumerWithDlqRePub() - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ISmartMessageConverter messageConverter = new CompositeMessageConverterFactory().MessageConverterForAllRegistered; - var inboundBindTarget = new DefaultPollableMessageSource(binder.ApplicationContext, messageConverter); - ConsumerOptions properties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - properties.MaxAttempts = 2; - properties.BackOffInitialInterval = 0; - rabbitConsumerOptions.AutoBindDlq = true; - rabbitConsumerOptions.RepublishToDlq = true; - IBinding binding = binder.BindPollableConsumer("pollableDlqRePub", "group", inboundBindTarget, properties); - var template = new RabbitTemplate(GetResource()); - template.ConvertAndSend("pollableDlqRePub.group", "testPollable"); - bool polled = false; - int n = 0; - - while (n++ < 100 && !polled) - { - await Task.Delay(100); - - polled = inboundBindTarget.Poll(new TestMessageHandler - { - OnHandleMessage = _ => throw new Exception("test DLQ") - }); - } - - Assert.True(polled); - IMessage deadLetter = template.Receive("pollableDlqRePub.group.dlq", 10_000); - Assert.NotNull(deadLetter); - await binding.UnbindAsync(); - } - - // TestCustomBatchingStrategy - Test not ported because CustomBatching Strategy is not yet supported in Steeltoe - protected override string GetEndpointRouting(object endpoint) - { - var spelExp = GetPropertyValue(endpoint, "RoutingKeyExpression"); - return spelExp.ExpressionString; - } - - protected override void CheckRkExpressionForPartitionedModuleSpel(object endpoint) - { - string routingExpression = GetEndpointRouting(endpoint); - string delimiter = GetDestinationNameDelimiter(); - string dest = $"{GetExpectedRoutingBaseDestination($"'part{delimiter}0'", "test")} + '-' + Headers['{BinderHeaders.PartitionHeader}']"; - - Assert.Contains(dest, routingExpression, StringComparison.Ordinal); - } - - protected override string GetExpectedRoutingBaseDestination(string name, string group) - { - return name; - } - - protected override bool UsesExplicitRouting() - { - return true; - } - - private async Task TestAutoBindDlqPartionedConsumerFirstWithRepublishGutsAsync(bool withRetry) - { - var rabbitBindingsOptions = new RabbitBindingsOptions(); - RabbitTestBinder binder = GetBinder(rabbitBindingsOptions); - ConsumerOptions consumerProperties = GetConsumerOptions("input", rabbitBindingsOptions); - RabbitConsumerOptions rabbitConsumerOptions = rabbitBindingsOptions.GetRabbitConsumerOptions("input"); - - RegisterGlobalErrorChannel(binder); - - rabbitConsumerOptions.Prefix = "bindertest."; - rabbitConsumerOptions.AutoBindDlq = true; - rabbitConsumerOptions.RepublishToDlq = true; - rabbitConsumerOptions.RepublishDeliveryMode = MessageDeliveryMode.NonPersistent; - consumerProperties.MaxAttempts = withRetry ? 2 : 1; - consumerProperties.Partitioned = true; - consumerProperties.InstanceIndex = 0; - DirectChannel input0 = CreateBindableChannel("input", CreateConsumerBindingOptions(consumerProperties)); - input0.ComponentName = "test.input0DLQ"; - IBinding input0Binding = binder.BindConsumer("partPubDLQ.0", "dlqPartGrp", input0, consumerProperties); - - IBinding defaultConsumerBinding1 = - binder.BindConsumer("partPubDLQ.0", "default", new QueueChannel(LoggerFactory.CreateLogger()), consumerProperties); - - consumerProperties.InstanceIndex = 1; - - DirectChannel input1 = CreateBindableChannel("input1", CreateConsumerBindingOptions(consumerProperties)); - input1.ComponentName = "test.input1DLQ"; - IBinding input1Binding = binder.BindConsumer("partPubDLQ.0", "dlqPartGrp", input1, consumerProperties); - IBinding defaultConsumerBinding2 = binder.BindConsumer("partPubDLQ.0", "default", new QueueChannel(), consumerProperties); - - ProducerOptions producerProperties = GetProducerOptions("output", rabbitBindingsOptions); - RabbitProducerOptions rabbitProducerOptions = rabbitBindingsOptions.GetRabbitProducerOptions("output"); - rabbitProducerOptions.Prefix = "bindertest."; - rabbitProducerOptions.AutoBindDlq = true; - - binder.ApplicationContext.Register("pkExtractor", new TestPartitionSupport("pkExtractor")); - binder.ApplicationContext.Register("pkSelector", new TestPartitionSupport("pkSelector")); - - producerProperties.PartitionKeyExtractorName = "pkExtractor"; - producerProperties.PartitionSelectorName = "pkSelector"; - producerProperties.PartitionCount = 2; - BindingOptions bindingProperties = CreateProducerBindingOptions(producerProperties); - - DirectChannel output = CreateBindableChannel("output", bindingProperties); - output.ComponentName = "test.output"; - IBinding outputBinding = binder.BindProducer("partPubDLQ.0", output, producerProperties); - - var latch0 = new CountdownEvent(1); - - input0.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => - { - if (latch0.CurrentCount <= 0) - { - throw new Exception("dlq"); - } - - latch0.Signal(); - } - }); - - var latch1 = new CountdownEvent(1); - - input1.Subscribe(new TestMessageHandler - { - OnHandleMessage = _ => - { - if (latch1.CurrentCount <= 0) - { - throw new Exception("dlq"); - } - - latch1.Signal(); - } - }); - - IApplicationContext context = binder.ApplicationContext; - var boundErrorChannel = context.GetService("bindertest.partPubDLQ.0.dlqPartGrp-0.errors"); - var globalErrorChannel = context.GetService("errorChannel"); - - var boundErrorChannelMessage = new AtomicReference(); - var globalErrorChannelMessage = new AtomicReference(); - var hasRecovererInCallStack = new AtomicBoolean(!withRetry); - - boundErrorChannel.Subscribe(new TestMessageHandler - { - OnHandleMessage = message => - { - boundErrorChannelMessage.GetAndSet(message); - string stackTrace = new StackTrace().ToString(); - hasRecovererInCallStack.GetAndSet(stackTrace.Contains("ErrorMessageSendingRecoverer", StringComparison.Ordinal)); - } - }); - - globalErrorChannel.Subscribe(new TestMessageHandler - { - OnHandleMessage = message => - { - globalErrorChannelMessage.GetAndSet(message); - } - }); - - output.Send(Message.Create(1)); - Assert.True(latch1.Wait(TimeSpan.FromSeconds(10))); - - output.Send(Message.Create(0)); - Assert.True(latch0.Wait(TimeSpan.FromSeconds(10))); - - output.Send(Message.Create(1)); - - var template = new RabbitTemplate(GetResource()); - - template.ReceiveTimeout = 10000; - - const string streamDlqName = "bindertest.partPubDLQ.0.dlqPartGrp.dlq"; - - IMessage received = template.Receive(streamDlqName); - Assert.NotNull(received); - Assert.Equal("partPubDLQ.0-1", received.Headers["x-original-routingKey"]); - Assert.DoesNotContain(BinderHeaders.PartitionHeader, received.Headers); - Assert.Equal(MessageDeliveryMode.NonPersistent, received.Headers.ReceivedDeliveryMode()); - - output.Send(Message.Create(0)); - received = template.Receive(streamDlqName); - Assert.NotNull(received); - Assert.Equal("partPubDLQ.0-0", received.Headers["x-original-routingKey"]); - Assert.DoesNotContain(BinderHeaders.PartitionHeader, received.Headers); - - // verify we got a message on the dedicated error channel and the global (via bridge) - await Task.Delay(2000); - Assert.NotNull(boundErrorChannelMessage.Value); - - Assert.Equal(withRetry, hasRecovererInCallStack.Value); - Assert.NotNull(globalErrorChannelMessage.Value); - - await input0Binding.UnbindAsync(); - await input1Binding.UnbindAsync(); - await defaultConsumerBinding1.UnbindAsync(); - await defaultConsumerBinding2.UnbindAsync(); - await outputBinding.UnbindAsync(); - } - - private void RegisterGlobalErrorChannel(RabbitTestBinder binder) - { - IApplicationContext applicationContext = binder.ApplicationContext; - - var errorChannel = new BinderErrorChannel(applicationContext, IntegrationContextUtils.ErrorChannelBeanName, - LoggerFactory.CreateLogger()); - - applicationContext.Register(IntegrationContextUtils.ErrorChannelBeanName, errorChannel); - } -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/RabbitProxy.cs b/src/Stream/test/BinderRabbitMQ.Test/RabbitProxy.cs deleted file mode 100644 index 4486cf160d..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/RabbitProxy.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Net; -using System.Net.Sockets; -using Microsoft.Extensions.Logging; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Test; - -public sealed class RabbitProxy -{ - private readonly ILogger _logger; - private readonly TcpListener _listener; - private volatile bool _run = true; - private volatile bool _rejectConnections = true; - - public int Port => ((IPEndPoint)_listener?.LocalEndpoint)?.Port ?? 0; - - public RabbitProxy(ILogger logger) - { - _listener = new TcpListener(IPAddress.Loopback, 0); - _listener.Start(); - _logger = logger; - var listenerThread = new Thread(StartListener); - listenerThread.IsBackground = true; - listenerThread.Start(); - } - - public void Start() - { - _rejectConnections = false; - } - - public void Stop() - { - _run = false; - _rejectConnections = true; - _listener.Stop(); - } - - private void StartListener(object obj) - { - try - { - while (_run) - { - _logger.LogInformation("Waiting for a connection..."); - TcpClient client = _listener.AcceptTcpClient(); - - if (!_rejectConnections) - { - _logger.LogInformation("Connected to client!"); - var t = new Thread(HandleConnection); - t.IsBackground = true; - t.Start(client); - } - else - { - _logger.LogInformation("Disposing connection"); - client.Dispose(); - } - } - } - catch (SocketException) - { - _listener.Stop(); - } - } - - private void HandleConnection(object obj) - { - var client = (TcpClient)obj; - NetworkStream stream = client.GetStream(); - - var rabbitClient = new TcpClient(); - rabbitClient.Connect("localhost", 5672); - NetworkStream rabbitStream = rabbitClient.GetStream(); - - rabbitStream.ReadTimeout = 100; - stream.ReadTimeout = 100; - - byte[] bytes = new byte[1]; - byte[] serverBytes = new byte[1]; - - try - { - int totalServerBytes = 0; - int totalClientBytes = 0; - - Task t = Task.Run(() => - { - while (rabbitStream.CanRead && _run) - { - try - { - int serverBytesRead = rabbitStream.Read(serverBytes, 0, serverBytes.Length); - totalServerBytes += serverBytesRead; - - if (serverBytesRead != 0 && stream.CanWrite) - { - stream.Write(serverBytes, 0, serverBytes.Length); - } - } - catch (Exception) - { - // Ignore - } - } - }); - - while (stream.CanRead && _run) - { - try - { - int clientBytesRead = stream.Read(bytes, 0, bytes.Length); - totalClientBytes += clientBytesRead; - - if (clientBytesRead != 0 && rabbitStream.CanWrite) - { - rabbitStream.Write(bytes, 0, bytes.Length); - } - } - catch (Exception) - { - // Ignore - } - } - - t.GetAwaiter().GetResult(); - } - catch (Exception) - { - // Ignore - } - finally - { - client.Close(); - rabbitClient.Close(); - client.Dispose(); - rabbitClient.Dispose(); - } - } -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/RabbitTestBinder.cs b/src/Stream/test/BinderRabbitMQ.Test/RabbitTestBinder.cs deleted file mode 100644 index 246bea52af..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/RabbitTestBinder.cs +++ /dev/null @@ -1,205 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Steeltoe.Messaging.RabbitMQ.Connection; -using Steeltoe.Messaging.RabbitMQ.Core; -using Steeltoe.Stream.Binder.RabbitMQ.Configuration; -using Steeltoe.Stream.Binder.RabbitMQ.Provisioning; -using Steeltoe.Stream.Binder.Test; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Test; - -public sealed class RabbitTestBinder : AbstractPollableConsumerTestBinder -{ - private static IApplicationContext _applicationContext; - private readonly RabbitAdmin _rabbitAdmin; - private readonly HashSet _prefixes = new(); - - private readonly ILogger _logger; - private readonly ILoggerFactory _loggerFactory; - - public RabbitBindingsOptions BindingsOptions { get; } - - public RabbitTestBinder(IConnectionFactory connectionFactory, IOptionsMonitor rabbitOptions, - IOptionsMonitor binderOptions, IOptionsMonitor bindingsOptions, ILoggerFactory loggerFactory) - : this(connectionFactory, - new RabbitMessageChannelBinder(GetApplicationContext(), loggerFactory.CreateLogger(), connectionFactory, rabbitOptions, - binderOptions, bindingsOptions, - new RabbitExchangeQueueProvisioner(connectionFactory, bindingsOptions, GetApplicationContext(), - loggerFactory.CreateLogger())), loggerFactory.CreateLogger()) - { - BindingsOptions = bindingsOptions.CurrentValue; - _loggerFactory = loggerFactory; - } - - public RabbitTestBinder(IConnectionFactory connectionFactory, RabbitMessageChannelBinder binder, ILogger logger) - { - _logger = logger; - PollableConsumerBinder = binder; - _rabbitAdmin = new RabbitAdmin(GetApplicationContext(), connectionFactory, logger); - BindingsOptions = binder.BindingsOptions; - } - - public static IApplicationContext GetApplicationContext() - { - if (_applicationContext == null) - { - ServiceProvider serviceProvider = new ServiceCollection().BuildServiceProvider(true); - _applicationContext = new GenericApplicationContext(serviceProvider, new ConfigurationBuilder().Build()); - } - - return _applicationContext; - } - - public void ResetApplicationContext() - { - _applicationContext = null; - GetApplicationContext(); - } - - public override IBinding BindConsumer(string name, string group, IMessageChannel inboundTarget, IConsumerOptions consumerOptions) - { - CaptureConsumerResources(name, group, consumerOptions); - - return base.BindConsumer(name, group, inboundTarget, consumerOptions); - } - - public IBinding BindPollableConsumer(string name, string group, IPollableSource inboundBindTarget, IConsumerOptions consumerOptions) - { - CaptureConsumerResources(name, group, consumerOptions); - return BindConsumer(name, group, inboundBindTarget, consumerOptions); - } - - public override IBinding BindProducer(string name, IMessageChannel outboundTarget, IProducerOptions producerOptions) - { - RabbitProducerOptions properties = BindingsOptions.GetRabbitProducerOptions(producerOptions.BindingName); - - Queues.Add($"{properties.Prefix}{name}.default"); - Exchanges.Add(properties.Prefix + name); - - if (producerOptions.RequiredGroups != null) - { - foreach (string group in producerOptions.RequiredGroups) - { - if (properties.QueueNameGroupOnly == true) - { - Queues.Add(properties.Prefix + group); - } - else - { - Queues.Add($"{properties.Prefix}{name}.{group}"); - } - } - } - - _prefixes.Add(properties.Prefix); - DeadLetters(properties); - - return base.BindProducer(name, outboundTarget, producerOptions); - } - - public void ResetConnectionFactoryTimeout() - { - string host = _rabbitAdmin.ConnectionFactory.Host; - int port = _rabbitAdmin.ConnectionFactory.Port; - - _rabbitAdmin.ConnectionFactory = _rabbitAdmin.RabbitTemplate.ConnectionFactory = new CachingConnectionFactory(host, port, _loggerFactory); - } - - public override void Cleanup() - { - foreach (string name in Queues) - { - _logger.LogInformation("Deleting queue {queue}", name); - _rabbitAdmin.DeleteQueue(name); - _rabbitAdmin.DeleteQueue($"{name}.dlq"); - - // delete any partitioned queues - for (int i = 0; i < 10; i++) - { - _rabbitAdmin.DeleteQueue($"{name}-{i}"); - _rabbitAdmin.DeleteQueue($"{name}-{i}.dlq"); - } - } - - foreach (string exchange in Exchanges) - { - _logger.LogInformation("Deleting exchange {exchange}", exchange); - _rabbitAdmin.DeleteExchange(exchange); - } - - foreach (string prefix in _prefixes) - { - _rabbitAdmin.DeleteExchange($"{prefix}DLX"); - } - - _applicationContext = null; - } - - private void CaptureConsumerResources(string name, string group, IConsumerOptions options) - { - string[] names = null; - RabbitConsumerOptions consumerOptions = BindingsOptions.GetRabbitConsumerOptions(options.BindingName); - - if (group != null) - { - if (consumerOptions.QueueNameGroupOnly.GetValueOrDefault()) - { - Queues.Add(consumerOptions.Prefix + group); - } - else - { - if (options.Multiplex) - { - names = name.Split(','); - - foreach (string nextName in names) - { - Queues.Add($"{consumerOptions.Prefix}{nextName.Trim()}.{group}"); - } - } - else - { - Queues.Add($"{consumerOptions.Prefix}{name}.{group}"); - } - } - } - - if (names != null) - { - foreach (string nextName in names) - { - Exchanges.Add(consumerOptions.Prefix + nextName.Trim()); - } - } - else - { - Exchanges.Add(consumerOptions.Prefix + name.Trim()); - } - - _prefixes.Add(consumerOptions.Prefix); - DeadLetters(consumerOptions); - } - - private void DeadLetters(RabbitCommonOptions properties) - { - if (properties.DeadLetterExchange != null) - { - Exchanges.Add(properties.DeadLetterExchange); - } - - if (properties.DeadLetterQueueName != null) - { - Queues.Add(properties.DeadLetterQueueName); - } - } -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/Steeltoe.Stream.Binder.RabbitMQ.Test.csproj b/src/Stream/test/BinderRabbitMQ.Test/Steeltoe.Stream.Binder.RabbitMQ.Test.csproj deleted file mode 100644 index 6db0a0589c..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/Steeltoe.Stream.Binder.RabbitMQ.Test.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - net8.0;net6.0 - Exe - - - - - - - - - - - - diff --git a/src/Stream/test/BinderRabbitMQ.Test/TestChannelInterceptor.cs b/src/Stream/test/BinderRabbitMQ.Test/TestChannelInterceptor.cs deleted file mode 100644 index cab9ec2e0c..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/TestChannelInterceptor.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; - -namespace Steeltoe.Stream.Binder.RabbitMQ.Test; - -internal sealed class TestChannelInterceptor : AbstractChannelInterceptor -{ - public Func PreSendHandler { get; set; } - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - return PreSendHandler?.Invoke(message, channel) ?? message; - } -} diff --git a/src/Stream/test/BinderRabbitMQ.Test/xunit.runner.json b/src/Stream/test/BinderRabbitMQ.Test/xunit.runner.json deleted file mode 100644 index fdeefaa456..0000000000 --- a/src/Stream/test/BinderRabbitMQ.Test/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "maxParallelThreads": 1, - "parallelizeTestCollections": false -} diff --git a/src/Stream/test/MockBinder/Startup.cs b/src/Stream/test/MockBinder/Startup.cs deleted file mode 100644 index 83b674b3d6..0000000000 --- a/src/Stream/test/MockBinder/Startup.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.MockBinder; - -[assembly: Binder("mock", typeof(Startup))] - -namespace Steeltoe.Stream.MockBinder; - -public sealed class Startup -{ - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - var mock = new Mock> - { - DefaultValue = DefaultValue.Mock - }; - - mock.Setup(b => b.ServiceName).Returns("mock"); - services.AddSingleton(mock.Object); - services.AddSingleton(p => p.GetRequiredService>()); - } -} diff --git a/src/Stream/test/MockBinder/Steeltoe.Stream.MockBinder.csproj b/src/Stream/test/MockBinder/Steeltoe.Stream.MockBinder.csproj deleted file mode 100644 index 77e758e5ac..0000000000 --- a/src/Stream/test/MockBinder/Steeltoe.Stream.MockBinder.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - net8.0;net6.0 - False - - - - - - - - - - - - diff --git a/src/Stream/test/Stream.Test/AbstractTest.cs b/src/Stream/test/Stream.Test/AbstractTest.cs deleted file mode 100644 index 2bcfc94bed..0000000000 --- a/src/Stream/test/Stream.Test/AbstractTest.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Extensions; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Extensions; - -namespace Steeltoe.Stream.Test; - -public abstract class AbstractTest -{ - protected virtual ServiceCollection CreateStreamsContainerWithDefaultBindings(List searchDirectories, params string[] properties) - { - ServiceCollection container = CreateStreamsContainer(searchDirectories, properties); - container.AddDefaultBindings(); - return container; - } - - protected virtual ServiceCollection CreateStreamsContainerWithDefaultBindings(params string[] properties) - { - return CreateStreamsContainerWithDefaultBindings(null, properties); - } - - protected virtual ServiceCollection CreateStreamsContainer(params string[] properties) - { - return CreateStreamsContainer(null, properties); - } - - protected virtual ServiceCollection CreateStreamsContainer(List searchDirectories, params string[] properties) - { - IConfiguration configuration = CreateTestConfiguration(properties); - var container = new ServiceCollection(); - container.AddOptions(); - - container.AddLogging(b => - { - b.AddDebug(); - b.SetMinimumLevel(LogLevel.Trace); - }); - - container.AddSingleton(configuration); - container.AddSingleton(); - container.AddStreamConfiguration(configuration); - container.AddCoreServices(); - container.AddIntegrationServices(); - container.AddStreamCoreServices(configuration); - - if (searchDirectories == null || searchDirectories.Count == 0) - { - container.AddBinderServices(configuration); - } - else - { - var registry = new DefaultBinderTypeRegistry(searchDirectories, false); - container.AddSingleton(registry); - container.AddBinderServices(registry, configuration); - } - - return container; - } - - protected virtual ServiceCollection CreateStreamsContainerWithBinding(List searchDirectories, Type bindingType, params string[] properties) - { - ServiceCollection collection = CreateStreamsContainer(searchDirectories, properties); - collection.AddStreamBindings(bindingType); - return collection; - } - - protected virtual ServiceCollection CreateStreamsContainerWithIProcessorBinding(List searchDirectories, params string[] properties) - { - ServiceCollection collection = CreateStreamsContainer(searchDirectories, properties); - collection.AddProcessorStreamBinding(); - return collection; - } - - protected virtual ServiceCollection CreateStreamsContainerWithISinkBinding(List searchDirectories, params string[] properties) - { - ServiceCollection collection = CreateStreamsContainer(searchDirectories, properties); - collection.AddSinkStreamBinding(); - return collection; - } - - protected virtual ServiceCollection CreateStreamsContainerWithISourceBinding(List searchDirectories, params string[] properties) - { - ServiceCollection collection = CreateStreamsContainer(searchDirectories, properties); - collection.AddSourceStreamBinding(); - return collection; - } - - protected virtual IConfiguration CreateTestConfiguration(params string[] properties) - { - List> keyValuePairs = ParseProperties(properties); - var configurationBuilder = new ConfigurationBuilder(); - configurationBuilder.AddInMemoryCollection(keyValuePairs); - return configurationBuilder.Build(); - } - - protected virtual List GetSearchDirectories(params string[] names) - { - var results = new List(); - - string currentDirectory = Environment.CurrentDirectory; - - foreach (string name in names) - { - string dir = currentDirectory.Replace("Stream.Test", name, StringComparison.Ordinal); - results.Add(dir); - } - - return results; - } - - protected virtual List> ParseProperties(string[] properties) - { - var result = new List>(); - - if (properties == null) - { - return result; - } - - foreach (string prop in properties) - { - string[] split = prop.Split('='); - split[0] = split[0].Replace('.', ':'); - result.Add(new KeyValuePair(split[0], split[1])); - } - - return result; - } - - protected ConsumerOptions GetConsumerOptions(string bindingName) - { - var options = new ConsumerOptions(); - options.PostProcess(bindingName); - return options; - } - - protected ProducerOptions GetProducerOptions(string bindingName) - { - var options = new ProducerOptions(); - options.PostProcess(bindingName); - return options; - } -} diff --git a/src/Stream/test/Stream.Test/Binder/AbstractMessageChannelBinderTest.cs b/src/Stream/test/Stream.Test/Binder/AbstractMessageChannelBinderTest.cs deleted file mode 100644 index 25da951f81..0000000000 --- a/src/Stream/test/Stream.Test/Binder/AbstractMessageChannelBinderTest.cs +++ /dev/null @@ -1,147 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Dispatcher; -using Steeltoe.Integration.Handler; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.TestBinder; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class AbstractMessageChannelBinderTest : AbstractTest -{ - private readonly IServiceProvider _serviceProvider; - - public AbstractMessageChannelBinderTest() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - - _serviceProvider = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring:cloud:stream:defaultBinder=testbinder") - .BuildServiceProvider(true); - } - - [Fact] - public async Task TestEndpointLifecycle() - { - var binder = _serviceProvider.GetService() as TestChannelBinder; - Assert.NotNull(binder); - - var consumerProperties = new ConsumerOptions("testbinding") - { - MaxAttempts = 1 - }; - - consumerProperties.PostProcess("testbinding"); - - IBinding consumerBinding = - binder.BindConsumer("foo", "fooGroup", new DirectChannel(_serviceProvider.GetService()), consumerProperties); - - var defaultBinding = consumerBinding as DefaultBinding; - Assert.NotNull(defaultBinding); - - // lifecycle - var messageProducer = defaultBinding.Endpoint as TestChannelBinder.TestMessageProducerSupportEndpoint; - Assert.NotNull(messageProducer); - Assert.True(defaultBinding.Endpoint.IsRunning); - Assert.NotNull(messageProducer.OutputChannel); - - // lifecycle.ErrorChannel - Assert.NotNull(messageProducer.ErrorChannel); - var errorChannel = messageProducer.ErrorChannel as PublishSubscribeChannel; - Assert.NotNull(errorChannel.Dispatcher); - - // dispatcher.handlers - Assert.Equal(2, errorChannel.Dispatcher.HandlerCount); - var dispatcher = errorChannel.Dispatcher as AbstractDispatcher; - Assert.NotNull(dispatcher); - List handlers = dispatcher.Handlers; - Assert.True(handlers[0] is BridgeHandler); - Assert.True(handlers[1] is ILastSubscriberMessageHandler); - - var registry = _serviceProvider.GetRequiredService(); - Assert.True(registry.ContainsService("foo.fooGroup.errors")); - Assert.True(registry.ContainsService("foo.fooGroup.errors.recoverer")); - Assert.True(registry.ContainsService("foo.fooGroup.errors.handler")); - Assert.True(registry.ContainsService("foo.fooGroup.errors.bridge")); - - await consumerBinding.UnbindAsync(); - - Assert.False(registry.ContainsService("foo.fooGroup.errors")); - Assert.False(registry.ContainsService("foo.fooGroup.errors.recoverer")); - Assert.False(registry.ContainsService("foo.fooGroup.errors.handler")); - Assert.False(registry.ContainsService("foo.fooGroup.errors.bridge")); - - Assert.False(defaultBinding.Endpoint.IsRunning); - - var producerProps = new ProducerOptions("testbinding") - { - ErrorChannelEnabled = true - }; - - producerProps.PostProcess("testbinding"); - - IBinding producerBinding = binder.BindProducer("bar", new DirectChannel(_serviceProvider.GetService()), producerProps); - Assert.True(registry.ContainsService("bar.errors")); - Assert.True(registry.ContainsService("bar.errors.bridge")); - - await producerBinding.UnbindAsync(); - Assert.False(registry.ContainsService("bar.errors")); - Assert.False(registry.ContainsService("bar.errors.bridge")); - } - - [Fact] - public async Task TestEndpointBinderHasRecoverer() - { - var binder = _serviceProvider.GetService() as TestChannelBinder; - Assert.NotNull(binder); - - IBinding consumerBinding = binder.BindConsumer("foo", "fooGroup", new DirectChannel(_serviceProvider.GetService()), - GetConsumerOptions("testbinding")); - - var defaultBinding = consumerBinding as DefaultBinding; - Assert.NotNull(defaultBinding); - - // lifecycle - var messageProducer = defaultBinding.Endpoint as TestChannelBinder.TestMessageProducerSupportEndpoint; - Assert.NotNull(messageProducer); - - // lifecycle.ErrorChannel - Assert.Null(messageProducer.ErrorChannel); - - var callback = messageProducer.RecoveryCallback as ErrorMessagePublisher; - Assert.NotNull(callback); - var errorChannel = callback.Channel as PublishSubscribeChannel; - Assert.NotNull(errorChannel); - - Assert.NotNull(errorChannel.Dispatcher); - - // dispatcher.handlers - Assert.Equal(2, errorChannel.Dispatcher.HandlerCount); - var dispatcher = errorChannel.Dispatcher as AbstractDispatcher; - Assert.NotNull(dispatcher); - List handlers = dispatcher.Handlers; - Assert.True(handlers[0] is BridgeHandler); - Assert.True(handlers[1] is ILastSubscriberMessageHandler); - - var registry = _serviceProvider.GetRequiredService(); - Assert.True(registry.ContainsService("foo.fooGroup.errors")); - Assert.True(registry.ContainsService("foo.fooGroup.errors.recoverer")); - Assert.True(registry.ContainsService("foo.fooGroup.errors.handler")); - Assert.True(registry.ContainsService("foo.fooGroup.errors.bridge")); - - await consumerBinding.UnbindAsync(); - - Assert.False(registry.ContainsService("foo.fooGroup.errors")); - Assert.False(registry.ContainsService("foo.fooGroup.errors.recoverer")); - Assert.False(registry.ContainsService("foo.fooGroup.errors.handler")); - Assert.False(registry.ContainsService("foo.fooGroup.errors.bridge")); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/ArbitraryInterfaceWithBindingTargetsTest.cs b/src/Stream/test/Stream.Test/Binder/ArbitraryInterfaceWithBindingTargetsTest.cs deleted file mode 100644 index 7942bf5034..0000000000 --- a/src/Stream/test/Stream.Test/Binder/ArbitraryInterfaceWithBindingTargetsTest.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class ArbitraryInterfaceWithBindingTargetsTest : AbstractTest -{ - [Fact] - public async Task TestArbitraryInterfaceChannelsBound() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithBinding(searchDirectories, typeof(IFooChannels), "spring:cloud:stream:defaultBinder=mock", - "spring:cloud:stream:bindings:Foo:destination=someQueue.0", "spring:cloud:stream:bindings:Bar:destination=someQueue.1", - "spring:cloud:stream:bindings:Baz:destination=someQueue.2", "spring:cloud:stream:bindings:Qux:destination=someQueue.3").BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null, typeof(IMessageChannel)); - Assert.NotNull(binder); - var fooChannels = provider.GetService(); - Assert.NotNull(fooChannels); - - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindConsumer("someQueue.0", null, fooChannels.Foo, It.IsAny())); - mock.Verify(b => b.BindConsumer("someQueue.1", null, fooChannels.Bar, It.IsAny())); - - mock.Verify(b => b.BindProducer("someQueue.2", fooChannels.Baz, It.IsAny())); - mock.Verify(b => b.BindProducer("someQueue.3", fooChannels.Qux, It.IsAny())); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/ArbitraryInterfaceWithDefaultsTest.cs b/src/Stream/test/Stream.Test/Binder/ArbitraryInterfaceWithDefaultsTest.cs deleted file mode 100644 index 06b66f7974..0000000000 --- a/src/Stream/test/Stream.Test/Binder/ArbitraryInterfaceWithDefaultsTest.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class ArbitraryInterfaceWithDefaultsTest : AbstractTest -{ - [Fact] - public async Task TestArbitraryInterfaceChannelsBound() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithBinding(searchDirectories, typeof(IFooChannels), "spring:cloud:stream:defaultBinder=mock") - .BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null, typeof(IMessageChannel)); - Assert.NotNull(binder); - var fooChannels = provider.GetService(); - Assert.NotNull(fooChannels); - - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindConsumer("Foo", null, fooChannels.Foo, It.IsAny())); - mock.Verify(b => b.BindConsumer("Bar", null, fooChannels.Bar, It.IsAny())); - - mock.Verify(b => b.BindProducer("Baz", fooChannels.Baz, It.IsAny())); - mock.Verify(b => b.BindProducer("Qux", fooChannels.Qux, It.IsAny())); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/BinderAwareChannelResolverTest.cs b/src/Stream/test/Stream.Test/Binder/BinderAwareChannelResolverTest.cs deleted file mode 100644 index 17f81c14ea..0000000000 --- a/src/Stream/test/Stream.Test/Binder/BinderAwareChannelResolverTest.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Contexts; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Configuration; -using Xunit; -using IntChannel = Steeltoe.Integration.Channel; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class BinderAwareChannelResolverTest : AbstractTest -{ - private readonly IServiceProvider _serviceProvider; - private readonly BinderAwareChannelResolver _resolver; - private readonly IBinder _binder; - private readonly SubscribableChannelBindingTargetFactory _bindingTargetFactory; - private readonly IOptions _options; - private readonly IApplicationContext _context; - - public BinderAwareChannelResolverTest() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - ServiceCollection container = CreateStreamsContainer(searchDirectories, "spring:cloud:stream:defaultBinder=testbinder"); - container.AddSingleton(); - _serviceProvider = container.BuildServiceProvider(true); - - _binder = _serviceProvider.GetServices().OfType>().Single(); - _bindingTargetFactory = _serviceProvider.GetServices().OfType().Single(); - _resolver = _serviceProvider.GetService>() as BinderAwareChannelResolver; - _options = _serviceProvider.GetService>(); - _context = _serviceProvider.GetService(); - - Assert.NotNull(_binder); - Assert.NotNull(_bindingTargetFactory); - Assert.NotNull(_resolver); - Assert.NotNull(_options); - } - - [Fact] - public void ResolveChannel() - { - IEnumerable bindables = _serviceProvider.GetServices(); - Assert.Single(bindables); - IBindable bindable = bindables.Single(); - Assert.Empty(bindable.Inputs); - Assert.Empty(bindable.Outputs); - IMessageChannel registered = _resolver.ResolveDestination("foo"); - - List interceptors = ((IntChannel.AbstractMessageChannel)registered).ChannelInterceptors; - Assert.Equal(2, interceptors.Count); - Assert.IsType(interceptors[1]); - - Assert.Empty(bindable.Inputs); - Assert.Single(bindable.Outputs); - - var testChannel = new IntChannel.DirectChannel(_context, "INPUT"); - var latch = new CountdownEvent(1); - IList received = new List(); - - testChannel.Subscribe(new LatchedMessageHandler - { - Latch = latch, - Received = received - }); - - _binder.BindConsumer("foo", null, testChannel, GetConsumerOptions("testbinding")); - Assert.Empty(received); - registered.Send(MessageBuilder.WithPayload("hello").Build()); - latch.Wait(1000); - - Assert.Single(received); - byte[] payload = received[0].Payload as byte[]; - Assert.Equal("hello", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public void ResolveNonRegisteredChannel() - { - IMessageChannel other = _resolver.ResolveDestination("other"); - var registry = _serviceProvider.GetService(); - var bean = registry.GetService("other"); - Assert.Same(bean, other); - } - - private sealed class LatchedMessageHandler : IMessageHandler - { - public CountdownEvent Latch { get; set; } - - public IList Received { get; set; } - - public string ServiceName { get; set; } - - public LatchedMessageHandler() - { - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - Received.Add(message); - Latch.Signal(); - } - } -} diff --git a/src/Stream/test/Stream.Test/Binder/BinderFactoryConfigurationTest.cs b/src/Stream/test/Stream.Test/Binder/BinderFactoryConfigurationTest.cs deleted file mode 100644 index 00ea6f7467..0000000000 --- a/src/Stream/test/Stream.Test/Binder/BinderFactoryConfigurationTest.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class BinderFactoryConfigurationTest : AbstractTest -{ - [Fact] - public void LoadBinderTypeRegistryWithOneBinder() - { - List searchDirectories = GetSearchDirectories("StubBinder1"); - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "spring:cloud:stream:defaultBinder=binder1").BuildServiceProvider(true); - var typeRegistry = provider.GetService(); - Assert.Single(typeRegistry.GetAll()); - - Assert.Contains("binder1", typeRegistry.GetAll().Keys); - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder1 = factory.GetBinder("binder1", typeof(IMessageChannel)); - Assert.NotNull(binder1); - - IBinder defaultBinder = factory.GetBinder(null, typeof(IMessageChannel)); - Assert.Same(binder1, defaultBinder); - } - - [Fact] - public void LoadBinderTypeRegistryWithOneBinderWithBinderSpecificConfigurationData() - { - List searchDirectories = GetSearchDirectories("StubBinder1"); - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "binder1:name=foo").BuildServiceProvider(true); - - var factory = provider.GetService(); - - _ = factory.GetBinder("binder1", typeof(IMessageChannel)); - - IConfigurationSection section = provider.GetService().GetSection("binder1"); - Assert.Equal("foobar", section["name"]); - } - - [Fact] - public void LoadBinderTypeRegistryWithTwoBinders() - { - List searchDirectories = GetSearchDirectories("StubBinder1", "StubBinder2"); - ServiceProvider provider = CreateStreamsContainer(searchDirectories).BuildServiceProvider(true); - - var typeRegistry = provider.GetService(); - - Assert.NotNull(typeRegistry); - Assert.Equal(2, typeRegistry.GetAll().Count); - Assert.Contains("binder1", typeRegistry.GetAll().Keys); - Assert.Contains("binder2", typeRegistry.GetAll().Keys); - var factory = provider.GetService(); - Assert.NotNull(factory); - Assert.Throws(() => factory.GetBinder(null, typeof(IMessageChannel))); - - IBinder binder1 = factory.GetBinder("binder1", typeof(IMessageChannel)); - Assert.NotNull(binder1); - IBinder binder2 = factory.GetBinder("binder2", typeof(IMessageChannel)); - Assert.NotNull(binder1); - Assert.NotSame(binder1, binder2); - } - - [Fact] - public void LoadBinderTypeRegistryWithCustomNonDefaultCandidate() - { - List searchDirectories = GetSearchDirectories("StubBinder1", "TestBinder"); - - ServiceProvider provider = CreateStreamsContainer(searchDirectories, - "spring.cloud.stream.binders.custom.configureclass=" + "Steeltoe.Stream.TestBinder.Startup, Steeltoe.Stream.TestBinder", - "spring.cloud.stream.binders.custom.defaultCandidate=false", "spring.cloud.stream.binders.custom.inheritEnvironment=false", - "spring.cloud.stream.defaultbinder=binder1").BuildServiceProvider(true); - - var typeRegistry = provider.GetService(); - Assert.NotNull(typeRegistry); - Assert.Equal(2, typeRegistry.GetAll().Count); - Assert.Contains("binder1", typeRegistry.GetAll().Keys); - var factory = provider.GetService(); - Assert.NotNull(factory); - - IBinder defaultBinder = factory.GetBinder(null, typeof(IMessageChannel)); - Assert.NotNull(defaultBinder); - Assert.Equal("binder1", defaultBinder.ServiceName); - - IBinder binder1 = factory.GetBinder("binder1", typeof(IMessageChannel)); - Assert.NotNull(binder1); - Assert.Equal("binder1", binder1.ServiceName); - - Assert.Same(binder1, defaultBinder); - } - - [Fact] - public void LoadDefaultBinderWithTwoBinders() - { - List searchDirectories = GetSearchDirectories("StubBinder1", "StubBinder2"); - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "spring.cloud.stream.defaultBinder=binder2").BuildServiceProvider(true); - var typeRegistry = provider.GetService(); - Assert.NotNull(typeRegistry); - Assert.Equal(2, typeRegistry.GetAll().Count); - Assert.Contains("binder1", typeRegistry.GetAll().Keys); - Assert.Contains("binder2", typeRegistry.GetAll().Keys); - - var factory = provider.GetService(); - Assert.NotNull(factory); - - IBinder binder1 = factory.GetBinder("binder1", typeof(IMessageChannel)); - Assert.Equal("binder1", binder1.ServiceName); - IBinder binder2 = factory.GetBinder("binder2", typeof(IMessageChannel)); - Assert.Equal("binder2", binder2.ServiceName); - IBinder defaultBinder = factory.GetBinder(null, typeof(IMessageChannel)); - Assert.Same(defaultBinder, binder2); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/DefaultBinderTypeRegistryTest.cs b/src/Stream/test/Stream.Test/Binder/DefaultBinderTypeRegistryTest.cs deleted file mode 100644 index dd09ea42b7..0000000000 --- a/src/Stream/test/Stream.Test/Binder/DefaultBinderTypeRegistryTest.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Runtime.InteropServices; -using Steeltoe.Stream.Binder; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class DefaultBinderTypeRegistryTest : AbstractTest -{ - [Fact] - public void LoadAndCheckAssembly_WithValidPath_ReturnsBinderType() - { - string binderDir = GetSearchDirectories("TestBinder")[0]; - List paths = BuildPaths(binderDir); - - var context = new MetadataLoadContext(new PathAssemblyResolver(paths)); - string binderAssembly = $"{binderDir}{Path.DirectorySeparatorChar}Steeltoe.Stream.TestBinder.dll"; - IBinderType result = DefaultBinderTypeRegistry.LoadAndCheckAssembly(context, binderAssembly); - Assert.Equal(binderAssembly, result.AssemblyPath); - - Assert.Matches(@"Steeltoe.Stream.TestBinder.Startup, Steeltoe.Stream.TestBinder, Version=[\d.]+, Culture=neutral, PublicKeyToken=null", - result.ConfigureClass); - - Assert.Equal("testbinder", result.Name); - context.Dispose(); - } - - [Fact] - public void LoadAndCheckAssembly_WithInValidPath_DoesNotReturnsBinderType() - { - List paths = BuildPaths(null); - var context = new MetadataLoadContext(new PathAssemblyResolver(paths)); - string binderAssembly = $"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}Steeltoe.Stream.FooBar.dll"; - IBinderType result = DefaultBinderTypeRegistry.LoadAndCheckAssembly(context, binderAssembly); - Assert.Null(result); - context.Dispose(); - } - - [Fact] - public void AddBinderTypes_WithValidDirectory_ReturnsBinder() - { - string binderDir = GetSearchDirectories("TestBinder")[0]; - var result = new Dictionary(); - DefaultBinderTypeRegistry.AddBinderTypes(binderDir, result); - Assert.Single(result); - - Assert.Matches(@"Steeltoe.Stream.TestBinder.Startup, Steeltoe.Stream.TestBinder, Version=[\d.]+, Culture=neutral, PublicKeyToken=null", - result["testbinder"].ConfigureClass); - } - - [Fact] - public void AddBinderTypes_WithInValidDirectory_ReturnsNoBinders() - { - var result = new Dictionary(); - DefaultBinderTypeRegistry.AddBinderTypes(Path.GetTempPath(), result); - Assert.Empty(result); - } - - [Fact] - public void AddBinderTypes_WithBinderAlreadyLoaded_ReturnsBinder() - { - bool isAlreadyLoaded = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(a => a.FullName == "Steeltoe.Stream.TestBinder") != null; - var result = new Dictionary(); - - DefaultBinderTypeRegistry.AddBinderTypes(AppDomain.CurrentDomain.GetAssemblies(), result); - CheckExpectedResult(isAlreadyLoaded, result); - } - - [Fact] - public void ShouldCheckFile_ReturnsExpected() - { - var fileInfo = new FileInfo("Steeltoe.Stream.dll"); - Assert.False(DefaultBinderTypeRegistry.ShouldCheckFile(fileInfo)); - fileInfo = new FileInfo("foo.bar"); - Assert.True(DefaultBinderTypeRegistry.ShouldCheckFile(fileInfo)); - } - - [Fact] - public void CheckAssembly_ReturnsExpected() - { - Assert.Null(DefaultBinderTypeRegistry.CheckAssembly(Assembly.GetExecutingAssembly())); - } - - [Fact] - public void ParseBinderConfigurations_ReturnsBinder() - { - List binderDir = GetSearchDirectories("TestBinder"); - bool isAlreadyLoaded = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(a => a.FullName == "Steeltoe.Stream.TestBinder") != null; - var result = new Dictionary(); - DefaultBinderTypeRegistry.ParseBinderConfigurations(binderDir, result); - CheckExpectedResult(isAlreadyLoaded, result); - } - - [Fact] - public void FindBinders_ReturnsLoadedBinder() - { - bool isAlreadyLoaded = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(a => a.FullName == "Steeltoe.Stream.TestBinder") != null; - var searchDirectories = new List(); - Dictionary result = DefaultBinderTypeRegistry.FindBinders(searchDirectories); - CheckExpectedResult(isAlreadyLoaded, result); - } - - [Fact] - public void Constructor_FindsBinder() - { - var registry = new DefaultBinderTypeRegistry(); - Assert.Single(registry.GetAll(), r => r.Key == "testbinder"); - - Assert.Matches(@"Steeltoe.Stream.TestBinder.Startup, Steeltoe.Stream.TestBinder, Version=[\d.]+, Culture=neutral, PublicKeyToken=null", - registry.Get("testbinder").ConfigureClass); - } - - private void CheckExpectedResult(bool isAlreadyLoaded, Dictionary results) - { - if (isAlreadyLoaded) - { - Assert.Matches(@"Steeltoe.Stream.TestBinder.Startup, Steeltoe.Stream.TestBinder, Version=[\d.]+, Culture=neutral, PublicKeyToken=null", - results["testbinder"].ConfigureClass); - } - } - - private List BuildPaths(string binderPath) - { - var paths = new List(); - paths.AddRange(Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll")); - - if (!Environment.CurrentDirectory.Equals(binderPath, StringComparison.OrdinalIgnoreCase)) - { - paths.AddRange(Directory.GetFiles(Environment.CurrentDirectory, "*.dll")); - } - - return paths; - } -} diff --git a/src/Stream/test/Stream.Test/Binder/ErrorBindingTest.cs b/src/Stream/test/Stream.Test/Binder/ErrorBindingTest.cs deleted file mode 100644 index bc0e19697e..0000000000 --- a/src/Stream/test/Stream.Test/Binder/ErrorBindingTest.cs +++ /dev/null @@ -1,132 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Extensions; -using Steeltoe.Stream.TestBinder; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class ErrorBindingTest : AbstractTest -{ - [Fact] - public async Task TestErrorChannelNotBoundByDefault() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithIProcessorBinding(searchDirectories, "spring:cloud:stream:defaultBinder=mock") - .BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Assert.NotNull(binder); - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindConsumer("input", null, It.IsAny(), It.IsAny())); - mock.Verify(b => b.BindProducer("output", It.IsAny(), It.IsAny())); - } - - [Fact] - public async Task TestConfigurationWithDefaultErrorHandler() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - - ServiceCollection container = - CreateStreamsContainerWithIProcessorBinding(searchDirectories, "spring:cloud:stream:bindings:input:consumer:maxAttempts=1"); - - container.AddStreamListeners(); - ServiceProvider provider = container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - SendAndValidate_ErrorConfigurationDefault(provider); - } - - [Fact] - public async Task TestConfigurationWithCustomErrorHandler() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - - ServiceCollection container = - CreateStreamsContainerWithIProcessorBinding(searchDirectories, "spring:cloud:stream:bindings:input:consumer:maxAttempts=1"); - - container.AddStreamListeners(); - ServiceProvider provider = container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - SendAndValidate_ErrorConfigurationWithCustomErrorHandler(provider); - } - - private void SendAndValidate_ErrorConfigurationWithCustomErrorHandler(IServiceProvider provider) - { - var source = provider.GetService(); - source.Send(Message.Create(Encoding.UTF8.GetBytes("Hello1"))); - source.Send(Message.Create(Encoding.UTF8.GetBytes("Hello2"))); - source.Send(Message.Create(Encoding.UTF8.GetBytes("Hello3"))); - - var errorConfig = provider.GetService(); - Assert.NotNull(errorConfig); - Assert.Equal(6, errorConfig.Counter); - } - - private void SendAndValidate_ErrorConfigurationDefault(IServiceProvider provider) - { - var source = provider.GetService(); - source.Send(Message.Create(Encoding.UTF8.GetBytes("Hello1"))); - source.Send(Message.Create(Encoding.UTF8.GetBytes("Hello2"))); - source.Send(Message.Create(Encoding.UTF8.GetBytes("Hello3"))); - - var errorConfig = provider.GetService(); - Assert.NotNull(errorConfig); - Assert.Equal(3, errorConfig.Counter); - } - - public sealed class ErrorConfigurationDefault - { - public int Counter { get; private set; } - - [StreamListener("input")] - public void Handle(object value) - { - Counter++; - throw new Exception("BOOM!"); - } - } - - public sealed class ErrorConfigurationWithCustomErrorHandler - { - public int Counter { get; private set; } - - [StreamListener("input")] - public void Handle(object value) - { - Counter++; - throw new Exception("BOOM!"); - } - - [StreamListener("input.anonymous.errors")] - public void Error(IMessage message) - { - Counter++; - } - } -} diff --git a/src/Stream/test/Stream.Test/Binder/IFooChannels.cs b/src/Stream/test/Stream.Test/Binder/IFooChannels.cs deleted file mode 100644 index c94fb26a43..0000000000 --- a/src/Stream/test/Stream.Test/Binder/IFooChannels.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Test.Binder; - -public interface IFooChannels -{ - [Input] - IMessageChannel Foo { get; } - - [Input] - IMessageChannel Bar { get; } - - [Output] - IMessageChannel Baz { get; } - - [Output] - IMessageChannel Qux { get; } -} diff --git a/src/Stream/test/Stream.Test/Binder/InputOutputBindingOrderTest.cs b/src/Stream/test/Stream.Test/Binder/InputOutputBindingOrderTest.cs deleted file mode 100644 index d581c5f693..0000000000 --- a/src/Stream/test/Stream.Test/Binder/InputOutputBindingOrderTest.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class InputOutputBindingOrderTest : AbstractTest -{ - [Fact] - public async Task TestInputOutputBindingOrder() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - ServiceCollection container = CreateStreamsContainerWithBinding(searchDirectories, typeof(IProcessor), "spring:cloud:stream:defaultBinder=mock"); - - container.AddSingleton(); - container.AddSingleton(p => p.GetService()); - ServiceProvider provider = container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - IBinder binder = factory.GetBinder(null, typeof(IMessageChannel)); - var processor = provider.GetService(); - - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindConsumer("input", null, processor.Input, It.IsAny())); - - var lifecycle = provider.GetService(); - Assert.True(lifecycle.IsRunning); - } - - public sealed class SomeLifecycle : ISmartLifecycle - { - public bool IsRunning { get; private set; } - - public IBinderFactory Factory { get; } - - public IProcessor Processor { get; } - - public bool IsAutoStartup => true; - - public int Phase => 0; - - public SomeLifecycle(IBinderFactory factory, IProcessor processor) - { - Factory = factory; - Processor = processor; - } - - public Task StartAsync() - { - IBinder binder = Factory.GetBinder(null, typeof(IMessageChannel)); - - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindProducer("output", Processor.Output, It.IsAny())); - - IsRunning = true; - return Task.CompletedTask; - } - - public Task StopAsync() - { - IsRunning = false; - return Task.CompletedTask; - } - - public async Task StopAsync(Action callback) - { - await StopAsync(); - callback?.Invoke(); - } - } -} diff --git a/src/Stream/test/Stream.Test/Binder/LifecycleBinderTest.cs b/src/Stream/test/Stream.Test/Binder/LifecycleBinderTest.cs deleted file mode 100644 index 371ac20ecc..0000000000 --- a/src/Stream/test/Stream.Test/Binder/LifecycleBinderTest.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Lifecycle; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class LifecycleBinderTest : AbstractTest -{ - [Fact] - public async Task TestOnlySmartLifecyclesStarted() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - ServiceCollection container = CreateStreamsContainerWithBinding(searchDirectories, typeof(IFooChannels), "spring:cloud:stream:defaultBinder=mock"); - - container.AddSingleton(); - - ServiceProvider provider = container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var lifecycle = provider.GetService(); - Assert.False(lifecycle.IsRunning); - } - - public sealed class SimpleLifecycle : ILifecycle - { - public bool IsRunning { get; private set; } - - public Task StartAsync() - { - IsRunning = true; - return Task.CompletedTask; - } - - public Task StopAsync() - { - IsRunning = false; - return Task.CompletedTask; - } - } -} diff --git a/src/Stream/test/Stream.Test/Binder/MessageConverterTest.cs b/src/Stream/test/Stream.Test/Binder/MessageConverterTest.cs deleted file mode 100644 index 0c754527bc..0000000000 --- a/src/Stream/test/Stream.Test/Binder/MessageConverterTest.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class MessageConverterTest -{ - [Fact] - public void TestHeaderEmbedding() - { - IMessage message = IntegrationMessageBuilder.WithPayload(Encoding.UTF8.GetBytes("Hello")).SetHeader("foo", "bar") - .SetHeader("baz", "quxx").Build(); - - byte[] embedded = EmbeddedHeaderUtils.EmbedHeaders(new MessageValues(message), "foo", "baz"); - Assert.Equal(0xff, embedded[0]); - string embeddedString = Encoding.UTF8.GetString(embedded); - Assert.Equal("\u0002\u0003foo\u0000\u0000\u0000\u0005\"bar\"\u0003baz\u0000\u0000\u0000\u0006\"quxx\"Hello", embeddedString.Substring(1)); - MessageValues extracted = EmbeddedHeaderUtils.ExtractHeaders(IntegrationMessageBuilder.WithPayload(embedded).Build(), false); - string extractedString = Encoding.UTF8.GetString((byte[])extracted.Payload); - Assert.Equal("Hello", extractedString); - Assert.Equal("bar", extracted["foo"]); - Assert.Equal("quxx", extracted["baz"]); - } - - [Fact] - public void TestConfigurableHeaders() - { - IMessage message = IntegrationMessageBuilder.WithPayload(Encoding.UTF8.GetBytes("Hello")).SetHeader("foo", "bar") - .SetHeader("baz", "quxx").SetHeader("contentType", "text/plain").Build(); - - string[] headers = - { - "foo" - }; - - byte[] embedded = EmbeddedHeaderUtils.EmbedHeaders(new MessageValues(message), EmbeddedHeaderUtils.HeadersToEmbed(headers)); - Assert.Equal(0xff, embedded[0]); - string embeddedString = Encoding.UTF8.GetString(embedded); - Assert.Equal("\u0002\u000BcontentType\u0000\u0000\u0000\u000C\"text/plain\"\u0003foo\u0000\u0000\u0000\u0005\"bar\"Hello", embeddedString.Substring(1)); - MessageValues extracted = EmbeddedHeaderUtils.ExtractHeaders(IntegrationMessageBuilder.WithPayload(embedded).Build(), false); - - Assert.Equal("Hello", Encoding.UTF8.GetString((byte[])extracted.Payload)); - Assert.Equal("bar", extracted["foo"]); - Assert.Null(extracted["baz"]); - Assert.Equal("text/plain", extracted["contentType"]); - Assert.Null(extracted["timestamp"]); - - MessageValues extractedWithRequestHeaders = EmbeddedHeaderUtils.ExtractHeaders(IntegrationMessageBuilder.WithPayload(embedded).Build(), true); - Assert.Equal("bar", extractedWithRequestHeaders["foo"]); - Assert.Null(extractedWithRequestHeaders["baz"]); - Assert.Equal("text/plain", extractedWithRequestHeaders["contentType"]); - Assert.NotNull(extractedWithRequestHeaders["timestamp"]); - Assert.NotNull(extractedWithRequestHeaders["id"]); - Assert.IsType(extractedWithRequestHeaders["timestamp"]); - Assert.IsType(extractedWithRequestHeaders["id"]); - } - - [Fact] - public void TestHeaderExtractionWithDirectPayload() - { - IMessage message = IntegrationMessageBuilder.WithPayload(Encoding.UTF8.GetBytes("Hello")).SetHeader("foo", "bar") - .SetHeader("baz", "quxx").Build(); - - byte[] embedded = EmbeddedHeaderUtils.EmbedHeaders(new MessageValues(message), "foo", "baz"); - Assert.Equal(0xff, embedded[0]); - string embeddedString = Encoding.UTF8.GetString(embedded); - Assert.Equal("\u0002\u0003foo\u0000\u0000\u0000\u0005\"bar\"\u0003baz\u0000\u0000\u0000\u0006\"quxx\"Hello", embeddedString.Substring(1)); - - MessageValues extracted = EmbeddedHeaderUtils.ExtractHeaders(embedded); - Assert.Equal("Hello", Encoding.UTF8.GetString((byte[])extracted.Payload)); - Assert.Equal("bar", extracted["foo"]); - Assert.Equal("quxx", extracted["baz"]); - } - - [Fact] - public void TestUnicodeHeader() - { - IMessage message = IntegrationMessageBuilder.WithPayload(Encoding.UTF8.GetBytes("Hello")).SetHeader("foo", "bar") - .SetHeader("baz", "ØØØØØØØØ").Build(); - - byte[] embedded = EmbeddedHeaderUtils.EmbedHeaders(new MessageValues(message), "foo", "baz"); - Assert.Equal(0xff, embedded[0]); - string embeddedString = Encoding.UTF8.GetString(embedded); - Assert.Equal("\u0002\u0003foo\u0000\u0000\u0000\u0005\"bar\"\u0003baz\u0000\u0000\u0000\u0012\"ØØØØØØØØ\"Hello", embeddedString.Substring(1)); - - MessageValues extracted = EmbeddedHeaderUtils.ExtractHeaders(IntegrationMessageBuilder.WithPayload(embedded).Build(), false); - Assert.Equal("Hello", Encoding.UTF8.GetString((byte[])extracted.Payload)); - Assert.Equal("bar", extracted["foo"]); - Assert.Equal("ØØØØØØØØ", extracted["baz"]); - } - - [Fact] - public void TestHeaderEmbeddingMissingHeader() - { - IMessage message = IntegrationMessageBuilder.WithPayload(Encoding.UTF8.GetBytes("Hello")).SetHeader("foo", "bar").Build(); - byte[] embedded = EmbeddedHeaderUtils.EmbedHeaders(new MessageValues(message), "foo", "baz"); - Assert.Equal(0xff, embedded[0]); - string embeddedString = Encoding.UTF8.GetString(embedded); - Assert.Equal("\u0001\u0003foo\u0000\u0000\u0000\u0005\"bar\"Hello", embeddedString.Substring(1)); - } - - [Fact] - public void TestBadDecode() - { - byte[] bytes = - { - 0xff, - 99 - }; - - IMessage message = Message.Create(bytes); - Assert.Throws(() => EmbeddedHeaderUtils.ExtractHeaders(message, false)); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/PollableConsumerTest.cs b/src/Stream/test/Stream.Test/Binder/PollableConsumerTest.cs deleted file mode 100644 index 767694600f..0000000000 --- a/src/Stream/test/Stream.Test/Binder/PollableConsumerTest.cs +++ /dev/null @@ -1,603 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Integration; -using Steeltoe.Integration.Acks; -using Steeltoe.Integration.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Configuration; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class PollableConsumerTest : AbstractTest -{ - [Fact] - public void TestSimple() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - IServiceProvider serviceProvider = CreateStreamsContainer(searchDirectories).BuildServiceProvider(true); - var messageConverter = serviceProvider.GetService(); - Assert.NotNull(messageConverter); - - var binder = serviceProvider.GetService() as AbstractPollableMessageSourceBinder; - Assert.NotNull(binder); - - var configurer = serviceProvider.GetService(); - Assert.NotNull(configurer); - - var pollableSource = new DefaultPollableMessageSource(serviceProvider.GetService(), messageConverter); - configurer.ConfigurePolledMessageSource(pollableSource, "foo"); - pollableSource.AddInterceptor(new TestSimpleChannelInterceptor()); - - var properties = new ConsumerOptions - { - MaxAttempts = 2, - BackOffInitialInterval = 0 - }; - - properties.PostProcess("testbinding"); - - binder.BindConsumer("foo", "bar", pollableSource, properties); - var handler = new TestSimpleHandler(); - Assert.True(pollableSource.Poll(handler)); - - Assert.Equal(2, handler.Count); - } - - [Fact] - public void TestConvertSimple() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - IServiceProvider serviceProvider = CreateStreamsContainer(searchDirectories).BuildServiceProvider(true); - var messageConverter = serviceProvider.GetService(); - Assert.NotNull(messageConverter); - - var binder = serviceProvider.GetService() as AbstractPollableMessageSourceBinder; - Assert.NotNull(binder); - - var configurer = serviceProvider.GetService(); - Assert.NotNull(configurer); - - MethodInfo setter = binder.GetType().GetProperty("MessageSourceDelegate").GetSetMethod(); - var messageSource = new TestMessageSource("{\"foo\":\"bar\"}"); - - setter.Invoke(binder, new object[] - { - messageSource - }); - - var pollableSource = new DefaultPollableMessageSource(serviceProvider.GetService(), messageConverter); - configurer.ConfigurePolledMessageSource(pollableSource, "foo"); - - var properties = new ConsumerOptions - { - MaxAttempts = 2, - BackOffInitialInterval = 0 - }; - - properties.PostProcess("testbinding"); - - binder.BindConsumer("foo", "bar", pollableSource, properties); - var handler = new TestConvertSimpleHandler(); - Assert.True(pollableSource.Poll(handler, typeof(FooType))); - - Assert.IsType(handler.Payload); - var fooType = handler.Payload as FooType; - Assert.Equal("bar", fooType.Foo); - - Assert.True(pollableSource.Poll(handler, typeof(FooType))); - - Assert.IsType(handler.Payload); - fooType = handler.Payload as FooType; - Assert.Equal("bar", fooType.Foo); - } - - [Fact] - public void TestConvertSimpler() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - - IServiceProvider serviceProvider = CreateStreamsContainer(searchDirectories, "spring:cloud:stream:bindings:foo:contentType=text/plain") - .BuildServiceProvider(true); - - var messageConverter = serviceProvider.GetService(); - Assert.NotNull(messageConverter); - - var binder = serviceProvider.GetService() as AbstractPollableMessageSourceBinder; - Assert.NotNull(binder); - - var configurer = serviceProvider.GetService(); - Assert.NotNull(configurer); - - var options = serviceProvider.GetService>(); - options.Value.Bindings.TryGetValue("foo", out BindingOptions bindingOptions); - Assert.Equal("text/plain", bindingOptions.ContentType); - - MethodInfo setter = binder.GetType().GetProperty("MessageSourceDelegate").GetSetMethod(); - var messageSource = new TestMessageSource("foo"); - - setter.Invoke(binder, new object[] - { - messageSource - }); - - var pollableSource = new DefaultPollableMessageSource(serviceProvider.GetService(), messageConverter); - configurer.ConfigurePolledMessageSource(pollableSource, "foo"); - - var properties = new ConsumerOptions - { - MaxAttempts = 1, - BackOffInitialInterval = 0 - }; - - properties.PostProcess("testbinding"); - - binder.BindConsumer("foo", "bar", pollableSource, properties); - var handler = new TestConvertSimpleHandler(); - Assert.True(pollableSource.Poll(handler, typeof(string))); - - Assert.IsType(handler.Payload); - string str = handler.Payload as string; - Assert.Equal("foo", str); - - Assert.True(pollableSource.Poll(handler, typeof(string))); - - Assert.IsType(handler.Payload); - str = handler.Payload as string; - Assert.Equal("foo", str); - } - - [Fact] - public void TestConvertList() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - IServiceProvider serviceProvider = CreateStreamsContainer(searchDirectories).BuildServiceProvider(true); - var messageConverter = serviceProvider.GetService(); - Assert.NotNull(messageConverter); - - var binder = serviceProvider.GetService() as AbstractPollableMessageSourceBinder; - Assert.NotNull(binder); - - var configurer = serviceProvider.GetService(); - Assert.NotNull(configurer); - - MethodInfo setter = binder.GetType().GetProperty("MessageSourceDelegate").GetSetMethod(); - var messageSource = new TestMessageSource("[{\"foo\":\"bar\"},{\"foo\":\"baz\"}]"); - - setter.Invoke(binder, new object[] - { - messageSource - }); - - var pollableSource = new DefaultPollableMessageSource(serviceProvider.GetService(), messageConverter); - configurer.ConfigurePolledMessageSource(pollableSource, "foo"); - - var properties = new ConsumerOptions - { - MaxAttempts = 1, - BackOffInitialInterval = 0 - }; - - properties.PostProcess("testbinding"); - - binder.BindConsumer("foo", "bar", pollableSource, properties); - var handler = new TestConvertSimpleHandler(); - Assert.True(pollableSource.Poll(handler, typeof(List))); - - Assert.IsType>(handler.Payload); - var list = handler.Payload as List; - Assert.Equal("bar", list[0].Foo); - Assert.Equal("baz", list[1].Foo); - } - - [Fact] - public void TestConvertMap() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - IServiceProvider serviceProvider = CreateStreamsContainer(searchDirectories).BuildServiceProvider(true); - var messageConverter = serviceProvider.GetService(); - Assert.NotNull(messageConverter); - - var binder = serviceProvider.GetService() as AbstractPollableMessageSourceBinder; - Assert.NotNull(binder); - - var configurer = serviceProvider.GetService(); - Assert.NotNull(configurer); - - MethodInfo setter = binder.GetType().GetProperty("MessageSourceDelegate").GetSetMethod(); - var messageSource = new TestMessageSource("{\"qux\":{\"foo\":\"bar\"}}"); - - setter.Invoke(binder, new object[] - { - messageSource - }); - - var pollableSource = new DefaultPollableMessageSource(serviceProvider.GetService(), messageConverter); - configurer.ConfigurePolledMessageSource(pollableSource, "foo"); - - var properties = new ConsumerOptions - { - MaxAttempts = 1, - BackOffInitialInterval = 0 - }; - - properties.PostProcess("testbinding"); - - binder.BindConsumer("foo", "bar", pollableSource, properties); - var handler = new TestConvertSimpleHandler(); - Assert.True(pollableSource.Poll(handler, typeof(Dictionary))); - - Assert.IsType>(handler.Payload); - var dict = handler.Payload as Dictionary; - Assert.Single(dict); - Assert.Equal("bar", dict["qux"].Foo); - } - - [Fact] - public void TestEmbedded() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - - IServiceProvider serviceProvider = CreateStreamsContainer( - searchDirectories, $"spring:cloud:stream:bindings:foo:consumer:headerMode={HeaderMode.EmbeddedHeaders}").BuildServiceProvider(true); - - var messageConverter = serviceProvider.GetService(); - Assert.NotNull(messageConverter); - - var binder = serviceProvider.GetService() as AbstractPollableMessageSourceBinder; - Assert.NotNull(binder); - - var configurer = serviceProvider.GetService(); - Assert.NotNull(configurer); - - MethodInfo setter = binder.GetType().GetProperty("MessageSourceDelegate").GetSetMethod(); - - var messageSource = new TestFuncMessageSource(() => - { - var original = new MessageValues(Encoding.UTF8.GetBytes("foo"), new Dictionary - { - { MessageHeaders.ContentType, "application/octet-stream" } - }); - - byte[] payload = Array.Empty(); - - try - { - payload = EmbeddedHeaderUtils.EmbedHeaders(original, MessageHeaders.ContentType); - } - catch (Exception e) - { - Assert.NotNull(e); - } - - return Message.Create(payload); - }); - - setter.Invoke(binder, new object[] - { - messageSource - }); - - var options = serviceProvider.GetService>(); - options.Value.Bindings.TryGetValue("foo", out BindingOptions bindingOptions); - Assert.Equal(HeaderMode.EmbeddedHeaders, bindingOptions.Consumer.HeaderMode); - - var pollableSource = new DefaultPollableMessageSource(serviceProvider.GetService(), messageConverter); - configurer.ConfigurePolledMessageSource(pollableSource, "foo"); - pollableSource.AddInterceptor(new TestEmbeddedChannelInterceptor()); - - binder.BindConsumer("foo", "bar", pollableSource, bindingOptions.Consumer); - - var handler = new TestConvertSimpleHandler(); - Assert.True(pollableSource.Poll(handler)); - - Assert.IsType(handler.Payload); - string str = handler.Payload as string; - Assert.Equal("FOO", str); - handler.Message.Headers.TryGetValue(MessageHeaders.ContentType, out object contentType); - Assert.Equal("application/octet-stream", contentType.ToString()); - } - - [Fact] - public void TestErrors() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - IServiceProvider serviceProvider = CreateStreamsContainer(searchDirectories).BuildServiceProvider(true); - var messageConverter = serviceProvider.GetService(); - Assert.NotNull(messageConverter); - - var binder = serviceProvider.GetService() as AbstractPollableMessageSourceBinder; - Assert.NotNull(binder); - - var configurer = serviceProvider.GetService(); - Assert.NotNull(configurer); - - var pollableSource = new DefaultPollableMessageSource(serviceProvider.GetService(), messageConverter); - configurer.ConfigurePolledMessageSource(pollableSource, "foo"); - pollableSource.AddInterceptor(new TestSimpleChannelInterceptor()); - - var properties = new ConsumerOptions - { - MaxAttempts = 2, - BackOffInitialInterval = 0, - RetryableExceptions = new List - { - "!System.InvalidOperationException" - } - }; - - properties.PostProcess("testbinding"); - - var latch = new CountdownEvent(2); - binder.BindConsumer("foo", "bar", pollableSource, properties); - - var errorChan = - serviceProvider.GetServices().Single(chan => chan.ServiceName == IntegrationContextUtils.ErrorChannelBeanName) as - ISubscribableChannel; - - var errorChanHandler = new TestErrorsErrorChannelHandler(latch); - errorChan.Subscribe(errorChanHandler); - - var h1 = new TestFuncMessageHandler(_ => throw new Exception("test recoverer")); - - Assert.True(pollableSource.Poll(h1)); - Assert.Equal(2, h1.Count); - - MethodInfo getter = binder.GetType().GetProperty("LastError").GetGetMethod(); - - var lastError = getter.Invoke(binder, Array.Empty()) as IMessage; - Assert.NotNull(lastError); - - string lastErrorMessage = ((Exception)lastError.Payload).InnerException.Message; - Assert.Equal("test recoverer", lastErrorMessage); - - var h2 = new TestFuncMessageHandler(_ => throw new InvalidOperationException("no retries")); - - Assert.True(pollableSource.Poll(h2)); - Assert.Equal(1, h2.Count); - - lastError = getter.Invoke(binder, Array.Empty()) as IMessage; - lastErrorMessage = ((Exception)lastError.Payload).InnerException.Message; - Assert.Equal("no retries", lastErrorMessage); - } - - [Fact] - public void TestErrorsNoRetry() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - IServiceProvider serviceProvider = CreateStreamsContainer(searchDirectories).BuildServiceProvider(true); - var messageConverter = serviceProvider.GetService(); - Assert.NotNull(messageConverter); - - var binder = serviceProvider.GetService() as AbstractPollableMessageSourceBinder; - Assert.NotNull(binder); - - var configurer = serviceProvider.GetService(); - Assert.NotNull(configurer); - - var pollableSource = new DefaultPollableMessageSource(serviceProvider.GetService(), messageConverter); - configurer.ConfigurePolledMessageSource(pollableSource, "foo"); - pollableSource.AddInterceptor(new TestSimpleChannelInterceptor()); - - var properties = new ConsumerOptions - { - MaxAttempts = 1 - }; - - properties.PostProcess("testbinding"); - - var latch = new CountdownEvent(1); - binder.BindConsumer("foo", "bar", pollableSource, properties); - - var errorChan = - serviceProvider.GetServices().Single(chan => chan.ServiceName == IntegrationContextUtils.ErrorChannelBeanName) as - ISubscribableChannel; - - var errorChanHandler = new TestErrorsErrorChannelHandler(latch); - errorChan.Subscribe(errorChanHandler); - - var h1 = new TestFuncMessageHandler(_ => throw new Exception("test recoverer")); - - Assert.True(pollableSource.Poll(h1)); - Assert.Equal(1, h1.Count); - } - - [Fact] - public void TestRequeue() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - IServiceProvider serviceProvider = CreateStreamsContainer(searchDirectories).BuildServiceProvider(true); - var messageConverter = serviceProvider.GetService(); - Assert.NotNull(messageConverter); - - var binder = serviceProvider.GetService() as AbstractPollableMessageSourceBinder; - Assert.NotNull(binder); - - var configurer = serviceProvider.GetService(); - Assert.NotNull(configurer); - - var pollableSource = new DefaultPollableMessageSource(serviceProvider.GetService(), messageConverter); - configurer.ConfigurePolledMessageSource(pollableSource, "foo"); - - var mockCallback = new Mock(MockBehavior.Default); - - pollableSource.AddInterceptor(new TestRequeueChannelInterceptor(mockCallback)); - - var properties = new ConsumerOptions - { - MaxAttempts = 2, - BackOffInitialInterval = 0 - }; - - properties.PostProcess("testbinding"); - - binder.BindConsumer("foo", "bar", pollableSource, properties); - var h1 = new TestFuncMessageHandler(_ => throw new RequeueCurrentMessageException("test retry")); - - Assert.True(pollableSource.Poll(h1)); - Assert.Equal(2, h1.Count); - mockCallback.Verify(call => call.Acknowledge(Status.Requeue)); - } - - private sealed class TestErrorsErrorChannelHandler : IMessageHandler - { - private readonly CountdownEvent _latch; - - public string ServiceName { get; set; } - - public TestErrorsErrorChannelHandler(CountdownEvent latch) - { - _latch = latch; - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - _latch.Signal(); - } - } - - private sealed class TestFuncMessageHandler : IMessageHandler - { - public int Count { get; set; } - - public string ServiceName { get; set; } - - public Action Act { get; } - - public TestFuncMessageHandler(Action action) - { - Act = action; - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - Count++; - Act(message); - } - } - - private sealed class TestFuncMessageSource : IMessageSource - { - public Func Func { get; } - - public TestFuncMessageSource(Func func) - { - Func = func; - } - - public IMessage Receive() - { - return Func(); - } - } - - private sealed class TestMessageSource : IMessageSource - { - private readonly string _payload; - - public TestMessageSource(string payload) - { - _payload = payload; - } - - public IMessage Receive() - { - return Message.Create(Encoding.UTF8.GetBytes(_payload)); - } - } - - private sealed class TestConvertSimpleHandler : IMessageHandler - { - public object Payload { get; set; } - - public IMessage Message { get; set; } - - public string ServiceName { get; set; } - - public TestConvertSimpleHandler() - { - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - Message = message; - Payload = message.Payload; - } - } - - private sealed class TestSimpleHandler : IMessageHandler - { - public int Count { get; set; } - - public string ServiceName { get; set; } - - public TestSimpleHandler() - { - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - Assert.Equal("POLLED DATA", message.Payload); - object contentType = message.Headers[MessageHeaders.ContentType]; - Assert.Equal("text/plain", contentType.ToString()); - Count++; - - if (Count == 1) - { - throw new Exception("test retry"); - } - } - } - - private sealed class TestRequeueChannelInterceptor : AbstractChannelInterceptor - { - public Mock Mock { get; } - - public TestRequeueChannelInterceptor(Mock mock) - { - Mock = mock; - } - - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - return MessageBuilder.FromMessage(message).SetHeader(IntegrationMessageHeaderAccessor.AcknowledgmentCallback, Mock.Object).Build(); - } - } - - private sealed class TestEmbeddedChannelInterceptor : AbstractChannelInterceptor - { - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - return MessageBuilder.WithPayload(Encoding.UTF8.GetString((byte[])message.Payload).ToUpperInvariant()).CopyHeaders(message.Headers).Build(); - } - } - - private sealed class TestSimpleChannelInterceptor : AbstractChannelInterceptor - { - public override IMessage PreSend(IMessage message, IMessageChannel channel) - { - return MessageBuilder.WithPayload(((string)message.Payload).ToUpperInvariant()).CopyHeaders(message.Headers).Build(); - } - } - - internal sealed class FooType - { - public string Foo { get; set; } - } -} diff --git a/src/Stream/test/Stream.Test/Binder/ProcessorBindingWithBindingTargetsTest.cs b/src/Stream/test/Stream.Test/Binder/ProcessorBindingWithBindingTargetsTest.cs deleted file mode 100644 index 94281e9e27..0000000000 --- a/src/Stream/test/Stream.Test/Binder/ProcessorBindingWithBindingTargetsTest.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class ProcessorBindingWithBindingTargetsTest : AbstractTest -{ - [Fact] - public async Task TestSourceOutputChannelBound() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithIProcessorBinding(searchDirectories, "spring:cloud:stream:defaultBinder=mock", - "spring.cloud.stream.bindings.input.destination=testtock.0", "spring.cloud.stream.bindings.output.destination=testtock.1") - .BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Assert.NotNull(binder); - - var processor = provider.GetService(); - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindConsumer("testtock.0", null, processor.Input, It.IsAny())); - mock.Verify(b => b.BindProducer("testtock.1", processor.Output, It.IsAny())); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/ProcessorBindingsWithDefaultsTest.cs b/src/Stream/test/Stream.Test/Binder/ProcessorBindingsWithDefaultsTest.cs deleted file mode 100644 index 9c626b1e63..0000000000 --- a/src/Stream/test/Stream.Test/Binder/ProcessorBindingsWithDefaultsTest.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class ProcessorBindingsWithDefaultsTest : AbstractTest -{ - [Fact] - public async Task TestSourceOutputChannelBound() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring:cloud:stream:defaultBinder=mock") - .BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Assert.NotNull(binder); - - var processor = provider.GetService(); - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindConsumer("input", null, processor.Input, It.IsAny())); - mock.Verify(b => b.BindProducer("output", processor.Output, It.IsAny())); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/SinkBindingWithDefaultTargetsTest.cs b/src/Stream/test/Stream.Test/Binder/SinkBindingWithDefaultTargetsTest.cs deleted file mode 100644 index b93e224aeb..0000000000 --- a/src/Stream/test/Stream.Test/Binder/SinkBindingWithDefaultTargetsTest.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class SinkBindingWithDefaultTargetsTest : AbstractTest -{ - [Fact] - public async Task TestSourceOutputChannelBound() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithISinkBinding(searchDirectories, "spring:cloud:stream:defaultBinder=mock", - "spring.cloud.stream.bindings.input.destination=testtock").BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Assert.NotNull(binder); - - var sink = provider.GetService(); - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindConsumer("testtock", null, sink.Input, It.IsAny())); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/SinkBindingWithDefaultsTest.cs b/src/Stream/test/Stream.Test/Binder/SinkBindingWithDefaultsTest.cs deleted file mode 100644 index 9651b7d2be..0000000000 --- a/src/Stream/test/Stream.Test/Binder/SinkBindingWithDefaultsTest.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class SinkBindingWithDefaultsTest : AbstractTest -{ - [Fact] - public async Task TestSourceOutputChannelBound() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithISinkBinding(searchDirectories, "spring:cloud:stream:defaultBinder=mock") - .BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Assert.NotNull(binder); - - var sink = provider.GetService(); - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindConsumer("input", null, sink.Input, It.IsAny())); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/SourceBindingWithBindingTargetsTest.cs b/src/Stream/test/Stream.Test/Binder/SourceBindingWithBindingTargetsTest.cs deleted file mode 100644 index f3c075662b..0000000000 --- a/src/Stream/test/Stream.Test/Binder/SourceBindingWithBindingTargetsTest.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class SourceBindingWithBindingTargetsTest : AbstractTest -{ - [Fact] - public async Task TestSourceOutputChannelBound() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithISourceBinding(searchDirectories, "spring:cloud:stream:defaultBinder=mock", - "spring.cloud.stream.bindings.output.destination=testtock").BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Assert.NotNull(binder); - - var source = provider.GetService(); - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindProducer("testtock", source.Output, It.IsAny())); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/SourceBindingWithDefaultsTest.cs b/src/Stream/test/Stream.Test/Binder/SourceBindingWithDefaultsTest.cs deleted file mode 100644 index de26a51e3b..0000000000 --- a/src/Stream/test/Stream.Test/Binder/SourceBindingWithDefaultsTest.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class SourceBindingWithDefaultsTest : AbstractTest -{ - [Fact] - public async Task TestSourceOutputChannelBound() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithISourceBinding(searchDirectories, "spring:cloud:stream:defaultBinder=mock") - .BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Assert.NotNull(binder); - - var source = provider.GetService(); - Mock mock = Mock.Get(binder); - mock.Verify(b => b.BindProducer("output", source.Output, It.IsAny())); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/SourceBindingWithGlobalPropertiesOnlyTest.cs b/src/Stream/test/Stream.Test/Binder/SourceBindingWithGlobalPropertiesOnlyTest.cs deleted file mode 100644 index 39bb2a5d59..0000000000 --- a/src/Stream/test/Stream.Test/Binder/SourceBindingWithGlobalPropertiesOnlyTest.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class SourceBindingWithGlobalPropertiesOnlyTest : AbstractTest -{ - [Fact] - public async Task TestGlobalPropertiesSet() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - - ServiceProvider provider = CreateStreamsContainerWithISourceBinding(searchDirectories, "spring.cloud.stream.default.contentType=application/json", - "spring.cloud.stream.default.producer.partitionKeyExpression=key").BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Assert.NotNull(binder); - - var bindingServiceProperties = provider.GetService>(); - Assert.NotNull(bindingServiceProperties.Value); - BindingOptions bindingProperties = bindingServiceProperties.Value.GetBindingOptions("output"); - Assert.NotNull(bindingProperties); - Assert.Equal("application/json", bindingProperties.ContentType); - Assert.NotNull(bindingProperties.Producer); - Assert.Equal("key", bindingProperties.Producer.PartitionKeyExpression); - } -} diff --git a/src/Stream/test/Stream.Test/Binder/SourceBindingWithGlobalPropertiesTest.cs b/src/Stream/test/Stream.Test/Binder/SourceBindingWithGlobalPropertiesTest.cs deleted file mode 100644 index 5fba95e486..0000000000 --- a/src/Stream/test/Stream.Test/Binder/SourceBindingWithGlobalPropertiesTest.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Xunit; - -namespace Steeltoe.Stream.Test.Binder; - -public sealed class SourceBindingWithGlobalPropertiesTest : AbstractTest -{ - [Fact] - public async Task TestGlobalPropertiesSet() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.default.contentType=application/json", - "spring.cloud.stream.bindings.output.destination=ticktock", "spring.cloud.stream.default.producer.requiredGroups:0=someGroup", - "spring.cloud.stream.default.producer.partitionCount=1", "spring.cloud.stream.bindings.output.producer.headerMode=none", - "spring.cloud.stream.bindings.output.producer.partitionCount=4", "spring.cloud.stream.defaultBinder=mock").BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Assert.NotNull(binder); - - var bindingServiceProperties = provider.GetService>(); - Assert.NotNull(bindingServiceProperties.Value); - BindingOptions bindingProperties = bindingServiceProperties.Value.GetBindingOptions("output"); - Assert.NotNull(bindingProperties); - Assert.Equal("application/json", bindingProperties.ContentType); - Assert.Equal("ticktock", bindingProperties.Destination); - Assert.NotNull(bindingProperties.Producer); - Assert.Single(bindingProperties.Producer.RequiredGroups); - Assert.Contains("someGroup", bindingProperties.Producer.RequiredGroups); - Assert.Equal(4, bindingProperties.Producer.PartitionCount); - Assert.Equal(HeaderMode.None, bindingProperties.Producer.HeaderMode); - } -} diff --git a/src/Stream/test/Stream.Test/Binding/BindableProxyGeneratorTest.cs b/src/Stream/test/Stream.Test/Binding/BindableProxyGeneratorTest.cs deleted file mode 100644 index 3e3928175c..0000000000 --- a/src/Stream/test/Stream.Test/Binding/BindableProxyGeneratorTest.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Binding; - -public sealed class BindableProxyGeneratorTest -{ - [Fact] - public void GenerateProxy_ThrowsOnNulls() - { - Assert.Throws(() => BindingProxyGenerator.GenerateProxy(null)); - } - - [Fact] - public void GenerateProxy_ForISink_CreatesProxy_CallsFactory() - { - var bindableFactory = new TestBindableFactory(typeof(ISink)); - var proxy = BindingProxyGenerator.GenerateProxy(bindableFactory) as ISink; - Assert.NotNull(proxy); - _ = proxy.Input; - Assert.NotNull(bindableFactory.Method); - Assert.Equal("get_Input", bindableFactory.Method.Name); - } - - [Fact] - public void GenerateProxy_ForISource_CreatesProxy_CallsFactory() - { - var bindableFactory = new TestBindableFactory(typeof(ISource)); - var proxy = BindingProxyGenerator.GenerateProxy(bindableFactory) as ISource; - Assert.NotNull(proxy); - _ = proxy.Output; - Assert.NotNull(bindableFactory.Method); - Assert.Equal("get_Output", bindableFactory.Method.Name); - } - - [Fact] - public void GenerateProxy_ForIProcessor_CreatesProxy_CallsFactory() - { - var bindableFactory = new TestBindableFactory(typeof(IProcessor)); - var proxy = BindingProxyGenerator.GenerateProxy(bindableFactory) as IProcessor; - Assert.NotNull(proxy); - _ = proxy.Output; - Assert.NotNull(bindableFactory.Method); - Assert.Equal("get_Output", bindableFactory.Method.Name); - _ = proxy.Input; - Assert.NotNull(bindableFactory.Method); - Assert.Equal("get_Input", bindableFactory.Method.Name); - } - - [Fact] - public void GenerateProxy_ForIBarista_CreatesProxy_CallsFactory() - { - var bindableFactory = new TestBindableFactory(typeof(IBarista)); - var proxy = BindingProxyGenerator.GenerateProxy(bindableFactory) as IBarista; - Assert.NotNull(proxy); - _ = proxy.ColdDrinks(); - Assert.NotNull(bindableFactory.Method); - Assert.Equal("ColdDrinks", bindableFactory.Method.Name); - _ = proxy.HotDrinks(); - Assert.NotNull(bindableFactory.Method); - Assert.Equal("HotDrinks", bindableFactory.Method.Name); - _ = proxy.Orders(); - Assert.NotNull(bindableFactory.Method); - Assert.Equal("Orders", bindableFactory.Method.Name); - } - - private sealed class TestBindableFactory : IBindableProxyFactory - { - public MethodInfo Method { get; private set; } - - public Type BindingType { get; } - - public TestBindableFactory(Type bindingType) - { - BindingType = bindingType; - } - - public object Invoke(MethodInfo info) - { - Method = info; - return null; - } - } -} diff --git a/src/Stream/test/Stream.Test/Binding/BindingLifecycleTests.cs b/src/Stream/test/Stream.Test/Binding/BindingLifecycleTests.cs deleted file mode 100644 index 9adb63b3a9..0000000000 --- a/src/Stream/test/Stream.Test/Binding/BindingLifecycleTests.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Moq; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Binding; -using Xunit; - -namespace Steeltoe.Stream.Test.Binding; - -public sealed class BindingLifecycleTests -{ - [Fact] - public async Task TestInputBindingLifecycle() - { - var bindables = new Dictionary(); - - IBindable bindableWithTwo = new TestBindable(new Mock(), new Mock()); - IBindable bindableWithThree = new TestBindable(new Mock(), new Mock(), new Mock()); - IBindable bindableEmpty = new TestBindable(); - - bindables.Add("two", bindableWithTwo); - bindables.Add("empty", bindableEmpty); - bindables.Add("three", bindableWithThree); - - var lifecycle = new InputBindingLifecycle(new Mock().Object, bindables.Values); - await lifecycle.StartAsync(); - - Assert.Equal(5, lifecycle.InputBindings.Count); - - await lifecycle.StopAsync(); - } - - [Fact] - public async Task TestOutputBindingLifecycle() - { - var bindables = new Dictionary(); - - IBindable bindableWithTwo = new TestBindable(new Mock(), new Mock()); - IBindable bindableWithThree = new TestBindable(new Mock(), new Mock(), new Mock()); - IBindable bindableEmpty = new TestBindable(); - - bindables.Add("two", bindableWithTwo); - bindables.Add("empty", bindableEmpty); - bindables.Add("three", bindableWithThree); - - var lifecycle = new OutputBindingLifecycle(new Mock().Object, bindables.Values); - await lifecycle.StartAsync(); - - Assert.Equal(5, lifecycle.OutputBindings.Count); - - await lifecycle.StopAsync(); - } - - private sealed class TestBindable : IBindable - { - private readonly List _bindings = new(); - - public Type BindingType => throw new NotImplementedException(); - - public ICollection Inputs => throw new NotImplementedException(); - - public ICollection Outputs => throw new NotImplementedException(); - - public TestBindable(params Mock[] bindings) - { - foreach (Mock mocks in bindings) - { - _bindings.Add(mocks.Object); - } - } - - public ICollection CreateAndBindInputs(IBindingService bindingService) - { - return _bindings; - } - - public ICollection CreateAndBindOutputs(IBindingService bindingService) - { - return _bindings; - } - - public object GetBoundInputTarget(string name) - { - throw new NotImplementedException(); - } - - public object GetBoundOutputTarget(string name) - { - throw new NotImplementedException(); - } - - public object GetBoundTarget(string name) - { - throw new NotImplementedException(); - } - - public void UnbindInputs(IBindingService bindingService) - { - } - - public void UnbindOutputs(IBindingService bindingService) - { - } - } -} diff --git a/src/Stream/test/Stream.Test/Binding/BindingServiceTest.cs b/src/Stream/test/Stream.Test/Binding/BindingServiceTest.cs deleted file mode 100644 index 1bb89035a1..0000000000 --- a/src/Stream/test/Stream.Test/Binding/BindingServiceTest.cs +++ /dev/null @@ -1,363 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Moq; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Util; -using Xunit; - -namespace Steeltoe.Stream.Test.Binding; - -public sealed class BindingServiceTest : AbstractTest -{ - [Fact] - public void TestDefaultGroup() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "spring.cloud.stream.bindings.input.destination=foo").BuildServiceProvider(true); - - var factory = provider.GetService(); - IBinder binder = factory.GetBinder("mock"); - - IMessageChannel inputChannel = new DirectChannel(provider.GetService()); - Mock mockBinder = Mock.Get(binder); - - var service = provider.GetService(); - ICollection bindings = service.BindConsumer(inputChannel, "input"); - - Assert.Single(bindings); - IBinding binding = bindings.First(); - Mock mockBinding = Mock.Get(binding); - - service.UnbindConsumers("input"); - - mockBinder.Verify(b => b.BindConsumer("foo", null, inputChannel, It.IsAny())); - mockBinding.Verify(b => b.UnbindAsync()); - } - - [Fact] - public void TestMultipleConsumerBindings() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "spring.cloud.stream.bindings.input.destination=foo,bar") - .BuildServiceProvider(true); - - var factory = provider.GetService(); - IBinder binder = factory.GetBinder("mock"); - - IMessageChannel inputChannel = new DirectChannel(provider.GetService()); - Mock mockBinder = Mock.Get(binder); - - var service = provider.GetService(); - ICollection bindings = service.BindConsumer(inputChannel, "input"); - - List bindingsAsList = bindings.ToList(); - - Assert.Equal(2, bindingsAsList.Count); - IBinding binding1 = bindingsAsList[0]; - IBinding binding2 = bindingsAsList[1]; - Mock mockBinding1 = Mock.Get(binding1); - Mock mockBinding2 = Mock.Get(binding2); - - service.UnbindConsumers("input"); - - mockBinder.Verify(b => b.BindConsumer("foo", null, inputChannel, It.IsAny())); - mockBinder.Verify(b => b.BindConsumer("bar", null, inputChannel, It.IsAny())); - - mockBinding1.Verify(b => b.UnbindAsync()); - mockBinding2.Verify(b => b.UnbindAsync()); - } - - [Fact] - public void TestConsumerBindingWhenMultiplexingIsEnabled() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "spring.cloud.stream.bindings.input.destination=foo,bar", - "spring.cloud.stream.bindings.input.consumer.multiplex=true").BuildServiceProvider(true); - - var factory = provider.GetService(); - IBinder binder = factory.GetBinder("mock"); - - IMessageChannel inputChannel = new DirectChannel(provider.GetService()); - Mock mockBinder = Mock.Get(binder); - - var service = provider.GetService(); - ICollection bindings = service.BindConsumer(inputChannel, "input"); - - Assert.Single(bindings); - IBinding binding = bindings.First(); - Mock mockBinding = Mock.Get(binding); - - service.UnbindConsumers("input"); - - mockBinder.Verify(b => b.BindConsumer("foo,bar", null, inputChannel, It.IsAny())); - mockBinding.Verify(b => b.UnbindAsync()); - } - - [Fact] - public void TestExplicitGroup() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "spring.cloud.stream.bindings.input.destination=foo", - "spring.cloud.stream.bindings.input.group=fooGroup").BuildServiceProvider(true); - - var factory = provider.GetService(); - IBinder binder = factory.GetBinder("mock"); - - IMessageChannel inputChannel = new DirectChannel(provider.GetService()); - Mock mockBinder = Mock.Get(binder); - - var service = provider.GetService(); - ICollection bindings = service.BindConsumer(inputChannel, "input"); - - Assert.Single(bindings); - IBinding binding = bindings.First(); - Mock mockBinding = Mock.Get(binding); - - service.UnbindConsumers("input"); - - mockBinder.Verify(b => b.BindConsumer("foo", "fooGroup", inputChannel, It.IsAny())); - mockBinding.Verify(b => b.UnbindAsync()); - } - - [Fact] - public void TestProducerPropertiesValidation() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "spring.cloud.stream.bindings.output.destination=foo", - "spring.cloud.stream.bindings.output.producer.partitionCount=0").BuildServiceProvider(true); - - IMessageChannel outputChannel = new DirectChannel(provider.GetService()); - - var service = provider.GetService(); - Assert.Throws(() => service.BindProducer(outputChannel, "output")); - } - - [Fact] - public async Task TestDefaultPropertyBehavior() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceCollection container = CreateStreamsContainerWithBinding(searchDirectories, typeof(IFooBinding), - "spring.cloud.stream.default.contentType=text/plain", "spring.cloud.stream.bindings.input1.contentType=application/json", - "spring.cloud.stream.default.group=foo", "spring.cloud.stream.bindings.input2.group=bar", "spring.cloud.stream.default.consumer.concurrency=5", - "spring.cloud.stream.bindings.input2.consumer.concurrency=1", "spring.cloud.stream.bindings.input1.consumer.partitioned=true", - "spring.cloud.stream.default.producer.partitionCount=10", "spring.cloud.stream.bindings.output2.producer.partitionCount=1", - "spring.cloud.stream.bindings.inputXyz.contentType=application/json", "spring.cloud.stream.bindings.inputFooBar.contentType=application/avro", - "spring.cloud.stream.bindings.input_snake_case.contentType=application/avro"); - - ServiceProvider provider = container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var bindServiceOptions = provider.GetService>(); - Dictionary bindings = bindServiceOptions.CurrentValue.Bindings; - - Assert.Equal("application/json", bindings["input1"].ContentType); - Assert.Equal("text/plain", bindings["input2"].ContentType); - Assert.Equal("foo", bindings["input1"].Group); - Assert.Equal("bar", bindings["input2"].Group); - Assert.Equal(5, bindings["input1"].Consumer.Concurrency); - Assert.Equal(1, bindings["input2"].Consumer.Concurrency); - Assert.True(bindings["input1"].Consumer.Partitioned); - Assert.False(bindings["input2"].Consumer.Partitioned); - Assert.Equal(10, bindings["output1"].Producer.PartitionCount); - Assert.Equal(1, bindings["output2"].Producer.PartitionCount); - Assert.Equal("application/json", bindings["inputXyz"].ContentType); - Assert.Equal("application/avro", bindings["inputFooBar"].ContentType); - Assert.Equal("text/plain", bindings["inputFooBarBuzz"].ContentType); - Assert.Equal("application/avro", bindings["input_snake_case"].ContentType); - } - - [Fact] - public void TestConsumerPropertiesValidation() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "spring.cloud.stream.bindings.input.destination=foo", - "spring.cloud.stream.bindings.input.consumer.concurrency=0").BuildServiceProvider(true); - - IMessageChannel inputChannel = new DirectChannel(provider.GetService()); - - var service = provider.GetService(); - Assert.Throws(() => service.BindConsumer(inputChannel, "input")); - } - - [Fact] - public void TestUnknownBinderOnBindingFailure() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainer(searchDirectories, "spring.cloud.stream.bindings.input.destination=fooInput", - "spring.cloud.stream.bindings.input.binder=mock", "spring.cloud.stream.bindings.output.destination=fooOutput", - "spring.cloud.stream.bindings.output.binder=mockError").BuildServiceProvider(true); - - IMessageChannel inputChannel = new DirectChannel(provider.GetService()); - IMessageChannel outputChannel = new DirectChannel(provider.GetService()); - - var service = provider.GetService(); - service.BindConsumer(inputChannel, "input"); - - Assert.Throws(() => service.BindProducer(outputChannel, "output")); - } - - [Fact] - public void TestResolveBindableType() - { - Type bindableType = GenericsUtils.GetParameterType(typeof(FooBinder), typeof(IBinder<>), 0); - Assert.Same(typeof(SomeBindableType), bindableType); - } - - [Fact] - public void TestLateBindingConsumer() - { - List searchDirectories = GetSearchDirectories("StubBinder1"); - - ServiceProvider provider = CreateStreamsContainer( - searchDirectories, "spring.cloud.stream.bindings.input.destination=foo", "spring.cloud.stream.bindingretryinterval=1").BuildServiceProvider(true); - - var service = provider.GetService(); - - var factory = provider.GetService(); - IBinder binder = factory.GetBinder("binder1"); - PropertyInfo prop = binder.GetType().GetProperty("BindConsumerFunc"); - var fail = new CountdownEvent(2); - var mockBinding = new Mock(); - - Func func = (_, _, _, _) => - { - fail.Signal(); - - if (fail.CurrentCount == 1) - { - throw new Exception("fail"); - } - - return mockBinding.Object; - }; - - prop.GetSetMethod().Invoke(binder, new object[] - { - func - }); - - IMessageChannel inputChannel = new DirectChannel(provider.GetService()); - ICollection bindings = service.BindConsumer(inputChannel, "input"); - Assert.True(fail.IsSet); - Assert.Single(bindings); - Assert.Same(mockBinding.Object, bindings.Single()); - service.UnbindConsumers("input"); - mockBinding.Verify(b => b.UnbindAsync()); - } - - [Fact] - public void TestLateBindingProducer() - { - List searchDirectories = GetSearchDirectories("StubBinder1"); - - ServiceProvider provider = CreateStreamsContainer( - searchDirectories, "spring.cloud.stream.bindings.output.destination=foo", "spring.cloud.stream.bindingretryinterval=1").BuildServiceProvider(true); - - var service = provider.GetService(); - - var factory = provider.GetService(); - IBinder binder = factory.GetBinder("binder1"); - - var fail = new CountdownEvent(2); - var mockBinding = new Mock(); - PropertyInfo prop = binder.GetType().GetProperty("BindProducerFunc"); - - Func func = (_, _, _) => - { - fail.Signal(); - - if (fail.CurrentCount == 1) - { - throw new Exception("fail"); - } - - return mockBinding.Object; - }; - - prop.GetSetMethod().Invoke(binder, new object[] - { - func - }); - - IMessageChannel inputChannel = new DirectChannel(provider.GetService()); - IBinding binding = service.BindProducer(inputChannel, "output"); - - Assert.True(fail.IsSet); - Assert.NotNull(binding); - Assert.Same(mockBinding.Object, binding); - service.UnbindProducers("output"); - mockBinding.Verify(b => b.UnbindAsync()); - } - - [Fact] - public async Task TestBindingAutoStartup() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - - ServiceProvider provider = CreateStreamsContainerWithISinkBinding(searchDirectories, "spring.cloud.stream.bindings.input.consumer.autostartup=false") - .BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var service = provider.GetService(); - IDictionary> bindings = service.ConsumerBindings; - bindings.TryGetValue("input", out List inputBindings); - Assert.Single(inputBindings); - IBinding binding = inputBindings[0]; - Assert.False(binding.IsRunning); - } - - private sealed class FooBinder : IBinder - { - public string ServiceName { get; set; } = "foobinder"; - - public Type TargetType => typeof(SomeBindableType); - - public IBinding BindConsumer(string name, string group, SomeBindableType inboundTarget, IConsumerOptions consumerOptions) - { - throw new InvalidOperationException(); - } - - public IBinding BindConsumer(string name, string group, object inboundTarget, IConsumerOptions consumerOptions) - { - throw new InvalidOperationException(); - } - - public IBinding BindProducer(string name, SomeBindableType outboundTarget, IProducerOptions producerOptions) - { - throw new InvalidOperationException(); - } - - public IBinding BindProducer(string name, object outboundTarget, IProducerOptions producerOptions) - { - throw new InvalidOperationException(); - } - - public void Dispose() - { - } - } - - private sealed class SomeBindableType - { - } -} diff --git a/src/Stream/test/Stream.Test/Binding/CustomPartitionedProducerTest.cs b/src/Stream/test/Stream.Test/Binding/CustomPartitionedProducerTest.cs deleted file mode 100644 index 2ea2b105d0..0000000000 --- a/src/Stream/test/Stream.Test/Binding/CustomPartitionedProducerTest.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Messaging; -using Steeltoe.Stream.Test.Partitioning; -using Xunit; - -namespace Steeltoe.Stream.Test.Binding; - -public sealed class CustomPartitionedProducerTest : AbstractTest -{ - [Fact] - public async Task TestCustomPartitionedProducerByName() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceCollection container = CreateStreamsContainerWithISourceBinding(searchDirectories, "spring.cloud.stream.bindings.output.destination=partOut", - "spring.cloud.stream.bindings.output.producer.partitionCount=3", - "spring.cloud.stream.bindings.output.producer.partitionKeyExtractorName=CustomPartitionKeyExtractorClass", - "spring.cloud.stream.bindings.output.producer.partitionSelectorName=CustomPartitionSelectorClass", "spring.cloud.stream.defaultbinder=mock"); - - container.AddSingleton(); - container.AddSingleton(); - - ServiceProvider provider = container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var source = provider.GetService(); - var messageChannel = source.Output as DirectChannel; - bool foundPartInterceptor = false; - - foreach (IChannelInterceptor interceptor in messageChannel.ChannelInterceptors) - { - if (interceptor is PartitioningInterceptor partInterceptor) - { - foundPartInterceptor = true; - Assert.NotNull(partInterceptor.PartitionHandler); - Assert.IsType(partInterceptor.PartitionHandler.PartitionKeyExtractorStrategy); - Assert.IsType(partInterceptor.PartitionHandler.PartitionSelectorStrategy); - } - } - - Assert.True(foundPartInterceptor); - - await provider.GetRequiredService().StopAsync(); - } - - [Fact] - public async Task TestCustomPartitionedProducerAsSingletons() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceCollection container = CreateStreamsContainerWithISourceBinding(searchDirectories, "spring.cloud.stream.bindings.output.destination=partOut", - "spring.cloud.stream.bindings.output.producer.partitionCount=3", "spring.cloud.stream.defaultbinder=mock"); - - container.AddSingleton(); - container.AddSingleton(); - - ServiceProvider provider = container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var source = provider.GetService(); - var messageChannel = source.Output as DirectChannel; - bool foundPartInterceptor = false; - - foreach (IChannelInterceptor interceptor in messageChannel.ChannelInterceptors) - { - if (interceptor is PartitioningInterceptor partInterceptor) - { - foundPartInterceptor = true; - Assert.NotNull(partInterceptor.PartitionHandler); - Assert.IsType(partInterceptor.PartitionHandler.PartitionKeyExtractorStrategy); - Assert.IsType(partInterceptor.PartitionHandler.PartitionSelectorStrategy); - } - } - - Assert.True(foundPartInterceptor); - await provider.GetRequiredService().StopAsync(); - } - - [Fact] - public async Task TestCustomPartitionedProducerMultipleInstances() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceCollection container = CreateStreamsContainerWithISourceBinding(searchDirectories, "spring.cloud.stream.bindings.output.destination=partOut", - "spring.cloud.stream.bindings.output.producer.partitionCount=3", - "spring.cloud.stream.bindings.output.producer.partitionKeyExtractorName=CustomPartitionKeyExtractorClassOne", - "spring.cloud.stream.bindings.output.producer.partitionSelectorName=CustomPartitionSelectorClassTwo", "spring.cloud.stream.defaultbinder=mock"); - - container.AddSingleton(); - container.AddSingleton(); - - container.AddSingleton(); - container.AddSingleton(); - - ServiceProvider provider = container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var source = provider.GetService(); - var messageChannel = source.Output as DirectChannel; - bool foundPartInterceptor = false; - - foreach (IChannelInterceptor interceptor in messageChannel.ChannelInterceptors) - { - if (interceptor is PartitioningInterceptor partInterceptor) - { - foundPartInterceptor = true; - Assert.NotNull(partInterceptor.PartitionHandler); - Assert.IsType(partInterceptor.PartitionHandler.PartitionKeyExtractorStrategy); - Assert.IsType(partInterceptor.PartitionHandler.PartitionSelectorStrategy); - } - } - - Assert.True(foundPartInterceptor); - - await provider.GetRequiredService().StopAsync(); - } - - [Fact] - public async Task TestCustomPartitionedProducerMultipleInstancesFailNoFilter() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceCollection container = CreateStreamsContainerWithISourceBinding(searchDirectories, "spring.cloud.stream.bindings.output.destination=partOut", - "spring.cloud.stream.bindings.output.producer.partitionCount=3", "spring.cloud.stream.defaultbinder=mock"); - - container.AddSingleton(); - container.AddSingleton(); - - container.AddSingleton(); - container.AddSingleton(); - - ServiceProvider provider = container.BuildServiceProvider(true); - - bool exceptionThrown = false; - - try - { - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - } - catch (Exception) - { - exceptionThrown = true; - } - - Assert.True(exceptionThrown); - } -} diff --git a/src/Stream/test/Stream.Test/Binding/IFooBinding.cs b/src/Stream/test/Stream.Test/Binding/IFooBinding.cs deleted file mode 100644 index ed847043b3..0000000000 --- a/src/Stream/test/Stream.Test/Binding/IFooBinding.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Test.Binding; - -public interface IFooBinding -{ - [Input("input1")] - ISubscribableChannel In1(); - - [Input("input2")] - ISubscribableChannel In2(); - - [Output("output1")] - IMessageChannel Out1(); - - [Output("output2")] - IMessageChannel Out2(); - - [Input("inputXyz")] - ISubscribableChannel InXyz(); - - [Input("inputFooBar")] - ISubscribableChannel InFooBar(); - - [Input("inputFooBarBuzz")] - ISubscribableChannel InFooBarBuzz(); - - [Input("input_snake_case")] - ISubscribableChannel InWithSnakeCase(); -} diff --git a/src/Stream/test/Stream.Test/Binding/ITestInvalidBinding.cs b/src/Stream/test/Stream.Test/Binding/ITestInvalidBinding.cs deleted file mode 100644 index 3f1c70e496..0000000000 --- a/src/Stream/test/Stream.Test/Binding/ITestInvalidBinding.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Test.Binding; - -public interface ITestInvalidBinding -{ - [Input("testname")] - ISubscribableChannel In { get; } - - [Output("testname")] - IMessageChannel Out { get; } -} diff --git a/src/Stream/test/Stream.Test/Binding/InvalidBindingConfigurationTest.cs b/src/Stream/test/Stream.Test/Binding/InvalidBindingConfigurationTest.cs deleted file mode 100644 index f9b3f9d461..0000000000 --- a/src/Stream/test/Stream.Test/Binding/InvalidBindingConfigurationTest.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Xunit; - -namespace Steeltoe.Stream.Test.Binding; - -public sealed class InvalidBindingConfigurationTest : AbstractTest -{ - [Fact] - public void TestDuplicateBindingConfig() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - Assert.Throws(() => CreateStreamsContainerWithBinding( - searchDirectories, typeof(ITestInvalidBinding), "spring.cloud.stream.defaultbinder=mock")); - } -} diff --git a/src/Stream/test/Stream.Test/Binding/MessageConverterConfigurerTest.cs b/src/Stream/test/Stream.Test/Binding/MessageConverterConfigurerTest.cs deleted file mode 100644 index eb13e0a23b..0000000000 --- a/src/Stream/test/Stream.Test/Binding/MessageConverterConfigurerTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Test.Binding; - -public sealed class MessageConverterConfigurerTest : AbstractTest -{ -} diff --git a/src/Stream/test/Stream.Test/Configuration/ArgumentResolversTest.cs b/src/Stream/test/Stream.Test/Configuration/ArgumentResolversTest.cs deleted file mode 100644 index 4939394e95..0000000000 --- a/src/Stream/test/Stream.Test/Configuration/ArgumentResolversTest.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Text; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Stream.Configuration; -using Xunit; - -namespace Steeltoe.Stream.Test.Configuration; - -public sealed class ArgumentResolversTest -{ - [Fact] - public void TestSmartPayloadArgumentResolver() - { - var resolver = new SmartPayloadArgumentResolver(new TestMessageConverter()); - byte[] payload = Encoding.UTF8.GetBytes("hello"); - IMessage message = Message.Create(payload); - ParameterInfo parameter = GetType().GetMethod(nameof(ByteArray)).GetParameters()[0]; - object resolvedArgument = resolver.ResolveArgument(parameter, message); - Assert.Same(payload, resolvedArgument); - - parameter = GetType().GetMethod(nameof(Object)).GetParameters()[0]; - resolvedArgument = resolver.ResolveArgument(parameter, message); - Assert.True(resolvedArgument is IMessage); - - var payload2 = new Dictionary(); - IMessage> message2 = Message.Create(payload2); - parameter = GetType().GetMethod(nameof(Dict)).GetParameters()[0]; - resolvedArgument = resolver.ResolveArgument(parameter, message2); - Assert.Same(payload2, resolvedArgument); - - parameter = GetType().GetMethod(nameof(Object)).GetParameters()[0]; - resolvedArgument = resolver.ResolveArgument(parameter, message2); - Assert.True(resolvedArgument is IMessage); - } - - private sealed class TestMessageConverter : IMessageConverter - { - public const string DefaultServiceName = nameof(TestMessageConverter); - - public string ServiceName { get; set; } = DefaultServiceName; - - public object FromMessage(IMessage message, Type targetType) - { - return message; - } - - public T FromMessage(IMessage message) - { - return (T)message; - } - - public IMessage ToMessage(object payload, IMessageHeaders headers) - { - return Message.Create(payload); - } - } - -#pragma warning disable xUnit1013 // Public method should be marked as test - public void ByteArray(byte[] p) - { - } - - public void ByteArrayMessage(IMessage p) - { - } - - public void Object(object p) - { - } - - public void Dict(Dictionary p) - { - } -#pragma warning restore xUnit1013 // Public method should be marked as test -} diff --git a/src/Stream/test/Stream.Test/Configuration/BindingServiceOptionsTest.cs b/src/Stream/test/Stream.Test/Configuration/BindingServiceOptionsTest.cs deleted file mode 100644 index d29716cb1c..0000000000 --- a/src/Stream/test/Stream.Test/Configuration/BindingServiceOptionsTest.cs +++ /dev/null @@ -1,231 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Steeltoe.Stream.Configuration; -using Xunit; -using BinderOptions = Steeltoe.Stream.Configuration.BinderOptions; - -namespace Steeltoe.Stream.Test.Configuration; - -public sealed class BindingServiceOptionsTest -{ - [Fact] - public void Initialize_ConfiguresOptionsCorrectly() - { - var builder = new ConfigurationBuilder(); - - builder.AddInMemoryCollection(new Dictionary - { - { "spring:cloud:stream:instanceCount", "100" }, - { "spring:cloud:stream:instanceIndex", "1" }, - { "spring:cloud:stream:dynamicDestinations:0", "dynamicDestinations" }, - { "spring:cloud:stream:defaultBinder", "defaultBinder" }, - { "spring:cloud:stream:overrideCloudConnectors", "true" }, - { "spring:cloud:stream:bindingRetryInterval", "500" }, - { "spring:cloud:stream:default:destination", "destination" }, - { "spring:cloud:stream:default:group", "group" }, - { "spring:cloud:stream:default:contentType", "contentType" }, - { "spring:cloud:stream:default:binder", "binder" }, - { "spring:cloud:stream:bindings:input:destination", "inputdestination" }, - { "spring:cloud:stream:bindings:input:group", "inputgroup" }, - { "spring:cloud:stream:bindings:input:contentType", "inputcontentType" }, - { "spring:cloud:stream:bindings:input:binder", "inputbinder" }, - { "spring:cloud:stream:bindings:input:consumer:autoStartup", "false" }, - { "spring:cloud:stream:bindings:input:consumer:concurrency", "10" }, - { "spring:cloud:stream:bindings:input:consumer:partitioned", "true" }, - { "spring:cloud:stream:bindings:input:consumer:headerMode", "headers" }, - { "spring:cloud:stream:bindings:input:consumer:maxAttempts", "10" }, - { "spring:cloud:stream:bindings:input:consumer:backOffInitialInterval", "10" }, - { "spring:cloud:stream:bindings:input:consumer:backOffMaxInterval", "10" }, - { "spring:cloud:stream:bindings:input:consumer:backOffMultiplier", "5.0" }, - { "spring:cloud:stream:bindings:input:consumer:defaultRetryable", "false" }, - { "spring:cloud:stream:bindings:input:consumer:instanceIndex", "10" }, - { "spring:cloud:stream:bindings:input:consumer:instanceCount", "10" }, - { "spring:cloud:stream:bindings:input:consumer:retryableExceptions:0", "notused" }, - { "spring:cloud:stream:bindings:input:consumer:useNativeDecoding", "true" }, - { "spring:cloud:stream:bindings:output:destination", "outputdestination" }, - { "spring:cloud:stream:bindings:output:group", "outputgroup" }, - { "spring:cloud:stream:bindings:output:contentType", "outputcontentType" }, - { "spring:cloud:stream:bindings:output:binder", "outputbinder" }, - { "spring:cloud:stream:bindings:output:producer:autoStartup", "false" }, - { "spring:cloud:stream:bindings:output:producer:partitionKeyExpression", "partitionKeyExpression" }, - { "spring:cloud:stream:bindings:output:producer:partitionSelectorExpression", "partitionSelectorExpression" }, - { "spring:cloud:stream:bindings:output:producer:partitionKeyExtractorName", "partitionKeyExtractorName" }, - { "spring:cloud:stream:bindings:output:producer:partitionSelectorName", "partitionSelectorName" }, - { "spring:cloud:stream:bindings:output:producer:partitionCount", "10" }, - { "spring:cloud:stream:bindings:output:producer:requiredGroups:0", "requiredGroups" }, - { "spring:cloud:stream:bindings:output:producer:headerMode", "headers" }, - { "spring:cloud:stream:bindings:output:producer:useNativeEncoding", "true" }, - { "spring:cloud:stream:bindings:output:producer:errorChannelEnabled", "true" }, - { "spring:cloud:stream:binders:foobar:inheritEnvironment", "false" }, - { "spring:cloud:stream:binders:foobar:defaultCandidate", "false" }, - { "spring:cloud:stream:binders:foobar:environment:key1", "value1" }, - { "spring:cloud:stream:binders:foobar:environment:key2", "value2" } - }); - - IConfigurationSection configuration = builder.Build().GetSection("spring:cloud:stream"); - var options = new BindingServiceOptions(configuration); - options.PostProcess(); - - Assert.Equal(100, options.InstanceCount); - Assert.Equal(1, options.InstanceIndex); - Assert.Equal("dynamicDestinations", options.DynamicDestinations[0]); - Assert.Equal("defaultBinder", options.DefaultBinder); - Assert.Equal(true, options.OverrideCloudConnectors); - Assert.Equal(500, options.BindingRetryInterval); - - Assert.NotNull(options.Default); - Assert.Equal("destination", options.Default.Destination); - Assert.Equal("group", options.Default.Group); - Assert.Equal("contentType", options.Default.ContentType); - Assert.Equal("binder", options.Default.Binder); - - BindingOptions input = options.GetBindingOptions("input"); - - Assert.NotNull(input); - Assert.Equal("inputdestination", input.Destination); - Assert.Equal("inputgroup", input.Group); - Assert.Equal("inputcontentType", input.ContentType); - Assert.Equal("inputbinder", input.Binder); - Assert.Equal(false, input.Consumer.AutoStartup); - Assert.Equal(10, input.Consumer.Concurrency); - Assert.Equal(true, input.Consumer.Partitioned); - Assert.Equal(HeaderMode.Headers, input.Consumer.HeaderMode); - Assert.Equal(10, input.Consumer.MaxAttempts); - Assert.Equal(10, input.Consumer.BackOffInitialInterval); - Assert.Equal(10, input.Consumer.BackOffMaxInterval); - Assert.Equal(5.0, input.Consumer.BackOffMultiplier); - Assert.Equal(false, input.Consumer.DefaultRetryable); - Assert.Equal(10, input.Consumer.InstanceIndex); - Assert.Equal(10, input.Consumer.InstanceCount); - - Assert.Equal(new List - { - "notused" - }, input.Consumer.RetryableExceptions); - - Assert.Equal(true, input.Consumer.UseNativeDecoding); - - BindingOptions output = options.GetBindingOptions("output"); - - Assert.Equal("outputdestination", output.Destination); - Assert.Equal("outputgroup", output.Group); - Assert.Equal("outputcontentType", output.ContentType); - Assert.Equal("outputbinder", output.Binder); - Assert.Equal(false, output.Producer.AutoStartup); - Assert.Equal("partitionKeyExpression", output.Producer.PartitionKeyExpression); - Assert.Equal("partitionSelectorExpression", output.Producer.PartitionSelectorExpression); - Assert.Equal("partitionKeyExtractorName", output.Producer.PartitionKeyExtractorName); - Assert.Equal("partitionSelectorName", output.Producer.PartitionSelectorName); - Assert.Equal(10, output.Producer.PartitionCount); - Assert.Equal("requiredGroups", output.Producer.RequiredGroups[0]); - Assert.Equal(HeaderMode.Headers, output.Producer.HeaderMode); - Assert.Equal(true, output.Producer.UseNativeEncoding); - Assert.Equal(true, output.Producer.ErrorChannelEnabled); - - BinderOptions foobar = options.Binders["foobar"]; - Assert.NotNull(foobar); - - Assert.Equal(false, foobar.InheritEnvironment); - Assert.Equal(false, foobar.DefaultCandidate); - Assert.Equal("value1", foobar.Environment["key1"]); - Assert.Equal("value2", foobar.Environment["key2"]); - } - - [Fact] - public void Defaults_ConfiguresOptionsCorrectly() - { - var builder = new ConfigurationBuilder(); - builder.AddInMemoryCollection(new Dictionary()); - IConfigurationSection configuration = builder.Build().GetSection(BindingServiceOptions.Prefix); - var options = new BindingServiceOptions(configuration); - options.PostProcess(); - - Assert.Equal(1, options.InstanceCount); - Assert.Equal(0, options.InstanceIndex); - Assert.False(options.OverrideCloudConnectors); - Assert.Empty(options.DynamicDestinations); - Assert.Null(options.DefaultBinder); - Assert.Equal(30, options.BindingRetryInterval); - Assert.Empty(options.Binders); - Assert.Empty(options.Bindings); - Assert.Null(options.GetBinder("foobar")); - Assert.NotNull(options.GetBindingOptions("foobar")); // Creates binding with that name using defaults - Assert.Equal("foobar", options.GetBindingDestination("foobar")); - Assert.NotNull(options.GetConsumerOptions("foobar")); - Assert.Null(options.GetGroup("foobar")); - Assert.NotNull(options.GetProducerOptions("foobar")); - } - - [Fact] - public void NonDefaults_ConfiguresOptionsCorrectly() - { - var builder = new ConfigurationBuilder(); - - builder.AddInMemoryCollection(new Dictionary - { - { "spring:cloud:stream:instanceCount", "100" }, - { "spring:cloud:stream:instanceIndex", "1" }, - { "spring:cloud:stream:dynamicDestinations:0", "dynamicDestinations" }, - { "spring:cloud:stream:defaultBinder", "defaultBinder" }, - { "spring:cloud:stream:overrideCloudConnectors", "true" }, - { "spring:cloud:stream:bindingRetryInterval", "500" } - }); - - IConfigurationSection configuration = builder.Build().GetSection(BindingServiceOptions.Prefix); - var options = new BindingServiceOptions(configuration); - options.PostProcess(); - - Assert.Equal(100, options.InstanceCount); - Assert.Equal(1, options.InstanceIndex); - Assert.True(options.OverrideCloudConnectors); - Assert.Single(options.DynamicDestinations); - Assert.Equal("dynamicDestinations", options.DynamicDestinations[0]); - Assert.Equal("defaultBinder", options.DefaultBinder); - Assert.Equal(500, options.BindingRetryInterval); - Assert.Empty(options.Binders); - Assert.Empty(options.Bindings); - Assert.Null(options.GetBinder("foobar")); - Assert.NotNull(options.GetBindingOptions("foobar")); // Creates binding with that name using defaults - Assert.Equal("foobar", options.GetBindingDestination("foobar")); - Assert.NotNull(options.GetConsumerOptions("foobar")); - Assert.Null(options.GetGroup("foobar")); - Assert.NotNull(options.GetProducerOptions("foobar")); - } - - [Fact] - public void Mixture_Default_NonDefault_ConfiguresOptionsCorrectly() - { - var builder = new ConfigurationBuilder(); - - builder.AddInMemoryCollection(new Dictionary - { - { "spring:cloud:stream:instanceIndex", "2" }, - { "spring:cloud:stream:dynamicDestinations:0", "dynamicDestinations" }, - { "spring:cloud:stream:overrideCloudConnectors", "true" }, - { "spring:cloud:stream:bindingRetryInterval", "100" } - }); - - IConfigurationSection configuration = builder.Build().GetSection(BindingServiceOptions.Prefix); - var options = new BindingServiceOptions(configuration); - options.PostProcess(); - - Assert.Equal(1, options.InstanceCount); - Assert.Equal(2, options.InstanceIndex); - Assert.True(options.OverrideCloudConnectors); - Assert.Single(options.DynamicDestinations); - Assert.Equal("dynamicDestinations", options.DynamicDestinations[0]); - Assert.Null(options.DefaultBinder); - Assert.Equal(100, options.BindingRetryInterval); - Assert.Empty(options.Binders); - Assert.Empty(options.Bindings); - Assert.Null(options.GetBinder("foobar")); - Assert.NotNull(options.GetBindingOptions("foobar")); // Creates binding with that name using defaults - Assert.Equal("foobar", options.GetBindingDestination("foobar")); - Assert.NotNull(options.GetConsumerOptions("foobar")); - Assert.Null(options.GetGroup("foobar")); - Assert.NotNull(options.GetProducerOptions("foobar")); - } -} diff --git a/src/Stream/test/Stream.Test/Extensions/BinderServicesExtensionsTest.cs b/src/Stream/test/Stream.Test/Extensions/BinderServicesExtensionsTest.cs deleted file mode 100644 index 0d3a229e30..0000000000 --- a/src/Stream/test/Stream.Test/Extensions/BinderServicesExtensionsTest.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Extensions; -using Xunit; - -namespace Steeltoe.Stream.Test.Extensions; - -public sealed class BinderServicesExtensionsTest -{ - [Fact] - public void AddBinderServices_AddsServices() - { - var container = new ServiceCollection(); - container.AddOptions(); - container.AddLogging(b => b.AddConsole()); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - container.AddSingleton(configurationRoot); - container.AddSingleton(); - container.AddBinderServices(configurationRoot); - ServiceProvider serviceProvider = container.BuildServiceProvider(true); - - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - } -} diff --git a/src/Stream/test/Stream.Test/Extensions/CoreServicesExtensionsTest.cs b/src/Stream/test/Stream.Test/Extensions/CoreServicesExtensionsTest.cs deleted file mode 100644 index e64327a43a..0000000000 --- a/src/Stream/test/Stream.Test/Extensions/CoreServicesExtensionsTest.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Converter; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Extensions; -using Xunit; - -namespace Steeltoe.Stream.Test.Extensions; - -public sealed class CoreServicesExtensionsTest -{ - [Fact] - public void AddCoreServices_AddsServices() - { - var container = new ServiceCollection(); - container.AddOptions(); - container.AddLogging(b => b.AddConsole()); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - container.AddSingleton(configurationRoot); - container.AddCoreServices(); - ServiceProvider serviceProvider = container.BuildServiceProvider(true); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - } -} diff --git a/src/Stream/test/Stream.Test/Extensions/EnableBindingsExtensionsTest.cs b/src/Stream/test/Stream.Test/Extensions/EnableBindingsExtensionsTest.cs deleted file mode 100644 index a6aa40beb9..0000000000 --- a/src/Stream/test/Stream.Test/Extensions/EnableBindingsExtensionsTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Steeltoe.Messaging; -using Steeltoe.Stream.Extensions; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Extensions; - -public sealed class EnableBindingsExtensionsTest -{ - [Fact] - public void AddProcessor_AddsServices() - { - var container = new ServiceCollection(); - container.AddOptions(); - container.AddLogging(b => b.AddDebug()); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - container.AddSingleton(configurationRoot); - container.AddStreamServices(configurationRoot); - container.AddProcessorStreamBinding(); - ServiceProvider serviceProvider = container.BuildServiceProvider(true); - - var binding = serviceProvider.GetService(); - Assert.NotNull(binding); - IEnumerable channels = serviceProvider.GetServices(); - - // NullChannel, Integration Error Channel, Processor channels (input and output) - Assert.Equal(4, channels.Count()); - - Assert.NotNull(binding.Input); - Assert.NotNull(binding.Output); - } - - [Fact] - public void AddSink_AddsServices() - { - var container = new ServiceCollection(); - container.AddOptions(); - container.AddLogging(b => b.AddDebug()); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - container.AddSingleton(configurationRoot); - container.AddStreamServices(configurationRoot); - container.AddSinkStreamBinding(); - ServiceProvider serviceProvider = container.BuildServiceProvider(true); - - var binding = serviceProvider.GetService(); - Assert.NotNull(binding); - IEnumerable channels = serviceProvider.GetServices(); - - // NullChannel, Integration Error Channel, Sink channel (input) - Assert.Equal(3, channels.Count()); - - Assert.NotNull(binding.Input); - } - - [Fact] - public void AddSource_AddsServices() - { - var container = new ServiceCollection(); - container.AddOptions(); - container.AddLogging(b => b.AddDebug()); - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - container.AddSingleton(configurationRoot); - container.AddStreamServices(configurationRoot); - container.AddSourceStreamBinding(); - ServiceProvider serviceProvider = container.BuildServiceProvider(true); - - var binding = serviceProvider.GetService(); - Assert.NotNull(binding); - IEnumerable channels = serviceProvider.GetServices(); - - // NullChannel, Integration Error Channel, Source channel (output) - Assert.Equal(3, channels.Count()); - - Assert.NotNull(binding.Output); - } -} diff --git a/src/Stream/test/Stream.Test/Extensions/HostBuilderExtensionsTest.cs b/src/Stream/test/Stream.Test/Extensions/HostBuilderExtensionsTest.cs deleted file mode 100644 index 18a15ed9df..0000000000 --- a/src/Stream/test/Stream.Test/Extensions/HostBuilderExtensionsTest.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Steeltoe.Common.TestResources; -using Steeltoe.Configuration.SpringBoot; -using Steeltoe.Stream.Extensions; -using Steeltoe.Stream.StreamHost; -using Steeltoe.Stream.Test.StreamsHost; -using Xunit; - -namespace Steeltoe.Stream.Test.Extensions; - -public sealed class HostBuilderExtensionsTest -{ - [Fact] - public void HostBuilderExtensionTest() - { - IHostBuilder hostBuilder = Host.CreateDefaultBuilder().UseDefaultServiceProvider(options => options.ValidateScopes = true) - .AddStreamServices(); - - hostBuilder.ConfigureServices(services => services.AddSingleton(isp => isp.GetRequiredService() as IConfigurationRoot)); - IHost host = hostBuilder.Build(); - var configurationRoot = host.Services.GetService(); - Assert.NotNull(hostBuilder); - Assert.Single(host.Services.GetServices().Where(svc => svc is StreamLifeCycleService)); - Assert.Single(configurationRoot.Providers.Where(p => p is SpringBootEnvironmentVariableProvider)); - Assert.Single(configurationRoot.Providers.Where(p => p is SpringBootCommandLineProvider)); - } - - [Fact] - public void WebHostBuilderExtensionTest() - { - IWebHostBuilder hostBuilder = WebHost.CreateDefaultBuilder().UseDefaultServiceProvider(options => options.ValidateScopes = true) - .Configure(HostingHelpers.EmptyAction).AddStreamServices(); - - hostBuilder.ConfigureServices(services => services.AddSingleton(isp => isp.GetRequiredService() as IConfigurationRoot)); - IWebHost host = hostBuilder.Build(); - var configurationRoot = host.Services.GetService(); - Assert.NotNull(hostBuilder); - Assert.Single(host.Services.GetServices().Where(svc => svc is StreamLifeCycleService)); - Assert.Single(configurationRoot.Providers.Where(p => p is SpringBootEnvironmentVariableProvider)); - Assert.Single(configurationRoot.Providers.Where(p => p is SpringBootCommandLineProvider)); - } - - [Fact] - public void WebApplicationBuilderExtensionTest() - { - WebApplicationBuilder hostBuilder = TestHelpers.GetTestWebApplicationBuilder().AddStreamServices(); - hostBuilder.Services.AddSingleton(isp => isp.GetRequiredService() as IConfigurationRoot); - WebApplication host = hostBuilder.Build(); - var configurationRoot = host.Services.GetService(); - Assert.NotNull(hostBuilder); - Assert.Single(host.Services.GetServices().Where(svc => svc is StreamLifeCycleService)); - Assert.Single(configurationRoot.Providers.Where(p => p is SpringBootEnvironmentVariableProvider)); - Assert.Single(configurationRoot.Providers.Where(p => p is SpringBootCommandLineProvider)); - } -} diff --git a/src/Stream/test/Stream.Test/Extensions/IntegrationServicesExtensionsTest.cs b/src/Stream/test/Stream.Test/Extensions/IntegrationServicesExtensionsTest.cs deleted file mode 100644 index 1136682165..0000000000 --- a/src/Stream/test/Stream.Test/Extensions/IntegrationServicesExtensionsTest.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Steeltoe.Integration.Extensions; -using Steeltoe.Integration.Support; -using Steeltoe.Integration.Support.Converter; -using Steeltoe.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Extensions; - -public sealed class IntegrationServicesExtensionsTest -{ - [Fact] - public void AddIntegrationServices_AddsServices() - { - var container = new ServiceCollection(); - container.AddOptions(); - container.AddLogging(b => b.AddConsole()); - container.AddIntegrationServices(); - ServiceProvider serviceProvider = container.BuildServiceProvider(true); - - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - - IEnumerable channels = serviceProvider.GetServices(); - Assert.Equal(2, channels.Count()); - } -} diff --git a/src/Stream/test/Stream.Test/Extensions/StreamListenerExtensionsTest.cs b/src/Stream/test/Stream.Test/Extensions/StreamListenerExtensionsTest.cs deleted file mode 100644 index c2b4b20cd3..0000000000 --- a/src/Stream/test/Stream.Test/Extensions/StreamListenerExtensionsTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Test.Extensions; - -public sealed class StreamListenerExtensionsTest -{ -} diff --git a/src/Stream/test/Stream.Test/Extensions/StreamServicesExtensionsTest.cs b/src/Stream/test/Stream.Test/Extensions/StreamServicesExtensionsTest.cs deleted file mode 100644 index 912e42b69c..0000000000 --- a/src/Stream/test/Stream.Test/Extensions/StreamServicesExtensionsTest.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Steeltoe.Common.Expression.Internal; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Integration.Extensions; -using Steeltoe.Integration.Support.Converter; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Core; -using Steeltoe.Messaging.Handler.Attributes.Support; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Extensions; -using Steeltoe.Stream.Messaging; -using Steeltoe.Stream.Test.StreamsHost; -using Xunit; - -namespace Steeltoe.Stream.Test.Extensions; - -public sealed class StreamServicesExtensionsTest -{ - [Fact] - public void AddStreamConfiguration_AddsServices() - { - var container = new ServiceCollection(); - container.AddOptions(); - container.AddLogging(b => b.AddConsole()); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - container.AddStreamConfiguration(configurationRoot); - ServiceProvider serviceProvider = container.BuildServiceProvider(true); - ValidateConfigurationServices(serviceProvider); - } - - [Fact] - public void AddStreamCoreServices_AddsServices() - { - var container = new ServiceCollection(); - container.AddOptions(); - container.AddLogging(b => b.AddConsole()); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - container.AddSingleton(configurationRoot); - container.AddCoreServices(); - container.AddIntegrationServices(); - container.AddBinderServices(configurationRoot); - container.AddStreamCoreServices(configurationRoot); - ServiceProvider serviceProvider = container.BuildServiceProvider(true); - ValidateCoreServices(serviceProvider); - } - - [Fact] - public void AddStreamServices_AddsServices() - { - var container = new ServiceCollection(); - container.AddOptions(); - container.AddLogging(b => b.AddConsole()); - - IConfigurationRoot configurationRoot = new ConfigurationBuilder().Build(); - container.AddSingleton(configurationRoot); - container.AddStreamServices(configurationRoot); - ServiceProvider serviceProvider = container.BuildServiceProvider(true); - ValidateConfigurationServices(serviceProvider); - ValidateCoreServices(serviceProvider); - } - - [Fact] - public void AddStreamsServicesGeneric_AddsServices() - { - var serviceCollection = new ServiceCollection(); - IConfigurationRoot configuration = new ConfigurationBuilder().Build(); - - serviceCollection.AddSingleton(configuration); - serviceCollection.AddStreamServices(configuration); - - ServiceProvider provider = serviceCollection.BuildServiceProvider(true); - - Assert.True(provider.GetService() != null, "SampleSink not found in Container"); - - Assert.True(provider.GetService() != null, "ISource not found in Container"); - - Assert.True(provider.GetService() != null, "ISink not found in Container"); - - Assert.True(provider.GetService() != null, "IExpressionParser not found in Container"); - - Assert.True(provider.GetService() != null, "IEvaluationContext not found in Container"); - } - - private void ValidateCoreServices(ServiceProvider serviceProvider) - { - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - IEnumerable factories = serviceProvider.GetServices(); - Assert.Equal(2, factories.Count()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService>()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - Assert.NotNull(serviceProvider.GetService()); - IEnumerable lifecycles = serviceProvider.GetServices(); - Assert.Equal(3, lifecycles.Count()); - Assert.NotNull(serviceProvider.GetService()); - } - - private void ValidateConfigurationServices(ServiceProvider serviceProvider) - { - Assert.NotNull(serviceProvider.GetService>()); - Assert.NotNull(serviceProvider.GetService>()); - } -} diff --git a/src/Stream/test/Stream.Test/IBarista.cs b/src/Stream/test/Stream.Test/IBarista.cs deleted file mode 100644 index 86c9f8d5ce..0000000000 --- a/src/Stream/test/Stream.Test/IBarista.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; - -namespace Steeltoe.Stream.Test; - -public interface IBarista -{ - [Input] - ISubscribableChannel Orders(); - - [Output] - IMessageChannel HotDrinks(); - - [Output] - IMessageChannel ColdDrinks(); -} diff --git a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionKeyExtractorClass.cs b/src/Stream/test/Stream.Test/Partitioning/CustomPartitionKeyExtractorClass.cs deleted file mode 100644 index f660a10b5b..0000000000 --- a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionKeyExtractorClass.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Test.Partitioning; - -public sealed class CustomPartitionKeyExtractorClass : IPartitionKeyExtractorStrategy -{ - public string ServiceName { get; set; } - - public CustomPartitionKeyExtractorClass() - { - ServiceName = GetType().Name; - } - - public object ExtractKey(IMessage message) - { - return message.Headers.Get("key"); - } -} diff --git a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionKeyExtractorClassOne.cs b/src/Stream/test/Stream.Test/Partitioning/CustomPartitionKeyExtractorClassOne.cs deleted file mode 100644 index eff10a7f2e..0000000000 --- a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionKeyExtractorClassOne.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Test.Partitioning; - -public sealed class CustomPartitionKeyExtractorClassOne : IPartitionKeyExtractorStrategy -{ - public string ServiceName { get; set; } - - public CustomPartitionKeyExtractorClassOne() - { - ServiceName = GetType().Name; - } - - public object ExtractKey(IMessage message) - { - return message.Headers.Get("key"); - } -} diff --git a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionKeyExtractorClassTwo.cs b/src/Stream/test/Stream.Test/Partitioning/CustomPartitionKeyExtractorClassTwo.cs deleted file mode 100644 index 807184ab97..0000000000 --- a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionKeyExtractorClassTwo.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Test.Partitioning; - -public sealed class CustomPartitionKeyExtractorClassTwo : IPartitionKeyExtractorStrategy -{ - public string ServiceName { get; set; } - - public CustomPartitionKeyExtractorClassTwo() - { - ServiceName = GetType().Name; - } - - public object ExtractKey(IMessage message) - { - return message.Headers.Get("key"); - } -} diff --git a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionSelectorClass.cs b/src/Stream/test/Stream.Test/Partitioning/CustomPartitionSelectorClass.cs deleted file mode 100644 index a80b3985f0..0000000000 --- a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionSelectorClass.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Test.Partitioning; - -public sealed class CustomPartitionSelectorClass : IPartitionSelectorStrategy -{ - public string ServiceName { get; set; } - - public CustomPartitionSelectorClass() - { - ServiceName = GetType().Name; - } - - public int SelectPartition(object key, int partitionCount) - { - return int.Parse((string)key, CultureInfo.InvariantCulture); - } -} diff --git a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionSelectorClassOne.cs b/src/Stream/test/Stream.Test/Partitioning/CustomPartitionSelectorClassOne.cs deleted file mode 100644 index 9036f526eb..0000000000 --- a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionSelectorClassOne.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Test.Partitioning; - -public sealed class CustomPartitionSelectorClassOne : IPartitionSelectorStrategy -{ - public string ServiceName { get; set; } - - public CustomPartitionSelectorClassOne() - { - ServiceName = GetType().Name; - } - - public int SelectPartition(object key, int partitionCount) - { - return int.Parse((string)key, CultureInfo.InvariantCulture); - } -} diff --git a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionSelectorClassTwo.cs b/src/Stream/test/Stream.Test/Partitioning/CustomPartitionSelectorClassTwo.cs deleted file mode 100644 index 827aee0bbb..0000000000 --- a/src/Stream/test/Stream.Test/Partitioning/CustomPartitionSelectorClassTwo.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using Steeltoe.Stream.Binder; - -namespace Steeltoe.Stream.Test.Partitioning; - -public sealed class CustomPartitionSelectorClassTwo : IPartitionSelectorStrategy -{ - public string ServiceName { get; set; } - - public CustomPartitionSelectorClassTwo() - { - ServiceName = GetType().Name; - } - - public int SelectPartition(object key, int partitionCount) - { - return int.Parse((string)key, CultureInfo.InvariantCulture); - } -} diff --git a/src/Stream/test/Stream.Test/Partitioning/PartitionedConsumerTest.cs b/src/Stream/test/Stream.Test/Partitioning/PartitionedConsumerTest.cs deleted file mode 100644 index 14e73f1e97..0000000000 --- a/src/Stream/test/Stream.Test/Partitioning/PartitionedConsumerTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Partitioning; - -public sealed class PartitionedConsumerTest : AbstractTest -{ - [Fact] - public async Task TestBindingPartitionedConsumer() - { - List searchDirectories = GetSearchDirectories("MockBinder"); - - ServiceProvider provider = CreateStreamsContainerWithISinkBinding(searchDirectories, "spring.cloud.stream.bindings.input.destination=partIn", - "spring.cloud.stream.bindings.input.consumer.partitioned=true", "spring.cloud.stream.instanceCount=2", "spring.cloud.stream.instanceIndex=0") - .BuildServiceProvider(true); - - var factory = provider.GetService(); - Assert.NotNull(factory); - IBinder binder = factory.GetBinder(null); - Mock mockBinder = Mock.Get(binder); - var sink = provider.GetService(); - IConsumerOptions captured = null; - - mockBinder.Setup(b => b.BindConsumer("partIn", null, sink.Input, It.IsAny())).Callback( - (_, _, _, d) => - { - captured = d; - }); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - mockBinder.Verify(b => b.BindConsumer("partIn", null, sink.Input, It.IsAny())); - - Assert.Equal(0, captured.InstanceIndex); - Assert.Equal(2, captured.InstanceCount); - } -} diff --git a/src/Stream/test/Stream.Test/Steeltoe.Stream.Test.csproj b/src/Stream/test/Stream.Test/Steeltoe.Stream.Test.csproj deleted file mode 100644 index 106d9a1136..0000000000 --- a/src/Stream/test/Stream.Test/Steeltoe.Stream.Test.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - net8.0;net6.0 - - - - - - - - - - diff --git a/src/Stream/test/Stream.Test/StreamsHost/FakeHostedService.cs b/src/Stream/test/Stream.Test/StreamsHost/FakeHostedService.cs deleted file mode 100644 index f29cce895b..0000000000 --- a/src/Stream/test/Stream.Test/StreamsHost/FakeHostedService.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Hosting; - -namespace Steeltoe.Stream.Test.StreamsHost; - -public sealed class FakeHostedService : IHostedService, IDisposable -{ - public int StartCount { get; internal set; } - - public int StopCount { get; internal set; } - - public int DisposeCount { get; internal set; } - - public Action StartAction { get; set; } - - public Action StopAction { get; set; } - - public Action DisposeAction { get; set; } - - public Task StartAsync(CancellationToken cancellationToken) - { - StartCount++; - StartAction?.Invoke(cancellationToken); - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) - { - StopCount++; - StopAction?.Invoke(cancellationToken); - return Task.CompletedTask; - } - - public void Dispose() - { - DisposeCount++; - DisposeAction?.Invoke(); - } -} diff --git a/src/Stream/test/Stream.Test/StreamsHost/SampleSink.cs b/src/Stream/test/Stream.Test/StreamsHost/SampleSink.cs deleted file mode 100644 index 81e3a99231..0000000000 --- a/src/Stream/test/Stream.Test/StreamsHost/SampleSink.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.StreamsHost; - -[EnableBinding(typeof(ISink))] -public sealed class SampleSink -{ - [StreamListener("input")] - public void HandleInputMessage(string foo) - { - } -} diff --git a/src/Stream/test/Stream.Test/StreamsHost/StreamsHostTest.cs b/src/Stream/test/Stream.Test/StreamsHost/StreamsHostTest.cs deleted file mode 100644 index 46b14dbd7b..0000000000 --- a/src/Stream/test/Stream.Test/StreamsHost/StreamsHostTest.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; -using Steeltoe.Configuration.CloudFoundry.ServiceBinding; -using Steeltoe.Connectors.RabbitMQ; -using Steeltoe.Messaging.RabbitMQ.Configuration; -using Xunit; - -namespace Steeltoe.Stream.Test.StreamsHost; - -public sealed class StreamsHostTest -{ - [Fact] - public void HostCanBeStarted() - { - FakeHostedService service; - - using (IHost host = StreamHost.StreamHost.CreateDefaultBuilder().UseDefaultServiceProvider(options => options.ValidateScopes = true) - .ConfigureServices(svc => svc.AddSingleton()).Start()) - { - Assert.NotNull(host); - service = (FakeHostedService)host.Services.GetRequiredService(); - Assert.NotNull(service); - Assert.Equal(1, service.StartCount); - Assert.Equal(0, service.StopCount); - Assert.Equal(0, service.DisposeCount); - } - - Assert.Equal(1, service.StartCount); - Assert.Equal(0, service.StopCount); - Assert.Equal(1, service.DisposeCount); - } - - [Fact] - public void HostConfiguresRabbitOptions() - { - IHostBuilder builder = StreamHost.StreamHost.CreateDefaultBuilder().UseDefaultServiceProvider(options => options.ValidateScopes = true); - - builder.ConfigureAppConfiguration(configurationBuilder => - { - configurationBuilder.AddCloudFoundryServiceBindings(new StringServiceBindingsReader(GetCloudFoundryRabbitMqConfiguration())); - configurationBuilder.ConfigureRabbitMQ(); - }); - - using IHost host = builder.Start(); - - var rabbitOptionsMonitor = host.Services.GetService>(); - Assert.NotNull(rabbitOptionsMonitor); - RabbitOptions rabbitOptions = rabbitOptionsMonitor.CurrentValue; - - Assert.Equal("Dd6O1BPXUHdrmzbP", rabbitOptions.Username); - Assert.Equal("7E1LxXnlH2hhlPVt", rabbitOptions.Password); - Assert.Equal("cf_b4f8d2fa_a3ea_4e3a_a0e8_2cd040790355", rabbitOptions.VirtualHost); - Assert.Equal("Dd6O1BPXUHdrmzbP:7E1LxXnlH2hhlPVt@192.168.0.90:3306", rabbitOptions.Addresses); - } - - private static string GetCloudFoundryRabbitMqConfiguration() - { - return @" - { - ""p-rabbitmq"": [{ - ""credentials"": { - ""protocols"": { - ""amqp"": { - ""host"": ""192.168.0.90"", - ""password"": ""7E1LxXnlH2hhlPVt"", - ""port"": 3306, - ""username"": ""Dd6O1BPXUHdrmzbP"", - ""vhost"": ""cf_b4f8d2fa_a3ea_4e3a_a0e8_2cd040790355"" - } - }, - ""ssl"": false, - }, - ""name"": ""myRabbitMQService1"", - ""tags"": [ - ""rabbitmq"" - ] - }] - }"; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/AlwaysStringMessageConverter.cs b/src/Stream/test/Stream.Test/Tck/AlwaysStringMessageConverter.cs deleted file mode 100644 index 3f58283299..0000000000 --- a/src/Stream/test/Stream.Test/Tck/AlwaysStringMessageConverter.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class AlwaysStringMessageConverter : AbstractMessageConverter -{ - public const string DefaultServiceName = nameof(AlwaysStringMessageConverter); - - public override string ServiceName { get; set; } = DefaultServiceName; - - public AlwaysStringMessageConverter() - : this(MimeType.ToMimeType("application/x-java-object")) - { - } - - public AlwaysStringMessageConverter(MimeType supportedMimeType) - : base(supportedMimeType) - { - } - - protected override bool Supports(Type type) - { - return type == null || typeof(string).IsAssignableFrom(type); - } - - protected override object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - return GetType().Name; - } - - protected override object ConvertToInternal(object payload, IMessageHeaders headers, object conversionHint) - { - return Encoding.UTF8.GetBytes((string)payload); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/ByteArrayMessageToStringJsonMessageServiceActivator.cs b/src/Stream/test/Stream.Test/Tck/ByteArrayMessageToStringJsonMessageServiceActivator.cs deleted file mode 100644 index 6781f2e57f..0000000000 --- a/src/Stream/test/Stream.Test/Tck/ByteArrayMessageToStringJsonMessageServiceActivator.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Integration.Attributes; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class ByteArrayMessageToStringJsonMessageServiceActivator -{ - [ServiceActivator(InputChannel = ISink.InputName, OutputChannel = ISource.OutputName)] - public IMessage Echo(IMessage value) - { - var settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - // assume it is string because CT is text/plain - var serializer = JsonSerializer.Create(settings); - var textReader = new StreamReader(new MemoryStream(value.Payload), true); - var person = (Person)serializer.Deserialize(textReader, typeof(Person)); - person.Name = "bob"; - var writer = new StringWriter(); - serializer.Serialize(writer, person); - - return (IMessage)MessageBuilder.WithPayload(writer.ToString()).Build(); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/ByteArrayToByteArrayStreamListener.cs b/src/Stream/test/Stream.Test/Tck/ByteArrayToByteArrayStreamListener.cs deleted file mode 100644 index 89a1906d88..0000000000 --- a/src/Stream/test/Stream.Test/Tck/ByteArrayToByteArrayStreamListener.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class ByteArrayToByteArrayStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public byte[] Echo(byte[] value) - { - return value; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/ByteArrayToPojoStreamListener.cs b/src/Stream/test/Stream.Test/Tck/ByteArrayToPojoStreamListener.cs deleted file mode 100644 index 29649e6c73..0000000000 --- a/src/Stream/test/Stream.Test/Tck/ByteArrayToPojoStreamListener.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class ByteArrayToPojoStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public Person Echo(byte[] value) - { - var settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - var serializer = JsonSerializer.Create(settings); - var textReader = new StreamReader(new MemoryStream(value), true); - return (Person)serializer.Deserialize(textReader, typeof(Person)); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/CollectionWithParameterizedTypesStreamListener.cs b/src/Stream/test/Stream.Test/Tck/CollectionWithParameterizedTypesStreamListener.cs deleted file mode 100644 index 32aea4434b..0000000000 --- a/src/Stream/test/Stream.Test/Tck/CollectionWithParameterizedTypesStreamListener.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class CollectionWithParameterizedTypesStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public List> Echo(List> value) - { - Assert.True(value.Count > 0); - Assert.NotNull(value[0]); - return value; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/ContentTypeTckTest.cs b/src/Stream/test/Stream.Test/Tck/ContentTypeTckTest.cs deleted file mode 100644 index bccc5dbd38..0000000000 --- a/src/Stream/test/Stream.Test/Tck/ContentTypeTckTest.cs +++ /dev/null @@ -1,859 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Channel; -using Steeltoe.Integration.Configuration; -using Steeltoe.Integration.Extensions; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Extensions; -using Steeltoe.Stream.TestBinder; -using Xunit; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class ContentTypeTckTest : AbstractTest -{ - private IServiceCollection _container; - - public ContentTypeTckTest() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories); - } - - [Fact] - public async Task StringToMapStreamListener() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task StringToMapMessageStreamListener() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task StringToMapMessageStreamListenerOriginalContentType() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - var source = provider.GetService(); - var target = provider.GetService(); - const string jsonPayload = "{\"name\":\"oleg\"}"; - - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes(jsonPayload)).SetHeader(MessageHeaders.ContentType, "text/plain") - .SetHeader("originalContentType", "application/json;charset=UTF-8").Build(); - - source.Send((IMessage)message); - IMessage outputMessage = target.Receive(); - Assert.NotNull(outputMessage); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task WithInternalPipeline() - { - _container.AddStreamListeners(); - _container.AddSingleton(p => new DirectChannel(p.GetService(), "internalchannel")); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("OLEG", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task PojoToPojo() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task PojoToString() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task PojoToStringOutboundContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.output.contentType=text/plain"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.TextPlain, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task PojoToByteArray() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task PojoToByteArrayOutboundContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.output.contentType=text/plain"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task StringToPojoInboundContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.input.contentType=text/plain"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TypelessToPojoInboundContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.input.contentType=text/plain"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TypelessToPojoInboundContentTypeBindingJson() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.input.contentType=application/json"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TypelessMessageToPojoInboundContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.input.contentType=text/plain"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TypelessMessageToPojoInboundContentTypeBindingJson() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.input.contentType=application/json"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TypelessToPojoWithTextHeaderContentTypeBinding() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - - IMessage outputMessage = DoSendReceive(provider, jsonPayload, - new KeyValuePair(MessageHeaders.ContentType, MimeType.ToMimeType("text/plain"))); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TypelessToPojoOutboundContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.output.contentType=text/plain"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - - IMessage outputMessage = DoSendReceive(provider, jsonPayload, - new KeyValuePair(MessageHeaders.ContentType, MimeType.ToMimeType("text/plain"))); - - Assert.Equal(MimeTypeUtils.TextPlain, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task OutboundMessageWithTextContentTypeOnly() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload, new KeyValuePair(MessageHeaders.ContentType, new MimeType("text"))); - - Assert.Equal(MimeTypeUtils.TextPlain, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - string str = Encoding.UTF8.GetString(payload); - Assert.Equal(jsonPayload, str); - } - - [Fact] - public async Task StringToPojoInboundContentTypeHeader() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload, new KeyValuePair(MessageHeaders.ContentType, MimeTypeUtils.TextPlain)); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task ByteArrayToPojoInboundContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.input.contentType=text/plain"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task ByteArrayToPojoInboundContentTypeHeader() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - - IMessage message = Message.Create(Encoding.UTF8.GetBytes(jsonPayload), new MessageHeaders(new Dictionary - { - { MessageHeaders.ContentType, MimeTypeUtils.TextPlain } - })); - - IMessage outputMessage = DoSendReceive(provider, message); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task ByteArrayToByteArray() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task ByteArrayToByteArrayInboundOutboundContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.bindings.input.contentType=text/plain", - "spring.cloud.stream.bindings.output.contentType=text/plain"); - - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task PojoMessageToStringMessage() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.TextPlain, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task PojoMessageToStringMessageServiceActivator() - { - _container.AddServiceActivators(); - - ServiceProvider provider = _container.BuildServiceProvider(true); - - var activatorProcessor = provider.GetRequiredService(); - activatorProcessor.Initialize(); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.TextPlain, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task ByteArrayMessageToStringJsonMessageServiceActivator() - { - _container.AddServiceActivators(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - var activatorProcessor = provider.GetRequiredService(); - activatorProcessor.Initialize(); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.ApplicationJson, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("{\"name\":\"bob\"}", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task ByteArrayMessageToStringMessageServiceActivator() - { - _container.AddServiceActivators(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - var activatorProcessor = provider.GetRequiredService(); - activatorProcessor.Initialize(); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - Assert.Equal(MimeTypeUtils.TextPlain, outputMessage.Headers.Get(MessageHeaders.ContentType)); - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("oleg", Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task OverrideMessageConverter_DefaultContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.default.contentType=application/x-java-object"); - _container.AddStreamListeners(); - _container.AddSingleton(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("AlwaysStringMessageConverter", Encoding.UTF8.GetString(payload)); - Assert.Equal(MimeType.ToMimeType("application/x-java-object"), outputMessage.Headers.Get(MessageHeaders.ContentType)); - } - - [Fact] - public async Task CustomMessageConverter_DefaultContentTypeBinding() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.default.contentType=foo/bar"); - _container.AddStreamListeners(); - _container.AddSingleton(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal("FooBarMessageConverter", Encoding.UTF8.GetString(payload)); - Assert.Equal(MimeType.ToMimeType("foo/bar"), outputMessage.Headers.Get(MessageHeaders.ContentType)); - } - - [Fact] - public async Task JsonToPojoWrongDefaultContentTypeProperty() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.default.contentType=text/plain"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - DoSendReceive(provider, jsonPayload, typeof(MessagingException)); - } - - [Fact] - public async Task ToStringDefaultContentTypePropertyUnknownContentType() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories, "spring.cloud.stream.default.contentType=foo/bar"); - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - DoSendReceive(provider, jsonPayload, typeof(MessageConversionException)); - } - - [Fact] - public async Task ToCollectionWithParameterizedType() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "[{\"person\":{\"name\":\"jon\"},\"id\":123},{\"person\":{\"name\":\"jane\"},\"id\":456}]"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - - var settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - var serializer = JsonSerializer.Create(settings); - var textReader = new StringReader(Encoding.UTF8.GetString(payload)); - var list = (List)serializer.Deserialize(textReader, typeof(List)); - Assert.Equal(2, list.Count); - } - - [Fact] - public async Task TestWithMapInputParameter() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TestWithMapPayloadParameter() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TestWithListInputParameter() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "[\"foo\",\"bar\"]"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TestWithMessageHeadersInputParameter() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "{\"name\":\"oleg\"}"; - IMessage outputMessage = DoSendReceive(provider, jsonPayload); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.NotEqual(jsonPayload, Encoding.UTF8.GetString(payload)); - Assert.True(outputMessage.Headers.ContainsKey(MessageHeaders.IdName)); - Assert.True(outputMessage.Headers.ContainsKey(MessageHeaders.ContentType)); - } - - [Fact] - public async Task TestWithTypelessInputParameterAndOctetStream() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - const string jsonPayload = "[\"foo\",\"bar\"]"; - - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes(jsonPayload)).SetHeader(MessageHeaders.ContentType, MimeTypeUtils.ApplicationJson) - .Build(); - - IMessage outputMessage = DoSendReceive(provider, (IMessage)message); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TestWithTypelessInputParameterAndServiceActivator() - { - _container.AddServiceActivators(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - const string jsonPayload = "[\"foo\",\"bar\"]"; - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes(jsonPayload)).Build(); - IMessage outputMessage = DoSendReceive(provider, (IMessage)message); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - [Fact] - public async Task TestWithTypelessMessageInputParameterAndServiceActivator() - { - _container.AddServiceActivators(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - const string jsonPayload = "[\"foo\",\"bar\"]"; - IMessage message = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes(jsonPayload)).Build(); - IMessage outputMessage = DoSendReceive(provider, (IMessage)message); - - byte[] payload = outputMessage.Payload as byte[]; - Assert.NotNull(payload); - Assert.Equal(jsonPayload, Encoding.UTF8.GetString(payload)); - } - - private void DoSendReceive(ServiceProvider provider, string jsonPayload, Type lastError) - { - var source = provider.GetService(); - _ = provider.GetService(); - source.Send(Message.Create(Encoding.UTF8.GetBytes(jsonPayload))); - var binder = provider.GetService() as TestChannelBinder; - Assert.Equal(lastError, binder.LastError?.Payload?.GetType()); - } - - private IMessage DoSendReceive(ServiceProvider provider, string jsonPayload) - { - var source = provider.GetService(); - var target = provider.GetService(); - source.Send(Message.Create(Encoding.UTF8.GetBytes(jsonPayload))); - IMessage outputMessage = target.Receive(); - Assert.NotNull(outputMessage); - return outputMessage; - } - - private IMessage DoSendReceive(ServiceProvider provider, IMessage message) - { - var source = provider.GetService(); - var target = provider.GetService(); - source.Send(message); - IMessage outputMessage = target.Receive(); - Assert.NotNull(outputMessage); - return outputMessage; - } - - private IMessage DoSendReceive(ServiceProvider provider, string jsonPayload, params KeyValuePair[] headers) - { - var source = provider.GetService(); - var target = provider.GetService(); - AbstractMessageBuilder builder = MessageBuilder.WithPayload(Encoding.UTF8.GetBytes(jsonPayload)); - - foreach (KeyValuePair header in headers) - { - builder.SetHeader(header.Key, header.Value); - } - - source.Send((IMessage)builder.Build()); - IMessage outputMessage = target.Receive(); - Assert.NotNull(outputMessage); - return outputMessage; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/Employee.cs b/src/Stream/test/Stream.Test/Tck/Employee.cs deleted file mode 100644 index 8c4ec631c9..0000000000 --- a/src/Stream/test/Stream.Test/Tck/Employee.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class Employee -{ - public int Id { get; set; } - - public TPerson Person { get; set; } -} diff --git a/src/Stream/test/Stream.Test/Tck/ErrorHandlingTest.cs b/src/Stream/test/Stream.Test/Tck/ErrorHandlingTest.cs deleted file mode 100644 index b13b1426d5..0000000000 --- a/src/Stream/test/Stream.Test/Tck/ErrorHandlingTest.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Common.Lifecycle; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binding; -using Steeltoe.Stream.Extensions; -using Steeltoe.Stream.TestBinder; -using Xunit; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class ErrorHandlingTest : AbstractTest -{ - private readonly IServiceCollection _container; - - public ErrorHandlingTest() - { - List searchDirectories = GetSearchDirectories("TestBinder"); - _container = CreateStreamsContainerWithDefaultBindings(searchDirectories); - } - - [Fact] - public async Task TestGlobalErrorWithMessage() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - DoSend(provider, message); - - var errorHandler = provider.GetService(); - Assert.True(errorHandler.GlobalErrorInvoked); - } - - [Fact] - public async Task TestGlobalErrorWithThrowable() - { - _container.AddStreamListeners(); - ServiceProvider provider = _container.BuildServiceProvider(true); - - await provider.GetRequiredService().OnRefreshAsync(); // Only starts Autostart - - var streamProcessor = provider.GetRequiredService(); - streamProcessor.Initialize(); - IMessage message = Message.Create(Encoding.UTF8.GetBytes("foo")); - DoSend(provider, message); - - var errorHandler = provider.GetService(); - Assert.True(errorHandler.GlobalErrorInvoked); - } - - private void DoSend(ServiceProvider provider, IMessage message) - { - var source = provider.GetService(); - source.Send(message); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/FooBarMessageConverter.cs b/src/Stream/test/Stream.Test/Tck/FooBarMessageConverter.cs deleted file mode 100644 index 4866ad65f5..0000000000 --- a/src/Stream/test/Stream.Test/Tck/FooBarMessageConverter.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Converter; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class FooBarMessageConverter : AbstractMessageConverter -{ - public const string DefaultServiceName = nameof(FooBarMessageConverter); - - public override string ServiceName { get; set; } = DefaultServiceName; - - public FooBarMessageConverter() - : this(MimeType.ToMimeType("foo/bar")) - { - } - - public FooBarMessageConverter(MimeType supportedMimeType) - : base(supportedMimeType) - { - } - - protected override bool Supports(Type type) - { - return type == null || typeof(string).IsAssignableFrom(type); - } - - protected override object ConvertFromInternal(IMessage message, Type targetClass, object conversionHint) - { - return GetType().Name; - } - - protected override object ConvertToInternal(object payload, IMessageHeaders headers, object conversionHint) - { - return Encoding.UTF8.GetBytes((string)payload); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/GlobalErrorHandlerWithErrorMessageConfiguration.cs b/src/Stream/test/Stream.Test/Tck/GlobalErrorHandlerWithErrorMessageConfiguration.cs deleted file mode 100644 index 265d76114f..0000000000 --- a/src/Stream/test/Stream.Test/Tck/GlobalErrorHandlerWithErrorMessageConfiguration.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class GlobalErrorHandlerWithErrorMessageConfiguration -{ - public bool GlobalErrorInvoked { get; set; } - - [StreamListener(ISink.InputName)] - public void Input(string value) - { - throw new Exception("test exception"); - } - - [StreamListener("errorChannel")] - public void GeneralError(IMessage message) - { - GlobalErrorInvoked = true; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/GlobalErrorHandlerWithExceptionConfig.cs b/src/Stream/test/Stream.Test/Tck/GlobalErrorHandlerWithExceptionConfig.cs deleted file mode 100644 index a9d7725c32..0000000000 --- a/src/Stream/test/Stream.Test/Tck/GlobalErrorHandlerWithExceptionConfig.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class GlobalErrorHandlerWithExceptionConfig -{ - public bool GlobalErrorInvoked { get; set; } - - [StreamListener(ISink.InputName)] - public void Input(string value) - { - throw new Exception("test exception"); - } - - [StreamListener("errorChannel")] - public void GeneralError(Exception exception) - { - GlobalErrorInvoked = true; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/InternalPipeLine.cs b/src/Stream/test/Stream.Test/Tck/InternalPipeLine.cs deleted file mode 100644 index 3e12b57f5e..0000000000 --- a/src/Stream/test/Stream.Test/Tck/InternalPipeLine.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class InternalPipeLine -{ - [StreamListener(ISink.InputName)] - [SendTo("internalchannel")] - public string HandleA(Person value) - { - return $"{{\"name\":\"{value.Name.ToUpperInvariant()}\"}}"; - } - - [StreamListener("internalchannel")] - [SendTo(ISource.OutputName)] - public string HandleB(Person value) - { - return value.ToString(); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/ListInputConfiguration.cs b/src/Stream/test/Stream.Test/Tck/ListInputConfiguration.cs deleted file mode 100644 index e0c5ba794e..0000000000 --- a/src/Stream/test/Stream.Test/Tck/ListInputConfiguration.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class ListInputConfiguration -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public List Echo(List value) - { - return value; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/MapInputConfiguration.cs b/src/Stream/test/Stream.Test/Tck/MapInputConfiguration.cs deleted file mode 100644 index e3e4ca7ea0..0000000000 --- a/src/Stream/test/Stream.Test/Tck/MapInputConfiguration.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class MapInputConfiguration -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public Dictionary Echo(Dictionary value) - { - return value; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/MapPayloadConfiguration.cs b/src/Stream/test/Stream.Test/Tck/MapPayloadConfiguration.cs deleted file mode 100644 index 2d77017396..0000000000 --- a/src/Stream/test/Stream.Test/Tck/MapPayloadConfiguration.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class MapPayloadConfiguration -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public Dictionary Echo(IMessage> value) - { - return value.Payload; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/MessageHeadersInputConfiguration.cs b/src/Stream/test/Stream.Test/Tck/MessageHeadersInputConfiguration.cs deleted file mode 100644 index c6d4f10153..0000000000 --- a/src/Stream/test/Stream.Test/Tck/MessageHeadersInputConfiguration.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class MessageHeadersInputConfiguration -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public IDictionary Echo(MessageHeaders value) - { - return value; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/Person.cs b/src/Stream/test/Stream.Test/Tck/Person.cs deleted file mode 100644 index 14d423f562..0000000000 --- a/src/Stream/test/Stream.Test/Tck/Person.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class Person -{ - public string Name { get; set; } - - public override string ToString() - { - return Name; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/PojoMessageToStringMessageServiceActivator.cs b/src/Stream/test/Stream.Test/Tck/PojoMessageToStringMessageServiceActivator.cs deleted file mode 100644 index 081dffe138..0000000000 --- a/src/Stream/test/Stream.Test/Tck/PojoMessageToStringMessageServiceActivator.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Integration.Attributes; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class PojoMessageToStringMessageServiceActivator -{ - [ServiceActivator(InputChannel = ISink.InputName, OutputChannel = ISource.OutputName)] - public IMessage Echo(IMessage value) - { - return (IMessage)MessageBuilder.WithPayload(value.Payload.ToString()).SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build(); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/PojoMessageToStringMessageStreamListener.cs b/src/Stream/test/Stream.Test/Tck/PojoMessageToStringMessageStreamListener.cs deleted file mode 100644 index e6260b5e68..0000000000 --- a/src/Stream/test/Stream.Test/Tck/PojoMessageToStringMessageStreamListener.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class PojoMessageToStringMessageStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public IMessage Echo(IMessage value) - { - return (IMessage)MessageBuilder.WithPayload(value.Payload.ToString()).SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build(); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/PojoToByteArrayStreamListener.cs b/src/Stream/test/Stream.Test/Tck/PojoToByteArrayStreamListener.cs deleted file mode 100644 index 9a9a71ba69..0000000000 --- a/src/Stream/test/Stream.Test/Tck/PojoToByteArrayStreamListener.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class PojoToByteArrayStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public byte[] Echo(Person value) - { - return Encoding.UTF8.GetBytes(value.ToString()); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/PojoToPojoStreamListener.cs b/src/Stream/test/Stream.Test/Tck/PojoToPojoStreamListener.cs deleted file mode 100644 index 9ed5f7c2af..0000000000 --- a/src/Stream/test/Stream.Test/Tck/PojoToPojoStreamListener.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class PojoToPojoStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public Person Echo(Person value) - { - return value; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/PojoToStringStreamListener.cs b/src/Stream/test/Stream.Test/Tck/PojoToStringStreamListener.cs deleted file mode 100644 index 0530769e28..0000000000 --- a/src/Stream/test/Stream.Test/Tck/PojoToStringStreamListener.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class PojoToStringStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public string Echo(Person value) - { - return value.ToString(); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/StringMessageToStringMessageServiceActivator.cs b/src/Stream/test/Stream.Test/Tck/StringMessageToStringMessageServiceActivator.cs deleted file mode 100644 index b77ef334b9..0000000000 --- a/src/Stream/test/Stream.Test/Tck/StringMessageToStringMessageServiceActivator.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Common.Util; -using Steeltoe.Integration.Attributes; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class StringMessageToStringMessageServiceActivator -{ - [ServiceActivator(InputChannel = ISink.InputName, OutputChannel = ISource.OutputName)] - public IMessage Echo(IMessage value) - { - var settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - // assume it is string because CT is text/plain - var serializer = JsonSerializer.Create(settings); - var textReader = new StringReader(value.Payload); - var person = (Person)serializer.Deserialize(textReader, typeof(Person)); - - return (IMessage)MessageBuilder.WithPayload(person.ToString()).SetHeader(MessageHeaders.ContentType, MimeTypeUtils.TextPlain).Build(); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/StringToMapMessageStreamListener.cs b/src/Stream/test/Stream.Test/Tck/StringToMapMessageStreamListener.cs deleted file mode 100644 index 43ea2d9169..0000000000 --- a/src/Stream/test/Stream.Test/Tck/StringToMapMessageStreamListener.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; -using Xunit; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class StringToMapMessageStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public string Echo(IMessage> value) - { - Assert.IsType>(value.Payload); - value.Payload.TryGetValue("name", out object result); - return (string)result; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/StringToMapStreamListener.cs b/src/Stream/test/Stream.Test/Tck/StringToMapStreamListener.cs deleted file mode 100644 index 1cbd0de598..0000000000 --- a/src/Stream/test/Stream.Test/Tck/StringToMapStreamListener.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class StringToMapStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public string Echo([Payload] Dictionary value) - { - value.TryGetValue("name", out object result); - return (string)result; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/StringToPojoStreamListener.cs b/src/Stream/test/Stream.Test/Tck/StringToPojoStreamListener.cs deleted file mode 100644 index aa310e923b..0000000000 --- a/src/Stream/test/Stream.Test/Tck/StringToPojoStreamListener.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class StringToPojoStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public Person Echo(string value) - { - var settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - var serializer = JsonSerializer.Create(settings); - var textReader = new StringReader(value); - return (Person)serializer.Deserialize(textReader, typeof(Person)); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/StringToStringStreamListener.cs b/src/Stream/test/Stream.Test/Tck/StringToStringStreamListener.cs deleted file mode 100644 index bf166aa0f6..0000000000 --- a/src/Stream/test/Stream.Test/Tck/StringToStringStreamListener.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class StringToStringStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public string Echo(string value) - { - return value; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/TypelessMessageConfigurationServiceActivator.cs b/src/Stream/test/Stream.Test/Tck/TypelessMessageConfigurationServiceActivator.cs deleted file mode 100644 index eb42525500..0000000000 --- a/src/Stream/test/Stream.Test/Tck/TypelessMessageConfigurationServiceActivator.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Integration.Attributes; -using Steeltoe.Messaging; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class TypelessMessageConfigurationServiceActivator -{ - [ServiceActivator(InputChannel = ISink.InputName, OutputChannel = ISource.OutputName)] - public object Echo(IMessage value) - { - Console.WriteLine(value.Payload); - return value.Payload; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/TypelessMessageToPojoStreamListener.cs b/src/Stream/test/Stream.Test/Tck/TypelessMessageToPojoStreamListener.cs deleted file mode 100644 index 024e3ca47b..0000000000 --- a/src/Stream/test/Stream.Test/Tck/TypelessMessageToPojoStreamListener.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class TypelessMessageToPojoStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public Person Echo(IMessage value) - { - var settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - // assume it is string because CT is text/plain - var serializer = JsonSerializer.Create(settings); - var textReader = new StringReader((string)value.Payload); - return (Person)serializer.Deserialize(textReader, typeof(Person)); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/TypelessPayloadConfiguration.cs b/src/Stream/test/Stream.Test/Tck/TypelessPayloadConfiguration.cs deleted file mode 100644 index 270f2db75f..0000000000 --- a/src/Stream/test/Stream.Test/Tck/TypelessPayloadConfiguration.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class TypelessPayloadConfiguration -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public object Echo(object value) - { - return value; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/TypelessPayloadConfigurationServiceActivator.cs b/src/Stream/test/Stream.Test/Tck/TypelessPayloadConfigurationServiceActivator.cs deleted file mode 100644 index 16df90bdfe..0000000000 --- a/src/Stream/test/Stream.Test/Tck/TypelessPayloadConfigurationServiceActivator.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Integration.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class TypelessPayloadConfigurationServiceActivator -{ - [ServiceActivator(InputChannel = ISink.InputName, OutputChannel = ISource.OutputName)] - public object Echo(object value) - { - Console.WriteLine(value); - return value; - } -} diff --git a/src/Stream/test/Stream.Test/Tck/TypelessToMessageStreamListener.cs b/src/Stream/test/Stream.Test/Tck/TypelessToMessageStreamListener.cs deleted file mode 100644 index 37dfe6db76..0000000000 --- a/src/Stream/test/Stream.Test/Tck/TypelessToMessageStreamListener.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class TypelessToMessageStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public IMessage Echo(object value) - { - return (IMessage)MessageBuilder.WithPayload(value.ToString()).SetHeader("contentType", new MimeType("text", "plain")).Build(); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/TypelessToMessageTextOnlyContentTypeStreamListener.cs b/src/Stream/test/Stream.Test/Tck/TypelessToMessageTextOnlyContentTypeStreamListener.cs deleted file mode 100644 index 1ec67fea54..0000000000 --- a/src/Stream/test/Stream.Test/Tck/TypelessToMessageTextOnlyContentTypeStreamListener.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Util; -using Steeltoe.Messaging; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Messaging.Support; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class TypelessToMessageTextOnlyContentTypeStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public IMessage Echo(object value) - { - return MessageBuilder.WithPayload(value.ToString()).SetHeader("contentType", new MimeType("text")).Build(); - } -} diff --git a/src/Stream/test/Stream.Test/Tck/TypelessToPojoStreamListener.cs b/src/Stream/test/Stream.Test/Tck/TypelessToPojoStreamListener.cs deleted file mode 100644 index b1cb928b7a..0000000000 --- a/src/Stream/test/Stream.Test/Tck/TypelessToPojoStreamListener.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Steeltoe.Messaging.Handler.Attributes; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Messaging; - -namespace Steeltoe.Stream.Test.Tck; - -public sealed class TypelessToPojoStreamListener -{ - [StreamListener(ISink.InputName)] - [SendTo(ISource.OutputName)] - public Person Echo(object value) - { - var settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - // assume it is string because CT is text/plain - var serializer = JsonSerializer.Create(settings); - var textReader = new StringReader((string)value); - return (Person)serializer.Deserialize(textReader, typeof(Person)); - } -} diff --git a/src/Stream/test/Stream.Test/xunit.runner.json b/src/Stream/test/Stream.Test/xunit.runner.json deleted file mode 100644 index fdeefaa456..0000000000 --- a/src/Stream/test/Stream.Test/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "maxParallelThreads": 1, - "parallelizeTestCollections": false -} diff --git a/src/Stream/test/StubBinder1/Startup.cs b/src/Stream/test/StubBinder1/Startup.cs deleted file mode 100644 index 644b23b797..0000000000 --- a/src/Stream/test/StubBinder1/Startup.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.StubBinder1; - -[assembly: Binder(StubBinder1.BinderName, typeof(Startup))] - -namespace Steeltoe.Stream.StubBinder1; - -public sealed class Startup -{ - public IConfiguration Configuration { get; } - - public bool ConfigureServicesInvoked { get; set; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - ConfigureServicesInvoked = true; // Testing - IConfigurationSection section = Configuration.GetSection("binder1"); - section["name"] = "foobar"; // Unit test checks for this change to verify access to configuration - services.AddSingleton(); - } -} diff --git a/src/Stream/test/StubBinder1/Steeltoe.Stream.StubBinder1.csproj b/src/Stream/test/StubBinder1/Steeltoe.Stream.StubBinder1.csproj deleted file mode 100644 index 05a990de1c..0000000000 --- a/src/Stream/test/StubBinder1/Steeltoe.Stream.StubBinder1.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - net8.0;net6.0 - False - - - - - - - - - - - - diff --git a/src/Stream/test/StubBinder1/StubBinder1.cs b/src/Stream/test/StubBinder1/StubBinder1.cs deleted file mode 100644 index d1539dfdb6..0000000000 --- a/src/Stream/test/StubBinder1/StubBinder1.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.StubBinder1; - -public sealed class StubBinder1 : IBinder -{ - public const string BinderName = "binder1"; - - public Func BindConsumerFunc { get; set; } - - public Func BindProducerFunc { get; set; } - - public string ServiceName { get; set; } = BinderName; - - public Type TargetType => typeof(object); - - public IServiceProvider ServiceProvider { get; } - - public IConfiguration Configuration { get; } - - public StubBinder1(IServiceProvider serviceProvider, IConfiguration configuration) - { - ServiceProvider = serviceProvider; - Configuration = configuration; - } - - public IBinding BindConsumer(string name, string group, object inboundTarget, IConsumerOptions consumerOptions) - { - if (BindConsumerFunc != null) - { - return BindConsumerFunc(name, group, inboundTarget, consumerOptions); - } - - return null; - } - - public IBinding BindProducer(string name, object outboundTarget, IProducerOptions producerOptions) - { - if (BindProducerFunc != null) - { - return BindProducerFunc(name, outboundTarget, producerOptions); - } - - return null; - } - - public void Dispose() - { - } -} diff --git a/src/Stream/test/StubBinder2/Startup.cs b/src/Stream/test/StubBinder2/Startup.cs deleted file mode 100644 index f3731a93c5..0000000000 --- a/src/Stream/test/StubBinder2/Startup.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.StubBinder2; - -[assembly: Binder(StubBinder2.BinderName, typeof(Startup))] - -namespace Steeltoe.Stream.StubBinder2; - -public sealed class Startup -{ - public IConfiguration Configuration { get; } - - public bool ConfigureServicesInvoked { get; set; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - ConfigureServicesInvoked = true; // Testing - services.AddSingleton(); - services.AddSingleton(); - } -} diff --git a/src/Stream/test/StubBinder2/Steeltoe.Stream.StubBinder2.csproj b/src/Stream/test/StubBinder2/Steeltoe.Stream.StubBinder2.csproj deleted file mode 100644 index 05a990de1c..0000000000 --- a/src/Stream/test/StubBinder2/Steeltoe.Stream.StubBinder2.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - net8.0;net6.0 - False - - - - - - - - - - - - diff --git a/src/Stream/test/StubBinder2/StubBinder2.cs b/src/Stream/test/StubBinder2/StubBinder2.cs deleted file mode 100644 index d9c3c7dd3b..0000000000 --- a/src/Stream/test/StubBinder2/StubBinder2.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; - -namespace Steeltoe.Stream.StubBinder2; - -public sealed class StubBinder2 : IBinder -{ - public const string BinderName = "binder2"; - - public string ServiceName { get; set; } = BinderName; - - public Type TargetType => typeof(object); - - public IServiceProvider ServiceProvider { get; } - - public StubBinder2Dependency StubBinder2Dependency { get; } - - public StubBinder2(IServiceProvider serviceProvider, StubBinder2Dependency stubBinder2Dependency) - { - ServiceProvider = serviceProvider; - StubBinder2Dependency = stubBinder2Dependency; - } - - public IBinding BindConsumer(string name, string group, object inboundTarget, IConsumerOptions consumerOptions) - { - return null; - } - - public IBinding BindProducer(string name, object outboundTarget, IProducerOptions producerOptions) - { - return null; - } - - public void Dispose() - { - } -} diff --git a/src/Stream/test/StubBinder2/StubBinder2Dependency.cs b/src/Stream/test/StubBinder2/StubBinder2Dependency.cs deleted file mode 100644 index a966a67ecc..0000000000 --- a/src/Stream/test/StubBinder2/StubBinder2Dependency.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -namespace Steeltoe.Stream.StubBinder2; - -public sealed class StubBinder2Dependency -{ - public int Value { get; set; } -} diff --git a/src/Stream/test/TestBinder/AbstractDestination.cs b/src/Stream/test/TestBinder/AbstractDestination.cs deleted file mode 100644 index 7881ef08c2..0000000000 --- a/src/Stream/test/TestBinder/AbstractDestination.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.TestBinder; - -public class AbstractDestination -{ - private ISubscribableChannel _channel; - - internal ISubscribableChannel Channel - { - get => _channel; - set - { - _channel = value; - AfterChannelIsSet(); - } - } - - protected internal virtual void AfterChannelIsSet() - { - } -} diff --git a/src/Stream/test/TestBinder/InputDestination.cs b/src/Stream/test/TestBinder/InputDestination.cs deleted file mode 100644 index 75b04f1e77..0000000000 --- a/src/Stream/test/TestBinder/InputDestination.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.TestBinder; - -public sealed class InputDestination : AbstractDestination -{ - public void Send(IMessage message) - { - Channel.Send(message); - } -} diff --git a/src/Stream/test/TestBinder/OutputDestination.cs b/src/Stream/test/TestBinder/OutputDestination.cs deleted file mode 100644 index e4d1e99531..0000000000 --- a/src/Stream/test/TestBinder/OutputDestination.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using Steeltoe.Messaging; - -namespace Steeltoe.Stream.TestBinder; - -public sealed class OutputDestination : AbstractDestination -{ - private BlockingCollection _messages; - - public IMessage Receive(long timeout) - { - try - { - _messages.TryTake(out IMessage result, TimeSpan.FromMilliseconds(timeout)); - return result; - } - catch (Exception) - { - // Log - } - - return null; - } - - public IMessage Receive() - { - return Receive(0); - } - - protected internal override void AfterChannelIsSet() - { - _messages = new BlockingCollection(); - Channel.Subscribe(new MessageHandler(this)); - } - - private sealed class MessageHandler : IMessageHandler - { - private readonly OutputDestination _outputDestination; - - public string ServiceName { get; set; } - - public MessageHandler(OutputDestination destination) - { - _outputDestination = destination; - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - _outputDestination._messages.Add(message); - } - } -} diff --git a/src/Stream/test/TestBinder/Startup.cs b/src/Stream/test/TestBinder/Startup.cs deleted file mode 100644 index 9aa1603e30..0000000000 --- a/src/Stream/test/TestBinder/Startup.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Steeltoe.Stream.Attributes; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.TestBinder; - -[assembly: Binder("testbinder", typeof(Startup))] - -namespace Steeltoe.Stream.TestBinder; - -public sealed class Startup -{ - public IConfiguration Configuration { get; } - - public bool ConfigureServicesInvoked { get; set; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - ConfigureServicesInvoked = true; // Testing - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(p => p.GetRequiredService()); - } -} diff --git a/src/Stream/test/TestBinder/Steeltoe.Stream.TestBinder.csproj b/src/Stream/test/TestBinder/Steeltoe.Stream.TestBinder.csproj deleted file mode 100644 index 05a990de1c..0000000000 --- a/src/Stream/test/TestBinder/Steeltoe.Stream.TestBinder.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - net8.0;net6.0 - False - - - - - - - - - - - - diff --git a/src/Stream/test/TestBinder/TestChannelBinder.cs b/src/Stream/test/TestBinder/TestChannelBinder.cs deleted file mode 100644 index 5bbb7b1aee..0000000000 --- a/src/Stream/test/TestBinder/TestChannelBinder.cs +++ /dev/null @@ -1,239 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Steeltoe.Common.Contexts; -using Steeltoe.Common.Retry; -using Steeltoe.Common.Util; -using Steeltoe.Integration; -using Steeltoe.Integration.Endpoint; -using Steeltoe.Integration.Handler; -using Steeltoe.Integration.Support; -using Steeltoe.Messaging; -using Steeltoe.Stream.Binder; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Provisioning; -using static Steeltoe.Stream.TestBinder.TestChannelBinderProvisioner; - -namespace Steeltoe.Stream.TestBinder; - -public sealed class TestChannelBinder : AbstractPollableMessageSourceBinder -{ - private readonly ILogger _logger; - - public IMessage LastError { get; private set; } - - public override string ServiceName { get; set; } = "testbinder"; - - public IMessageSource MessageSourceDelegate { get; set; } = new MessageSource(); - - public TestChannelBinder(IApplicationContext context, TestChannelBinderProvisioner provisioningProvider, ILogger logger) - : base(context, Array.Empty(), provisioningProvider, logger) - { - _logger = logger; - } - - protected override IMessageHandler CreateProducerMessageHandler(IProducerDestination destination, IProducerOptions producerProperties, - IMessageChannel errorChannel) - { - var handler = new BridgeHandler(ApplicationContext) - { - OutputChannel = ((SpringIntegrationProducerDestination)destination).Channel - }; - - return handler; - } - - protected override IMessageProducer CreateConsumerEndpoint(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - IErrorMessageStrategy errorMessageStrategy = new DefaultErrorMessageStrategy(); - ISubscribableChannel siBinderInputChannel = ((SpringIntegrationConsumerDestination)destination).Channel; - - var messageListenerContainer = new TestMessageListeningContainer(); - var endpoint = new TestMessageProducerSupportEndpoint(ApplicationContext, messageListenerContainer, _logger); - - string groupName = !string.IsNullOrEmpty(group) ? group : "anonymous"; - ErrorInfrastructure errorInfrastructure = RegisterErrorInfrastructure(destination, groupName, consumerOptions, _logger); - - if (consumerOptions.MaxAttempts > 1) - { - endpoint.RetryTemplate = BuildRetryTemplate(consumerOptions); - endpoint.RecoveryCallback = errorInfrastructure.Recoverer; - } - else - { - endpoint.ErrorMessageStrategy = errorMessageStrategy; - endpoint.ErrorChannel = errorInfrastructure.ErrorChannel; - } - - endpoint.Init(); - - siBinderInputChannel.Subscribe(messageListenerContainer); - - return endpoint; - } - - protected override IMessageHandler GetErrorMessageHandler(IConsumerDestination destination, string group, IConsumerOptions consumerOptions) - { - return new ErrorMessageHandler(this); - } - - protected override PolledConsumerResources CreatePolledConsumerResources(string name, string group, IConsumerDestination destination, - IConsumerOptions consumerOptions) - { - return new PolledConsumerResources(MessageSourceDelegate, RegisterErrorInfrastructure(destination, group, consumerOptions, _logger)); - } - - public sealed class ErrorMessageHandler : ILastSubscriberMessageHandler - { - public TestChannelBinder Binder { get; } - - public string ServiceName { get; set; } - - public ErrorMessageHandler(TestChannelBinder binder) - { - Binder = binder; - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - Binder.LastError = message; - } - } - - public sealed class MessageSource : IMessageSource - { - public IMessage Receive() - { - IMessage message = Message.Create("polled data", new MessageHeaders(new Dictionary - { - { MessageHeaders.ContentType, "text/plain" } - })); - - return message; - } - } - - public sealed class TestMessageListeningContainer : IMessageHandler - { - public string ServiceName { get; set; } - - public Action MessageListener { get; set; } - - public TestMessageListeningContainer() - { - ServiceName = $"{GetType().Name}@{GetHashCode()}"; - } - - public void HandleMessage(IMessage message) - { - MessageListener.Invoke(message); - } - } - - public sealed class TestMessageProducerSupportEndpoint : MessageProducerSupportEndpoint - { - private static readonly AsyncLocal AttributesHolder = new(); - private readonly TestMessageListeningContainer _messageListenerContainer; - - public RetryTemplate RetryTemplate { get; set; } - - public IRecoveryCallback RecoveryCallback { get; set; } - - public TestMessageProducerSupportEndpoint(IApplicationContext context, TestMessageListeningContainer messageListenerContainer, ILogger logger) - : base(context, logger) - { - _messageListenerContainer = messageListenerContainer; - } - - public void Init() - { - if (RetryTemplate != null && ErrorChannel != null) - { - throw new InvalidOperationException("Cannot have an 'errorChannel' property when a 'RetryTemplate' is " + - "provided; use an 'ErrorMessageSendingRecoverer' in the 'recoveryCallback' property to " + - "send an error message when retries are exhausted"); - } - - var messageListener = new Listener(this); - - if (RetryTemplate != null) - { - RetryTemplate.RegisterListener(messageListener); - } - - _messageListenerContainer.MessageListener = m => messageListener.Accept(m); - } - - private sealed class Listener : IRetryListener - { - private readonly TestMessageProducerSupportEndpoint _adapter; - - public Listener(TestMessageProducerSupportEndpoint adapter) - { - _adapter = adapter; - } - - public void Accept(IMessage message) - { - try - { - if (_adapter.RetryTemplate == null) - { - try - { - ProcessMessage(message); - } - finally - { - AttributesHolder.Value = null; - } - } - else - { - _adapter.RetryTemplate.Execute(_ => ProcessMessage(message), _adapter.RecoveryCallback); - } - } - catch (Exception e) - { - if (_adapter.ErrorChannel != null) - { - _adapter.MessagingTemplate.Send(_adapter.ErrorChannel, - _adapter.BuildErrorMessage(null, new InvalidOperationException($"Message conversion failed: {message}", e))); - } - else - { - throw; - } - } - } - - public bool Open(IRetryContext context) - { - if (_adapter.RecoveryCallback != null) - { - AttributesHolder.Value = context; - } - - return true; - } - - public void Close(IRetryContext context, Exception exception) - { - AttributesHolder.Value = null; - } - - public void OnError(IRetryContext context, Exception exception) - { - // Ignore - } - - private void ProcessMessage(IMessage message) - { - _adapter.SendMessage(message); - } - } - } -} diff --git a/src/Stream/test/TestBinder/TestChannelBinderProvisioner.cs b/src/Stream/test/TestBinder/TestChannelBinderProvisioner.cs deleted file mode 100644 index ce018f7ad4..0000000000 --- a/src/Stream/test/TestBinder/TestChannelBinderProvisioner.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using Steeltoe.Common.Contexts; -using Steeltoe.Integration.Channel; -using Steeltoe.Messaging; -using Steeltoe.Stream.Configuration; -using Steeltoe.Stream.Provisioning; - -namespace Steeltoe.Stream.TestBinder; - -public sealed class TestChannelBinderProvisioner : IProvisioningProvider -{ - private readonly Dictionary _provisionedDestinations = new(); - private readonly IApplicationContext _context; - - public InputDestination InputDestination { get; } - - public OutputDestination OutputDestination { get; } - - public TestChannelBinderProvisioner(IApplicationContext context, InputDestination inputDestination, OutputDestination outputDestination) - { - InputDestination = inputDestination; - OutputDestination = outputDestination; - _context = context; - } - - public IProducerDestination ProvisionProducerDestination(string name, IProducerOptions options) - { - ISubscribableChannel destination = ProvisionDestination(name, true); - OutputDestination.Channel = destination; - return new SpringIntegrationProducerDestination(name, destination); - } - - public IConsumerDestination ProvisionConsumerDestination(string name, string group, IConsumerOptions options) - { - ISubscribableChannel destination = ProvisionDestination(name, false); - - if (InputDestination != null) - { - InputDestination.Channel = destination; - } - - return new SpringIntegrationConsumerDestination(name, destination); - } - - private ISubscribableChannel ProvisionDestination(string name, bool pubSub) - { - string destinationName = $"{name}.destination"; - _provisionedDestinations.TryGetValue(destinationName, out ISubscribableChannel destination); - - if (destination == null) - { - if (pubSub) - { - destination = new PublishSubscribeChannel(_context); - } - else - { - destination = new DirectChannel(_context); - } - - ((AbstractMessageChannel)destination).ServiceName = destinationName; - _provisionedDestinations.Add(destinationName, destination); - } - - return destination; - } - - internal sealed class SpringIntegrationConsumerDestination : IConsumerDestination - { - public ISubscribableChannel Channel { get; } - - public string Name { get; } - - public SpringIntegrationConsumerDestination(string name, ISubscribableChannel channel) - { - Name = name; - Channel = channel; - } - - public string GetNameForPartition(int partition) - { - return Name + partition; - } - } - - internal sealed class SpringIntegrationProducerDestination : IProducerDestination - { - public ISubscribableChannel Channel { get; } - - public string Name { get; } - - public SpringIntegrationProducerDestination(string name, ISubscribableChannel channel) - { - Name = name; - Channel = channel; - } - - public string GetNameForPartition(int partition) - { - return Name + partition; - } - } -} diff --git a/versions.props b/versions.props index 91e34a5ddb..113b379578 100644 --- a/versions.props +++ b/versions.props @@ -5,9 +5,7 @@ It's safe to update these at any time, so wildcards are allowed. --> - 0.13.* 6.0.* - 2.0.* 7.0.* 6.1.* 6.12.* @@ -59,7 +57,6 @@ --> 2.2.* - 5.1.* 1.6.10.* 0.2.* 3.1.* @@ -71,17 +68,11 @@ --> 8.0.* - 13.0.* 12.1.* - 4.7.* 1.5.* 1.4.0-rc.3 1.0.0-rc9.2 1.6.* - 1.1.* - 8.0.* - 5.1.* - 4.3.* 7.0.* 7.0.* 4.1.*