  BrokerAclManifest.java : 141 줄
# 자바독이 적어 둔 두 검사
  BrokerAclManifest.java:12  * <p>Written down so the grant can be reviewed and diffed rather than discovered from a broker
  BrokerAclManifest.java:13  * dump. The manifest is what the platform checks itself against at startup: a runtime that holds
  BrokerAclManifest.java:14  * more than it declares is a finding, because the extra permission is the one nobody reasoned
  BrokerAclManifest.java:15  * about.
  BrokerAclManifest.java:16  *
  BrokerAclManifest.java:17  * <p>Destructive permissions are named separately from ordinary ones. {@code DELETE_TOPIC} and
  BrokerAclManifest.java:18  * {@code PURGE} are not "write, but more"; they destroy data an application can never restore, so
  BrokerAclManifest.java:19  * an application runtime declaring one is rejected outright.
  BrokerAclManifest.java:20  *

# 그 두 성질에 대응하는 코드가 이 타입 안에 있다
  BrokerAclManifest.java:94   public List<Grant> destructiveGrants() {
  BrokerAclManifest.java:95     return grants.stream().filter(grant -> grant.operation().isDestructive()).toList();
  BrokerAclManifest.java:98   /**
  BrokerAclManifest.java:99    * Refuses a manifest that gives an application runtime destructive power.
  BrokerAclManifest.java:100    *
  BrokerAclManifest.java:101    * @throws MessagingConfigurationException when a destructive grant is declared
  BrokerAclManifest.java:102    */
  BrokerAclManifest.java:103   public void requireApplicationRuntime() {
  BrokerAclManifest.java:104     List<Grant> destructive = destructiveGrants();
  BrokerAclManifest.java:105     if (!destructive.isEmpty()) {
  BrokerAclManifest.java:106       throw new MessagingConfigurationException(
  BrokerAclManifest.java:107           "APPLICATION_HOLDS_DESTRUCTIVE_GRANT",
  BrokerAclManifest.java:108           "principal %s declares %s, which an application runtime must never hold"
  BrokerAclManifest.java:109               .formatted(principal, destructive.stream().map(Grant::operation).toList()));
  BrokerAclManifest.java:110     }
  BrokerAclManifest.java:111   }
  BrokerAclManifest.java:113   /**
  BrokerAclManifest.java:114    * Reports the permissions the runtime holds but never declared.
  BrokerAclManifest.java:115    *
  BrokerAclManifest.java:116    * <p>Excess is the finding, not the shortfall: a missing grant fails loudly on first use, while
  BrokerAclManifest.java:117    * an undeclared extra one sits unnoticed until it is abused.
  BrokerAclManifest.java:118    *
  BrokerAclManifest.java:119    * @param observed the grants the broker actually reports for this principal
  BrokerAclManifest.java:120    * @return the observed grants that this manifest does not declare
  BrokerAclManifest.java:121    */
  BrokerAclManifest.java:122   public Set<Grant> undeclared(Set<Grant> observed) {
  BrokerAclManifest.java:123     Objects.requireNonNull(observed, "observed must not be null");
  BrokerAclManifest.java:124     Set<Grant> extra = new LinkedHashSet<>(observed);
  BrokerAclManifest.java:125     extra.removeAll(Set.copyOf(grants));
  BrokerAclManifest.java:126     return Set.copyOf(extra);
  BrokerAclManifest.java:127   }
  BrokerAclManifest.java:128 
  BrokerAclManifest.java:130    * Reports the declared permissions the broker does not actually grant.
  BrokerAclManifest.java:131    *
  BrokerAclManifest.java:132    * @param observed the grants the broker actually reports for this principal
  BrokerAclManifest.java:133    * @return the declared grants that are missing
  BrokerAclManifest.java:134    */
  BrokerAclManifest.java:135   public Set<Grant> missing(Set<Grant> observed) {
  BrokerAclManifest.java:136     Objects.requireNonNull(observed, "observed must not be null");
  BrokerAclManifest.java:137     Set<Grant> absent = new LinkedHashSet<>(grants);
  BrokerAclManifest.java:138     absent.removeAll(observed);
  BrokerAclManifest.java:139     return Set.copyOf(absent);
  BrokerAclManifest.java:140   }

# 간결 생성자는 그 검사를 하지 않는다
  BrokerAclManifest.java:81   public BrokerAclManifest {
  BrokerAclManifest.java:82     Objects.requireNonNull(grants, "grants must not be null");
  BrokerAclManifest.java:83     if (principal == null || principal.isBlank()) {
  BrokerAclManifest.java:84       throw new IllegalArgumentException("principal must not be blank");
  BrokerAclManifest.java:85     }
  BrokerAclManifest.java:86     grants = List.copyOf(grants);
  BrokerAclManifest.java:87   }

# 그 세 메서드를 부르는 자리 전부
  main · BrokerAclManifest.java:94   public List<Grant> destructiveGrants() {
  main · BrokerAclManifest.java:103   public void requireApplicationRuntime() {
  main · BrokerAclManifest.java:104     List<Grant> destructive = destructiveGrants();
  test · CredentialRuntimeRegistryTest.java:181     assertThatThrownBy(manifest::requireApplicationRuntime)
  test · CredentialRuntimeRegistryTest.java:200     assertThat(manifest.undeclared(Set.of(declared, extra)))
  그중 BrokerAclManifest.java 밖의 main 코드 :
    0 건
# 이 타입을 만들거나 받는 프로덕션 코드
  main 에서 0 건
  자기 파일 밖에서 이 이름이 나오는 파일 수 : 1
    messaging/messaging-security/src/test/java/dev/caskeleton/messaging/security/CredentialRuntimeRegistryTest.java
  그 시험이 단언하는 것 :
  CredentialRuntimeRegistryTest.java:167   void anApplicationRuntimeMayNotHoldADestructiveGrant() {
  CredentialRuntimeRegistryTest.java:181     assertThatThrownBy(manifest::requireApplicationRuntime)
  CredentialRuntimeRegistryTest.java:187   void aGrantTheBrokerHoldsButNobodyDeclaredIsTheFinding() {
  CredentialRuntimeRegistryTest.java:200     assertThat(manifest.undeclared(Set.of(declared, extra)))

# 파괴적 권한이 값으로 표시되는 방식
  BrokerAclManifest.java:49   public enum Operation {
  BrokerAclManifest.java:51     WRITE(false),
  BrokerAclManifest.java:53     READ(false),
  BrokerAclManifest.java:55     DESCRIBE(false),
  BrokerAclManifest.java:57     CREATE(false),
  BrokerAclManifest.java:59     ALTER(true),
  BrokerAclManifest.java:61     DELETE(true),
  BrokerAclManifest.java:63     PURGE(true);

# 기동 시점 검증으로 감싸인 프로파일은 셋이다
  KafkaMessagingAutoConfiguration.java:59     return new StartupProfileValidation<>(
  KafkaMessagingAutoConfiguration.java:60         () ->
  KafkaMessagingAutoConfiguration.java:61             java.util.stream.Stream.concat(
  KafkaMessagingAutoConfiguration.java:62                     compiled.kafkaBrokers().stream(), profiles.orderedStream())
  MessagingCoreAutoConfiguration.java:221     return new StartupProfileValidation<>(
  MessagingCoreAutoConfiguration.java:222         () ->
  MessagingCoreAutoConfiguration.java:223             java.util.stream.Stream.concat(compiled.security().stream(), profiles.orderedStream())
  MessagingCoreAutoConfiguration.java:224                 .toList(),
  RabbitMessagingAutoConfiguration.java:55     return new StartupProfileValidation<>(
  RabbitMessagingAutoConfiguration.java:56         () ->
  RabbitMessagingAutoConfiguration.java:57             java.util.stream.Stream.concat(
  RabbitMessagingAutoConfiguration.java:58                     compiled.rabbitBrokers().stream(), profiles.orderedStream())

# 파괴 연산 자체를 막는 다른 장치
  main · DestructiveMessagingAdmin.java:21 public interface DestructiveMessagingAdmin {
  main · MessagingAdminService.java:21  * <p>Destructive operations live in {@link DestructiveMessagingAdmin}, a separate interface that an
  main · MessagingAdminAutoConfiguration.java:22  * <p>{@link dev.caskeleton.messaging.admin.runtime.DestructiveMessagingAdmin} is deliberately
  그 인터페이스를 구현하는 main 클래스 : 0 건

# 브로커에 권한을 질의하는 코드
  describeAcls           0 파일
  DescribeAclsResult     0 파일
  AclBinding             0 파일
  createAcls             0 파일
  AclOperation           0 파일
  Authorizer             0 파일
  listPermissions        0 파일
  checkPermissions       0 파일
  그 0 들의 의미를 정하는 사실 — Kafka AdminClient 자체의 소스 세트별 분포 :
    test : 5 파일
