2525import static io .nats .client .PushSubscribeOptions .DEFAULT_PUSH_OPTS ;
2626import static io .nats .client .impl .MessageManager .ManageResult ;
2727import static io .nats .client .support .NatsJetStreamClientError .*;
28+ import static io .nats .client .support .NatsJetStreamUtil .generateConsumerName ;
2829import static io .nats .client .support .NatsRequestCompletableFuture .CancelAction ;
2930import static io .nats .client .support .Validator .*;
3031
@@ -255,6 +256,7 @@ JetStreamSubscription createSubscription(String userSubscribeSubject,
255256 SubscribeOptions so ;
256257 String stream ;
257258 ConsumerConfiguration userCC ;
259+ boolean ordered ;
258260 String settledDeliverGroup = null ; // push might set this
259261
260262 if (isPullMode ) {
@@ -290,7 +292,7 @@ JetStreamSubscription createSubscription(String userSubscribeSubject,
290292 }
291293 }
292294
293- // 1B. Flow Control / heartbeat not always valid
295+ // 1B. Flow Control / heartbeat is not always valid
294296 if (userCC .getIdleHeartbeat () != null && userCC .getIdleHeartbeat ().toMillis () > 0 ) {
295297 if (isPullMode ) {
296298 throw JsSubFcHbNotValidPull .instance ();
@@ -300,7 +302,7 @@ JetStreamSubscription createSubscription(String userSubscribeSubject,
300302 }
301303 }
302304
303- // 2. figure out user provided subjects and prepare the settledFilterSubjects
305+ // 2. figure out user- provided subjects and prepare the settledFilterSubjects
304306 userSubscribeSubject = emptyAsNull (userSubscribeSubject );
305307 List <String > settledFilterSubjects = new ArrayList <>();
306308 if (userCC .getFilterSubjects () == null ) { // empty filterSubjects gives null
@@ -312,13 +314,13 @@ JetStreamSubscription createSubscription(String userSubscribeSubject,
312314 else {
313315 // userCC.filterSubjects not empty, validate them
314316 settledFilterSubjects .addAll (userCC .getFilterSubjects ());
315- // If userSubscribeSubject is provided it must be one of the filter subjects.
317+ // If userSubscribeSubject is provided, it must be one of the filter subjects.
316318 if (userSubscribeSubject != null && !settledFilterSubjects .contains (userSubscribeSubject )) {
317319 throw JsSubSubjectDoesNotMatchFilter .instance ();
318320 }
319321 }
320322
321- // 3. Did they tell me what stream? No? look it up.
323+ // 3. Did they tell me what stream? No? Look it up.
322324 final String settledStream ;
323325 if (stream == null ) {
324326 if (settledFilterSubjects .isEmpty ()) {
@@ -338,18 +340,20 @@ JetStreamSubscription createSubscription(String userSubscribeSubject,
338340 if (consumerName == null ) {
339341 consumerName = userCC .getName ();
340342 }
343+
341344 String inboxDeliver = userCC .getDeliverSubject ();
342345
343- // 4. Does this consumer already exist? FastBind bypasses the lookup;
344- // the dev better know what they are doing...
345- if (!so .isFastBind () && consumerName != null ) {
346+ // 4. Does this consumer already exist?
347+ // * FastBind bypasses the lookup; the dev better know what they are doing...
348+ // * ordered also bypasses the lookup b/c we know it's always a new name
349+ if (!so .isFastBind () && !so .isOrdered () && consumerName != null ) {
346350 ConsumerInfo serverInfo = lookupConsumerInfo (settledStream , consumerName );
347351
348352 if (serverInfo != null ) { // the consumer for that durable already exists
349353 serverCC = serverInfo .getConsumerConfiguration ();
350354
351355 // check to see if the user sent a different version than the server has
352- // because modifications are not allowed during create subscription
356+ // because modifications are not allowed during " create" subscription
353357 ConsumerConfigurationComparer userCCC = new ConsumerConfigurationComparer (userCC );
354358 List <String > changes = userCCC .getChanges (serverCC );
355359 if (!changes .isEmpty ()) {
@@ -367,15 +371,15 @@ else if (nullOrEmpty(serverCC.getDeliverSubject())) {
367371 }
368372
369373 if (serverCC .getDeliverGroup () == null ) {
370- // lookedUp was null, means existing consumer is not a queue consumer
374+ // lookedUp was null, means the existing consumer is not a queue consumer
371375 if (settledDeliverGroup == null ) {
372- // ok fine, no queue requested and the existing consumer is also not a queue consumer
376+ // ok fine, no queue was requested, and the existing consumer is also not a queue consumer
373377 // we must check if the consumer is in use though
374378 if (serverInfo .isPushBound ()) {
375379 throw JsSubConsumerAlreadyBound .instance ();
376380 }
377381 }
378- else { // else they requested a queue but this durable was not configured as queue
382+ else { // else they requested a queue, but this durable was not configured as a queue.
379383 throw JsSubExistingConsumerNotQueue .instance ();
380384 }
381385 }
@@ -399,7 +403,7 @@ else if (!listsAreEquivalent(settledFilterSubjects, serverCC.getFilterSubjects()
399403 throw JsSubSubjectDoesNotMatchFilter .instance ();
400404 }
401405
402- inboxDeliver = serverCC .getDeliverSubject (); // use the deliver subject as the inbox. It may be null, that's ok, we'll fix that later
406+ inboxDeliver = serverCC .getDeliverSubject (); // Use the deliverSubject as the inbox. It may be null, that's ok, we'll fix that later
403407 }
404408 else if (so .isBind ()) {
405409 throw JsSubConsumerNotFoundRequiredInBind .instance ();
@@ -418,7 +422,7 @@ else if (inboxDeliver == null) {
418422 settledInboxDeliver = inboxDeliver ;
419423 }
420424
421- // 6. If consumer does not exist, create and settle on the config. Name will have to wait
425+ // 6. If the consumer does not exist, create and settle on the config. Name will have to wait
422426 // If the consumer exists, I know what the settled info is
423427 final ConsumerConfiguration settledCC ;
424428 final String settledConsumerName ;
@@ -429,20 +433,32 @@ else if (inboxDeliver == null) {
429433 else {
430434 ConsumerConfiguration .Builder ccBuilder = ConsumerConfiguration .builder (userCC );
431435
432- // Pull mode doesn't maintain a deliver subject . It's actually an error if we send it.
436+ // Pull mode doesn't maintain a deliverSubject . It's actually an error if we send it.
433437 if (!isPullMode ) {
434438 ccBuilder .deliverSubject (settledInboxDeliver );
435439 }
436440
437- // userCC.filterSubjects might have originally been empty
441+ // userCC.filterSubjects might have originally been empty,
438442 // but there might have been a userSubscribeSubject,
439443 // so this makes sure it's resolved either way
440444 ccBuilder .filterSubjects (settledFilterSubjects );
441445
442446 ccBuilder .deliverGroup (settledDeliverGroup );
443447
448+ if (so .isOrdered ()) {
449+ // we have to handle the fact that ordered consumers must always have a unique name
450+ // if the user supplied a name, well call generateConsumerName with the original name as a prefix
451+ if (consumerName != null ) {
452+ consumerName = generateConsumerName (userCC .getName ());
453+ ccBuilder .name (consumerName );
454+ }
455+ settledConsumerName = consumerName ;
456+ }
457+ else {
458+ settledConsumerName = null ; // the server will give us a name if the user's was null
459+ }
460+
444461 settledCC = ccBuilder .build ();
445- settledConsumerName = null ; // the server will give us a name
446462 }
447463
448464 // 7. create the subscription. lambda needs final or effectively final vars
@@ -481,9 +497,13 @@ else if (inboxDeliver == null) {
481497 }
482498
483499 // 8. The consumer might need to be created, do it here
484- if (settledConsumerName == null ) {
500+ if (settledConsumerName == null || so .isOrdered ()) {
501+ // the _create method sets the consumer name for us
485502 _createConsumerUnsubscribeOnException (settledStream , settledCC , sub );
486503 }
504+ else {
505+ sub .setConsumerName (settledConsumerName );
506+ }
487507
488508 return sub ;
489509 }
0 commit comments