/// <summary> /// See <see cref="Feature.Setup" />. /// </summary> protected internal override void Setup(FeatureConfigurationContext context) { if (!PersistenceStartup.HasSupportFor <StorageType.Sagas>(context.Settings)) { throw new Exception("The selected persistence doesn't have support for saga storage. Select another persistence or disable the sagas feature using endpointConfiguration.DisableFeature<Sagas>()"); } var sagaIdGenerator = context.Settings.GetOrDefault <ISagaIdGenerator>() ?? new DefaultSagaIdGenerator(); var sagaMetaModel = context.Settings.Get <SagaMetadataCollection>(); sagaMetaModel.Initialize(context.Settings.GetAvailableTypes(), conventions); var verifyIfEntitiesAreShared = !context.Settings.GetOrDefault <bool>(SagaSettings.DisableVerifyingIfEntitiesAreShared); if (verifyIfEntitiesAreShared) { sagaMetaModel.VerifyIfEntitiesAreShared(); } RegisterCustomFindersInContainer(context.Container, sagaMetaModel); foreach (var t in context.Settings.GetAvailableTypes()) { if (IsSagaNotFoundHandler(t)) { context.Container.ConfigureComponent(t, DependencyLifecycle.InstancePerCall); } } // Register the Saga related behaviors for incoming messages context.Pipeline.Register("InvokeSaga", b => new SagaPersistenceBehavior(b.GetRequiredService <ISagaPersister>(), sagaIdGenerator, sagaMetaModel), "Invokes the saga logic"); context.Pipeline.Register("InvokeSagaNotFound", new InvokeSagaNotFoundBehavior(), "Invokes saga not found logic"); context.Pipeline.Register("AttachSagaDetailsToOutGoingMessage", new AttachSagaDetailsToOutGoingMessageBehavior(), "Makes sure that outgoing messages have saga info attached to them"); }
public void Should_return_false_when_checking_if_persistence_supports_storage_type() { var settings = new SettingsHolder(); var supported = PersistenceStartup.HasSupportFor <StorageType.Subscriptions>(settings); Assert.IsFalse(supported); }
/// <summary> /// See <see cref="Feature.Setup" />. /// </summary> protected internal override void Setup(FeatureConfigurationContext context) { if (!PersistenceStartup.HasSupportFor <StorageType.Outbox>(context.Settings)) { throw new Exception("The selected persistence doesn't have support for outbox storage. Select another persistence or disable the outbox feature using endpointConfiguration.DisableFeature<Outbox>()"); } //note: in the future we should change the persister api to give us a "outbox factory" so that we can register it in DI here instead of relying on the persister to do it context.Pipeline.Register("ForceBatchDispatchToBeIsolated", new ForceBatchDispatchToBeIsolatedBehavior(), "Makes sure that we dispatch straight to the transport so that we can safely set the outbox record to dispatched one the dispatch pipeline returns."); }
public void Should_prevent_using_different_persistence_for_sagas_and_outbox() { var config = new EndpointConfiguration("MyEndpoint"); config.UsePersistence <FakeSagaPersistence, StorageType.Sagas>(); config.UsePersistence <FakeOutboxPersistence, StorageType.Outbox>(); var startup = new PersistenceStartup(); Assert.Throws <Exception>(() => { startup.Run(config.Settings); }, "Sagas and Outbox need to use the same type of persistence. Saga is configured to use FakeSagaPersistence. Outbox is configured to use FakeOutboxPersistence"); }
/// <summary> /// See <see cref="Feature.Setup" />. /// </summary> protected internal override void Setup(FeatureConfigurationContext context) { if (!PersistenceStartup.HasSupportFor <StorageType.Timeouts>(context.Settings)) { throw new Exception("The selected persistence doesn't have support for timeout storage. Select another persistence or disable the timeout manager feature using endpointConfiguration.DisableFeature<TimeoutManager>()"); } var pushRuntimeSettings = context.Settings.GetTimeoutManagerMaxConcurrency(); SetupStorageSatellite(context, pushRuntimeSettings); var dispatcherAddress = SetupDispatcherSatellite(context, pushRuntimeSettings); SetupTimeoutPoller(context, dispatcherAddress); }
/// <summary> /// See <see cref="Feature.Setup" />. /// </summary> protected internal override void Setup(FeatureConfigurationContext context) { // The MessageDrivenSubscriptions feature needs to be activated when using the subscription migration mode as some persister packages check this feature before enabling the subscription storage. if (SubscriptionMigrationMode.IsMigrationModeEnabled(context.Settings)) { return; } if (!PersistenceStartup.HasSupportFor <StorageType.Subscriptions>(context.Settings)) { throw new Exception("The selected persistence doesn't have support for subscription storage. Select another persistence or disable the message-driven subscriptions feature using endpointConfiguration.DisableFeature<MessageDrivenSubscriptions>()"); } var transportInfrastructure = context.Settings.Get <TransportInfrastructure>(); var canReceive = !context.Settings.GetOrDefault <bool>("Endpoint.SendOnly"); var conventions = context.Settings.Get <Conventions>(); var enforceBestPractices = context.Routing.EnforceBestPractices; var distributionPolicy = context.Routing.DistributionPolicy; var endpointInstances = context.Routing.EndpointInstances; var publishers = context.Routing.Publishers; var configuredPublishers = context.Settings.Get <ConfiguredPublishers>(); configuredPublishers.Apply(publishers, conventions, enforceBestPractices); context.Pipeline.Register(b => { var unicastPublishRouter = new UnicastPublishRouter(b.Build <MessageMetadataRegistry>(), i => transportInfrastructure.ToTransportAddress(LogicalAddress.CreateRemoteAddress(i)), b.Build <ISubscriptionStorage>()); return(new UnicastPublishRouterConnector(unicastPublishRouter, distributionPolicy)); }, "Determines how the published messages should be routed"); if (canReceive) { var subscriberAddress = context.Receiving.LocalAddress; var subscriptionRouter = new SubscriptionRouter(publishers, endpointInstances, i => transportInfrastructure.ToTransportAddress(LogicalAddress.CreateRemoteAddress(i))); context.Pipeline.Register(b => new MessageDrivenSubscribeTerminator(subscriptionRouter, subscriberAddress, context.Settings.EndpointName(), b.Build <IDispatchMessages>()), "Sends subscription requests when message driven subscriptions is in use"); context.Pipeline.Register(b => new MessageDrivenUnsubscribeTerminator(subscriptionRouter, subscriberAddress, context.Settings.EndpointName(), b.Build <IDispatchMessages>()), "Sends requests to unsubscribe when message driven subscriptions is in use"); var authorizer = context.Settings.GetSubscriptionAuthorizer(); if (authorizer == null) { authorizer = _ => true; } context.Container.RegisterSingleton(authorizer); context.Pipeline.Register <SubscriptionReceiverBehavior.Registration>(); } }
/// <summary> /// See <see cref="Feature.Setup" />. /// </summary> protected internal override void Setup(FeatureConfigurationContext context) { if (!PersistenceStartup.HasSupportFor <StorageType.Subscriptions>(context.Settings)) { throw new Exception("The selected persistence doesn't have support for subscription storage. Select another persistence or disable the message-driven subscriptions feature using endpointConfiguration.DisableFeature<MessageDrivenSubscriptions>()"); } context.Pipeline.Register <SubscriptionReceiverBehavior.Registration>(); var authorizer = context.Settings.GetSubscriptionAuthorizer(); if (authorizer == null) { authorizer = _ => true; } context.Container.RegisterSingleton(authorizer); }
/// <summary> /// See <see cref="Feature.Setup" />. /// </summary> protected internal override void Setup(FeatureConfigurationContext context) { if (!PersistenceStartup.HasSupportFor <StorageType.Outbox>(context.Settings)) { throw new Exception("The selected persistence doesn't have support for outbox storage. Select another persistence or disable the outbox feature using endpointConfiguration.DisableFeature<Outbox>()"); } if (context.Settings.GetRequiredTransactionModeForReceives() != TransportTransactionMode.ReceiveOnly) { throw new Exception( $"Outbox requires transport to be running in ${nameof(TransportTransactionMode.ReceiveOnly)} mode. Use ${nameof(TransportDefinition.TransportTransactionMode)} property on the transport definition to specify the transaction mode."); } //note: in the future we should change the persister api to give us a "outbox factory" so that we can register it in DI here instead of relying on the persister to do it context.Pipeline.Register("ForceBatchDispatchToBeIsolated", new ForceBatchDispatchToBeIsolatedBehavior(), "Makes sure that we dispatch straight to the transport so that we can safely set the outbox record to dispatched one the dispatch pipeline returns."); }
/// <summary> /// See <see cref="Feature.Setup"/> /// </summary> protected internal override void Setup(FeatureConfigurationContext context) { if (!PersistenceStartup.HasSupportFor(context.Settings, Storage.Outbox)) { throw new Exception("Selected persister doesn't have support for outbox storage. Please select another storage or disable the outbox feature using config.Features(f=>f.Disable<Outbox>())"); } context.Pipeline.Register <OutboxDeduplicationBehavior.OutboxDeduplicationRegistration>(); context.Pipeline.Register <OutboxRecordBehavior.OutboxRecorderRegistration>(); context.Pipeline.Replace(WellKnownStep.DispatchMessageToTransport, typeof(OutboxSendBehavior), "Sending behavior with a delay sending until all business transactions are committed to the outbox storage"); context.Container.ConfigureComponent <OutboxDeduplicationBehavior>(DependencyLifecycle.InstancePerCall) .ConfigureProperty(t => t.TransactionSettings, new TransactionSettings(context.Settings)); //make the audit use the outbox as well context.Container.ConfigureComponent <OutboxAwareAuditer>(DependencyLifecycle.InstancePerCall); context.Container.ConfigureProperty <AuditerWrapper>(t => t.AuditerImplType, typeof(OutboxAwareAuditer)); }
public void Should_not_prevent_using_different_persistence_for_sagas_and_outbox_if_only_one_of_the_features_is_enabled(bool sagasEnabled, bool outboxEnabled) { var config = new EndpointConfiguration("MyEndpoint"); config.UsePersistence <FakeSagaPersistence, StorageType.Sagas>(); config.UsePersistence <FakeOutboxPersistence, StorageType.Outbox>(); if (sagasEnabled) { config.EnableFeature <Sagas>(); } if (outboxEnabled) { config.EnableFeature <Outbox>(); } var startup = new PersistenceStartup(); Assert.DoesNotThrow(() => startup.Run(config.Settings), "Should not throw for a single single feature enabled out of the two."); }
/// <summary> /// See <see cref="Feature.Setup" />. /// </summary> protected internal override void Setup(FeatureConfigurationContext context) { // The MessageDrivenSubscriptions feature needs to be activated when using the subscription migration mode as some persister packages check this feature before enabling the subscription storage. if (SubscriptionMigrationMode.IsMigrationModeEnabled(context.Settings)) { return; } var transportDefinition = context.Settings.Get <TransportDefinition>(); var conventions = context.Settings.Get <Conventions>(); var enforceBestPractices = context.Routing.EnforceBestPractices; var distributionPolicy = context.Routing.DistributionPolicy; var endpointInstances = context.Routing.EndpointInstances; var publishers = context.Routing.Publishers; var configuredPublishers = context.Settings.Get <ConfiguredPublishers>(); configuredPublishers.Apply(publishers, conventions, enforceBestPractices); var publishingEnabled = context.Settings.Get <bool>(EnablePublishingSettingsKey); if (publishingEnabled) { if (!PersistenceStartup.HasSupportFor <StorageType.Subscriptions>(context.Settings)) { throw new Exception("The selected persistence doesn't have support for subscription storage. Select another persistence or disable the publish functionality using transportConfiguration.DisablePublishing()"); } context.Pipeline.Register("UnicastPublishRouterConnector", b => { var unicastPublishRouter = new UnicastPublishRouter(b.GetRequiredService <MessageMetadataRegistry>(), i => transportDefinition.ToTransportAddress(new QueueAddress(i.Endpoint, i.Discriminator, i.Properties, null)), b.GetRequiredService <ISubscriptionStorage>()); return(new UnicastPublishConnector(unicastPublishRouter, distributionPolicy)); }, "Determines how the published messages should be routed"); var authorizer = context.Settings.GetSubscriptionAuthorizer(); if (authorizer == null) { authorizer = _ => true; } context.Container.AddSingleton(authorizer); context.Pipeline.Register(typeof(SubscriptionReceiverBehavior), "Check for subscription messages and execute the requested behavior to subscribe or unsubscribe."); } else { context.Pipeline.Register(typeof(DisabledPublishingTerminator), "Throws an exception when trying to publish with publishing disabled"); } var canReceive = !context.Settings.GetOrDefault <bool>("Endpoint.SendOnly"); if (canReceive) { var subscriberAddress = context.Receiving.LocalAddress; var subscriptionRouter = new SubscriptionRouter(publishers, endpointInstances, i => transportDefinition.ToTransportAddress(new QueueAddress(i.Endpoint, i.Discriminator, i.Properties, null))); context.Pipeline.Register(b => new MessageDrivenSubscribeTerminator(subscriptionRouter, subscriberAddress, context.Settings.EndpointName(), b.GetRequiredService <IMessageDispatcher>()), "Sends subscription requests when message driven subscriptions is in use"); context.Pipeline.Register(b => new MessageDrivenUnsubscribeTerminator(subscriptionRouter, subscriberAddress, context.Settings.EndpointName(), b.GetRequiredService <IMessageDispatcher>()), "Sends requests to unsubscribe when message driven subscriptions is in use"); } else { context.Pipeline.Register(new SendOnlySubscribeTerminator(), "Throws an exception when trying to subscribe from a send-only endpoint"); context.Pipeline.Register(new SendOnlyUnsubscribeTerminator(), "Throws an exception when trying to unsubscribe from a send-only endpoint"); } // implementations of IInitializableSubscriptionStorage are optional and can be provided by persisters. context.RegisterStartupTask(b => new InitializableSubscriptionStorage(b.GetService <IInitializableSubscriptionStorage>())); }