private void doTest(int numOfUsers, int numOfPuts, boolean[] postAuthzAllowed,
      final AuthzCredentialGenerator authzGenerator, final Boolean keepAlive) throws Exception {
    CredentialGenerator credentialGenerator = authzGenerator.getCredentialGenerator();
    Properties extraAuthProps = credentialGenerator.getSystemProperties();
    Properties javaProps = credentialGenerator.getJavaProperties();
    Properties extraAuthzProps = authzGenerator.getSystemProperties();
    String authenticator = credentialGenerator.getAuthenticator();
    String accessor = authzGenerator.getAuthorizationCallback();
    String authInit = credentialGenerator.getAuthInit();
    TestAuthzCredentialGenerator tgen = new TestAuthzCredentialGenerator(authzGenerator);

    Properties serverProps =
        buildProperties(authenticator, accessor, true, extraAuthProps, extraAuthzProps);

    Properties opCredentials;
    credentialGenerator = tgen.getCredentialGenerator();
    final Properties javaProps2 =
        credentialGenerator != null ? credentialGenerator.getJavaProperties() : null;

    int[] indices = new int[numOfPuts];
    for (int index = 0; index < numOfPuts; ++index) {
      indices[index] = index;
    }

    Random random = new Random();
    Properties[] authProps = new Properties[numOfUsers];
    String durableClientId = "multiuser_durable_client_1";

    Properties client2Credentials = null;

    for (int i = 0; i < numOfUsers; i++) {
      int rand = random.nextInt(100) + 1;
      if (postAuthzAllowed[i]) {
        opCredentials = tgen.getAllowedCredentials(
            new OperationCode[] {OperationCode.EXECUTE_CQ, OperationCode.GET}, // For callback, GET
                                                                               // should be allowed
            new String[] {regionName}, indices, rand);

      } else {
        opCredentials = tgen.getDisallowedCredentials(new OperationCode[] {OperationCode.GET}, // For
                                                                                               // callback,
                                                                                               // GET
                                                                                               // should
                                                                                               // be
                                                                                               // disallowed
            new String[] {regionName}, indices, rand);
      }

      authProps[i] =
          concatProperties(new Properties[] {opCredentials, extraAuthProps, extraAuthzProps});

      if (client2Credentials == null) {
        client2Credentials = tgen.getAllowedCredentials(new OperationCode[] {OperationCode.PUT},
            new String[] {regionName}, indices, rand);
      }
    }

    // Get ports for the servers
    int[] randomAvailableTCPPorts = AvailablePortHelper.getRandomAvailableTCPPorts(2);
    int port1 = randomAvailableTCPPorts[0];
    int port2 = randomAvailableTCPPorts[1];

    // Close down any running servers
    server1.invoke(() -> closeCache());
    server2.invoke(() -> closeCache());

    server1.invoke(() -> createServerCache(serverProps, javaProps, port1));
    client1.invoke(() -> createClientCache(javaProps2, authInit, authProps,
        new int[] {port1, port2}, numOfUsers, durableClientId, postAuthzAllowed));

    client1.invoke(() -> createCQ(numOfUsers, true));
    client1.invoke(() -> executeCQ(numOfUsers, new boolean[] {false, false}, numOfPuts,
        new String[numOfUsers]));
    client1.invoke(this::readyForEvents);

    if (keepAlive == null) {
      client1.invoke(() -> closeCache());
    } else {
      client1.invoke(() -> closeCache(keepAlive));
    }

    server1.invoke(() -> doPuts(numOfPuts, true/* put last key */));

    client1.invoke(() -> createClientCache(javaProps2, authInit, authProps,
        new int[] {port1, port2}, numOfUsers, durableClientId, postAuthzAllowed));
    client1.invoke(() -> createCQ(numOfUsers, true));
    client1.invoke(() -> executeCQ(numOfUsers, new boolean[] {false, false}, numOfPuts,
        new String[numOfUsers]));
    client1.invoke(this::readyForEvents);

    if (!postAuthzAllowed[0] || keepAlive == null || !keepAlive) {
      // Don't wait as no user is authorized to receive cq events.
      Thread.sleep(1000); // TODO: use Awaitility
    } else {
      client1.invoke(() -> waitForLastKey(0, true));
    }

    int numOfCreates = keepAlive == null ? 0 : (keepAlive ? numOfPuts + 1/* last key */ : 0);
    client1.invoke(() -> checkCQListeners(numOfUsers, postAuthzAllowed, numOfCreates, 0));
    client1.invoke(() -> proxyCacheClose(new int[] {0, 1}, keepAlive));
    client1.invoke(() -> createProxyCache(new int[] {0, 1}, authProps));
    client1.invoke(() -> createCQ(numOfUsers, true));
    client1.invoke(() -> executeCQ(numOfUsers, new boolean[] {false, false}, numOfPuts,
        new String[numOfUsers]));

    server1.invoke(() -> doPuts(numOfPuts, true/* put last key */));

    if (!postAuthzAllowed[0] || keepAlive == null || !keepAlive) {
      // Don't wait as no user is authorized to receive cq events.
      Thread.sleep(1000); // TODO: use Awaitility
    } else {
      client1.invoke(() -> waitForLastKey(0, false));
    }

    int numOfUpdates = numOfPuts + 1;
    client1.invoke(() -> checkCQListeners(numOfUsers, postAuthzAllowed, 0, numOfUpdates));
  }
WARNING: "final Boolean keepAlive" is treated as a ternary value: null, true, false
geode-cq/src/distributedTest/java/org/apache/geode/security/MultiUserDurableCQAuthzDUnitTest.java