edu.umass.cs.gnsclient.client.integrationtests.ServerConnectTest.java Source code

Java tutorial

Introduction

Here is the source code for edu.umass.cs.gnsclient.client.integrationtests.ServerConnectTest.java

Source

/* Copyright (c) 2015 University of Massachusetts
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 * Initial developer(s): Westy, arun */
package edu.umass.cs.gnsclient.client.integrationtests;

import edu.umass.cs.gigapaxos.PaxosConfig;
import edu.umass.cs.gnscommon.CommandType;
import edu.umass.cs.gnscommon.GNSCommandProtocol;
import edu.umass.cs.gnscommon.GNSProtocol;
import edu.umass.cs.gnscommon.AclAccessType;
import edu.umass.cs.gnscommon.ResponseCode;
import edu.umass.cs.contextservice.client.ContextServiceClient;
import edu.umass.cs.gnsclient.client.GNSClient;
import edu.umass.cs.gnsclient.client.GNSClientCommands;
import edu.umass.cs.gnsclient.client.util.BasicGuidEntry;
import edu.umass.cs.gnsclient.client.util.GuidEntry;
import edu.umass.cs.gnsclient.client.util.GuidUtils;
import edu.umass.cs.gnsclient.client.util.JSONUtils;
import edu.umass.cs.gnsclient.client.util.SHA1HashFunction;
import edu.umass.cs.gnscommon.utils.RandomString;
import edu.umass.cs.gnscommon.exceptions.client.ClientException;
import edu.umass.cs.gnscommon.exceptions.client.FieldNotFoundException;
import edu.umass.cs.gnsclient.jsonassert.JSONAssert;
import edu.umass.cs.gnsclient.jsonassert.JSONCompareMode;
import edu.umass.cs.gnscommon.utils.Base64;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import static org.hamcrest.Matchers.*;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.AfterClass;
import static org.junit.Assert.*;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runners.MethodSorters;
import edu.umass.cs.gnscommon.utils.ThreadUtils;
import edu.umass.cs.gnsserver.database.MongoRecords;
import edu.umass.cs.gnsserver.main.GNSConfig;
import edu.umass.cs.reconfiguration.ReconfigurationConfig;
import edu.umass.cs.reconfiguration.reconfigurationutils.DefaultNodeConfig;
import edu.umass.cs.utils.Config;
import edu.umass.cs.utils.DefaultTest;
import edu.umass.cs.utils.Util;

import java.awt.geom.Point2D;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Set;

import org.apache.commons.lang3.RandomUtils;
import org.json.JSONException;

/**
 * Functionality test for core elements in the client using the
 * GNSClientCommands.
 *
 */
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ServerConnectTest extends DefaultTest {

    private static final String DEFAULT_ACCOUNT_ALIAS = "support@gns.name";

    private static String accountAlias = DEFAULT_ACCOUNT_ALIAS; // REPLACE //
    // ALIAS
    private static final String PASSWORD = "password";
    private static GNSClientCommands client = null;
    private static GuidEntry masterGuid;
    private static GuidEntry subGuidEntry;
    private static GuidEntry westyEntry;
    private static GuidEntry samEntry;
    private static GuidEntry barneyEntry;
    private static GuidEntry mygroupEntry;
    private static GuidEntry guidToDeleteEntry;

    /**
     *
     * @param alias
     */
    public static void setAccountAlias(String alias) {
        accountAlias = alias;
    }

    private static final String HOME = System.getProperty("user.home");
    private static final String GNS_DIR = "GNS";
    private static final String GNS_HOME = HOME + "/" + GNS_DIR + "/";

    private static final String getPath(String filename) {
        if (new File(filename).exists()) {
            return filename;
        }
        if (new File(GNS_HOME + filename).exists()) {
            return GNS_HOME + filename;
        } else {
            Util.suicide("Can not find server startup script: " + filename);
        }
        return null;
    }

    private static enum DefaultProps {
        SERVER_COMMAND("server.command", USE_GP_SCRIPTS ? GP_SERVER : SCRIPTS_SERVER, true), GIGAPAXOS_CONFIG(
                "gigapaxosConfig", "conf/gnsserver.3local.unittest.properties",
                true), KEYSTORE("javax.net.ssl.keyStore", "conf/keyStore.jks", true), KEYSTORE_PASSWORD(
                        "javax.net.ssl.keyStorePassword", "qwerty"), TRUSTSTORE("javax.net.ssl.trustStore",
                                "conf/trustStore.jks",
                                true), TRUSTSTORE_PASSWORD("javax.net.ssl.trustStorePassword",
                                        "qwerty"), LOGGING_PROPERTIES("java.util.logging.config.file",
                                                "conf/logging.gns.properties"), START_SERVER("startServer",
                                                        "true"),;

        final String key;
        final String value;
        final boolean isFile;

        DefaultProps(String key, String value, boolean isFile) {
            this.key = key;
            this.value = value;
            this.isFile = isFile;
        }

        DefaultProps(String key, String value) {
            this(key, value, false);
        }
    }

    private static final void setProperties() {
        for (DefaultProps prop : DefaultProps.values()) {
            if (System.getProperty(prop.key) == null) {
                System.setProperty(prop.key, prop.isFile ? getPath(prop.value) : prop.value);
            }
        }
    }

    // this static block must be above GP_OPTIONS
    static {
        setProperties();
    }

    private static final String SCRIPTS_SERVER = "scripts/3nodeslocal/reset_and_restart.sh";
    private static final String SCRIPTS_OPTIONS = " "; // can't give any options

    private static final String GP_SERVER = "bin/gpServer.sh";
    private static final String GP_OPTIONS = getGigaPaxosOptions();
    private static final boolean USE_GP_SCRIPTS = true;
    private static String options = USE_GP_SCRIPTS ? GP_OPTIONS : SCRIPTS_OPTIONS;

    private static final String getGigaPaxosOptions() {
        String gpOptions = "";
        for (DefaultProps prop : DefaultProps.values()) {
            gpOptions += " -D" + prop.key + "=" + System.getProperty(prop.key);
        }
        return gpOptions + " -ea";
    }

    private static final boolean useGPScript() {
        return System.getProperty(DefaultProps.SERVER_COMMAND.key).contains(GP_SERVER);
    }

    private static final void failWithStackTrace(String message, Exception... e) {
        if (e != null && e.length > 0) {
            e[0].printStackTrace();
        }
        org.junit.Assert.fail(message);
    }

    /* We need this below even though a majority being up suffices and account GUID 
      * creation success (with retransmission) auto-detects whether a majority is up,
      * it can happen that one server is not yet ready, which sometimes leads to 
      * some tests like lookupPrimaryGuid failing because the request goes to a 
      * still-not-up server and simply times out.
      * 
      * The clean way to obviate this wait is to build fault-tolerance into the 
      * tests, i.e., every request should be retransmitted until success
      * assuming that any server can fail at any time.
     */
    private static long WAIT_TILL_ALL_SERVERS_READY = 5000;

    /**
     *
     * @throws Exception
     */
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        // Run the server.
        String waitString = System.getProperty("waitTillAllServersReady");
        if (waitString != null) {
            WAIT_TILL_ALL_SERVERS_READY = Integer.parseInt(waitString);
        }

        if (System.getProperty("startServer") != null && System.getProperty("startServer").equals("true")) {

            // clear explicitly if gigapaxos
            if (useGPScript()) {
                RunServer.command("kill -s TERM `ps -ef | grep GNS.jar | grep -v grep | "
                        + "grep -v ServerConnectTest  | grep -v \"context\" | awk '{print $2}'`", ".");
                System.out.println(System.getProperty(DefaultProps.SERVER_COMMAND.key) + " " + getGigaPaxosOptions()
                        + " forceclear all");

                RunServer.command(System.getProperty(DefaultProps.SERVER_COMMAND.key) + " " + getGigaPaxosOptions()
                        + " forceclear all", ".");

                /* We need to do this to limit the number of files used by mongo.
                     * Otherwise failed runs quickly lead to more failed runs because
                     * index files created in previous runs are not removed.
                 */
                dropAllDatabases();

                options = getGigaPaxosOptions() + " restart all";
            } else {
                options = SCRIPTS_OPTIONS;
            }

            System.out.println(System.getProperty(DefaultProps.SERVER_COMMAND.key) + " " + options);
            ArrayList<String> output = RunServer
                    .command(System.getProperty(DefaultProps.SERVER_COMMAND.key) + " " + options, ".");
            if (output != null) {
                for (String line : output) {
                    System.out.println(line);
                }
            } else {
                failWithStackTrace("Server command failure: ; aborting all tests.");
            }
        }

        System.out.println("Starting client");

        client = new GNSClientCommands();
        // Make all the reads be coordinated
        client.setForceCoordinatedReads(true);
        // arun: connectivity check embedded in GNSClient constructor
        boolean connected = client instanceof GNSClient;
        if (connected) {
            System.out.println("Client created and connected to server.");
        }
        //
        int tries = 5;
        boolean accountCreated = false;

        long t = System.currentTimeMillis();
        Thread.sleep(WAIT_TILL_ALL_SERVERS_READY);

        do {
            try {
                System.out.println("Creating account guid: " + (tries - 1) + " attempt remaining.");
                masterGuid = GuidUtils.lookupOrCreateAccountGuid(client, accountAlias, PASSWORD, true);
                accountCreated = true;
            } catch (Exception e) {
                e.printStackTrace();
                ThreadUtils.sleep((5 - tries) * 5000);
            }
        } while (!accountCreated && --tries > 0);
        if (accountCreated == false) {
            failWithStackTrace("Failure setting up account guid; aborting all tests.");
        }

        while (System.currentTimeMillis() - t < WAIT_TILL_ALL_SERVERS_READY) {
            Thread.sleep(WAIT_TILL_ALL_SERVERS_READY - (System.currentTimeMillis() - t));
        }
    }

    /**
     *
     * @throws Exception
     */
    @AfterClass
    public static void tearDownAfterClass() throws Exception {

        /* arun: need a more efficient, parallel implementation of removal
           * of sub-guids, otherwise this times out.
         */
        //client.accountGuidRemove(masterGuid);
        if (System.getProperty("startServer") != null && System.getProperty("startServer").equals("true")) {
            if (useGPScript()) {
                String command = System.getProperty(DefaultProps.SERVER_COMMAND.key) + " " + getGigaPaxosOptions()
                        + " stop all";
                System.out.print(
                        "Stopping all servers in " + System.getProperty(DefaultProps.GIGAPAXOS_CONFIG.key) + "...");

                try {
                    RunServer.command(command, ".");
                } catch (Exception e) {
                    System.out.println(" failed to stop all servers with [" + command + "]");
                    e.printStackTrace();
                    throw e;
                }
                System.out.println(" stopped all servers.");
            } else {
                ArrayList<String> output = RunServer.command(
                        new File(System.getProperty(DefaultProps.SERVER_COMMAND.key)).getParent() + "/shutdown.sh",
                        ".");
                if (output != null) {
                    for (String line : output) {
                        System.out.println(line);
                    }
                } else {
                    System.out.println("SHUTDOWN SERVER COMMAND FAILED!");
                }
            }
        }

        dropAllDatabases();

        if (client != null) {
            client.close();
        }
        System.out.println("\nPrinting reverse-engineered return types:");
        for (CommandType type : GNSClientCommands.reverseEngineer.keySet()) {
            System.out.println(type + " returns " + GNSClientCommands.reverseEngineer.get(type) + "; e.g., "
                    + Util.truncate(GNSClientCommands.returnValueExample.get(type), 64, 64));
        }
    }

    private static void dropAllDatabases() {
        for (String server : new DefaultNodeConfig<String>(PaxosConfig.getActives(),
                ReconfigurationConfig.getReconfigurators()).getNodeIDs()) {
            MongoRecords.dropNodeDatabase(server);
        }
    }

    /**
     * A test that pulls together a bunch of end--to-end tests.
     */
    public ServerConnectTest() {

    }

    private static final int RETRANSMISSION_INTERVAL = 100;
    // arun: this should be zero
    private static final int COORDINATION_WAIT = 00;

    /**
     * arun: Coordinated operations generally need some settling time before
     * they can be tested at "any" replica. That is, read-your-writes
     * consistency is not ensured if a read following a write happens to go to a
     * different replica. Thus, we either need to wait for a long enough
     * duration and/or retransmit upon failure.
     *
     * I have inserted waitSettle() haphazardly at places. These tests need to
     * be systematically fixed by retrying if the expected answer is not found.
     * Simply using the async client to resend the request should suffice as
     * ReconfigurableAppClientAsync is designed to automatically pick "good"
     * active replicas, i.e., it will forget crashed ones for some time; clear
     * its cache, re-query, and pick randomly upon an active replica error; and
     * pick the replica closest by distance and load otherwise.
     */
    private static void waitSettle() {
        try {
            if (COORDINATION_WAIT > 0) {
                Thread.sleep(COORDINATION_WAIT);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Creates a guid.
     */
    @Test
    public void test_010_CreateEntity() {
        String alias = "testGUID" + RandomString.randomString(12);
        GuidEntry guidEntry = null;
        try {
            guidEntry = client.guidCreate(masterGuid, alias);
        } catch (Exception e) {
            failWithStackTrace("Exception while creating guid: ", e);
        }
        assertNotNull(guidEntry);
        assertEquals(alias, guidEntry.getEntityName());
    }

    /**
     * Removes a guid.
     */
    @Test
    public void test_020_RemoveGuid() {
        String testGuidName = "testGUID" + RandomString.randomString(12);
        GuidEntry testGuid = null;
        try {
            testGuid = client.guidCreate(masterGuid, testGuidName);
        } catch (Exception e) {
            failWithStackTrace("Exception while creating testGuid: ", e);
        }
        waitSettle();
        try {
            client.guidRemove(masterGuid, testGuid.getGuid());
        } catch (Exception e) {
            failWithStackTrace("Exception while removing testGuid: ", e);
        }
        waitSettle();
        try {
            client.lookupGuidRecord(testGuid.getGuid());
            failWithStackTrace("Lookup testGuid should have throw an exception.");
        } catch (ClientException e) {

        } catch (IOException e) {
            failWithStackTrace("Exception while doing Lookup testGuid: ", e);
        }
    }

    /**
     * Stops the client.
     */
    @Test
    public void test_9999_Stop() {
        try {
            client.close();
        } catch (Exception e) {
            failWithStackTrace("Exception during stop: ", e);
        }
    }

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(ServerConnectTest.class);
        System.out.println("\n\n-----------Completed all " + result.getRunCount() + " tests"
                + (result.getFailureCount() > 0 ? "; the following " + result.getFailureCount() + " tests failed:"
                        : " successfully")
                + "--------------");
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString() + "\n");
        }
    }
}