|
22 | 22 | import java.net.InetSocketAddress; |
23 | 23 | import java.nio.file.Files; |
24 | 24 | import java.time.Duration; |
| 25 | +import java.util.AbstractMap.SimpleEntry; |
25 | 26 | import java.util.ArrayList; |
26 | 27 | import java.util.Arrays; |
27 | 28 | import java.util.Collection; |
|
33 | 34 | import java.util.Properties; |
34 | 35 | import java.util.Set; |
35 | 36 | import java.util.UUID; |
| 37 | +import java.util.concurrent.ExecutionException; |
36 | 38 | import java.util.concurrent.TimeUnit; |
| 39 | +import java.util.concurrent.TimeoutException; |
37 | 40 | import java.util.concurrent.atomic.AtomicBoolean; |
| 41 | +import java.util.function.Function; |
38 | 42 | import java.util.stream.Collectors; |
39 | 43 |
|
40 | 44 | import org.apache.commons.logging.LogFactory; |
@@ -417,22 +421,100 @@ private void createTopics(AdminClient admin, List<NewTopic> newTopics) { |
417 | 421 | } |
418 | 422 | } |
419 | 423 |
|
| 424 | + /** |
| 425 | + * Add topics to the existing broker(s) using the configured number of partitions. |
| 426 | + * The broker(s) must be running. |
| 427 | + * @param topicsToAdd the topics. |
| 428 | + * @return the results; null values indicate success. |
| 429 | + * @since 2.5.4 |
| 430 | + */ |
| 431 | + public Map<String, Exception> addTopicsWithResults(String... topicsToAdd) { |
| 432 | + Assert.notNull(this.zookeeper, "Broker must be started before this method can be called"); |
| 433 | + HashSet<String> set = new HashSet<>(Arrays.asList(topicsToAdd)); |
| 434 | + this.topics.addAll(set); |
| 435 | + return createKafkaTopicsWithResults(set); |
| 436 | + } |
| 437 | + |
| 438 | + /** |
| 439 | + * Add topics to the existing broker(s) and returning a map of results. |
| 440 | + * The broker(s) must be running. |
| 441 | + * @param topicsToAdd the topics. |
| 442 | + * @return the results; null values indicate success. |
| 443 | + * @since 2.5.4 |
| 444 | + */ |
| 445 | + public Map<String, Exception> addTopicsWithResults(NewTopic... topicsToAdd) { |
| 446 | + Assert.notNull(this.zookeeper, "Broker must be started before this method can be called"); |
| 447 | + for (NewTopic topic : topicsToAdd) { |
| 448 | + Assert.isTrue(this.topics.add(topic.name()), () -> "topic already exists: " + topic); |
| 449 | + Assert.isTrue(topic.replicationFactor() <= this.count |
| 450 | + && (topic.replicasAssignments() == null |
| 451 | + || topic.replicasAssignments().size() <= this.count), |
| 452 | + () -> "Embedded kafka does not support the requested replication factor: " + topic); |
| 453 | + } |
| 454 | + |
| 455 | + return doWithAdminFunction(admin -> createTopicsWithResults(admin, Arrays.asList(topicsToAdd))); |
| 456 | + } |
| 457 | + |
| 458 | + /** |
| 459 | + * Create topics in the existing broker(s) using the configured number of partitions |
| 460 | + * and returning a map of results. |
| 461 | + * @param topicsToCreate the topics. |
| 462 | + * @return the results; null values indicate success. |
| 463 | + * @since 2.5.4 |
| 464 | + */ |
| 465 | + private Map<String, Exception> createKafkaTopicsWithResults(Set<String> topicsToCreate) { |
| 466 | + return doWithAdminFunction(admin -> { |
| 467 | + return createTopicsWithResults(admin, |
| 468 | + topicsToCreate.stream() |
| 469 | + .map(t -> new NewTopic(t, this.partitionsPerTopic, (short) this.count)) |
| 470 | + .collect(Collectors.toList())); |
| 471 | + }); |
| 472 | + } |
| 473 | + |
| 474 | + private Map<String, Exception> createTopicsWithResults(AdminClient admin, List<NewTopic> newTopics) { |
| 475 | + CreateTopicsResult createTopics = admin.createTopics(newTopics); |
| 476 | + Map<String, Exception> results = new HashMap<>(); |
| 477 | + createTopics.values() |
| 478 | + .entrySet() |
| 479 | + .stream() |
| 480 | + .map(entry -> { |
| 481 | + Exception result; |
| 482 | + try { |
| 483 | + entry.getValue().get(this.adminTimeout.getSeconds(), TimeUnit.SECONDS); |
| 484 | + result = null; |
| 485 | + } |
| 486 | + catch (InterruptedException | ExecutionException | TimeoutException e) { |
| 487 | + result = e; |
| 488 | + } |
| 489 | + return new SimpleEntry<>(entry.getKey(), result); |
| 490 | + }) |
| 491 | + .forEach(entry -> results.put(entry.getKey(), entry.getValue())); |
| 492 | + return results; |
| 493 | + } |
| 494 | + |
420 | 495 | /** |
421 | 496 | * Create an {@link AdminClient}; invoke the callback and reliably close the admin. |
422 | 497 | * @param callback the callback. |
423 | 498 | */ |
424 | 499 | public void doWithAdmin(java.util.function.Consumer<AdminClient> callback) { |
425 | 500 | Map<String, Object> adminConfigs = new HashMap<>(); |
426 | 501 | adminConfigs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, getBrokersAsString()); |
427 | | - AdminClient admin = null; |
428 | | - try { |
429 | | - admin = AdminClient.create(adminConfigs); |
| 502 | + try (AdminClient admin = AdminClient.create(adminConfigs)) { |
430 | 503 | callback.accept(admin); |
431 | 504 | } |
432 | | - finally { |
433 | | - if (admin != null) { |
434 | | - admin.close(this.adminTimeout); |
435 | | - } |
| 505 | + } |
| 506 | + |
| 507 | + /** |
| 508 | + * Create an {@link AdminClient}; invoke the callback and reliably close the admin. |
| 509 | + * @param callback the callback. |
| 510 | + * @return a map of results. |
| 511 | + * @since 2.5.4 |
| 512 | + */ |
| 513 | + public <T> T doWithAdminFunction(Function<AdminClient, T> callback) { |
| 514 | + Map<String, Object> adminConfigs = new HashMap<>(); |
| 515 | + adminConfigs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, getBrokersAsString()); |
| 516 | + try (AdminClient admin = AdminClient.create(adminConfigs)) { |
| 517 | + return callback.apply(admin); |
436 | 518 | } |
437 | 519 | } |
438 | 520 |
|
|
0 commit comments