com.googlecode.icegem.cacheutils.comparator.CompareTool.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.icegem.cacheutils.comparator.CompareTool.java

Source

/*
 * Icegem, Extensions library for VMWare vFabric GemFire
 * 
 * Copyright (c) 2010-2011, Grid Dynamics Consulting Services Inc. or third-party  
 * contributors as indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  
 * 
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License v3, as published by the Free Software Foundation.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * You should have received a copy of the GNU Lesser General Public License v3
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package com.googlecode.icegem.cacheutils.comparator;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;

import com.googlecode.icegem.cacheutils.Tool;
import com.googlecode.icegem.cacheutils.common.FileService;
import com.googlecode.icegem.cacheutils.common.Stopwatch;
import com.googlecode.icegem.cacheutils.common.Utils;
import com.googlecode.icegem.cacheutils.comparator.model.ComparisonResult;
import com.googlecode.icegem.cacheutils.comparator.model.Node;
import com.googlecode.icegem.cacheutils.comparator.task.GetNodesTask;
import com.googlecode.icegem.cacheutils.comparator.task.GetNodesTaskArguments;
import com.googlecode.icegem.utils.JavaProcessLauncher;

public class CompareTool extends Tool {

    public static final String PARTITION = "partition";
    public static final String REPLICATE = "replicate";

    private static final String PACKAGES_OPTION = "packages";
    private static final String TARGET_SERVER_OPTION = "target-server";
    private static final String TARGET_REGION_OPTION = "target-region";
    private static final String TARGET_LOCATORS_OPTION = "target-locators";
    private static final String SOURCE_SERVER_OPTION = "source-server";
    private static final String SOURCE_REGION_OPTION = "source-region";
    private static final String SOURCE_LOCATORS_OPTION = "source-locators";
    private static final String LOAD_FACTOR_OPTION = "load-factor";
    private static final String HELP_OPTION = "help";

    private static final String SOURCE_INPUT_FILENAME = "source-input-object";
    private static final String TARGET_INPUT_FILENAME = "target-input-object";
    private static final String SOURCE_OUTPUT_FILENAME = "source-output-object";
    private static final String TARGET_OUTPUT_FILENAME = "target-output-object";

    private static final int DEFAULT_LOAD_FACTOR = 50;

    private String sourceRegionName;
    private String sourceServer = null;
    private String sourceLocators = null;
    private String targetRegionName;
    private String targetServer = null;
    private String targetLocators = null;
    private List<String> packages;
    private int loadFactor = DEFAULT_LOAD_FACTOR;
    private Stopwatch stopwatch = new Stopwatch();

    private JavaProcessLauncher javaProcessLauncher = new JavaProcessLauncher(false, false, false);

    public void execute(String[] args, boolean debugEnabled, boolean quiet) {
        try {

            parseCommandLineArguments(args);

            compare(new long[] { 0 }, 64);

        } catch (Throwable t) {
            Utils.exitWithFailure("Unexpected throwable", t);
        }
    }

    private void compare(long[] ids, int shift) throws IOException, InterruptedException, ClassNotFoundException {

        stopwatch.start();
        long[] childIds = getDifferentChildrenIds(ids, shift);
        stopwatch.stop();

        System.out.println("level #" + ((80 - shift) / 16) + ", number of different entries detected: "
                + childIds.length + " in " + stopwatch.getDuration() + "ms");

        if (childIds.length > 0) {
            if (shift > 32) {
                compare(childIds, shift - 16);
            } else {
                stopwatch.start();
                ComparisonResult result = calculateResult(childIds);
                stopwatch.stop();

                System.out.println("level #4, number of different entries detected: [extra: "
                        + result.getExtra().size() + ", missed: " + result.getMissed().size() + ", different: "
                        + result.getDifferent().size() + "] in " + stopwatch.getDuration() + "ms");

                System.out.println(result);
                Utils.exitWithFailure();
            }
        } else {
            System.out.println("equal");
            Utils.exitWithSuccess();
        }

    }

    private Process runProcess(String mode, String address, String regionName, String inputFilename,
            String outputFilename, long[] ids, int shift) throws IOException, InterruptedException {

        Process process;

        GetNodesTaskArguments arguments = new GetNodesTaskArguments();
        arguments.setAddress(address);
        arguments.setRegionName(regionName);
        arguments.setFilename(outputFilename);
        arguments.setLoadFactor(loadFactor);
        arguments.setPackages(packages);
        arguments.setIds(ids);
        arguments.setShift(shift);
        arguments.setMode(mode);

        FileService.writeObject(inputFilename, arguments);

        process = javaProcessLauncher.runWithoutConfirmation("", GetNodesTask.class, null,
                new String[] { inputFilename });

        return process;
    }

    @SuppressWarnings("unchecked")
    private ComparisonResult calculateResult(long[] ids)
            throws IOException, InterruptedException, ClassNotFoundException {
        ComparisonResult result = new ComparisonResult();

        Process sourceProcess;
        Process targetProcess;

        if (isPartitioned()) {

            sourceProcess = runProcess(PARTITION, sourceLocators, sourceRegionName, SOURCE_INPUT_FILENAME,
                    SOURCE_OUTPUT_FILENAME, ids, 16);

            targetProcess = runProcess(PARTITION, targetLocators, targetRegionName, TARGET_INPUT_FILENAME,
                    TARGET_OUTPUT_FILENAME, ids, 16);

        } else {

            sourceProcess = runProcess(REPLICATE, sourceServer, sourceRegionName, SOURCE_INPUT_FILENAME,
                    SOURCE_OUTPUT_FILENAME, ids, 16);

            targetProcess = runProcess(REPLICATE, targetServer, targetRegionName, TARGET_INPUT_FILENAME,
                    TARGET_OUTPUT_FILENAME, ids, 16);

        }

        sourceProcess.waitFor();
        targetProcess.waitFor();

        Set<Node> sourceNodesSet = (Set<Node>) FileService.readObject(SOURCE_OUTPUT_FILENAME);
        Set<Node> targetNodesSet = (Set<Node>) FileService.readObject(TARGET_OUTPUT_FILENAME);

        Map<Object, Long> sourceDataToHashcodeMap = new HashMap<Object, Long>();
        Map<Object, Long> targetDataToHashcodeMap = new HashMap<Object, Long>();

        for (Node node : sourceNodesSet) {
            for (Node child : node.getChildren()) {
                sourceDataToHashcodeMap.put(child.getData(), child.getHashcode());
            }
        }

        for (Node node : targetNodesSet) {
            for (Node child : node.getChildren()) {
                targetDataToHashcodeMap.put(child.getData(), child.getHashcode());
            }
        }

        Set<Object> dataSet = new HashSet<Object>();
        dataSet.addAll(sourceDataToHashcodeMap.keySet());
        dataSet.addAll(targetDataToHashcodeMap.keySet());

        for (Object data : dataSet) {
            Long sourceHashcode = sourceDataToHashcodeMap.get(data);
            Long targetHashcode = targetDataToHashcodeMap.get(data);

            if (sourceHashcode == null) {
                result.addMissed(data);
            } else if (targetHashcode == null) {
                result.addExtra(data);
            } else if (sourceHashcode.longValue() != targetHashcode.longValue()) {
                result.addDifferent(data);
            } else {
                throw new IllegalStateException(
                        "The entry with key = " + data + " neither extra, missed nor different");
            }
        }

        return result;
    }

    @SuppressWarnings("unchecked")
    private long[] getDifferentChildrenIds(long[] ids, int shift)
            throws IOException, InterruptedException, ClassNotFoundException {
        Process sourceProcess;
        Process targetProcess;

        if (isPartitioned()) {

            sourceProcess = runProcess(PARTITION, sourceLocators, sourceRegionName, SOURCE_INPUT_FILENAME,
                    SOURCE_OUTPUT_FILENAME, ids, shift);

            targetProcess = runProcess(PARTITION, targetLocators, targetRegionName, TARGET_INPUT_FILENAME,
                    TARGET_OUTPUT_FILENAME, ids, shift);

        } else {

            sourceProcess = runProcess(REPLICATE, sourceServer, sourceRegionName, SOURCE_INPUT_FILENAME,
                    SOURCE_OUTPUT_FILENAME, ids, shift);

            targetProcess = runProcess(REPLICATE, targetServer, targetRegionName, TARGET_INPUT_FILENAME,
                    TARGET_OUTPUT_FILENAME, ids, shift);

        }

        sourceProcess.waitFor();
        targetProcess.waitFor();

        Set<Node> sourceNodesSet = (Set<Node>) FileService.readObject(SOURCE_OUTPUT_FILENAME);
        Set<Node> targetNodesSet = (Set<Node>) FileService.readObject(TARGET_OUTPUT_FILENAME);

        Map<Long, Node> sourceIdToNodeMap = new HashMap<Long, Node>();
        Map<Long, Node> targetIdToNodeMap = new HashMap<Long, Node>();

        for (Node node : sourceNodesSet) {
            sourceIdToNodeMap.put(node.getId(), node);
        }

        for (Node node : targetNodesSet) {
            targetIdToNodeMap.put(node.getId(), node);
        }

        Set<Long> idsSet = new HashSet<Long>();
        idsSet.addAll(sourceIdToNodeMap.keySet());
        idsSet.addAll(targetIdToNodeMap.keySet());

        Set<Long> differentIdsSet = new HashSet<Long>();
        for (Long id : idsSet) {
            Node sourceNode = sourceIdToNodeMap.get(id);
            Node targetNode = targetIdToNodeMap.get(id);

            if (sourceNode == null) {
                differentIdsSet.addAll(targetNode.getChildrenIdsSet());
            } else if (targetNode == null) {
                differentIdsSet.addAll(sourceNode.getChildrenIdsSet());
            } else {
                long sourceHashcode = sourceNode.getHashcode();
                long targetHashcode = targetNode.getHashcode();

                if (sourceHashcode != targetHashcode) {
                    Node[] sourceChildren = sourceNode.getChildren();
                    Node[] targetChildren = targetNode.getChildren();

                    Map<Long, Node> childrenSourceIdToNodeMap = new HashMap<Long, Node>();
                    Map<Long, Node> childrenTargetIdToNodeMap = new HashMap<Long, Node>();

                    for (Node node : sourceChildren) {
                        childrenSourceIdToNodeMap.put(node.getId(), node);
                    }

                    for (Node node : targetChildren) {
                        childrenTargetIdToNodeMap.put(node.getId(), node);
                    }

                    Set<Long> childrenIdsSet = new HashSet<Long>();
                    childrenIdsSet.addAll(childrenSourceIdToNodeMap.keySet());
                    childrenIdsSet.addAll(childrenTargetIdToNodeMap.keySet());

                    for (Long childId : childrenIdsSet) {
                        Node childSourceNode = childrenSourceIdToNodeMap.get(childId);
                        Node childTargetNode = childrenTargetIdToNodeMap.get(childId);

                        if (childSourceNode == null) {
                            differentIdsSet.add(childTargetNode.getId());
                        } else if (childTargetNode == null) {
                            differentIdsSet.add(childSourceNode.getId());
                        } else if (childSourceNode.getHashcode() != childTargetNode.getHashcode()) {
                            differentIdsSet.add(childSourceNode.getId());
                        }
                    }
                }
            }
        }

        long[] childIds = new long[differentIdsSet.size()];
        int i = 0;
        for (Long id : differentIdsSet) {
            childIds[i++] = id;
        }

        return childIds;
    }

    private boolean isPartitioned() {
        return ((sourceLocators != null) && (targetLocators != null));
    }

    protected void parseCommandLineArguments(String[] commandLineArguments) {

        Options options = constructGnuOptions();

        if (commandLineArguments.length < 1) {
            printHelp(options);
        }

        CommandLineParser parser = new GnuParser();
        try {
            CommandLine line = parser.parse(options, commandLineArguments);

            if (line.hasOption(HELP_OPTION)) {
                printHelp(options);
            }

            if (line.hasOption(SOURCE_REGION_OPTION)) {
                sourceRegionName = line.getOptionValue(SOURCE_REGION_OPTION);
            } else {
                printHelp(options);
            }

            if (line.hasOption(TARGET_REGION_OPTION)) {
                targetRegionName = line.getOptionValue(TARGET_REGION_OPTION);
            } else {
                printHelp(options);
            }

            if (line.hasOption(SOURCE_SERVER_OPTION) && line.hasOption(TARGET_SERVER_OPTION)) {

                sourceServer = line.getOptionValue(SOURCE_SERVER_OPTION);
                targetServer = line.getOptionValue(TARGET_SERVER_OPTION);

            } else if (line.hasOption(SOURCE_LOCATORS_OPTION) && line.hasOption(TARGET_LOCATORS_OPTION)) {

                sourceLocators = line.getOptionValue(SOURCE_LOCATORS_OPTION);
                targetLocators = line.getOptionValue(TARGET_LOCATORS_OPTION);

            } else {
                printHelp(options);
            }

            if (line.hasOption(PACKAGES_OPTION)) {
                packages = Arrays.asList(line.getOptionValue(PACKAGES_OPTION).split(","));
            }

            if (line.hasOption(LOAD_FACTOR_OPTION)) {
                String loadFactorString = line.getOptionValue(LOAD_FACTOR_OPTION);

                try {
                    loadFactor = Integer.parseInt(loadFactorString);
                } catch (Throwable t) {
                    Utils.exitWithFailure(
                            "Cannot parse the " + LOAD_FACTOR_OPTION + " option, value = " + loadFactorString);
                }

                if ((loadFactor < 1) || (loadFactor > 100)) {
                    Utils.exitWithFailure("The " + LOAD_FACTOR_OPTION + " option, value = " + loadFactorString
                            + " is out of range [1, 100]");
                }
            }

        } catch (Throwable t) {
            Utils.exitWithFailure("Throwable caught during the command-line arguments parsing", t);
        }
    }

    protected void printHelp(Options options) {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("compare <--" + SOURCE_REGION_OPTION + "> <--" + TARGET_REGION_OPTION + "> < --"
                + SOURCE_SERVER_OPTION + " --" + TARGET_SERVER_OPTION + " | --" + SOURCE_LOCATORS_OPTION + " --"
                + TARGET_LOCATORS_OPTION + " > [--" + PACKAGES_OPTION + "] [--" + LOAD_FACTOR_OPTION + "]",
                options);

        Utils.exitWithFailure();
    }

    protected Options constructGnuOptions() {
        final Options gnuOptions = new Options();
        gnuOptions.addOption("sr", SOURCE_REGION_OPTION, true, "The name of source region")
                .addOption("ss", SOURCE_SERVER_OPTION, true, "Source server in format host[port]")
                .addOption("sl", SOURCE_LOCATORS_OPTION, true,
                        "Source cluster locators in format host1[port1],host2[port2]")
                .addOption("tr", TARGET_REGION_OPTION, true, "The name of target region")
                .addOption("ts", TARGET_SERVER_OPTION, true, "Target server in format host[port]")
                .addOption("tl", TARGET_LOCATORS_OPTION, true,
                        "Target cluster locators in format host1[port1],host2[port2]")
                .addOption("lf", LOAD_FACTOR_OPTION, true,
                        "The percent of time the comparator tries to use on each server. "
                                + "The possible values range [1, 100]. Default value is " + DEFAULT_LOAD_FACTOR
                                + ".")
                .addOption("c", PACKAGES_OPTION, true,
                        "Enumerate packages to scan for @AutoSerializable model classes. Delimiter is a comma sign.")
                .addOption("h", HELP_OPTION, false, "Print usage information");
        return gnuOptions;
    }

}