示例#1
0
 public IJetStreamPullSubscription PullSubscribe(string subject, PullSubscribeOptions options)
 {
     ValidateNotNull(options, "Pull Subscribe Options");
     ValidateSubject(subject, IsSubjectRequired(options));
     return((IJetStreamPullSubscription)CreateSubscription(subject, null, null, false, null, options));
 }
 /// <summary>
 /// Builds the PullSubscribeOptions with this configuration
 /// </summary>
 /// <returns>The PullSubscribeOptions</returns>
 public PullSubscribeOptions BuildPullSubscribeOptions()
 {
     return(PullSubscribeOptions.Builder().WithConfiguration(Build()).Build());
 }
示例#3
0
        Subscription CreateSubscription(string subject, string queueName,
                                        EventHandler <MsgHandlerEventArgs> userHandler, bool autoAck,
                                        PushSubscribeOptions pushSubscribeOptions,
                                        PullSubscribeOptions pullSubscribeOptions)
        {
            // 1. Prepare for all the validation
            bool isPullMode = pullSubscribeOptions != null;

            SubscribeOptions      so;
            string                stream;
            string                qgroup;
            ConsumerConfiguration userCC;

            if (isPullMode)
            {
                so     = pullSubscribeOptions; // options must have already been checked to be non null
                stream = pullSubscribeOptions.Stream;

                userCC = so.ConsumerConfiguration;

                qgroup = null; // just to make compiler happy both paths set variable
                ValidateNotSupplied(userCC.DeliverGroup, JsSubPullCantHaveDeliverGroup);
                ValidateNotSupplied(userCC.DeliverSubject, JsSubPullCantHaveDeliverSubject);
            }
            else
            {
                so     = pushSubscribeOptions ?? DefaultPushOpts;
                stream = so.Stream; // might be null, that's ok (see directBind)

                userCC = so.ConsumerConfiguration;

                if (!userCC.MaxPullWaiting.Equals(ConsumerConfiguration.IntUnset))
                {
                    throw JsSubPushCantHaveMaxPullWaiting.Instance();
                }
                if (!userCC.MaxBatch.Equals(ConsumerConfiguration.IntUnset))
                {
                    throw JsSubPushCantHaveMaxBatch.Instance();
                }
                if (!userCC.MaxBytes.Equals(ConsumerConfiguration.IntUnset))
                {
                    throw JsSubPushCantHaveMaxBytes.Instance();
                }

                // figure out the queue name
                qgroup = ValidateMustMatchIfBothSupplied(userCC.DeliverGroup, queueName, JsSubQueueDeliverGroupMismatch);
                if (so.Ordered && qgroup != null)
                {
                    throw JsSubOrderedNotAllowOnQueues.Instance();
                }
            }

            // 2A. Flow Control / heartbeat not always valid
            if (userCC.FlowControl || userCC.IdleHeartbeat != null && userCC.IdleHeartbeat.Millis > 0)
            {
                if (isPullMode)
                {
                    throw JsSubFcHbNotValidPull.Instance();
                }
                if (qgroup != null)
                {
                    throw JsSubFcHbHbNotValidQueue.Instance();
                }
            }

            // 2B. Did they tell me what stream? No? look it up.
            if (string.IsNullOrWhiteSpace(stream))
            {
                stream = LookupStreamBySubject(subject);
                if (stream == null)
                {
                    throw JsSubNoMatchingStreamForSubject.Instance();
                }
            }

            ConsumerConfiguration serverCC = null;
            string consumerName            = userCC.Durable;
            string inboxDeliver            = userCC.DeliverSubject;

            // 3. Does this consumer already exist?
            if (consumerName != null)
            {
                ConsumerInfo serverInfo = LookupConsumerInfo(stream, consumerName);

                if (serverInfo != null)   // the consumer for that durable already exists
                {
                    serverCC = serverInfo.ConsumerConfiguration;

                    // check to see if the user sent a different version than the server has
                    // modifications are not allowed
                    IList <string> changes = userCC.GetChanges(serverCC);
                    if (changes.Count > 0)
                    {
                        throw JsSubExistingConsumerCannotBeModified.Instance($"[{string.Join(",", changes)}]");
                    }

                    if (isPullMode)
                    {
                        if (!string.IsNullOrWhiteSpace(serverCC.DeliverSubject))
                        {
                            throw JsSubConsumerAlreadyConfiguredAsPush.Instance();
                        }
                    }
                    else if (string.IsNullOrWhiteSpace(serverCC.DeliverSubject))
                    {
                        throw JsSubConsumerAlreadyConfiguredAsPull.Instance();
                    }

                    if (string.IsNullOrWhiteSpace(serverCC.DeliverGroup))
                    {
                        // lookedUp was null/empty, means existing consumer is not a queue consumer
                        if (qgroup == null)
                        {
                            // ok fine, no queue requested and the existing consumer is also not a queue consumer
                            // we must check if the consumer is in use though
                            if (serverInfo.PushBound)
                            {
                                throw JsSubConsumerAlreadyBound.Instance();
                            }
                        }
                        else   // else they requested a queue but this durable was not configured as queue
                        {
                            throw JsSubExistingConsumerNotQueue.Instance();
                        }
                    }
                    else if (qgroup == null)
                    {
                        throw JsSubExistingConsumerIsQueue.Instance();
                    }
                    else if (!serverCC.DeliverGroup.Equals(qgroup))
                    {
                        throw JsSubExistingQueueDoesNotMatchRequestedQueue.Instance();
                    }

                    // durable already exists, make sure the filter subject matches
                    if (string.IsNullOrWhiteSpace(subject))
                    {
                        subject = userCC.FilterSubject;
                    }
                    else if (!IsFilterMatch(subject, serverCC.FilterSubject, stream))
                    {
                        throw JsSubSubjectDoesNotMatchFilter.Instance();
                    }

                    inboxDeliver = serverCC.DeliverSubject; // use the deliver subject as the inbox. It may be null, that's ok, we'll fix that later
                }
                else if (so.Bind)
                {
                    throw JsSubConsumerNotFoundRequiredInBind.Instance();
                }
            }

            // 4. If no deliver subject (inbox) provided or found, make an inbox.
            if (string.IsNullOrWhiteSpace(inboxDeliver))
            {
                inboxDeliver = Conn.NewInbox();
            }

            // 5. If consumer does not exist, create and settle on the config. Name will have to wait
            //    If the consumer exists, I know what the settled info is
            if (serverCC == null)
            {
                ConsumerConfiguration.ConsumerConfigurationBuilder ccBuilder = ConsumerConfiguration.Builder(userCC);

                // Pull mode doesn't maintain a deliver subject. It's actually an error if we send it.
                if (!isPullMode)
                {
                    ccBuilder.WithDeliverSubject(inboxDeliver);
                }

                if (string.IsNullOrWhiteSpace(userCC.FilterSubject))
                {
                    ccBuilder.WithFilterSubject(subject);
                }

                if (string.IsNullOrWhiteSpace(userCC.DeliverGroup) && !string.IsNullOrWhiteSpace(qgroup))
                {
                    ccBuilder.WithDeliverGroup(qgroup);
                }

                // createOrUpdateConsumer can fail for security reasons, maybe other reasons?
                serverCC     = ccBuilder.Build();
                consumerName = null;
            }

            // 6. create the subscription
            Subscription sub;

            if (isPullMode)
            {
                MessageManager[] managers = { new PullMessageManager() };
                SyncSubscription CreateSubDelegate(Connection lConn, string lSubject, string lQueueNa)
                {
                    return(new JetStreamPullSubscription(lConn, lSubject, this, stream, consumerName, inboxDeliver, managers));
                }

                sub = ((Connection)Conn).subscribeSync(inboxDeliver, queueName, CreateSubDelegate);
            }
            else
            {
                bool syncMode = userHandler == null;
                PushMessageManager pushMessageManager =
                    PushMessageManagerFactoryImpl((Connection)Conn, so, serverCC, qgroup != null, syncMode);
                MessageManager[] managers;
                if (so.Ordered)
                {
                    managers = new MessageManager[]
                    {
                        new SidCheckManager(),
                        pushMessageManager,
                        new OrderedMessageManager(this, stream, serverCC, syncMode)
                    };
                }
                else
                {
                    managers = new MessageManager[] { pushMessageManager };
                }

                if (syncMode)
                {
                    SyncSubscription CreateSubDelegate(Connection lConn, string lSubject, string lQueue)
                    {
                        return(new JetStreamPushSyncSubscription(lConn, lSubject, lQueue, this, stream, consumerName, inboxDeliver, managers));
                    }

                    sub = ((Connection)Conn).subscribeSync(inboxDeliver, queueName, CreateSubDelegate);
                }
                else
                {
                    EventHandler <MsgHandlerEventArgs> autoAckHandler;
                    if (autoAck && serverCC.AckPolicy != AckPolicy.None)
                    {
                        autoAckHandler = (sender, args) => args.Message.Ack();
                    }
                    else
                    {
                        autoAckHandler = (sender, args) => {};
                    }

                    EventHandler <MsgHandlerEventArgs> handler = (sender, args) =>
                    {
                        foreach (MessageManager mm in managers)
                        {
                            if (mm.Manage(args.Message))
                            {
                                return; // manager handled the message
                            }
                        }

                        userHandler.Invoke(sender, args);
                        autoAckHandler.Invoke(sender, args);
                    };

                    AsyncSubscription CreateAsyncSubDelegate(Connection lConn, string lSubject, string lQueue)
                    {
                        return(new JetStreamPushAsyncSubscription(lConn, lSubject, lQueue, this, stream, consumerName, inboxDeliver, managers));
                    }

                    sub = ((Connection)Conn).subscribeAsync(inboxDeliver, queueName, handler, CreateAsyncSubDelegate);
                }
            }

            // 7. The consumer might need to be created, do it here
            if (consumerName == null)
            {
                try
                {
                    ConsumerInfo ci = AddOrUpdateConsumerInternal(stream, serverCC);
                    if (sub is JetStreamAbstractSyncSubscription syncSub)
                    {
                        syncSub.Consumer = ci.Name;
                    }
                    else if (sub is JetStreamPushAsyncSubscription asyncSub)
                    {
                        asyncSub.Consumer = ci.Name;
                    }
                }
                catch
                {
                    // create consumer can fail, unsubscribe and then throw the exception to the user
                    sub.Unsubscribe();
                    throw;
                }
            }

            return(sub);
        }