Package org.lilyproject.tools.tester

Source Code of org.lilyproject.tools.tester.Tester$WorkerThread

/*
* Copyright 2012 NGDATA nv
*
* 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.
*/
package org.lilyproject.tools.tester;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.io.IOUtils;
import org.apache.zookeeper.KeeperException;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.node.ObjectNode;
import org.joda.time.DateTime;
import org.lilyproject.cli.OptionUtil;
import org.lilyproject.client.NoServersException;
import org.lilyproject.repository.api.FieldType;
import org.lilyproject.repository.api.QName;
import org.lilyproject.repository.api.RecordType;
import org.lilyproject.repository.api.RepositoryException;
import org.lilyproject.testclientfw.BaseRepositoryTestTool;
import org.lilyproject.testclientfw.Util;
import org.lilyproject.tools.import_.cli.DefaultImportListener;
import org.lilyproject.tools.import_.cli.ImportConflictException;
import org.lilyproject.tools.import_.cli.ImportException;
import org.lilyproject.tools.import_.cli.JsonImport;
import org.lilyproject.tools.import_.json.JsonFormatException;
import org.lilyproject.tools.import_.json.QNameConverter;
import org.lilyproject.util.io.Closer;
import org.lilyproject.util.json.JsonFormat;
import org.lilyproject.util.json.JsonUtil;

public class Tester extends BaseRepositoryTestTool {

    private Option configFileOption;
    private Option dumpSampleConfigOption;
    private RecordType recordType;
    private int maximumRunTime;
    private int maximumFailures;
    private String failuresFileName;
    private PrintStream errorStream;
    private long startTime;
    private int failureCount = 0;
    private Option iterationsOption;
    private int nrOfIterations;
    private TestActionFactory testActionFactory = new TestActionFactory();
    private List<TestAction> workersTestActions[] = null;
    private List<JsonNode> recordSpacesConfig = new ArrayList<JsonNode>();
    private List<RecordSpaces> workersRecordSpaces = null;

    private Map<QName, TestRecordType> recordTypes = new HashMap<QName, TestRecordType>();
    private Map<QName, TestFieldType> fieldTypes = new HashMap<QName, TestFieldType>();
    private JsonImport jsonImport;

    public static void main(String[] args) throws Exception {
        new Tester().start(args);
    }

    @Override
    protected String getCmdName() {
        return "lily-tester";
    }

    @Override
    protected String getVersion() {
        return org.lilyproject.util.Version.readVersion("org.lilyproject", "lily-tester");
    }

    @Override
    @SuppressWarnings("static-access")
    public List<Option> getOptions() {
        List<Option> options = super.getOptions();

        configFileOption = OptionBuilder
                .withArgName("config.json")
                .hasArg()
                .withDescription("Test tool configuration file")
                .withLongOpt("config")
                .create("c");
        options.add(configFileOption);

        dumpSampleConfigOption = OptionBuilder
                .withDescription("Dumps a sample configuration to standard out")
                .withLongOpt("dump-sample-config")
                .create("d");
        options.add(dumpSampleConfigOption);

        iterationsOption = OptionBuilder
                .withArgName("iterations")
                .hasArg()
                .withDescription("Number of times to run the scenario")
                .withLongOpt("iterations")
                .create("i");
        options.add(iterationsOption);

        return options;
    }

    @Override
    public int run(CommandLine cmd) throws Exception {
        int result = super.run(cmd);
        if (result != 0) {
            return result;
        }

        if (cmd.hasOption(dumpSampleConfigOption.getOpt())) {
            return dumpSampleConfig();
        }

        if (!cmd.hasOption(configFileOption.getOpt())) {
            printHelp();
            return 1;
        }

        setupLily();

        setupMetrics();

        String configFileName = cmd.getOptionValue(configFileOption.getOpt());

        workersRecordSpaces = new ArrayList<RecordSpaces>(workers);
        workersTestActions = new ArrayList[workers];
        for (int i = 0; i < workers; i++) {
            workersTestActions[i] = new ArrayList<TestAction>();
        }

        InputStream is = new FileInputStream(configFileName);
        loadConfig(is);
        is.close();

        try {
            System.out.println("Running tests...");
            System.out.println("Tail the output files if you wonder what is happening.");
            nrOfIterations = OptionUtil.getIntOption(cmd, iterationsOption, 1000);
            test();
        } finally {
            closeStreams();
        }

        finishMetrics();

        System.out.println("Test done.");
        return 0;
    }

    private void loadConfig(InputStream is)
            throws IOException, JsonFormatException, RepositoryException, ImportConflictException,
            ImportException, InterruptedException, SecurityException, IllegalArgumentException, NoSuchMethodException,
            InstantiationException, IllegalAccessException, InvocationTargetException {

        jsonImport = new JsonImport(table, repository, new DefaultImportListener());
        JsonParser jp = JsonFormat.JSON_FACTORY_NON_STD.createJsonParser(is);

        JsonToken current;
        current = jp.nextToken();

        if (current != JsonToken.START_OBJECT) {
            System.out.println("Error: expected object node as root of the input. Giving up.");
            return;
        }

        while (jp.nextToken() != JsonToken.END_OBJECT) {
            String fieldName = jp.getCurrentName();
            current = jp.nextToken(); // move from field name to field value
            if (fieldName.equals("namespaces")) {
                if (current == JsonToken.START_OBJECT) {
                    jsonImport.readNamespaces((ObjectNode) jp.readValueAsTree());
                } else {
                    System.out.println("Error: namespaces property should be an object. Skipping.");
                    jp.skipChildren();
                }
            } else if (fieldName.equals("failuresFile")) {
                if (current == JsonToken.VALUE_STRING) {
                    openStreams(jp.getText());
                }
            } else if (fieldName.equals("fieldTypes")) {
                if (current == JsonToken.START_ARRAY) {
                    while (jp.nextToken() != JsonToken.END_ARRAY) {
                        importFieldType(jp.readValueAsTree());
                    }
                } else {
                    System.out.println("Error: fieldTypes property should be an array. Skipping.");
                    jp.skipChildren();
                }
            } else if (fieldName.equals("recordTypes")) {
                if (current == JsonToken.START_ARRAY) {
                    while (jp.nextToken() != JsonToken.END_ARRAY) {
                        importRecordType(jp.readValueAsTree());
                    }
                } else {
                    System.out.println("Error: recordTypes property should be an array. Skipping.");
                    jp.skipChildren();
                }
            } else if (fieldName.equals("recordSpaces")) {
                if (current == JsonToken.START_ARRAY) {
                    while (jp.nextToken() != JsonToken.END_ARRAY) {
                        recordSpacesConfig.add(jp.readValueAsTree());
                    }
                    for (int i = 0; i < workers; i++) {
                        workersRecordSpaces.add(new RecordSpaces(recordSpacesConfig));
                    }
                } else {
                    System.out.println("Error: recordSpaces property should be an array. Skipping.");
                    jp.skipChildren();
                }
            } else if (fieldName.equals("scenario")) {
                if (current == JsonToken.START_ARRAY) {
                    while (jp.nextToken() != JsonToken.END_ARRAY) {
                        JsonNode actionNode = jp.readValueAsTree();
                        prepareAction(actionNode);
                    }
                } else {
                    System.out.println("Error: recordSpaces property should be an array. Skipping.");
                    jp.skipChildren();
                }
            } else if (fieldName.equals("stopConditions")) {
                if (current == JsonToken.START_OBJECT) {
                    readStopConditions((ObjectNode) jp.readValueAsTree());
                } else {
                    System.out.println("Error: stopConditions property should be an object. Skipping.");
                    jp.skipChildren();
                }
            }
        }
    }

    private void importFieldType(JsonNode fieldTypeNode)
            throws RepositoryException, ImportConflictException, ImportException, JsonFormatException,
            InterruptedException {
        int times = 0;
        JsonNode timesNode = fieldTypeNode.get("times");
        if (timesNode != null) {
            times = timesNode.getIntValue();
        }
        JsonNode propertiesNode = fieldTypeNode.get("properties");
        if (times == 0) {
            FieldType importFieldType = jsonImport.importFieldType(fieldTypeNode);
            fieldTypes.put(importFieldType.getName(), new TestFieldType(importFieldType, table, repository, propertiesNode));
        } else {
            List<FieldType> importFieldTypes = jsonImport.importFieldTypes(fieldTypeNode, times);
            for (FieldType importFieldType : importFieldTypes) {
                fieldTypes.put(importFieldType.getName(),
                        new TestFieldType(importFieldType, table, repository, propertiesNode));
            }
        }
    }

    private void importRecordType(JsonNode recordTypeNode)
            throws JsonFormatException, RepositoryException, ImportException, InterruptedException {
        String recordTypeName = JsonUtil.getString(recordTypeNode, "name");
        QName recordTypeQName = QNameConverter.fromJson(recordTypeName, jsonImport.getNamespaces());
        recordType = repository.getTypeManager().newRecordType(recordTypeQName);
        TestRecordType testRecordType = new TestRecordType();
        // Fields
        for (JsonNode fieldNode : recordTypeNode.get("fields")) {
            String fieldName = JsonUtil.getString(fieldNode, "name");
            int times = 0;
            JsonNode timesNode = fieldNode.get("times");
            if (timesNode != null) {
                times = timesNode.getIntValue();
            }
            if (times == 0) {
                TestFieldType fieldType = fieldTypes
                        .get(QNameConverter.fromJson(fieldName, jsonImport.getNamespaces()));
                recordType.addFieldTypeEntry(fieldType.getFieldType().getId(), false);
                testRecordType.addFieldType(fieldType);
            } else {
                for (int i = 0; i < times; i++) {
                    TestFieldType fieldType = fieldTypes.get(QNameConverter.fromJson(fieldName + i,
                            jsonImport.getNamespaces()));
                    recordType.addFieldTypeEntry(fieldType.getFieldType().getId(), false);
                    testRecordType.addFieldType(fieldType);
                }
            }
        }
        testRecordType.setRecordType(typeManager.createOrUpdateRecordType(recordType));
        recordTypes.put(recordType.getName(), testRecordType);
    }

    private int dumpSampleConfig() throws IOException {
        InputStream is = getClass().getClassLoader().getResourceAsStream("org/lilyproject/tools/tester/config.json");
        try {
            IOUtils.copy(is, System.out);
        } finally {
            Closer.close(is);
        }
        return 0;
    }

    private void prepareAction(JsonNode actionNode)
            throws IOException, SecurityException, IllegalArgumentException, NoSuchMethodException,
            InstantiationException, IllegalAccessException, InvocationTargetException {
        final RoundRobinPrefixGenerator roundRobinPrefixGenerator = actionNode.get("recordIdPrefixNbrOfChars") != null ?
                new RoundRobinPrefixGenerator(actionNode.get("recordIdPrefixNbrOfChars").getIntValue()) : null;

        for (int i = 0; i < workers; i++) {
            new RecordSpaces(recordSpacesConfig);
            TestActionContext testActionContext = new TestActionContext(recordTypes, fieldTypes,
                    jsonImport.getNamespaces(), workersRecordSpaces.get(i), table, repository, metrics, errorStream,
                    roundRobinPrefixGenerator);
            TestAction testAction = testActionFactory.getTestAction(actionNode, testActionContext);
            workersTestActions[i].add(testAction);
        }
    }

    private void readStopConditions(JsonNode stopConditions) {
        maximumRunTime = JsonUtil.getInt(stopConditions, "maximumRunTime");
        maximumFailures = JsonUtil.getInt(stopConditions, "maximumFailures");
    }

    private void createSchema(JsonNode configNode) throws IOException, RepositoryException, ImportConflictException,
            ImportException, JsonFormatException, NoServersException, InterruptedException, KeeperException {

        JsonImport jsonImport = new JsonImport(table, repository, new DefaultImportListener());

        // Namespaces
        ObjectNode namespacesNode = JsonUtil.getObject(configNode, "namespaces", null);
        if (namespacesNode != null) {
            jsonImport.readNamespaces(namespacesNode);
        }

        // Fields
        JsonNode fieldTypesNode = configNode.get("fieldTypes");
        if (fieldTypesNode != null && fieldTypesNode.isArray()) {
            for (JsonNode fieldTypeNode : fieldTypesNode) {
                FieldType importFieldType = jsonImport.importFieldType(fieldTypeNode);
                JsonNode propertiesNode = fieldTypeNode.get("properties");

                fieldTypes.put(importFieldType.getName(),
                        new TestFieldType(importFieldType, table, repository, propertiesNode));
            }
        }

        // Record type
        JsonNode recordTypesNode = configNode.get("recordTypes");
        if (recordTypesNode != null && recordTypesNode.isArray()) {
            for (JsonNode recordTypeNode : recordTypesNode) {
                String recordTypeName = JsonUtil.getString(recordTypeNode, "name");
                QName recordTypeQName = QNameConverter.fromJson(recordTypeName, jsonImport.getNamespaces());
                recordType = repository.getTypeManager().newRecordType(recordTypeQName);
                TestRecordType testRecordType = new TestRecordType();
                // Fields
                for (JsonNode fieldNode : recordTypeNode.get("fields")) {
                    String fieldName = JsonUtil.getString(fieldNode, "name");
                    TestFieldType fieldType = fieldTypes.get(fieldName);
                    recordType.addFieldTypeEntry(fieldType.getFieldType().getId(), false);
                    testRecordType.addFieldType(fieldType);
                }
                testRecordType.setRecordType(typeManager.createOrUpdateRecordType(recordType));
                recordTypes.put(recordType.getName(), testRecordType);
            }
        }
    }

    private void openStreams(String failuresFileName) throws IOException {
        errorStream = new PrintStream(Util.getOutputFileRollOldOne(failuresFileName));
        errorStream.println(new DateTime() + " Opening file");
    }

    private void closeStreams() {
        errorStream.println(new DateTime() + " Closing file");
        Closer.close(errorStream);
    }

    private void test() throws InterruptedException {
        startTime = System.currentTimeMillis();
        HashSet<Thread> threads = new HashSet<Thread>(workers);
        for (int i = 0; i < workers; i++) {
            threads.add(new WorkerThread(workersTestActions[i]));
        }

        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
    }

    private class WorkerThread extends Thread {
        private final List<TestAction> testActions;

        WorkerThread(List<TestAction> testActions) {
            this.testActions = testActions;
        }

        @Override
        public void run() {
            for (int j = 0; j < nrOfIterations; j++) {
                for (TestAction testAction : testActions) {
                    incFailureCount(testAction.run());
                }
                if (checkStopConditions()) {
                    return;
                }
            }
        }

    }

    private synchronized void incFailureCount(int amount) {
        failureCount = failureCount + amount;
    }

    private synchronized int getFailureCount() {
        return failureCount;
    }

    private boolean checkStopConditions() {
        if (getFailureCount() >= maximumFailures) {
            System.out.println("Stopping because maximum number of failures is reached: " + maximumFailures);
            return true;
        }

        int ran = (int) Math.floor((System.currentTimeMillis() - startTime) / 1000 / 60);
        if (ran >= maximumRunTime) {
            System.out.println("Stopping because maximum running time is reached: " + maximumRunTime + " minutes.");
            return true;
        }

        return false;
    }
}
TOP

Related Classes of org.lilyproject.tools.tester.Tester$WorkerThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.