package org.jgroups.protocols;

import org.jgroups.Global;
import org.jgroups.JChannel;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.protocols.pbcast.NAKACK2;
import org.jgroups.protocols.pbcast.STABLE;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.net.InetAddress;

/**
 * Tests graceful leaving of the coordinator and second-in-line in a 10 node cluster with ASYM_ENCRYPT configured
 * @author Bela Ban
 * @since  4.0.12
 */
@Test(groups=Global.FUNCTIONAL,singleThreaded=true)
public class ASYM_ENCRYPT_LeaveTest {
    protected static final String      KEYSTORE="my-keystore.jks";
    protected static final String      KEYSTORE_PWD="password";
    protected static final int         NUM=10;
    protected static final InetAddress LOOPBACK;

    static {
        try {
            LOOPBACK=Util.getLocalhost();
        }
        catch(Exception e) {
            throw new RuntimeException(e);
        }
    }




    protected JChannel[] channels=new JChannel[NUM];

    @BeforeMethod
    protected void setup() throws Exception {
        for(int i=0; i < channels.length; i++) {
            channels[i]=create(String.valueOf(i+1)).connect(ASYM_ENCRYPT_LeaveTest.class.getSimpleName());
            Util.sleep(i == 0? 2000 : 1000);
        }
        Util.waitUntilAllChannelsHaveSameView(10000, 1000, channels);
    }

    @AfterMethod
    protected void destroy() {
        for(int i=channels.length-2; i >= 0; i--)
            channels[i].close();
    }


    public void testGracefulLeave() throws Exception {
        for(int j=0; j < channels.length; j++) {
            System.out.printf("%-4s: view is %s\n", channels[j].getAddress(), channels[j].getView());
        }
        System.out.println("\n");

        JChannel[] remaining_channels=new JChannel[channels.length-2];
        System.arraycopy(channels, 2, remaining_channels, 0, channels.length-2);

        Util.close(channels[0], channels[1]);
        Util.waitUntilAllChannelsHaveSameView(30000, 1000, remaining_channels);
        for(int i=0; i < remaining_channels.length; i++)
            System.out.printf("%-4s: view is %s\n", remaining_channels[i].getAddress(), remaining_channels[i].getView());
    }




    /** Creates a channel with a config similar to ./conf/asym-ssl.xml */
    protected static JChannel create(String name) throws Exception {
        return new JChannel(
          new TCP().setBindAddress(LOOPBACK), // .setBindPort(BIND_PORT),
          new MPING(), // new TCPPING().portRange(10).initialHosts(Collections.singleton(new InetSocketAddress(LOOPBACK, BIND_PORT))),
          new MERGE3().setMinInterval(2000).setMaxInterval(5000),
          new FD_SOCK(),
          new FD_ALL(),
          new VERIFY_SUSPECT(),
          new SSL_KEY_EXCHANGE().setKeystoreName(KEYSTORE).setKeystorePassword(KEYSTORE_PWD).setPortRange(10),
          new ASYM_ENCRYPT().setUseExternalKeyExchange(true).setChangeKeyOnLeave(true)
            .symKeylength(128).symAlgorithm("AES").asymKeylength(512).asymAlgorithm("RSA"),
          new NAKACK2().setUseMcastXmit(false),
          new UNICAST3(),
          new STABLE(),
          new GMS().joinTimeout(1000))
          .name(name);
    }

}

@@ -393,16 +393,48 @@
 Address;
+%0Aimport java.util.stream.Stream;
 %0A%0A/**%0A *
@@ -855,16 +855,70 @@
 NUM=10;%0A
+    protected static final int         NUM_LEAVERS=2;%0A
     prot
@@ -1618,17 +1618,27 @@
 .length-
-2
+NUM_LEAVERS
 ; i %3E= 0
@@ -2003,17 +2003,27 @@
 .length-
-2
+NUM_LEAVERS
 %5D;%0A     
@@ -2052,17 +2052,27 @@
 annels, 
-2
+NUM_LEAVERS
 , remain
@@ -2104,17 +2104,27 @@
 .length-
-2
+NUM_LEAVERS
 );%0A%0A    
@@ -2131,43 +2131,66 @@
     
-Util.close(channels%5B0%5D, channels%5B1%5D
+Stream.of(channels).limit(NUM_LEAVERS).forEach(Util::close
 );%0A 

Make number of leavers in ASYM_ENCRYPT_LeaveTest configurable